Formas más rápidas de calcular frecuencias y transmitir de largo a ancho

Resuelto user592419 asked hace 54 años • 4 respuestas

Estoy intentando obtener recuentos de cada combinación de niveles de dos variables, "semana" e "id". Me gustaría que el resultado tuviera "id" como filas, "semana" como columnas y los recuentos como valores.

Ejemplo de lo que he probado hasta ahora (probé muchas otras cosas, incluida la adición de una variable ficticia = 1 y luego fun.aggregate = sumsobre eso):

library(plyr)
ddply(data, .(id), dcast, id ~ week, value_var = "id", 
        fun.aggregate = length, fill = 0, .parallel = TRUE)

Sin embargo, debo estar haciendo algo mal porque esta función no finaliza. ¿Hay una mejor manera de hacer esto?

Aporte:

id      week
1       1
1       2
1       3
1       1
2       3

Producción:

  1  2  3
1 2  1  1
2 0  0  1
user592419 avatar Jan 01 '70 08:01 user592419
Aceptado

Podrías usar el tablecomando:

table(data$id,data$week)

    1 2 3
  1 2 1 1
  2 0 0 1

Si "id" y "semana" son las únicas columnas en su marco de datos, simplemente puede usar:

table(data)
#    week
# id  1 2 3
#   1 2 1 1
#   2 0 0 1
Joshua Ulrich avatar Nov 18 '2011 17:11 Joshua Ulrich

La razón ddplypor la que se tarda tanto es que la división por grupo no se ejecuta en paralelo (solo los cálculos de las 'divisiones'), por lo tanto, con una gran cantidad de grupos será lento (y .parallel = T) no ayudará.

Un enfoque que utilice data.table::dcast( data.tableversión >= 1.9.2) debería ser extremadamente eficiente en tiempo y memoria. En este caso, podemos confiar en los valores de argumento predeterminados y simplemente usar:

library(data.table) 
dcast(setDT(data), id ~ week)
# Using 'week' as value column. Use 'value.var' to override
# Aggregate function missing, defaulting to 'length'
#    id 1 2 3
# 1:  1 2 1 1
# 2:  2 0 0 1

O establecer los argumentos explícitamente:

dcast(setDT(data), id ~ week, value.var = "week", fun = length)
#    id 1 2 3
# 1:  1 2 1 1
# 2:  2 0 0 1

Para data.tablealternativas anteriores a 1.9.2, consulte las ediciones.

mnel avatar Sep 14 '2012 02:09 mnel

Una tidyverseopción podría ser:

library(dplyr)
library(tidyr)

df %>%
  count(id, week) %>%
  pivot_wider(names_from = week, values_from = n, values_fill = list(n = 0))
  #spread(week, n, fill = 0) #In older version of tidyr

#     id   `1`   `2`   `3`
#   <dbl> <dbl> <dbl> <dbl>
#1     1     2     1     1
#2     2     0     0     1

Usando solo pivot_wider-

tidyr::pivot_wider(df, names_from = week, 
                   values_from = week, values_fn = length, values_fill = 0)

O usando tabyldesde janitor:

janitor::tabyl(df, id, week)
# id 1 2 3
#  1 2 1 1
#  2 0 0 1

datos

df <- structure(list(id = c(1L, 1L, 1L, 1L, 2L), week = c(1L, 2L, 3L, 
1L, 3L)), class = "data.frame", row.names = c(NA, -5L))
Ronak Shah avatar Feb 05 '2019 00:02 Ronak Shah