Измените класс с фактора на числовой для многих столбцов во фрейме данных


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
Нет ли универсального решения? Некоторые из предложенных здесь решений работают только с факторами, другие работают всегда, кроме факторов, и так далее ...
скан

Ответы:


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 (). Изменить: я добавил ответ, иллюстрирующий это.
Джорис Мейс,

Этот подход работает только в этом конкретном случае. Я пытался использовать его для преобразования столбцов, 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выборочно, но это тоже тривиально.
Гэвин Симпсон

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это уместно для такого рода проблем.
Marek

1
apply отлично работает в этих ситуациях. ошибка в моем коде заключалась в использовании поля = 1 вместо 2, поскольку функция должна применяться по столбцам. я соответствующим образом отредактировал свой ответ.
Рамнат,

Теперь это работает. Но я думаю, что без этого можно было обойтись apply. Проверьте мою правку.
Марек,

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

@ Ramnath , почему ты используешь =? Почему бы и нет <-?
kittygirl

40

Это можно сделать в одной строке, нет необходимости в цикле, будь то цикл for или применение. Вместо этого используйте 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: правда. Я люблю эту игру :-)
Joris Meys

Я просто собираюсь добавить для тех, кто ищет ответы в будущем, это не эквивалентно методу op + gavin, если фрейм данных состоит только из одного столбца. В этом случае он будет преобразован в вектор, тогда как op по-прежнему будет фреймом данных.
themartinmcfly

1
для тех, кто работает с tidyverse: интересно, это, похоже, не работает, когда объект также является тибблом: код не работает послеDf <- tibble::as_tibble(Df)
tjebo 03

1
@Tjebo с обновлениями tibble и переключением между tibble и фреймами данных, этот старый подход действительно не лучший вариант для 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.
Урва Шабир

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

Кронштейн теперь добавлен.
Джо

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. Обратите внимание, что этот вопрос касается преобразования коэффициента в числовое представление, а не наоборот. См. Решение Марека.
Аарон покинул Stack Overflow

@ Аарон, понял. Я опубликовал этот ответ из-за двусмысленности названия OP, исходя из предположения, что другие могут попасть сюда в поисках способа легко преобразовать несколько столбцов, независимо от класса. Во всяком случае, я отредактировал свой ответ, чтобы более
точно

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 в любом столбце, просто использовать индексы не получится. Если в факторе есть НА, вы должны использовать сценарий применения, предоставленный Рамнатхом.

Например

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()функцию из пакета varhandle из CRAN:

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)

Проблема заключается в том, что в некоторых строках есть запятая - например, «1,024,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

Я попробовал несколько из них по аналогичной проблеме и продолжал получать NA. В 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.