R - Как использовать sum и group_by внутри применяются? - PullRequest
0 голосов
/ 13 декабря 2018

Я довольно новичок в R, и у меня возникает следующая проблема.

У меня есть такой фрейм данных:

A | B | C | E | F |G 
1   02 XXX XXX XXX 1
1   02 XXX XXX XXX 1
2   02 XXX XXX XXX NA
2   02 XXX XXX XXX NA
3   02 XXX XXX XXX 1
3   Z1 XXX XXX XXX 1
4   02 XXX XXX XXX 2
....
M   02 XXX XXX XXX 1 

Дело в том, что фрейм данных может содержать 150 тыс. Строк илибольше, и мне нужно сгенерировать другую группировку данных по A (которая является идентификатором) и подсчитать следующие вхождения:

Когда B равно 02 и G имеет 1 <- V <br>Когда B равно 02и G представляет собой NA <- W <br>Когда B представляет собой Z1 и G имеет 1 <- X <br>Когда B представляет собой Z1 и G представляет собой NA <- Y <br>Любой другой вид вхождения <- Z <br>

В этом простом примере результат должен выглядеть примерно так:

A | V | W | X | Y | Z
1   2   0   0   0   0
2   0   2   0   0   0
3   1   1   0   0   0
4   0   0   0   0   1
...
M   1   0   0   0   0

На данный момент мне удалось получить результаты с помощью цикла for:

  get_counters <- function(df){

  counters <- data.frame(matrix(ncol = 6, nrow = length(unique(df$A))))
  colnames(counters) <- c("A", "V", "W", "X", "Y", "Z")

  counters$A<- unique(df$A)

  for (i in 1:nrow(counters)) {
    counters$V[i] <- sum(df$A == counters$A[i] & df$B == "02" & df$G == 1, na.rm = TRUE)
    counters$W[i] <- sum(df$A == counters$A[i] & df$B == "02" & is.na(df$G), na.rm = TRUE)
    counters$X[i] <- sum(df$A == counters$A[i] & df$B == "Z1" & df$G== 1, na.rm = TRUE)
    counters$Y[i] <- sum(df$A == counters$A[i] & df$B == "Z1" & is.na(df$G), na.rm = TRUE)
    counters$Z[i] <- sum(df$A == counters$A[i] & (df$B == "Z1" | df$B == "02") & df$G!= 1, na.rm = TRUE)
  }

  return(counters)
}

Попыткачто на небольшом тесте датафрейм возвращает все правильные результаты, но с реальными данными это крайне медленно.Я не уверен, как использовать функции apply, кажется простой проблемой, но я не нашел ответа.До сих пор я предполагал, что если бы я мог использовать apply с оператором sum в моем цикле for (возможно, используя group_by(A)), я мог бы это сделать, но я получаю все виды ошибок.

counters$V <- df%>%
                group_by(A)%>%
                sum(df$A == counters$A& df$B == "02" &df$G == 1, na.rm = TRUE)
Error in FUN(X[[i]], ...) : 
  only defined on a data frame with all numeric variables
In addition: Warning message:
In df$A== counters$A:
  longer object length is not a multiple of shorter object length

Если я изменю функцию, чтобы не использовать цикл for и не использовал $ (я получаю сообщение об ошибке "$ operator is invalid for atomic vectors"), я либо получаю больше ошибок, либо странные нечитаемые результаты (Большие списки, которые содержат больше значений, чем исходный фрейм данных, огромные пустые матрицы и т. Д.)

Существует ли простой (возможно, не простой, но быстрый и эффективный) способ решения этой проблемы?Заранее спасибо.

1 Ответ

0 голосов
/ 13 декабря 2018

Вы можете сделать это очень быстро, используя data.table.

Создание фиктивных данных:

set.seed(123)
counters <- data.frame(A = rep(1:100000, each = 3), B = sample(c("02","Z1"), size = 300000, replace = T), G = sample(c(1,NA), size = 300000, replace = T))

Все, что я делаю, - это подсчет экземпляров комбинации, а затем изменение формы данные в нужном вам формате:

library(data.table)
setDT(counters)
counters[,comb := paste0(B,"_",G)]
dcast(counters, A ~ comb, fun.aggregate = length, value.var = "A")
             A 02_1 02_NA Z1_1 Z1_NA
     1:      1    0     2    1     0
     2:      2    1     0    1     1
     3:      3    0     0    2     1
     4:      4    1     1    0     1
     5:      5    0     1    2     0
    ---                             
 99996:  99996    0     1    1     1
 99997:  99997    0     2    1     0
 99998:  99998    2     0    1     0
 99999:  99999    1     0    1     1
100000: 100000    0     2    0     1

Я принял соглашение об именах, которое немного более расширяемо (новые столбцы показывают, какую комбинацию вы считаете), но если вы хотите переопределить,замените строку comb := четырьмя строками, как показано ниже:

counters[B == "02" & is.na(G), comb := "V"]
counters[B == "02" & !is.na(G), comb := "X"]
....

Но я думаю, что приведенное выше немного более гибко.

...