Bagaimana cara mengkonversi faktor ke integer \ numeric tanpa kehilangan informasi?

599

Ketika saya mengonversi faktor menjadi numerik atau bilangan bulat, saya mendapatkan kode level yang mendasarinya, bukan nilai sebagai angka.

f <- factor(sample(runif(5), 20, replace = TRUE))
##  [1] 0.0248644019011408 0.0248644019011408 0.179684827337041 
##  [4] 0.0284090070053935 0.363644931698218  0.363644931698218 
##  [7] 0.179684827337041  0.249704354675487  0.249704354675487 
## [10] 0.0248644019011408 0.249704354675487  0.0284090070053935
## [13] 0.179684827337041  0.0248644019011408 0.179684827337041 
## [16] 0.363644931698218  0.249704354675487  0.363644931698218 
## [19] 0.179684827337041  0.0284090070053935
## 5 Levels: 0.0248644019011408 0.0284090070053935 ... 0.363644931698218

as.numeric(f)
##  [1] 1 1 3 2 5 5 3 4 4 1 4 2 3 1 3 5 4 5 3 2

as.integer(f)
##  [1] 1 1 3 2 5 5 3 4 4 1 4 2 3 1 3 5 4 5 3 2

Saya harus menggunakan pasteuntuk mendapatkan nilai-nilai nyata:

as.numeric(paste(f))
##  [1] 0.02486440 0.02486440 0.17968483 0.02840901 0.36364493 0.36364493
##  [7] 0.17968483 0.24970435 0.24970435 0.02486440 0.24970435 0.02840901
## [13] 0.17968483 0.02486440 0.17968483 0.36364493 0.24970435 0.36364493
## [19] 0.17968483 0.02840901

Apakah ada cara yang lebih baik untuk mengubah faktor menjadi angka?

Adam SO
sumber
6
Tingkatan faktor disimpan sebagai tipe data karakter ( attributes(f)), jadi saya tidak berpikir ada yang salah dengan itu as.numeric(paste(f)). Mungkin akan lebih baik untuk berpikir mengapa (dalam konteks tertentu) Anda mendapatkan faktor di tempat pertama, dan mencoba untuk menghentikannya. Misalnya, apakah decargumen dalam read.tableset dengan benar?
CJB
Jika Anda menggunakan dataframe, Anda bisa menggunakan convert dari hablar. df %>% convert(num(column)). Atau jika Anda memiliki vektor faktor yang dapat Anda gunakanas_reliable_num(factor_vector)
davsjob

Jawaban:

711

Lihat bagian Peringatan ?factor:

Secara khusus, as.numericditerapkan pada suatu faktor tidak ada artinya, dan dapat terjadi dengan paksaan implisit. Untuk mengubah faktor fmenjadi sekitar nilai numerik aslinya, as.numeric(levels(f))[f]disarankan dan sedikit lebih efisien daripada as.numeric(as.character(f)).

FAQ tentang R memiliki saran serupa .


Mengapa as.numeric(levels(f))[f]lebih efisien daripada as.numeric(as.character(f))?

as.numeric(as.character(f))efektif as.numeric(levels(f)[f]), sehingga Anda melakukan konversi ke numerik pada length(x)nilai, bukan pada nlevels(x)nilai. Perbedaan kecepatan akan paling jelas untuk vektor panjang dengan beberapa level. Jika sebagian besar nilai unik, tidak akan ada banyak perbedaan dalam kecepatan. Bagaimanapun Anda melakukan konversi, operasi ini tidak mungkin menjadi hambatan dalam kode Anda, jadi jangan terlalu khawatir tentang hal itu.


Beberapa pengaturan waktu

library(microbenchmark)
microbenchmark(
  as.numeric(levels(f))[f],
  as.numeric(levels(f)[f]),
  as.numeric(as.character(f)),
  paste0(x),
  paste(x),
  times = 1e5
)
## Unit: microseconds
##                         expr   min    lq      mean median     uq      max neval
##     as.numeric(levels(f))[f] 3.982 5.120  6.088624  5.405  5.974 1981.418 1e+05
##     as.numeric(levels(f)[f]) 5.973 7.111  8.352032  7.396  8.250 4256.380 1e+05
##  as.numeric(as.character(f)) 6.827 8.249  9.628264  8.534  9.671 1983.694 1e+05
##                    paste0(x) 7.964 9.387 11.026351  9.956 10.810 2911.257 1e+05
##                     paste(x) 7.965 9.387 11.127308  9.956 11.093 2419.458 1e+05
Joshua Ulrich
sumber
4
Untuk pengaturan waktu, lihat jawaban ini: stackoverflow.com/questions/6979625/…
Ari B. Friedman
3
Terima kasih banyak atas solusi Anda. Bisakah saya bertanya mengapa as.numeric (level (f)) [f] lebih tepat dan lebih cepat? Terima kasih.
Sam
7
@ Sam as.character (f) memerlukan "pencarian primitif" untuk menemukan fungsi as.character.factor (), yang didefinisikan sebagai.numeric (level (f)) [f].
Jonathan
12
ketika menerapkan as.numeric (level (f)) [f] OR as.numeric (as.character (f)), saya memiliki pesan peringatan: Pesan peringatan: NAs diperkenalkan dengan paksaan. Apakah Anda tahu di mana masalahnya? Terima kasih !
maycca
@maycca apakah Anda mengatasi masalah ini?
user08041991
91

R memiliki sejumlah fungsi kenyamanan (tanpa dokumen) untuk mengonversi faktor:

  • as.character.factor
  • as.data.frame.factor
  • as.Date.factor
  • as.list.factor
  • as.vector.factor
  • ...

Tapi yang menjengkelkan, tidak ada yang menangani faktor -> konversi numerik . Sebagai perpanjangan dari jawaban Joshua Ulrich, saya akan menyarankan untuk mengatasi kelalaian ini dengan definisi fungsi idiomatik Anda sendiri:

as.numeric.factor <- function(x) {as.numeric(levels(x))[x]}

Anda dapat menyimpan di awal skrip Anda, atau bahkan lebih baik di .Rprofilefile Anda .

Jealie
sumber
14
Tidak ada yang menangani konversi faktor-ke-integer (atau numerik) karena diharapkan akan as.integer(factor)mengembalikan kode integer yang mendasarinya (seperti yang ditunjukkan pada bagian contoh ?factor). Mungkin boleh saja mendefinisikan fungsi ini di lingkungan global Anda, tetapi Anda mungkin menimbulkan masalah jika Anda benar-benar mendaftarkannya sebagai metode S3.
Joshua Ulrich
1
Itu poin yang bagus dan saya setuju: redefinisi lengkap dari faktor-> konversi numerik cenderung mengacaukan banyak hal. Saya menemukan diri saya menulis rumit factor->numerickonversi banyak sebelum menyadari bahwa itu sebenarnya kelemahan dari R: beberapa fungsi kenyamanan harus tersedia ... Menyebutnya as.numeric.factormasuk akal bagi saya, tapi YMMV.
Jealie
4
Jika Anda menemukan diri Anda melakukan hal itu banyak , maka Anda harus melakukan sesuatu hulu untuk menghindari itu semua-bersama-sama.
Joshua Ulrich
2
as.numeric.factor mengembalikan NA?
jO.
@ jo: dalam kasus di mana Anda menggunakan sesuatu seperti v=NA;as.numeric.factor(v)atau v='something';as.numeric.factor(v), maka seharusnya, kalau tidak, Anda memiliki hal aneh terjadi di suatu tempat.
Jealie
33

Cara paling mudah adalah menggunakan unfactorfungsi dari paket varhandle

unfactor(your_factor_variable)

Contoh ini bisa menjadi awal yang cepat:

x <- rep(c("a", "b", "c"), 20)
y <- rep(c(1, 1, 0), 20)

class(x)  # -> "character"
class(y)  # -> "numeric"

x <- factor(x)
y <- factor(y)

class(x)  # -> "factor"
class(y)  # -> "factor"

library(varhandle)
x <- unfactor(x)
y <- unfactor(y)

class(x)  # -> "character"
class(y)  # -> "numeric"
Mehrad Mahmoudian
sumber
The unfactorbertobat fungsi untuk tipe data karakter pertama dan kemudian bertobat kembali ke numerik. Ketik unfactordi konsol dan Anda dapat melihatnya di tengah fungsi. Karena itu tidak benar-benar memberikan solusi yang lebih baik daripada apa yang sudah dimiliki penanya.
CJB
Karena itu, level faktor adalah tipe karakter, jadi tidak ada yang hilang dengan pendekatan ini.
CJB
The unfactorFungsi mengurus hal-hal yang tidak dapat dikonversi ke numerik. Lihat contoh dihelp("unfactor")
Mehrad Mahmoudian
2
@ Selrac Saya sudah menyebutkan bahwa fungsi ini tersedia dalam paket varhandle , artinya Anda harus memuat paket ( library("varhandle")) terlebih dahulu (seperti yang saya sebutkan di baris pertama dari jawaban saya !!)
Mehrad Mahmoudian
1
@ Gregor menambahkan ketergantungan cahaya tidak membahayakan biasanya dan tentu saja jika Anda mencari cara yang paling efisien, menulis kode diri Anda mungkin melakukan lebih cepat. tetapi seperti yang dapat Anda lihat dalam komentar Anda, ini tidak sepele karena Anda juga meletakkan as.numeric()dan as.character()dalam urutan yang salah;) Apa yang dilakukan oleh kode Anda adalah mengubah indeks level faktor menjadi matriks karakter, jadi apa yang akan Anda miliki di dan adalah vektor karakter yang berisi beberapa angka yang pernah ditetapkan ke tingkat tertentu dari faktor Anda. Fungsi-fungsi dalam paket itu ada untuk mencegah kebingungan ini
Mehrad Mahmoudian
23

Catatan: jawaban khusus ini bukan untuk mengonversi faktor bernilai numerik menjadi angka, melainkan untuk mengonversi faktor kategori ke angka level yang sesuai.


Setiap jawaban dalam posting ini gagal menghasilkan hasil untuk saya, NA semakin dihasilkan.

y2<-factor(c("A","B","C","D","A")); 
as.numeric(levels(y2))[y2] 
[1] NA NA NA NA NA Warning message: NAs introduced by coercion

Apa yang berhasil untuk saya adalah ini -

as.integer(y2)
# [1] 1 2 3 4 1
Indi
sumber
Apakah Anda yakin memiliki faktor? Lihatlah contoh ini. y<-factor(c("5","15","20","2")); unclass(y) %>% as.numericIni mengembalikan 4,1,3,2, bukan 5,15,20,2. Ini sepertinya informasi yang salah.
MrFlick
Ok, ini mirip dengan apa yang saya coba lakukan hari ini: - y2 <-factor (c ("A", "B", "C", "D", "A")); as.numeric (level (y2)) [y2] [1] NA NA NA NA NA Pesan peringatan: NAS diperkenalkan oleh paksaan sedangkan unclass (y2)%>% as.numeric memberi saya hasil yang saya butuhkan.
Indi
4
OKE, nah itu bukan pertanyaan yang ditanyakan di atas. Dalam pertanyaan ini level faktor semuanya "numerik". Dalam kasus Anda, as.numeric(y)seharusnya bekerja dengan baik, tidak perlu unclass(). Tetapi sekali lagi, bukan itu pertanyaannya. Jawaban ini tidak sesuai di sini.
MrFlick
3
Yah, aku benar-benar berharap ini membantu seseorang yang sedang terburu-buru sepertiku dan membaca judulnya saja!
Indi
1
Jika Anda memiliki karakter yang mewakili bilangan bulat sebagai faktor, inilah yang akan saya rekomendasikan. ini satu-satunya yang bekerja untuk saya.
aimme
9

Mungkin hanya dalam kasus ketika label faktor cocok dengan nilai asli. Saya akan menjelaskannya dengan sebuah contoh.

Anggap datanya vektor x:

x <- c(20, 10, 30, 20, 10, 40, 10, 40)

Sekarang saya akan membuat faktor dengan empat label:

f <- factor(x, levels = c(10, 20, 30, 40), labels = c("A", "B", "C", "D"))

1) xdengan tipe ganda, fdengan tipe integer. Ini adalah kehilangan informasi pertama yang tak terhindarkan. Faktor selalu disimpan sebagai bilangan bulat.

> typeof(x)
[1] "double"
> typeof(f)
[1] "integer"

2) Tidak mungkin kembali ke nilai semula (10, 20, 30, 40) hanya ftersedia. Kita dapat melihat bahwa fhanya memegang nilai integer 1, 2, 3, 4 dan dua atribut - daftar label ("A", "B", "C", "D") dan atribut kelas "faktor". Tidak ada lagi.

> str(f)
 Factor w/ 4 levels "A","B","C","D": 2 1 3 2 1 4 1 4
> attributes(f)
$levels
[1] "A" "B" "C" "D"

$class
[1] "factor"

Untuk mengembalikan kembali ke nilai asli kita harus mengetahui nilai level yang digunakan dalam menciptakan faktor. Dalam hal ini c(10, 20, 30, 40). Jika kita mengetahui level aslinya (dalam urutan yang benar), kita dapat kembali ke nilai semula.

> orig_levels <- c(10, 20, 30, 40)
> x1 <- orig_levels[f]
> all.equal(x, x1)
[1] TRUE

Dan ini hanya akan berfungsi jika label telah ditentukan untuk semua nilai yang mungkin dalam data asli.

Jadi, jika Anda membutuhkan nilai-nilai asli, Anda harus menyimpannya. Kalau tidak, ada kemungkinan besar tidak akan mungkin untuk kembali kepada mereka hanya dari faktor.

djhurio
sumber
2

Anda dapat menggunakan hablar::convertjika Anda memiliki bingkai data. Sintaksnya mudah:

Contoh df

library(hablar)
library(dplyr)

df <- dplyr::tibble(a = as.factor(c("7", "3")),
                    b = as.factor(c("1.5", "6.3")))

Larutan

df %>% 
  convert(num(a, b))

Memberi anda:

# A tibble: 2 x 2
      a     b
  <dbl> <dbl>
1    7.  1.50
2    3.  6.30

Atau jika Anda ingin satu kolom menjadi bilangan bulat dan satu angka:

df %>% 
  convert(int(a),
          num(b))

menghasilkan:

# A tibble: 2 x 2
      a     b
  <int> <dbl>
1     7  1.50
2     3  6.30
davsjob
sumber
0

Sepertinya solusinya sebagai.numerik (level (f)) [f] tidak lagi berfungsi dengan R 4.0.

Solusi alternatif:

factor2number <- function(x){
    data.frame(levels(x), 1:length(levels(x)), row.names = 1)[x, 1]
}

factor2number(yourFactor)
Life_Searching_Steps
sumber
-1

Dari banyak jawaban yang bisa saya baca, satu-satunya cara yang diberikan adalah memperluas jumlah variabel sesuai dengan jumlah faktor. Jika Anda memiliki variabel "pet" dengan level "dog" dan "cat", Anda akan berakhir dengan pet_dog dan pet_cat.

Dalam kasus saya, saya ingin tetap dengan jumlah variabel yang sama, dengan hanya menerjemahkan variabel faktor ke variabel numerik, dengan cara yang dapat diterapkan ke banyak variabel dengan banyak level, sehingga cat = 1 dan dog = 0 misalnya.

Silakan temukan solusi yang sesuai di bawah ini:

crime <- data.frame(city = c("SF", "SF", "NYC"),
                    year = c(1990, 2000, 1990),
                    crime = 1:3)

indx <- sapply(crime, is.factor)

crime[indx] <- lapply(crime[indx], function(x){ 
  listOri <- unique(x)
  listMod <- seq_along(listOri)
  res <- factor(x, levels=listOri)
  res <- as.numeric(res)
  return(res)
}
)
Xavier Prudent
sumber
-2

terlambat ke permainan, secara tidak sengaja, saya menemukan trimws()dapat mengkonversi factor(3:5)ke c("3","4","5"). Maka Anda bisa menelepon as.numeric(). Itu adalah:

as.numeric(trimws(x_factor_var))
Jerry T
sumber
3
Apakah ada alasan Anda akan merekomendasikan penggunaan trimwslebih dari yang as.characterdijelaskan dalam jawaban yang diterima? Sepertinya saya kecuali Anda benar-benar memiliki spasi putih yang perlu Anda hapus, trimwshanya akan melakukan banyak pekerjaan ekspresi reguler yang tidak perlu untuk mengembalikan hasil yang sama.
MrFlick
as.numeric (level (f)) [f] mungkin agak membingungkan dan sulit diingat untuk pemula. trimws tidak ada salahnya.
Jerry T