Mengonversi kelas kolom di data.table


118

Saya punya masalah menggunakan data.table: Bagaimana cara mengonversi kelas kolom? Berikut ini contoh sederhananya: Dengan data.frame saya tidak memiliki masalah untuk mengubahnya, dengan data.table saya tidak tahu caranya:

df <- data.frame(ID=c(rep("A", 5), rep("B",5)), Quarter=c(1:5, 1:5), value=rnorm(10))
#One way: http://stackoverflow.com/questions/2851015/r-convert-data-frame-columns-from-factors-to-characters
df <- data.frame(lapply(df, as.character), stringsAsFactors=FALSE)
#Another way
df[, "value"] <- as.numeric(df[, "value"])

library(data.table)
dt <- data.table(ID=c(rep("A", 5), rep("B",5)), Quarter=c(1:5, 1:5), value=rnorm(10))
dt <- data.table(lapply(dt, as.character), stringsAsFactors=FALSE) 
#Error in rep("", ncol(xi)) : invalid 'times' argument
#Produces error, does data.table not have the option stringsAsFactors?
dt[, "ID", with=FALSE] <- as.character(dt[, "ID", with=FALSE]) 
#Produces error: Error in `[<-.data.table`(`*tmp*`, , "ID", with = FALSE, value = "c(1, 1, 1, 1, 1, 2, 2, 2, 2, 2)") : 
#unused argument(s) (with = FALSE)

Apakah saya melewatkan sesuatu yang jelas di sini?

Pembaruan karena posting Matthew: Saya menggunakan versi yang lebih lama sebelumnya, tetapi bahkan setelah memperbarui ke 1.6.6 (versi yang saya gunakan sekarang) saya masih mendapatkan kesalahan.

Pembaruan 2: Katakanlah saya ingin mengonversi setiap kolom dari kelas "faktor" ke kolom "karakter", tetapi tidak tahu sebelumnya kolom mana dari kelas yang mana. Dengan data.frame, saya dapat melakukan hal berikut:

classes <- as.character(sapply(df, class))
colClasses <- which(classes=="factor")
df[, colClasses] <- sapply(df[, colClasses], as.character)

Bisakah saya melakukan sesuatu yang serupa dengan data.table?

Pembaruan 3:

sessionInfo () R versi 2.13.1 (2011-07-08) Platform: x86_64-pc-mingw32 / x64 (64-bit)

locale:
[1] C

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] data.table_1.6.6

loaded via a namespace (and not attached):
[1] tools_2.13.1

Argumen operator "[" dalam data.tablemetode berbeda dari yang ada untukdata.frame
IRTFM

1
Harap tempel kesalahan yang sebenarnya daripada #Produces error. Tetap +1. Saya tidak mendapatkan kesalahan apa pun, versi mana yang Anda miliki? Ada masalah di area ini meskipun, ini telah diangkat sebelumnya, FR # 1224 dan FR # 1493 adalah prioritas tinggi untuk diatasi. Namun, jawaban Andrie adalah cara terbaik.
Matt Dowle

Maaf @MatthewDowle karena melewatkannya di pertanyaan saya, saya memperbarui posting saya.
Christoph_J

1
@ChristophJ Terima kasih. Apakah Anda yakin tentang invalid times argumentkesalahan itu? Bekerja dengan baik untuk saya. Versi mana yang kamu punya
Matt Dowle

Saya memperbarui posting saya dengan sessionInfo (). Namun, saya memeriksanya di mesin kerja saya hari ini. Kemarin, di mesin rumah saya (Ubuntu), kesalahan yang sama terjadi. Saya akan memperbarui R dan melihat apakah masalahnya masih ada.
Christoph_J

Jawaban:


104

Untuk satu kolom:

dtnew <- dt[, Quarter:=as.character(Quarter)]
str(dtnew)

Classes ‘data.table’ and 'data.frame':  10 obs. of  3 variables:
 $ ID     : Factor w/ 2 levels "A","B": 1 1 1 1 1 2 2 2 2 2
 $ Quarter: chr  "1" "2" "3" "4" ...
 $ value  : num  -0.838 0.146 -1.059 -1.197 0.282 ...

Menggunakan lapplydan as.character:

dtnew <- dt[, lapply(.SD, as.character), by=ID]
str(dtnew)

Classes ‘data.table’ and 'data.frame':  10 obs. of  3 variables:
 $ ID     : Factor w/ 2 levels "A","B": 1 1 1 1 1 2 2 2 2 2
 $ Quarter: chr  "1" "2" "3" "4" ...
 $ value  : chr  "1.487145280568" "-0.827845218358881" "0.028977182770002" "1.35392750102305" ...

2
@Christoph_J Tolong tunjukkan perintah pengelompokan yang Anda perjuangkan (masalah sebenarnya). Pikirkan Anda mungkin melewatkan sesuatu yang sederhana. Mengapa Anda mencoba mengonversi kelas kolom?
Matt Dowle

1
@Christoph_J Jika Anda kesulitan memanipulasi data.tables, mengapa tidak mengonversinya untuk sementara ke data.frames, melakukan pembersihan data dan kemudian mengubahnya kembali ke data.tables?
Andrie

17
Apa cara idiomatik untuk melakukan ini untuk subset kolom (bukan semuanya)? Saya telah mendefinisikan vektor karakter convcolskolom. dt[,lapply(.SD,as.numeric),.SDcols=convcols]hampir instan sementara dt[,convcols:=lapply(.SD,as.numeric),.SDcols=convcols]hampir macet R, jadi saya rasa saya salah melakukannya. Terima kasih
Frank

4
@Frank Lihat komentar Matt Dowle untuk jawaban Geneorama di bawah ini ( stackoverflow.com/questions/7813578/… ); itu sangat membantu dan cukup idiomatis bagi saya [mulai kutipan] Cara lain dan lebih mudah adalah dengan menggunakan set()misalnya for (col in names_factors) set(dt, j=col, value=as.factor(dt[[col]]))[kutipan akhir]
swihart

4
Mengapa Anda menggunakan opsi by = ID?
skan

48

Coba ini

DT <- data.table(X1 = c("a", "b"), X2 = c(1,2), X3 = c("hello", "you"))
changeCols <- colnames(DT)[which(as.vector(DT[,lapply(.SD, class)]) == "character")]

DT[,(changeCols):= lapply(.SD, as.factor), .SDcols = changeCols]

7
sekarang Anda dapat menggunakan Filterfungsi untuk mengidentifikasi kolom, misalnya: changeCols<- names(Filter(is.character, DT))
David Leal

1
IMO ini adalah jawaban yang lebih baik, karena alasan yang saya berikan dalam jawaban yang dipilih.
James Hirschorn

1
atau lebih singkat: changeCols <- names(DT)[sapply(DT, is.character)].
sindri_baldur

8

Mengangkat komentar Matt Dowle untuk jawaban Geneorama ( https://stackoverflow.com/a/20808945/4241780 ) agar lebih jelas (seperti yang dianjurkan), Anda dapat menggunakan for(...)set(...).


library(data.table)

DT = data.table(a = LETTERS[c(3L,1:3)], b = 4:7, c = letters[1:4])
DT1 <- copy(DT)
names_factors <- c("a", "c")

for(col in names_factors)
  set(DT, j = col, value = as.factor(DT[[col]]))

sapply(DT, class)
#>         a         b         c 
#>  "factor" "integer"  "factor"

Dibuat pada 2020-02-12 oleh paket reprex (v0.3.0)

Lihat komentar Matt lainnya di https://stackoverflow.com/a/33000778/4241780 untuk info lebih lanjut.

Edit.

Seperti dicatat oleh Espen dan in help(set), jmungkin "Nama kolom (karakter) atau angka (bilangan bulat) yang akan diberi nilai ketika kolom sudah ada". Begitu names_factors <- c(1L, 3L)juga akan berhasil.


Anda mungkin ingin menambahkan apa yang names_factorsada di sini. Saya kira itu diambil dari stackoverflow.com/a/20808945/1666063 jadi names_factors = c('fac1', 'fac2')dalam kasus ini - yang merupakan nama kolom. Tapi bisa juga nomor kolom misalnya 1; ncol (dt) yang akan mengonversi semua kolom
Espen Riskedal

@EspenRiskedal Terima kasih poin yang bagus, saya telah mengedit posting untuk membuatnya lebih jelas.
JWilliman

2

Ini cara yang BURUK untuk melakukannya! Saya hanya meninggalkan jawaban ini jika jawaban itu menyelesaikan masalah aneh lainnya. Metode yang lebih baik ini mungkin sebagian hasil dari versi data.table yang lebih baru ... jadi ada baiknya untuk mendokumentasikan dengan cara yang sulit ini. Plus, ini adalah contoh sintaks yang bagus untuk eval substitutesintaks.

library(data.table)
dt <- data.table(ID = c(rep("A", 5), rep("B",5)), 
                 fac1 = c(1:5, 1:5), 
                 fac2 = c(1:5, 1:5) * 2, 
                 val1 = rnorm(10),
                 val2 = rnorm(10))

names_factors = c('fac1', 'fac2')
names_values = c('val1', 'val2')

for (col in names_factors){
  e = substitute(X := as.factor(X), list(X = as.symbol(col)))
  dt[ , eval(e)]
}
for (col in names_values){
  e = substitute(X := as.numeric(X), list(X = as.symbol(col)))
  dt[ , eval(e)]
}

str(dt)

yang memberi Anda

Classes ‘data.table’ and 'data.frame':  10 obs. of  5 variables:
 $ ID  : chr  "A" "A" "A" "A" ...
 $ fac1: Factor w/ 5 levels "1","2","3","4",..: 1 2 3 4 5 1 2 3 4 5
 $ fac2: Factor w/ 5 levels "2","4","6","8",..: 1 2 3 4 5 1 2 3 4 5
 $ val1: num  0.0459 2.0113 0.5186 -0.8348 -0.2185 ...
 $ val2: num  -0.0688 0.6544 0.267 -0.1322 -0.4893 ...
 - attr(*, ".internal.selfref")=<externalptr> 

42
Cara lain dan lebih mudah adalah dengan menggunakan set()misalnyafor (col in names_factors) set(dt, j=col, value=as.factor(dt[[col]]))
Matt Dowle

1
Saya pikir jawaban saya menyelesaikan ini dalam satu baris, untuk semua versi. Tidak yakin apakah setlebih tepat.
Ben Rollert

1
Info lebih lanjut di for(...)set(...)sini: stackoverflow.com/a/33000778/403310
Matt Dowle

1
@kan Pertanyaan yang bagus. Jika Anda tidak dapat menemukannya ditanyakan sebelumnya, silakan ajukan pertanyaan baru. Membantu orang lain di masa depan.
Matt Dowle

1
@skan inilah cara saya melakukannya: github.com/geneorama/geneorama/blob/master/R/…
geneorama

0

Saya mencoba beberapa pendekatan.

# BY {dplyr}
data.table(ID      = c(rep("A", 5), rep("B",5)), 
           Quarter = c(1:5, 1:5), 
           value   = rnorm(10)) -> df1
df1 %<>% dplyr::mutate(ID      = as.factor(ID),
                       Quarter = as.character(Quarter))
# check classes
dplyr::glimpse(df1)
# Observations: 10
# Variables: 3
# $ ID      (fctr) A, A, A, A, A, B, B, B, B, B
# $ Quarter (chr) "1", "2", "3", "4", "5", "1", "2", "3", "4", "5"
# $ value   (dbl) -0.07676732, 0.25376110, 2.47192852, 0.84929175, -0.13567312,  -0.94224435, 0.80213218, -0.89652819...

, atau sebaliknya

# from list to data.table using data.table::setDT
list(ID      = as.factor(c(rep("A", 5), rep("B",5))), 
     Quarter = as.character(c(1:5, 1:5)), 
     value   = rnorm(10)) %>% setDT(list.df) -> df2
class(df2)
# [1] "data.table" "data.frame"

0

Saya memberikan cara yang lebih umum dan lebih aman untuk melakukan hal ini,

".." <- function (x) 
{
  stopifnot(inherits(x, "character"))
  stopifnot(length(x) == 1)
  get(x, parent.frame(4))
}


set_colclass <- function(x, class){
  stopifnot(all(class %in% c("integer", "numeric", "double","factor","character")))
  for(i in intersect(names(class), names(x))){
    f <- get(paste0("as.", class[i]))
    x[, (..("i")):=..("f")(get(..("i")))]
  }
  invisible(x)
}

Fungsi ..tersebut memastikan kita mendapatkan variabel dari ruang lingkup data.table; set_colclass akan mengatur kelas cols Anda. Anda bisa menggunakannya seperti ini:

dt <- data.table(i=1:3,f=3:1)
set_colclass(dt, c(i="character"))
class(dt$i)

-1

Jika Anda memiliki daftar nama kolom di data.table, Anda ingin mengubah kelas do:

convert_to_character <- c("Quarter", "value")

dt[, convert_to_character] <- dt[, lapply(.SD, as.character), .SDcols = convert_to_character]

Jawaban ini pada dasarnya adalah versi buruk dari jawaban @ Nera di bawah ini. Lakukan saja dt[, c(convert_to_character) := lapply(.SD, as.character), .SDcols=convert_to_character]untuk menetapkan dengan referensi, daripada menggunakan tugas data.frame yang lebih lambat.
altabq

-3

mencoba:

dt <- data.table(A = c(1:5), 
                 B= c(11:15))

x <- ncol(dt)

for(i in 1:x) 
{
     dt[[i]] <- as.character(dt[[i]])
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.