Змініть клас із множника на числовий для багатьох стовпців у фреймі даних


82

Який найшвидший / найкращий спосіб змінити велику кількість стовпців на числові з коефіцієнта?

Я використав наступний код, але, схоже, змінив порядок моїх даних.

> head(stats[,1:2])
  rk                 team
1  1 Washington Capitals*
2  2     San Jose Sharks*
3  3  Chicago Blackhawks*
4  4     Phoenix Coyotes*
5  5   New Jersey Devils*
6  6   Vancouver Canucks*

for(i in c(1,3:ncol(stats))) {
    stats[,i] <- as.numeric(stats[,i])
}

> head(stats[,1:2])
  rk                 team
1  2 Washington Capitals*
2 13     San Jose Sharks*
3 24  Chicago Blackhawks*
4 26     Phoenix Coyotes*
5 27   New Jersey Devils*
6 28   Vancouver Canucks*

Який найкращий спосіб, за винятком іменування кожного стовпця, як у:

df$colname <- as.numeric(ds$colname)

4
Чи немає загального рішення ?. Деякі із запропонованих тут рішень працюють лише з факторами, інші працюють завжди, крім факторів, і так далі ...
skan

Відповіді:


56

На додаток до відповіді Рамната, поведінка, яку ви відчуваєте, полягає в тому, що завдяки as.numeric(x)поверненню внутрішнього числового подання коефіцієнта xна рівні R. Якщо ви хочете зберегти числа, що є рівнями множника (а не їх внутрішнє подання), вам потрібно перетворити на символ за допомогою as.character()першого, як на прикладі Рамната.

Ваш forцикл є настільки ж розумним, як applyдзвінок, і може бути трохи зручнішим для читання щодо того, у чому полягає ціль коду. Просто змініть цей рядок:

stats[,i] <- as.numeric(stats[,i])

читати

stats[,i] <- as.numeric(as.character(stats[,i]))

Це FAQ 7.10 у R FAQ.

HTH


2
Не потрібно ніякого циклу. Просто використовуйте індекси та unlist (). Редагувати: Я додав відповідь, що ілюструє це.
Joris Meys,

Цей підхід працює лише в цьому конкретному випадку. Я намагався використовувати його для перетворення стовпців у, factorі це не спрацювало. sapplyабо mutate_ifздаються більш загальноприйнятними рішеннями.
Лев

@Leo Догляд за розширенням, тому що я точно знаю, що це працює. Це точно таке ж рішення, як і нижче, Рамната, за винятком того, що він використовує applyдля запуску циклу, а OP використовує forцикл явно. Насправді, всі голосуючі відповіді використовують as.numeric(as.character())ідіому.
Гевін Сімпсон,

Так, це працює, щоб змінити клас кількох стовпців на numeric, але він не працює в зворотному порядку (щоб змінити клас декількох стовпців на factor). Якщо ви використовуєте необхідні індекси, unlist()і при застосуванні до стовпців із символами він видає кожен окремий символ, що змушує його більше не працювати при поверненні результату назад stats[,i]. Перевірте відповідь тут: stackoverflow.com/questions/45713473/…
Лев

@ Leo, звичайно, це не працює навпаки! Що у вас на землі створило враження, що так буде? Він ніколи не розроблявся, і ОП ніколи про це не просив. Важко відповісти на запитання, які не задаються. Якщо ви хочете перетворити на коефіцієнт використання as.factor()замість as.numeric(as.character())тут, і це буде працювати чудово. Звичайно, якщо у вас поєднання стовпців, вам доведеться обирати iвибірково, але це також тривіально.
Gavin Simpson

73

Ви повинні бути обережними, змінюючи коефіцієнти на числові. Ось рядок коду, який міняв би набір стовпців з фактора на числовий. Тут я припускаю, що стовпці, які потрібно змінити на числові, становлять 1, 3, 4 та 5 відповідно. Ви можете змінити це відповідно

cols = c(1, 3, 4, 5);    
df[,cols] = apply(df[,cols], 2, function(x) as.numeric(as.character(x)));

3
Це не буде працювати належним чином. Приклад: x<-as.factor(1:3); df<-data.frame(a=x,y=runif(3),b=x,c=x,d=x). Я не думаю, що applyце підходить для такого роду проблем.
Марек

1
застосовувати відмінно працює в цих ситуаціях. помилка в моєму коді використовувала margin = 1, а не 2, оскільки функцію потрібно застосовувати по стовпцях. я відповідним чином відредагував свою відповідь.
Рамнат

Зараз це працює. Але я думаю, що можна було б обійтися і без цього apply. Перевірте мою редакцію.
Марек

2
... або Джоріс відповідь з unlist. І as.characterперетворення у вашому рішенні не потрібне, тому що applyперетворювачі df[,cols]на characterце apply(df[,cols], 2, function(x) as.numeric(x))теж будуть працювати.
Марек

@ Ramnath , чому ти використовуєш =? Чому ні <-?
kittygirl

40

Це можна зробити в один рядок, немає необхідності в циклі, будь то for-loop або apply. Замість цього використовуйте unlist ():

# testdata
Df <- data.frame(
  x = as.factor(sample(1:5,30,r=TRUE)),
  y = as.factor(sample(1:5,30,r=TRUE)),
  z = as.factor(sample(1:5,30,r=TRUE)),
  w = as.factor(sample(1:5,30,r=TRUE))
)
##

Df[,c("y","w")] <- as.numeric(as.character(unlist(Df[,c("y","w")])))

str(Df)

Редагувати: для вашого коду це стає:

id <- c(1,3:ncol(stats))) 
stats[,id] <- as.numeric(as.character(unlist(stats[,id])))

Очевидно, що якщо у вас є кадр даних з одним стовпцем, і ви не хочете, щоб автоматичне зменшення розмірів R перетворило його на вектор, вам доведеться додати drop=FALSEаргумент.


1
Невеликим покращенням можуть бути налаштування recursiveта use.namesпараметри unlistобох до FALSE.
Марек

@Marek: правда. Я люблю цю гру :-)
Джоріс Мейс

Я просто збираюся додати для тих, хто шукає відповіді в майбутньому, це не еквівалентно методу op + gavin, якщо фрейм даних має лише один стовпець. У такому випадку він перетвориться на вектор, тоді як операції все ще залишатимуться фреймом даних.
themartinmcfly

1
для тих, хто працює з tidyverse: що цікаво, це, здається, не спрацьовує, коли об'єкт також є тиблом: Код не вдається післяDf <- tibble::as_tibble(Df)
тібо

1
@Tjebo з оновленнями tibble та перенаправленням між tibbles та фреймами даних, цей старий підхід насправді не є найкращим варіантом у tidyverse. Вам краще використовувати функції tidyselect у поєднанні з mutate_if. Або будь-який новий підхід стане доступним на наступній ітерації dplyr...
Джоріс Мейс

30

Я знаю, що це питання давно вирішено, але нещодавно у мене була подібна проблема, і я думаю, що знайшов трохи більш елегантне та функціональне рішення, хоча для цього потрібен пакет magrittr.

library(magrittr)
cols = c(1, 3, 4, 5)
df[,cols] %<>% lapply(function(x) as.numeric(as.character(x)))

У %<>%оператор труби і перепризначає, що дуже корисно для очищення даних обліку і трансформації просто. Тепер функцію застосування списку набагато легше читати, лише вказавши функцію, яку ви хочете застосувати.


2
акуратний розчин. ви забули одну дужку, але я не можу зробити це редагування, оскільки воно занадто коротке:df[,cols] %<>% lapply(function(x) as.numeric(as.character(x)))
epo3

1
Я не думаю, що вам навіть потрібно обгортати те, що у lappy df[,cols] %<>% as.numeric(as.character(.))працює однаково
Нейт

коли я пробую цю команду, я отримую таку помилкуError in [.data.table(Results, , cols) : j (the 2nd argument inside [...]) is a single symbol but column name 'cols' is not found. Perhaps you intended DT[,..cols] or DT[,cols,with=FALSE]. This difference to data.frame is deliberate and explained in FAQ 1.1.
Urvah Shabbir

Код такий:cols <- c("a","b"); df[,cols] %<>% lapply(function(x) as.numeric(as.character(x)))
Urvah Shabbir

Кронштейн додано.
Джо

9

Ось кілька dplyrваріантів:

# by column type:
df %>% 
  mutate_if(is.factor, ~as.numeric(as.character(.)))

# by specific columns:
df %>% 
  mutate_at(vars(x, y, z), ~as.numeric(as.character(.))) 

# all columns:
df %>% 
  mutate_all(~as.numeric(as.character(.))) 

6

Я думаю, що ucfagls виявив, чому ваш цикл не працює.

Якщо ви все ще не хочете використовувати цикл, ось рішення з lapply:

factorToNumeric <- function(f) as.numeric(levels(f))[as.integer(f)] 
cols <- c(1, 3:ncol(stats))
stats[cols] <- lapply(stats[cols], factorToNumeric)

Редагувати. Я знайшов більш просте рішення. Здається, as.matrixперетворити на характер. Так

stats[cols] <- as.numeric(as.matrix(stats[cols]))

повинен робити те, що ти хочеш.


5

lapply в значній мірі розроблений для цього

unfactorize<-c("colA","colB")
df[,unfactorize]<-lapply(unfactorize, function(x) as.numeric(as.character(df[,x])))

Привіт @transcom, і ласкаво просимо до stackoverflow. Зверніть увагу, що це питання стосується перетворення на числове подання з коефіцієнта, а не навпаки. Дивіться рішення Марека.
Аарон залишив переповнення стека

@ Аарон, зрозумів. Я опублікував цю відповідь через неоднозначність заголовка OP, працюючи з припущенням, що інші можуть приземлятися тут, шукаючи спосіб легко перетворити кілька стовпців, незалежно від класу. У будь-якому випадку, я відредагував свою відповідь, щоб більш доречно вирішити питання :)
transcom

2

Я знайшов цю функцію на декількох інших дублікатах і знайшов її елегантним і загальним способом вирішення цієї проблеми. Ця тема відображається спочатку під час більшості пошукових запитів за цією темою, тому я ділюсь тут, щоб заощадити час. Я не приймаю за це поваги, тому дивіться оригінальні публікації тут і тут для деталей.

df <- data.frame(x = 1:10,
                 y = rep(1:2, 5),
                 k = rnorm(10, 5,2),
                 z = rep(c(2010, 2012, 2011, 2010, 1999), 2),
                 j = c(rep(c("a", "b", "c"), 3), "d"))

convert.magic <- function(obj, type){
  FUN1 <- switch(type,
                 character = as.character,
                 numeric = as.numeric,
                 factor = as.factor)
  out <- lapply(obj, FUN1)
  as.data.frame(out)
}

str(df)
str(convert.magic(df, "character"))
str(convert.magic(df, "factor"))
df[, c("x", "y")] <- convert.magic(df[, c("x", "y")], "factor")

1

Я хотів би зазначити, що якщо у вас є NA у будь-якому стовпці, просто використання індексів не буде працювати. Якщо у факторі є NA, ви повинні використовувати сценарій застосування, наданий Ramnath.

Напр

Df <- data.frame(
  x = c(NA,as.factor(sample(1:5,30,r=T))),
  y = c(NA,as.factor(sample(1:5,30,r=T))),
  z = c(NA,as.factor(sample(1:5,30,r=T))),
  w = c(NA,as.factor(sample(1:5,30,r=T)))
)

Df[,c(1:4)] <- as.numeric(as.character(Df[,c(1:4)]))

Повертає наступне:

Warning message:
NAs introduced by coercion 

    > head(Df)
       x  y  z  w
    1 NA NA NA NA
    2 NA NA NA NA
    3 NA NA NA NA
    4 NA NA NA NA
    5 NA NA NA NA
    6 NA NA NA NA

Але:

Df[,c(1:4)]= apply(Df[,c(1:4)], 2, function(x) as.numeric(as.character(x)))

Повернення:

> head(Df)
   x  y  z  w
1 NA NA NA NA
2  2  3  4  1
3  1  5  3  4
4  2  3  4  1
5  5  3  5  5
6  4  2  4  4

1

Ви можете скористатися unfactor()функцією із форми пакунка CRHAN "varhandle":

library("varhandle")

my_iris <- data.frame(Sepal.Length = factor(iris$Sepal.Length),
                      sample_id = factor(1:nrow(iris)))

my_iris <- unfactor(my_iris)

1

Мені подобається цей код, оскільки він дуже зручний:

  data[] <- lapply(data, function(x) type.convert(as.character(x), as.is = TRUE)) #change all vars to their best fitting data type

Це не зовсім те, про що просили (перевести в числове), але в багатьох випадках навіть більш доречне.


1

df$colname <- as.numeric(df$colname)

Я спробував цей спосіб змінити один тип стовпця, і я думаю, що це краще, ніж багато інших версій, якщо ви не збираєтеся змінювати всі типи стовпців

df$colname <- as.character(df$colname)

для навпаки.


0

У мене були проблеми з перетворенням усіх стовпців у числові під час apply()виклику:

apply(data, 2, as.numeric)

Виявляється, проблема в тому, що в деяких рядках була кома - наприклад, "1024,63" замість "1024,63" - і R не любить такий спосіб форматування чисел. Тож я їх видалив, а потім побіг as.numeric():

data = as.data.frame(apply(data, 2, function(x) {
  y = str_replace_all(x, ",", "") #remove commas
  return(as.numeric(y)) #then convert
}))

Зверніть увагу, що для цього потрібно завантажити пакет stringr.


0

Це те, що мені вдалося. У apply()функції намагається примусити ФР до матричних і повертає НС.

numeric.df <- as.data.frame(sapply(df, 2, as.numeric))


0

На основі відповіді @ SDahm, це було "оптимальним" рішенням для мого tibble:

data %<>% lapply(type.convert) %>% as.data.table()

Для цього потрібно dplyrі magrittr.


0

Я спробував купу з них на подібній проблемі і продовжував отримувати НС. Base R має кілька справді дратуючих способів примусу, які зазвичай фіксуються в пакетах Tidyverse. Раніше я уникав їх, бо не хотів створювати залежності, але вони настільки полегшують життя, що зараз я навіть не намагаюся з’ясувати рішення Base R більшість часу.

Ось рішення Tidyverse, яке надзвичайно просте та елегантне:

library(purrr)

mydf <- data.frame(
  x1 = factor(c(3, 5, 4, 2, 1)),
  x2 = factor(c("A", "C", "B", "D", "E")),
  x3 = c(10, 8, 6, 4, 2))

map_df(mydf, as.numeric)

Більшість відповідей (принаймні всі найпопулярніші відповіді) обов’язково виконують as.numeric(as.character())перетворення, щоб уникнути занадто поширеного перетворення цілочисельних рівнів замість значень у числові. Я з радістю підтримую цю відповідь, якщо ви покажете цей варіант.
Грегор Томас
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.