Konversi data.frame kolom dari faktor ke karakter

352

Saya memiliki bingkai data. Sebut saja dia 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-

Saya ingin menyatukan baris bingkai data ini (ini akan menjadi pertanyaan lain). Tapi lihatlah:

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

BobKolom adalah faktor. Jadi, misalnya:

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

Saya tidak mulai memahami hal ini, tetapi saya kira ini adalah indeks-indeks ke dalam tingkat faktor-faktor kolom (pengadilan raja caractacus) bob? Bukan yang saya butuhkan.

Anehnya saya bisa melalui kolom bobdengan tangan, dan lakukan

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

yang bekerja dengan baik. Dan, setelah mengetik, saya bisa mendapatkan data.frame yang kolomnya karakter daripada faktor. Jadi pertanyaan saya adalah: bagaimana saya bisa melakukan ini secara otomatis? Bagaimana cara mengubah data.frame dengan kolom faktor menjadi data.frame dengan kolom karakter tanpa harus secara manual melalui setiap kolom?

Pertanyaan bonus: mengapa pendekatan manual berfungsi?

Mike Dewar
sumber
3
akan lebih baik jika Anda membuat pertanyaan dapat direproduksi, jadi sertakan struktur bob.
jangorecki

Jawaban:

362

Mengikuti Matt dan Dirk. Jika Anda ingin membuat ulang bingkai data yang ada tanpa mengubah opsi global, Anda dapat membuatnya kembali dengan pernyataan yang berlaku:

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

Ini akan mengonversi semua variabel ke "karakter" kelas, jika Anda hanya ingin mengonversi faktor, lihat solusi Marek di bawah ini .

Seperti @hadley tunjukkan, berikut ini lebih ringkas.

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

Dalam kedua kasus, lapplymenampilkan daftar; Namun, karena properti magis R, penggunaan []dalam kasus kedua menjaga kelas data.frame bobobjek, sehingga menghilangkan kebutuhan untuk mengkonversi kembali ke data.frame menggunakan as.data.frameargumen stringsAsFactors = FALSE.

Shane
sumber
27
Shane, itu juga akan mengubah kolom angka menjadi karakter.
Dirk Eddelbuettel
@ Malas: Itu benar, meskipun tidak jelas apakah itu masalah di sini. Jelas, menciptakan sesuatu dengan benar di muka adalah solusi terbaik. Saya tidak berpikir bahwa mudah untuk secara otomatis mengonversi tipe data di seluruh bingkai data. Salah satu pilihan adalah menggunakan di atas tetapi kemudian gunakan type.convertsetelah casting segalanya untuk character, kemudian menyusun factorskembali kembali ke characterlagi.
Shane
Ini sepertinya membuang nama baris.
piccolbo
2
@piccolbo yang Anda gunakan bob[] <- dalam contoh atau bob <- ?; yang pertama menyimpan data.frame; yang kedua mengubah data.frame ke daftar, menjatuhkan rownames. Saya akan memperbarui jawabannya
David LeBauer
6
Varian yang hanya mengonversi kolom faktor menjadi karakter menggunakan fungsi anonim: iris[] <- lapply(iris, function(x) if (is.factor(x)) as.character(x) else {x})
Stefan F
313

Untuk mengganti hanya faktor:

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

Dalam paket dplyr di versi 0.5.0, fungsi baru mutate_ifdiperkenalkan :

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

Paket purrr dari RStudio memberi alternatif lain:

library(purrr)
library(dplyr)
bob %>% map_if(is.factor, as.character) %>% as_tibble -> bob
Marek
sumber
Sayangnya, tidak bekerja untuk saya. Tidak tahu kenapa Mungkin karena saya punya colnames?
Autumnsault
@ mohawkjohn Tidak seharusnya menjadi masalah. Anda mendapat kesalahan atau hasil tidak seperti yang Anda harapkan?
Marek
2
Catatan: purrrBaris mengembalikan daftar, bukan data.frame!
RoyalTS
Ini juga berfungsi jika Anda sudah memiliki ivektor colnames().
verbamour
39

Opsi global

stringsAsFactors: Pengaturan default untuk argumen data.frame dan read.table.

mungkin sesuatu yang ingin Anda atur FALSEdi file startup Anda (mis. ~ /. Profil). Silakan lihat help(options).

Dirk Eddelbuettel
sumber
5
Masalahnya adalah ketika Anda mengeksekusi kode Anda di lingkungan di mana file .Rofile hilang Anda akan mendapatkan bug!
waferthin
4
Saya cenderung menyebutnya di awal skrip daripada pengaturan ada di. Profil.
gregmacfarlane
22

Jika Anda memahami bagaimana faktor disimpan, Anda dapat menghindari menggunakan fungsi berbasis-berlaku untuk mencapai hal ini. Yang sama sekali tidak menyiratkan bahwa solusi yang berlaku tidak berfungsi dengan baik.

Faktor terstruktur sebagai indeks numerik yang diikat ke daftar 'level'. Ini dapat dilihat jika Anda mengonversi faktor menjadi numerik. Begitu:

> 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

Angka-angka yang dikembalikan pada baris terakhir sesuai dengan tingkat faktor.

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

Perhatikan bahwa levels()mengembalikan array karakter. Anda dapat menggunakan fakta ini untuk dengan mudah dan kompak mengonversi faktor menjadi string atau angka seperti ini:

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

Ini juga berfungsi untuk nilai numerik, asalkan Anda memasukkan ekspresi Anda 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
sumber
Jawaban ini tidak mengatasi masalah, yaitu bagaimana cara mengubah semua kolom faktor dalam bingkai data saya menjadi karakter. as.character(f), lebih baik dalam keterbacaan dan efisiensi levels(f)[as.numeric(f)]. Jika Anda ingin menjadi pintar, Anda bisa menggunakannya levels(f)[f]. Perhatikan bahwa ketika mengonversi faktor dengan nilai numerik, Anda memang mendapatkan manfaat dari as.numeric(levels(f))[f]lebih, misalnya as.numeric(as.character(f)), tetapi ini karena Anda hanya perlu mengonversi level menjadi numerik lalu subset. as.character(f)tidak apa-apa.
De Novo
20

Jika Anda ingin bingkai data baru di bobcmana setiap vektor faktor bobfdikonversi menjadi vektor karakter, coba ini:

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

Jika Anda ingin mengubahnya kembali, Anda dapat membuat vektor logis dari kolom mana yang merupakan faktor, dan menggunakannya untuk menerapkan faktor secara selektif.

f <- sapply(bobf, class) == "factor"
bobc[,f] <- lapply(bobc[,f], factor)
scentoni
sumber
2
+1 hanya untuk melakukan apa yang diperlukan (yaitu tidak mengubah seluruh data.frame menjadi karakter). Solusi ini kuat untuk kerangka data yang berisi tipe campuran.
Joshua Ulrich
3
Contoh ini harus di bagian `Contoh 'untuk rapply, seperti di: stat.ethz.ch/R-manual/R-devel/library/base/html/rapply.html . Adakah yang tahu bagaimana cara meminta itu?
mpettis
Jika Anda ingin berakhir dengan bingkai data, bungkus sederhana rapply dalam panggilan data.frame (menggunakan stringsAsFactors yang disetel ke argumen FALSE)
Situs Web Taylored
13

Saya biasanya membuat fungsi ini terpisah dari semua proyek saya. Cepat dan mudah.

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

Cara lain adalah mengonversinya menggunakan apply

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

Dan yang lebih baik (yang sebelumnya adalah kelas 'matriks')

bob2 <- as.data.frame(as.matrix(bob),stringsAsFactors=F)
George Dontas
sumber
Mengikuti komentar @ Shane: untuk mendapatkan data.frame, doas.data.frame(lapply(...
aL3xa
7

Pembaruan: Berikut adalah contoh dari sesuatu yang tidak berfungsi. Saya pikir itu akan, tetapi saya berpikir bahwa opsi stringsAsFactors hanya bekerja pada string karakter - ia meninggalkan faktor sendirian.

Coba ini:

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

Secara umum, setiap kali Anda mengalami masalah dengan faktor-faktor yang seharusnya menjadi karakter, ada stringsAsFactorspengaturan di suatu tempat untuk membantu Anda (termasuk pengaturan global).

Matt Parker
sumber
1
Ini berhasil, jika ia mengaturnya saat membuat bobuntuk memulai (tetapi tidak setelah fakta).
Shane
Baik. Hanya ingin menjadi jelas bahwa ini tidak menyelesaikan masalah, per se - tetapi terima kasih telah mencatat bahwa itu memang mencegahnya.
Matt Parker
7

Atau Anda dapat mencoba transform:

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

Pastikan untuk memasukkan setiap faktor yang ingin dikonversi menjadi karakter.

Atau Anda dapat melakukan sesuatu seperti ini dan membunuh semua hama dengan satu pukulan:

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)

Ini bukan ide yang baik untuk mendorong data dalam kode seperti ini, aku bisa melakukansapply bagian secara terpisah (sebenarnya, itu jauh lebih mudah untuk melakukannya seperti itu), tetapi Anda mendapatkan intinya ... Saya belum memeriksa kode, karena Saya tidak di rumah, jadi saya harap itu berhasil! =)

Pendekatan ini, bagaimanapun, memiliki kerugian ... Anda harus mengatur ulang kolom setelahnya, sementara dengan transformAnda dapat melakukan apa pun yang Anda suka, tetapi dengan biaya "wrest-style-code-writting" ...

Jadi disana ... =)

aL3xa
sumber
6

Di awal bingkai data Anda termasuk stringsAsFactors = FALSEuntuk mengabaikan semua kesalahpahaman.


sumber
4

Jika Anda akan menggunakan data.tablepaket untuk operasi pada data.frame maka masalahnya tidak ada.

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

Jika Anda sudah memiliki kolom faktor dalam dataset Anda dan Anda ingin mengonversinya menjadi karakter, Anda dapat melakukan hal berikut.

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
sumber
DT menghindari perbaikan yang diajukan oleh 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.Lebih mudah untuk memperbaiki DF dan membuat ulang DT.
Matt Chambers
2

Ini bekerja untuk saya - saya akhirnya menemukan satu liner

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

Fungsi ini berfungsi

df <- stacomirtools::killfactor(df)
Cedric
sumber
2

Mungkin opsi yang lebih baru?

library("tidyverse")

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

Anda harus menggunakan convertdi hablaryang memberikan sintaks dibaca kompatibel dengan tidyversepipa:

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

yang memberi Anda:

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

Dengan penggunaan dplyrpaket-dimuat

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

jika Anda hanya ingin mengubah phenotypekolom-khusus.

nexonvantec
sumber
0

Ini berfungsi mentransformasikan semua ke karakter dan kemudian angka ke angka:

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

Diadaptasi dari: Dapatkan tipe kolom lembar excel secara otomatis

Ferroao
sumber