r data.table

Uso de lapply.SD en data.table R



(1)

No tengo muy claro el uso de .SD y by .

Por ejemplo, ¿significa el fragmento a continuación: ''cambiar todas las columnas en DT a factor excepto A y B ?'' También dice en el manual data.table : " .SD refiere al Subconjunto de data.table para cada grupo (excluyendo las columnas de agrupación)" - ¿entonces las columnas A y B están excluidas?

DT = DT[ ,lapply(.SD, as.factor), by=.(A,B)]

Sin embargo, también leí eso by medios como ''agrupar por'' en SQL cuando haces la agregación. Por ejemplo, si quisiera sumar (como colsum en SQL) sobre todas las columnas excepto A y B ¿sigo usando algo similar? O, en este caso, ¿significa el siguiente código tomar la suma y agrupar por valores en las columnas A y B ? (tome la suma y agrupe por A,B como en SQL)

DT[,lapply(.SD,sum),by=.(A,B)]

Entonces, ¿cómo hago un simple colsum sobre todas las columnas excepto A y B ?


Solo para ilustrar los comentarios anteriores con un ejemplo, tomemos

set.seed(10238) # A and B are the "id" variables within which the # "data" variables C and D vary meaningfully DT = data.table(A = rep(1:3, each = 5), B = rep(1:5, 3), C = sample(15), D = sample(15)) DT # A B C D # 1: 1 1 14 11 # 2: 1 2 3 8 # 3: 1 3 15 1 # 4: 1 4 1 14 # 5: 1 5 5 9 # 6: 2 1 7 13 # 7: 2 2 2 12 # 8: 2 3 8 6 # 9: 2 4 9 15 # 10: 2 5 4 3 # 11: 3 1 6 5 # 12: 3 2 12 10 # 13: 3 3 10 4 # 14: 3 4 13 7 # 15: 3 5 11 2

Compare lo siguiente:

#Sum all columns DT[ , lapply(.SD, sum)] # A B C D # 1: 30 45 120 120 #Sum all columns EXCEPT A, grouping BY A DT[ , lapply(.SD, sum), by = A] # A B C D # 1: 1 15 38 43 # 2: 2 15 30 49 # 3: 3 15 52 28 #Sum all columns EXCEPT A DT[ , lapply(.SD, sum), .SDcols = !"A"] # B C D # 1: 45 120 120 #Sum all columns EXCEPT A, grouping BY B DT[ , lapply(.SD, sum), by = B, .SDcols = !"A"] # B C D # 1: 1 27 29 # 2: 2 17 30 # 3: 3 33 11 # 4: 4 23 36 # 5: 5 20 14

Algunas notas

  • Dijiste "hace el fragmento de abajo ... cambia todas las columnas en DT ..."

La respuesta es no , y esto es muy importante para data.table . El objeto devuelto es un nuevo data.table , y todas las columnas en DT son exactamente como estaban antes de ejecutar el código.

  • Mencionaste querer cambiar los tipos de columna

Refiriéndose nuevamente al punto anterior, tenga en cuenta que su código ( DT[ , lapply(.SD, as.factor)] ) devuelve un nuevo data.table y no cambia DT en absoluto. Una forma ( incorrecta ) de hacer esto, que se realiza con data.frame s en la base , es sobrescribir la antigua data.table con la nueva data.table que ha devuelto, es decir, DT = DT[ , lapply(.SD, as.factor)] .

Esto es un desperdicio porque implica crear copias de DT que pueden ser un asesino de eficiencia cuando DT es grande. El enfoque correcto de data.table para este problema es actualizar las columnas por referencia usando `:=` , por ejemplo, DT[ , names(DT) := lapply(.SD, as.factor)] , que no crea copias de su datos. Consulte la viñeta semántica de referencia de data.table para obtener más información al respecto.

  • Usted mencionó comparar la eficiencia de lapply(.SD, sum) con la de colSums . sum está optimizado internamente en data.table (puede observar que esto es cierto a partir de la salida de agregar el argumento verbose = TRUE dentro de [] ); para ver esto en acción, DT un poco su DT y ejecutemos un punto de referencia:

Resultados:

library(data.table) set.seed(12039) nn = 1e7; kk = seq(100L) DT = as.data.table(replicate(26L, sample(kk, nn, TRUE))) DT[ , LETTERS[1:2] := .(sample(100L, nn, TRUE), sample(100L, nn, TRUE))] library(microbenchmark) microbenchmark(times = 100L, colsums = colSums(DT[ , !c("A", "B"), with = FALSE]), lapplys = DT[ , lapply(.SD, sum), .SDcols = !c("A", "B")]) # Unit: milliseconds # expr min lq mean median uq max neval # colsums 1624.2622 2020.9064 2028.9546 2034.3191 2049.9902 2140.8962 100 # lapplys 246.5824 250.3753 252.9603 252.1586 254.8297 266.1771 100