r data.table

Convertir clases de columna en data.table



(6)

Tengo un problema al utilizar data.table: ¿cómo convierto las clases de columna? Aquí hay un ejemplo simple: con data.frame no tengo problemas para convertirlo, con data.table simplemente no sé cómo:

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)

¿Extraño algo obvio aquí?

Actualización debido a la publicación de Matthew: utilicé una versión anterior antes, pero incluso después de actualizar a 1.6.6 (la versión que uso ahora) sigo recibiendo un error.

Actualización 2: supongamos que quiero convertir cada columna del "factor" de clase en una columna de "carácter", pero no sé de antemano qué columna es de qué clase. Con un data.frame, puedo hacer lo siguiente:

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

¿Puedo hacer algo similar con data.table?

Actualización 3:

sessionInfo () R versión 2.13.1 (2011-07-08) Plataforma: 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


¡Esta es una MALA manera de hacerlo! Solo estoy dejando esta respuesta en caso de que resuelva otros problemas extraños. Estos mejores métodos son probablemente el resultado, en parte, de las versiones más nuevas de data.table ... por lo que vale la pena documentar de esta manera. Además, este es un buen ejemplo de sintaxis para la sintaxis de substitute eval .

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)

que te da

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>


Intenté varios enfoques.

# 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...

, o de otro modo

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


Para una sola columna:

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

Usando lapply y 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" ...


Proporciono una forma más general y segura de hacer esto,

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

La función .. se asegura de que obtengamos una variable fuera del alcance de data.table; set_colclass establecerá las clases de tus cols. Puedes usarlo así:

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


Prueba esto

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]


tratar:

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