Конвертировать столбцы data.frame из факторов в символы

263

У меня есть кадр данных. Позвоните ему bob:

> head(bob)
                 phenotype                         exclusion
GSM399350 3- 4- 8- 25- 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399351 3- 4- 8- 25- 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399352 3- 4- 8- 25- 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399353 3- 4- 8- 25+ 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399354 3- 4- 8- 25+ 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399355 3- 4- 8- 25+ 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-

Я хотел бы конкатенировать строки этого фрейма данных (это будет другой вопрос). Но посмотрите:

> class(bob$phenotype)
[1] "factor"
Столбцы

bob являются факторами. Итак, например:

> as.character(head(bob))
[1] "c(3, 3, 3, 6, 6, 6)"       "c(3, 3, 3, 3, 3, 3)"      
[3] "c(29, 29, 29, 30, 30, 30)"

Я не начинаю это понимать, но я думаю, что это индексы в уровни факторов столбцов (суда короля caractacus) bob? Не то, что мне нужно.

Странно я могу вручную пройти через столбцы bob и сделать

bob$phenotype <- as.character(bob$phenotype)

который отлично работает. И, после некоторого ввода, я могу получить data.frame, чьи столбцы являются символами, а не факторами. Поэтому мой вопрос: как я могу это сделать автоматически? Как преобразовать data.frame с столбцами факторов в data.frame с колонками символов без необходимости вручную проходить через каждый столбец?

Бонусный вопрос: почему работает ручной подход?

  • 2
    было бы неплохо, если бы вы сделали вопрос воспроизводимым, поэтому включите структуру bob .
Теги:
dataframe

13 ответов

299
Лучший ответ

Просто после Мэтта и Дирка. Если вы хотите воссоздать существующий фрейм данных без изменения глобального параметра, вы можете создать его с помощью оператора apply:

bob <- data.frame(lapply(bob, as.character), stringsAsFactors=FALSE)

Это преобразует все переменные в класс "character", если вы хотите только преобразовать факторы, см. решение Marek ниже.

Как указывает @hadley, следующее более кратким.

bob[] <- lapply(bob, as.character)

В обоих случаях lapply выводит список; однако из-за магических свойств R использование [] во втором случае сохраняет класс data.frame объекта bob, тем самым устраняя необходимость преобразования обратно в data.frame с использованием as.data.frame с аргумент stringsAsFactors = FALSE.

  • 21
    Шейн, это также превратит числовые столбцы в характер.
  • 0
    @Dirk: Это правда, хотя не ясно, является ли это проблемой здесь. Очевидно, что правильное создание вещей - это лучшее решение. Я не думаю, что легко автоматически конвертировать типы данных через фрейм данных. Один из вариантов - использовать вышеизложенное, но затем использовать type.convert после приведения всего к character , а затем type.convert factors обратно в character .
Показать ещё 5 комментариев
254

Чтобы заменить только факторы:

i <- sapply(bob, is.factor)
bob[i] <- lapply(bob[i], as.character)

В пакете dplyr в версии 0.5.0 появилась новая функция mutate_if:

library(dplyr)
bob %>% mutate_if(is.factor, as.character) -> bob

Пакет purrr из RStudio дает еще одну альтернативу:

library(purrr)
library(dplyr)
bob %>% map_if(is.factor, as.character) %>% as_data_frame -> bob

(имейте в виду свежий пакет)

  • 2
    Это действительно полезный код, который можно быстро преобразовать в однострочник ....
  • 0
    +1 Хорошо сохраняет int при изменении этих надоедливых факторов
Показать ещё 4 комментария
37

Глобальная опция

stringsAsFactors:     Значение по умолчанию для аргументов data.frame и read.table.

может быть то, что вы хотите установить в FALSE в ваших загрузочных файлах (например, ~/.Rprofile). См. help(options).

  • 1
    Спасибо за это! Такие вещи постоянно кусают меня, когда я карабкаюсь по кривой обучения R!
  • 4
    Проблема в том, что когда вы выполняете свой код в среде, где этот файл .Rprofile отсутствует, вы получаете ошибки!
Показать ещё 1 комментарий
19

Я знаю, что этот ответ немного запоздал, но если вы понимаете, как факторы хранятся, вы можете избежать использования прикладных функций для достижения этого. Это вовсе не означает, что применяемые решения не работают хорошо.

Факторы структурированы как числовые индексы, привязанные к списку "уровней". Это можно увидеть, если вы преобразуете коэффициент в числовой. Итак:

> fact <- as.factor(c("a","b","a","d")
> fact
[1] a b a d
Levels: a b d

> as.numeric(fact)
[1] 1 2 1 3

Цифры, возвращаемые в последней строке, соответствуют уровням фактора.

> levels(fact)
[1] "a" "b" "d"

Обратите внимание, что levels() возвращает массив символов. Вы можете использовать этот факт для легкого и компактного преобразования факторов в строки или числовые значения следующим образом:

> fact_character <- levels(fact)[as.numeric(fact)]
> fact_character
[1] "a" "b" "a" "d"

Это также работает для числовых значений, если вы завершаете свое выражение в as.numeric().

> num_fact <- factor(c(1,2,3,6,5,4))
> num_fact
[1] 1 2 3 6 5 4
Levels: 1 2 3 4 5 6
> num_num <- as.numeric(levels(num_fact)[as.numeric(num_fact)])
> num_num
[1] 1 2 3 6 5 4
  • 0
    Этот ответ не решает проблему, как я могу преобразовать все столбцы фактора в моем фрейме данных в символ. as.character(f) лучше по читаемости и эффективности до levels(f)[as.numeric(f)] . Если вы хотите быть умным, вы можете использовать levels(f)[f] . Обратите внимание, что при преобразовании фактора в числовые значения вы получаете некоторую выгоду от as.numeric(levels(f))[f] , например as.numeric(as.character(f)) , но это потому, что вы только должны преобразовать уровни в числовые, а затем подмножество. as.character(f) просто отлично, как есть.
17

Если вам нужен новый кадр данных bobc, где каждый вектор факторов в bobf преобразуется в вектор символов, попробуйте следующее:

bobc <- rapply(bobf, as.character, classes="factor", how="replace")

Если вы хотите его переконвертировать, вы можете создать логический вектор, столбцы которого являются факторами, и использовать его для выборочного применения коэффициента

f <- sapply(bobf, class) == "factor"
bobc[,f] <- lapply(bobc[,f], factor)
  • 2
    +1 за выполнение только того, что было необходимо (т.е. не преобразование всего data.frame в символ). Это решение устойчиво к data.frame, который содержит смешанные типы.
  • 3
    Этот пример должен быть в разделе «Примеры» для rapply, например, по адресу: stat.ethz.ch/R-manual/R-devel/library/base/html/rapply.html . Кто-нибудь знает, как просить, чтобы это было так?
Показать ещё 1 комментарий
10

Я обычно делаю эту функцию отдельно от всех моих проектов. Быстро и легко.

unfactorize <- function(df){
  for(i in which(sapply(df, class) == "factor")) df[[i]] = as.character(df[[i]])
  return(df)
}
  • 0
    Мне это нравится. Легко и легко понять. За петли в R не хватит любви IMO
7

Другой способ - преобразовать его, применив

bob2 <- apply(bob,2,as.character)

И лучший (предыдущий - это "матрица" класса)

bob2 <- as.data.frame(as.matrix(bob),stringsAsFactors=F)
  • 0
    После комментария @ Шейна: чтобы получить data.frame, сделайте as.data.frame(lapply(...
6

Или вы можете попробовать transform:

newbob <- transform(bob, phenotype = as.character(phenotype))

Просто убедитесь, что все факторы, которые вы хотели бы преобразовать в символ.

Или вы можете сделать что-то подобное и убить всех вредителей одним ударом:

newbob_char <- as.data.frame(lapply(bob[sapply(bob, is.factor)], as.character), stringsAsFactors = FALSE)
newbob_rest <- bob[!(sapply(bob, is.factor))]
newbob <- cbind(newbob_char, newbob_rest)

не хорошая идея перетащить данные в код, как это, я мог бы сделать часть sapply по отдельности (на самом деле это намного проще сделать так), но вы получаете точку... Я не проверял код, потому что меня нет дома, поэтому я надеюсь, что это сработает! =)

Этот подход, однако, имеет недостаток... вы должны впоследствии реорганизовать столбцы, а при transform вы можете делать все, что захотите, но по цене "пешеходного стиля-написания кода"...

Итак, там... =)

6

Обновление: Здесь пример того, что не работает. Я думал, что это будет, но я думаю, что параметр strAsAsFactors работает только на символьных строках - он оставляет только факторы.

Попробуйте следующее:

bob2 <- data.frame(bob, stringsAsFactors = FALSE)

Вообще говоря, всякий раз, когда у вас возникают проблемы с факторами, которые должны быть персонажами, установите параметр stringsAsFactors где-нибудь, чтобы помочь вам (включая глобальные настройки).

  • 1
    Это работает, если он устанавливает его при создании bob для начала (но не по факту).
  • 0
    Правильно. Я просто хотел прояснить, что это не решает проблему как таковую - но спасибо, что отметили, что это предотвращает это.
4

В начале вашего фрейма данных включите stringsAsFactors = FALSE, чтобы игнорировать все недоразумения.

3

Если вы используете пакет data.table для операций с data.frame, тогда проблемы нет.

library(data.table)
dt = data.table(col1 = c("a","b","c"), col2 = 1:3)
sapply(dt, class)
#       col1        col2 
#"character"   "integer" 

Если у вас уже есть столбцы факторов в вашем наборе данных, и вы хотите преобразовать их в символ, вы можете сделать следующее.

library(data.table)
dt = data.table(col1 = factor(c("a","b","c")), col2 = 1:3)
sapply(dt, class)
#     col1      col2 
# "factor" "integer" 
upd.cols = sapply(dt, is.factor)
dt[, names(dt)[upd.cols] := lapply(.SD, as.character), .SDcols = upd.cols]
sapply(dt, class)
#       col1        col2 
#"character"   "integer" 
  • 0
    DT обходит исправление sapply, предложенное Мареком: In [<-.data.table(*tmp*, sapply(bob, is.factor), : Coerced 'character' RHS to 'double' to match the column's type. Either change the target column to 'character' first (by creating a new 'character' vector length 1234 (nrows of entire table) and assign that; ie 'replace' column), or coerce RHS to 'double' (eg 1L, NA_[real|integer]_, as.*, etc) to make your intent clear and for speed. Or, set the column type correctly up front when you create the table and stick to it, please. Проще исправить DF и воссоздать DT ,
2

Это работает для меня - я, наконец, понял один лайнер

df <- as.data.frame(lapply(df,function (y) if(class(y)=="factor" ) as.character(y) else y),stringsAsFactors=F)
0

Эта функция выполняет трюк

df <- stacomirtools::killfactor(df)

Ещё вопросы

Сообщество Overcoder
Наверх
Меню