Более быстрые способы вычисления частот и приведения от длинного к широкому

#r

#r #агрегировать #plyr #переформирование2

Вопрос:

Я пытаюсь получить подсчеты каждой комбинации уровней двух переменных: «неделя» и «идентификатор». Я бы хотел, чтобы результат имел «id» в виде строк и «week» в виде столбцов, а подсчеты — в качестве значений.

Пример того, что я пробовал до сих пор (пробовал кучу других вещей, включая добавление фиктивной переменной = 1, а затем fun.aggregate = sum поверх этого):

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

Однако я, должно быть, делаю что-то не так, потому что эта функция не завершается. Есть ли лучший способ сделать это?

Ввод:

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

Вывод:

   1  2  3
1 2  1  1
2 0  0  1
 

Ответ №1:

Вы могли бы просто использовать table команду:

 table(data$id,data$week)

    1 2 3
  1 2 1 1
  2 0 0 1
 

Если «id» и «неделя» являются единственными столбцами в вашем фрейме данных, вы можете просто использовать:

 table(data)
#    week
# id  1 2 3
#   1 2 1 1
#   2 0 0 1
 

Комментарии:

1. 1 взрыв. У вас есть умение заставлять мои решения выглядеть совершенно многословными, окольными и пешеходными.

2. Если у вас много данных и операций, которые нельзя так сильно упростить, вам может помочь пакет ‘data.table’.

Ответ №2:

Вам это не нужно ddply . Достаточно dcast from reshape2 :

 dat <- data.frame(
    id = c(rep(1, 4), 2),
    week = c(1:3, 1, 3)
)

library(reshape2)
dcast(dat, id~week, fun.aggregate=length)

  id 1 2 3
1  1 2 1 1
2  2 0 0 1
 

Редактировать: для базового решения R (кроме table — как опубликовано Джошуа Ульрихом), попробуйте xtabs :

 xtabs(~id week, data=dat)

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

Ответ №3:

Причина ddply , по которой это занимает так много времени, заключается в том, что разделение по группам не выполняется параллельно (только вычисления по «разделениям»), поэтому при большом количестве групп это будет медленно (и .parallel = T ) не поможет.

Подход, использующий data.table::dcast ( data.table версия> = 1.9.2), должен быть чрезвычайно эффективным по времени и памяти. В этом случае мы можем полагаться на значения аргументов по умолчанию и просто использовать:

 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
 

Или установка аргументов явно:

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

data.table Альтернативы, существовавшие до версии 1.9.2, см. в разделе Изменения.

Ответ №4:

Одним tidyverse из вариантов может быть :

 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
 

Используя только pivot_wider

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

Или использование tabyl из janitor :

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

данные

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