Konwertuj kolumny data.frame ze współczynników na znaki

352

Mam ramkę danych. Zadzwońmy do niego 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-

Chciałbym połączyć wiersze tej ramki danych (będzie to kolejne pytanie). Ale spójrz:

> class(bob$phenotype)
[1] "factor"

Bobkolumny są czynnikami. Na przykład:

> 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)"

Nie zaczynam tego rozumieć, ale sądzę, że są to wskaźniki poziomów czynników kolumn (dworu króla Karaktakusa) bob? Nie tego potrzebuję.

O dziwo mogę bobręcznie przejść przez kolumny i zrobić

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

co działa dobrze. Po wpisaniu mogę uzyskać ramkę data.frame, której kolumny to raczej znaki niż czynniki. Więc moje pytanie brzmi: jak mogę to zrobić automatycznie? Jak przekonwertować ramkę data.frame z kolumnami czynnikowymi na ramkę data.frame z kolumnami znaków bez konieczności ręcznego przechodzenia przez każdą kolumnę?

Pytanie dodatkowe: dlaczego działa podejście ręczne?

Mike Dewar
źródło
3
byłoby miło, gdyby pytanie było odtwarzalne, więc uwzględnij strukturę bob.
jangorecki

Odpowiedzi:

362

Po prostu śledzę Matta i Dirka. Jeśli chcesz odtworzyć istniejącą ramkę danych bez zmiany opcji globalnej, możesz ją odtworzyć za pomocą instrukcji Apply:

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

Spowoduje to konwersję wszystkich zmiennych do klasy „znak”, jeśli chcesz tylko przeliczać współczynniki, zobacz rozwiązanie Marka poniżej .

Jak zauważa @adley, poniższe są bardziej zwięzłe.

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

W obu przypadkach lapplywyświetla listę; jednak ze względu na magiczne właściwości R użycie []w drugim przypadku zachowuje klasę data.frame bobobiektu, eliminując w ten sposób potrzebę ponownej konwersji na data.frame przy użyciu as.data.frameargumentu stringsAsFactors = FALSE.

Shane
źródło
27
Shane, to zmieni również kolumny liczbowe w znaki.
Dirk Eddelbuettel
@Dirk: To prawda, chociaż nie jest jasne, czy to jest tutaj problem. Najwyraźniej najlepszym rozwiązaniem jest tworzenie rzeczy z góry. Nie sądzę, że łatwo jest automatycznie konwertować typy danych w ramce danych. Jedną z opcji jest użycie wyżej, ale następnie użyć type.convertpo odlaniu wszystko character, przekształconej następnie factorsz powrotem characterponownie.
Shane
To wydaje się odrzucać nazwy wierszy.
piccolbo
2
@piccolbo użyłeś bob[] <- w przykładzie lub bob <- ? pierwszy zachowuje data.frame; drugi zmienia data.frame na listę, upuszczając nazwy rown. Zaktualizuję odpowiedź
David LeBauer,
6
Wariant, który konwertuje kolumny czynnikowe na znaki za pomocą anonimowej funkcji: iris[] <- lapply(iris, function(x) if (is.factor(x)) as.character(x) else {x})
Stefan F
313

Aby zastąpić tylko czynniki:

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

W pakiecie dplyr w wersji 0.5.0 mutate_ifwprowadzono nową funkcję :

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

Mruczenie pakietów z RStudio daje kolejną alternatywę:

library(purrr)
library(dplyr)
bob %>% map_if(is.factor, as.character) %>% as_tibble -> bob
Marek
źródło
Niestety nie działa dla mnie. Nie wiem dlaczego. Prawdopodobnie dlatego, że mam nazwy pseudonimów?
Autumnsault
@mohawkjohn Nie powinno być problemu. Masz błąd lub wyniki niezgodne z oczekiwaniami?
Marek
2
Uwaga: purrrwiersz zwraca listę, a nie data.frame!
RoyalTS
Działa to również, jeśli masz już iwektor będący wektorem colnames().
verbamour,
39

Opcja globalna

stringsAsFactors: Domyślne ustawienie dla argumentów data.frame i read.table.

może być czymś, co chcesz ustawić FALSEw plikach startowych (np. ~ / .Rprofile). Proszę zobaczyć help(options).

Dirk Eddelbuettel
źródło
5
Problem polega na tym, że podczas wykonywania kodu w środowisku, w którym brakuje tego pliku .Rprofile, dostaniesz błędy!
opłatek
4
Zwykle nazywam to na początku skryptów, a nie ustawienie jest w .Rprofile.
gregmacfarlane
22

Jeśli zrozumiesz, w jaki sposób są przechowywane czynniki, możesz uniknąć korzystania z funkcji opartych na aplikacjach, aby to osiągnąć. Co wcale nie oznacza, że ​​zastosowane rozwiązania nie działają dobrze.

Czynniki mają strukturę wskaźników liczbowych powiązanych z listą „poziomów”. Można to zobaczyć po przeliczeniu współczynnika na wartość liczbową. Więc:

> 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

Liczby zwrócone w ostatnim wierszu odpowiadają poziomom współczynnika.

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

Zauważ, że levels()zwraca tablicę znaków. Możesz użyć tego faktu, aby łatwo i kompaktowo konwertować współczynniki na ciągi lub liczby takie jak to:

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

Działa to również w przypadku wartości liczbowych, pod warunkiem, że zawiniesz wyrażenie 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
Kikapp
źródło
Ta odpowiedź nie rozwiązuje problemu, jak przekonwertować wszystkie kolumny czynników w mojej ramce danych na znak. as.character(f), jest lepszy zarówno pod względem czytelności, jak i wydajności levels(f)[as.numeric(f)]. Jeśli chcesz być sprytny, możesz użyć levels(f)[f]zamiast tego. Zauważ, że konwertując współczynnik z wartościami liczbowymi, zyskujesz na przewadze as.numeric(levels(f))[f]np. as.numeric(as.character(f)), Ale dzieje się tak, ponieważ musisz tylko przekonwertować poziomy na wartości liczbowe, a następnie na podzbiór. as.character(f)jest w porządku, jak jest.
De Novo
20

Jeśli chcesz nową ramkę danych, w bobcktórej każdy wektor czynnika bobfjest konwertowany na wektor znakowy, spróbuj tego:

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

Jeśli następnie chcesz go przekonwertować, możesz utworzyć logiczny wektor, w którym kolumny są współczynnikami, i użyć go do selektywnego zastosowania współczynnika

f <- sapply(bobf, class) == "factor"
bobc[,f] <- lapply(bobc[,f], factor)
scentoni
źródło
2
+1 za robienie tylko tego, co było konieczne (tj. Nie konwertowanie całej data.frame na postać). To rozwiązanie jest odporne na ramkę danych, która zawiera typy mieszane.
Joshua Ulrich
3
Ten przykład powinien znajdować się w sekcji `Przykłady 'rapply, na przykład: stat.ethz.ch/R-manual/R-devel/library/base/html/rapply.html . Czy ktoś wie, jak o to poprosić?
mpettis,
Jeśli chcesz skończyć z ramką danych, po prostu zawiń rapply w wywołanie data.frame (używając stringsAsFactors ustawionego na FALSE argument)
Taylored Web Sites
13

Zazwyczaj tę funkcję dzielę na wszystkie moje projekty. Szybko i łatwo.

unfactorize <- function(df){
  for(i in which(sapply(df, class) == "factor")) df[[i]] = as.character(df[[i]])
  return(df)
}
by0
źródło
8

Innym sposobem jest konwersja za pomocą Apply

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

I lepszy (poprzedni dotyczy klasy „matrycy”)

bob2 <- as.data.frame(as.matrix(bob),stringsAsFactors=F)
George Dontas
źródło
Po komentarzu @ Shane'a: ​​aby uzyskać data.frame, zróbas.data.frame(lapply(...
aL3xa
7

Aktualizacja: Oto przykład czegoś, co nie działa. Myślałem, że tak, ale myślę, że opcja stringsAsFactors działa tylko na ciągach znaków - pozostawia czynniki w spokoju.

Spróbuj tego:

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

Ogólnie rzecz biorąc, ilekroć masz problemy z czynnikami, które powinny być postaciami, jest jakieś stringsAsFactorsustawienie, które może ci pomóc (w tym ustawienie globalne).

Matt Parker
źródło
1
Działa to, jeśli ustawia to bobna początku tworzenia (ale nie po fakcie).
Shane
Dobrze. Chciałem tylko wyjaśnić, że to nie rozwiązuje problemu per se - ale dziękuję, że zauważyłeś, że to zapobiega.
Matt Parker
7

Lub możesz spróbować transform:

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

Pamiętaj, aby umieścić każdy czynnik, który chcesz przekonwertować na postać.

Lub możesz zrobić coś takiego i zabić wszystkie szkodniki jednym ciosem:

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)

To nie dobry pomysł, aby wpakować dane w kodzie jak ten, mogę zrobić sapplyczęść osobno (właściwie, to o wiele łatwiej zrobić to w ten sposób), ale o co chodzi ... Nie sprawdzałem kod, bo Nie ma mnie w domu, więc mam nadzieję, że to zadziała! =)

Takie podejście ma jednak pewną wadę ... musisz później zreorganizować kolumny, a jednocześnie transformmożesz robić, co chcesz, ale kosztem „pisania kodu dla pieszych” ...

Więc tam ... =)

aL3xa
źródło
6

Na początku ramki danych należy stringsAsFactors = FALSEzignorować wszystkie nieporozumienia.


źródło
4

Jeśli użyjesz data.tablepakietu do operacji na data.frame, problem nie będzie obecny.

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

Jeśli masz już kolumny czynników w swoim zbiorze danych i chcesz przekonwertować je na znaki, możesz wykonać następujące czynności.

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" 
jangorecki
źródło
DT omija sapply fix zaproponowany przez Marka: 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.Łatwiej jest naprawić DF i odtworzyć DT.
Matt Chambers
2

To działa dla mnie - w końcu wymyśliłem jedną wkładkę

df <- as.data.frame(lapply(df,function (y) if(class(y)=="factor" ) as.character(y) else y),stringsAsFactors=F)
użytkownik1617979
źródło
2

Ta funkcja załatwia sprawę

df <- stacomirtools::killfactor(df)
Cedric
źródło
2

Może nowsza opcja?

library("tidyverse")

bob <- bob %>% group_by_if(is.factor, as.character)
Rachelette
źródło
1

Powinieneś użyć, convertw hablarktórym daje czytelną składnię kompatybilną z tidyversepotokami:

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))

co daje ci:

  a     b    
  <chr> <chr>
1 1     5    
2 2     6    
3 3     7    
4 4     8   
davsjob
źródło
1

Z dplyrzaładowanym pakietem

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

jeśli chcesz tylko zmienić phenotypekolumnę-konkretnie.

nexonvantec
źródło
0

To działa, przekształcając wszystko w znak, a następnie numerycznie w numeryczny:

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)
}

Zaadaptowano z: Pobierz automatycznie typy kolumn arkusza programu Excel

Ferroao
źródło