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

У меня есть кадр данных. Позвоните ему 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 с колонками символов без необходимости вручную проходить через каждый столбец?

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

Ответ 1

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

Ответ 2

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

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

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

Ответ 3

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

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

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

Ответ 4

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

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

> 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() выражение в 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

Ответ 5

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

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

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

f <- sapply(bobf, class) == "factor"
bobc[,f] <- lapply(bobc[,f], factor)

Ответ 6

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

unfactorize <- function(df){
  for(i in which(sapply(df, class) == "factor")) df[[i]] = as.character(df[[i]])
  return(df)
}

Ответ 7

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

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

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

bob2 <- as.data.frame(as.matrix(bob),stringsAsFactors=F)

Ответ 8

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

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

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

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

Ответ 9

Или вы можете попробовать 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 вы можете делать все, что захотите, но по цене "пешеходного стиля-написания кода"...

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

Ответ 10

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

Ответ 11

Если вы используете пакет 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" 

Ответ 12

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

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

Ответ 13

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

df <- stacomirtools::killfactor(df)

Ответ 14

Может быть, более новый вариант?

library("tidyverse")

bob <- bob %>% group_by_if(is.factor, as.character)

Ответ 15

Вы должны использовать convert в hablar который дает читаемый синтаксис, совместимый с tidyverse pipe:

library(dplyr)
library(hablar)

df <- tibble(a = factor(c(1, 2, 3, 4)),
             b = factor(c(5, 6, 7, 8)))

df %>% convert(chr(a:b))

что дает вам:

  a     b    
  <chr> <chr>
1 1     5    
2 2     6    
3 3     7    
4 4     8   

Ответ 16

Это работает, преобразуя все в символы, а затем цифры в цифры:

makenumcols<-function(df){
  df<-as.data.frame(df)
  df[] <- lapply(df, as.character)
  cond <- apply(df, 2, function(x) {
    x <- x[!is.na(x)]
    all(suppressWarnings(!is.na(as.numeric(x))))
  })
  numeric_cols <- names(df)[cond]
  df[,numeric_cols] <- sapply(df[,numeric_cols], as.numeric)
  return(df)
}

Адаптировано из: Автоматически получать типы столбцов таблицы Excel