Перетворити стовпці data.frame з факторів у символи


352

У мене є кадр даних. Давайте назвемо його 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)"

Я не починаю цього розуміти, але, мабуть, це покажчики на рівні факторів стовпців (суду короля Карактака) bob? Не те, що мені потрібно.

Як не дивно, я можу bobвручну проходити через колони та робити

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

що чудово працює. Після певного введення тексту я можу отримати data.frame, стовпці якого є символами, а не факторами. Отже, моє запитання: як я можу це зробити автоматично? Як перетворити data.frame з стовпцями-факторами в data.frame з символьними стовпцями без необхідності вручну проходити кожен стовпець?

Питання про бонус: чому працює підхід вручну?


3
Було б добре, якби ви зробили питання відтворюваним, тому включіть структуру bob.
jangorecki

Відповіді:


362

Тільки слід за Меттом та Дірком. Якщо ви хочете відтворити існуючий кадр даних, не змінюючи глобальний параметр, ви можете відтворити його за допомогою застосунку:

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

Це перетворить усі змінні в клас "характер", якщо ви хочете лише перетворювати фактори, див . Рішення Марека нижче .

Як зазначає @hadley, наступне є більш стислим.

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

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


27
Шейн, це також перетворить числові стовпці в символ.
Дірк Еддельбюттель

@Dirk: Це правда, хоча незрозуміло, чи це тут проблема. Зрозуміло, що найкраще рішення - створення речей вперед. Я не думаю, що легко конвертувати типи даних через кадр даних легко . Один із варіантів полягає в тому, щоб скористатись вищезазначеним, але потім використати type.convertпісля того, як все characterвикинеш на , а потім переробити factorsназад на characterзнову.
Шейн

Здається, це відкидає назви рядків.
piccolbo

2
@piccolbo ви використовували bob[] <- в прикладі або bob <- ?; перший зберігає data.frame; другий змінює фрейм data.frame до списку, видаляючи назви рядків. Я
оновлю

6
Варіант, який лише перетворює факторні стовпці в символи, використовуючи анонімну функцію: iris[] <- lapply(iris, function(x) if (is.factor(x)) as.character(x) else {x})
Stefan F

313

Щоб замінити лише фактори:

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_tibble -> bob

Не працює для мене, на жаль. Не знаю чому. Можливо, тому, що у мене є кольмари?
Осінько,

@mohawkjohn не повинно бути проблемою. Ви отримали помилку чи результати не так, як очікували?
Марек

2
Примітка: purrrрядок повертає список, а не data.frame!
RoyalTS

Це також працює, якщо у вас вже є iвектор, який є colnames().
verbamour

39

Глобальний варіант

stringsAsFactors: налаштування за замовчуванням для аргументів data.frame та read.table.

може бути те, що ви хочете встановити FALSEу своїх файлах запуску (наприклад, ~ / .Rprofile). Будь ласка, дивіться help(options).


5
Проблема в цьому полягає в тому, що при виконанні коду в середовищі, де цього файлу .Rprofile відсутній, ви отримаєте помилки!
вафельні

4
Я схильний називати це на початку сценаріїв, а не встановлення в .Rprofile.
gregmacfarlane

22

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

Фактори структуруються як числові індекси, прив'язані до списку "рівнів". Це можна побачити, якщо перетворити фактор у числовий. Тому:

> 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

Ця відповідь не вирішує проблему, а саме те, як я перетворять усі стовпці факторів у моєму кадрі даних у символ. as.character(f), краще як у читанні, так і в ефективності levels(f)[as.numeric(f)]. Якщо ви хотіли бути розумними, можете використати levels(f)[f]натомість. Зверніть увагу, що при перетворенні фактора з числовими значеннями ви отримуєте певну користь as.numeric(levels(f))[f], наприклад as.numeric(as.character(f)), але це тому, що вам потрібно лише перетворити рівні в числові, а потім підмножини. as.character(f)просто чудово, як є.
De Novo

20

Якщо ви хочете новий кадр даних, 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, який містить змішані типи.
Джошуа Ульріх

3
Цей приклад має бути у розділі "Приклади" для наближення, як-от: stat.ethz.ch/R-manual/R-devel/library/base/html/rapply.html . Хтось знає, як вимагати, щоб це було так?
mpettis

Якщо ви хочете створити кадр даних, просто загорніть репліку у виклик data.frame (використовуючи stringsAsFactors, встановлений на аргумент FALSE)
Taylored Web Sites

13

Я, як правило, виконую цю функцію крім усіх моїх проектів. Швидко і просто.

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

8

Інший спосіб - це перетворити його за допомогою Apply

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

І кращий (попередній - клас "матриця")

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

Після коментаря @ Shane: щоб отримати data.frame, зробітьas.data.frame(lapply(...
aL3xa

7

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

Спробуйте це:

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

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


1
Це спрацьовує, якщо він встановлює це при створенні bobдля початку (але не після факту).
Шейн

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

7

Або ви можете спробувати 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

На початку вашого кадру даних включайте, stringsAsFactors = FALSEщоб ігнорувати всі непорозуміння.


4

Якщо ви використовуєте 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" 

DT обходить фіксацію саплікації, запропоновану Marek: 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; i.e. 'replace' column), or coerce RHS to 'double' (e.g. 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)


2

Можливо, новіший варіант?

library("tidyverse")

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

1

Ви повинні використовувати, convertв hablarякому дає читабельний синтаксис, сумісний з tidyverseтрубами:

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   

1

З dplyrвикористанням завантаженого пакету

bob=bob%>%mutate_at("phenotype", as.character)

якщо ви хочете лише змінити phenotypeстовпець конкретно.


0

Це працює, перетворюючи все на символ, а потім числове на числове:

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 автоматично

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.