Uso de lapply .SD en data.table R

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

Por ejemplo, ¿significa el siguiente fragmento: ‘cambiar todas las columnas en DT a factor excepto A y B ?’ También dice en el manual de data.table : ” .SD refiere al subconjunto de la data.table de data.table para cada grupo (excluyendo las columnas de agrupamiento)” – ¿entonces las columnas A y B están excluidas?

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

Sin embargo, también lo leí by medios como ‘agrupar por’ en SQL cuando haces la agregación. Por ejemplo, si quisiera sumr (como colsum en SQL) sobre todas las columnas, excepto A y B ¿sigo usando algo similar? O en este caso, ¿significa el código siguiente tomar la sum y agrupar por valores en las columnas A y B ? (tomar sum y agrupar 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 ?

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 # ABCD # 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)] # ABCD # 1: 30 45 120 120 #Sum all columns EXCEPT A, grouping BY A DT[ , lapply(.SD, sum), by = A] # ABCD # 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"] # BCD # 1: 45 120 120 #Sum all columns EXCEPT A, grouping BY B DT[ , lapply(.SD, sum), by = B, .SDcols = !"A"] # BCD # 1: 1 27 29 # 2: 2 17 30 # 3: 3 33 11 # 4: 4 23 36 # 5: 5 20 14 

Algunas notas:

  • Usted dijo “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 una nueva data.table , y todas las columnas en DT están exactamente como estaban antes de ejecutar el código.

  • Mencionaste querer cambiar los tipos de columnas

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

Esto es un desperdicio, ya que implica la creación de copias de DT que pueden ser un factor decisivo 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 de semántica de referencia de data.table para obtener más información al respecto.

  • Usted mencionó la comparación de 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 del resultado 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