Вставка строк с нулями во фреймы данных с вложенными группами

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

set.seed(123)
df <- data.frame(Gr1 = rep(c("x", "y"), each = 10),
                 Gr2 = rep(c("x1", "x2", "y1", "y2"), each = 5),
                 ID = paste0(rep(c("x", "y"), each = 10), letters[1:5]),
                 var1 = round(rnorm(20), 2),
                 var2 = round(rnorm(20), 2))

rmv.rows <- sample(1:20, 5)
df <- df[-rmv.rows, ]

   Gr1 Gr2 ID  var1  var2
1    x  x1 xa -0.56 -1.07
3    x  x1 xc  1.56 -1.03
4    x  x1 xd  0.07 -0.73
6    x  x2 xa  1.72 -1.69
7    x  x2 xb  0.46  0.84
9    x  x2 xd -0.69 -1.14
10   x  x2 xe -0.45  1.25
11   y  y1 ya  1.22  0.43
12   y  y1 yb  0.36 -0.30
15   y  y1 ye -0.56  0.82
16   y  y2 ya  1.79  0.69
17   y  y2 yb  0.50  0.55
18   y  y2 yc -1.97 -0.06
19   y  y2 yd  0.70 -0.31
20   y  y2 ye -0.47 -0.38

Я хотел бы заполнить недостающие строки (т.е. комбинации Gr1, Gr2 и ID) нулями.

Я пробовал подходы, предложенные здесь, однако он возвращает все возможные комбинации Gr1, Gr2 и ID, а не те, которые присутствуют в данных. Другими словами, я хотел бы вставить только существующие комбинации Gr1, Gr2 и ID. Желаемый результат должен быть:

   Gr1 Gr2 ID  var1  var2
1    x  x1 xa -0.56 -1.07
2    x  x1 xb  0.00  0.00
3    x  x1 xc  1.56 -1.03
4    x  x1 xd  0.07 -0.73
5    x  x1 xe  0.00  0.00
6    x  x2 xa  1.72 -1.69
7    x  x2 xb  0.46  0.84
8    x  x2 xc  0.00  0.00
9    x  x2 xd -0.69 -1.14
10   x  x2 xe -0.45  1.25
11   y  y1 ya  1.22  0.43
12   y  y1 yb  0.36 -0.30
13   y  y1 yc  0.00  0.00
14   y  y1 yd  0.00  0.00
15   y  y1 ye -0.56  0.82
16   y  y2 ya  1.79  0.69
17   y  y2 yb  0.50  0.55
18   y  y2 yc -1.97 -0.06
19   y  y2 yd  0.70 -0.31
20   y  y2 ye -0.47 -0.38

person Adela    schedule 04.06.2019    source источник
comment
Вам нужно complete df %>% group_by(Gr1, Gr2) %>% complete(ID, fill = list(var1 = 0, var2 = 0))   -  person akrun    schedule 04.06.2019
comment
К сожалению, это также создаст комбинации, например. x, x2 и ya, что нежелательно.   -  person Adela    schedule 04.06.2019
comment
вы можете использовать ifelse(round(rnorm (20),2) == 0.00, rnorm(1),round(rnorm (20),2))   -  person Arun kumar mahesh    schedule 04.06.2019
comment
@akrun ты имел в виду: df %>% group_by(Gr1) %>% complete(Gr2, ID, fill = list(var1 = 0, var2 = 0))   -  person sindri_baldur    schedule 04.06.2019


Ответы (3)


Вот вариант, который использует data.table:

library(data.table)
setDT(df)
all_comb <- df[, CJ(Gr2, ID, unique = TRUE), by = Gr1]
df_out <- df[all_comb, on = .(Gr1, Gr2, ID)]
df_out[is.na(df_out)] <- 0
df_out

#     Gr1 Gr2 ID  var1  var2
#  1:   x  x1 xa -0.56 -1.07
#  2:   x  x1 xb -0.23 -0.22
#  3:   x  x1 xc  1.56 -1.03
#  4:   x  x1 xd  0.07 -0.73
#  5:   x  x1 xe  0.13 -0.63
#  6:   x  x2 xa  0.00  0.00
#  7:   x  x2 xb  0.00  0.00
#  8:   x  x2 xc  0.00  0.00
#  9:   x  x2 xd -0.69 -1.14
# 10:   x  x2 xe -0.45  1.25
# 11:   y  y1 ya  0.00  0.00
# 12:   y  y1 yb  0.36 -0.30
# 13:   y  y1 yc  0.40  0.90
# 14:   y  y1 yd  0.11  0.88
# 15:   y  y1 ye  0.00  0.00
# 16:   y  y2 ya  1.79  0.69
# 17:   y  y2 yb  0.50  0.55
# 18:   y  y2 yc -1.97 -0.06
# 19:   y  y2 yd  0.70 -0.31
# 20:   y  y2 ye -0.47 -0.38

PS.

Для пользователей, которые еще не обновились до R 3.6, вот данные, созданные OP-кодом в текущей версии R:

df <- structure(list(Gr1 = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 
2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L), .Label = c("x", "y"), class = "factor"), 
    Gr2 = structure(c(1L, 1L, 1L, 1L, 1L, 2L, 2L, 3L, 3L, 3L, 
    4L, 4L, 4L, 4L, 4L), .Label = c("x1", "x2", "y1", "y2"), class = "factor"), 
    ID = structure(c(1L, 2L, 3L, 4L, 5L, 4L, 5L, 7L, 8L, 9L, 
    6L, 7L, 8L, 9L, 10L), .Label = c("xa", "xb", "xc", "xd", 
    "xe", "ya", "yb", "yc", "yd", "ye"), class = "factor"), var1 = c(-0.56, 
    -0.23, 1.56, 0.07, 0.13, -0.69, -0.45, 0.36, 0.4, 0.11, 1.79, 
    0.5, -1.97, 0.7, -0.47), var2 = c(-1.07, -0.22, -1.03, -0.73, 
    -0.63, -1.14, 1.25, -0.3, 0.9, 0.88, 0.69, 0.55, -0.06, -0.31, 
    -0.38)), row.names = c(1L, 2L, 3L, 4L, 5L, 9L, 10L, 12L, 
13L, 14L, 16L, 17L, 18L, 19L, 20L), class = "data.frame")
person sindri_baldur    schedule 04.06.2019
comment
@avid_useR Я думаю, проблема в том, что распечатка, показанная OP df, неверна. - person sindri_baldur; 04.06.2019
comment
@avid_useR не следует делать наоборот, то есть: код для воспроизводимых данных - это то, что мы должны использовать? - person sindri_baldur; 04.06.2019
comment
@avid_useR странно, я не знаю (используя R 3.6), может быть, вы используете более старую версию? - person sindri_baldur; 04.06.2019
comment
Конечно, я сделал. Только что проверил и sample() --- он действительно был обновлен в версии 3.6: stat.ethz.ch/pipermail/r-announce/2019/000641.html - person sindri_baldur; 04.06.2019
comment
@avid_useR ВЫ уверены, что не используете старую версию R? - person sindri_baldur; 04.06.2019
comment
Я, но и ОП тоже. Поскольку у вас установлена ​​более новая/другая версия R, вы должны предоставить данные, которые вы используете, если вы не можете воспроизвести то, что предоставил OP. - person acylam; 04.06.2019
comment
@avid_useR Я не знаю, в чем проблема. Я использую текущую версию (версия R 3.6.0, выпущенная 26 апреля 2019 г.), а вы используете старую версию. - person sindri_baldur; 04.06.2019
comment
Проблема в том, что читатели, скорее всего, запутаются, что ваши выходные данные не совпадают с входными данными, и нет никакого способа получить ваши выходные данные, если у них есть более старая версия. Хорошо, что вы предоставили воспроизводимые данные. - person acylam; 04.06.2019

Мы можем сделать это с complete и nesting из tidyr:

library(dplyr)
library(tidyr)

df %>% 
  group_by(Gr1) %>%
  complete(nesting(ID), nesting(Gr2), fill = list(var1 = 0, var2 = 0)) %>%
  arrange(Gr1, Gr2, ID) %>%
  select(Gr1, Gr2, ID, everything())

Вывод:

# A tibble: 20 x 5
# Groups:   Gr1 [2]
   Gr1   Gr2   ID     var1  var2
   <fct> <fct> <fct> <dbl> <dbl>
 1 x     x1    xa    -0.56 -1.07
 2 x     x1    xb     0     0   
 3 x     x1    xc     1.56 -1.03
 4 x     x1    xd     0.07 -0.73
 5 x     x1    xe     0     0   
 6 x     x2    xa     1.72 -1.69
 7 x     x2    xb     0.46  0.84
 8 x     x2    xc     0     0   
 9 x     x2    xd    -0.69 -1.14
10 x     x2    xe    -0.45  1.25
11 y     y1    ya     1.22  0.43
12 y     y1    yb     0.36 -0.3 
13 y     y1    yc     0     0   
14 y     y1    yd     0     0   
15 y     y1    ye    -0.56  0.82
16 y     y2    ya     1.79  0.69
17 y     y2    yb     0.5   0.55
18 y     y2    yc    -1.97 -0.06
19 y     y2    yd     0.7  -0.31
20 y     y2    ye    -0.47 -0.38
person acylam    schedule 04.06.2019

Вариант с expand и left_join

library(tidyverse)
df %>% 
  group_by(Gr1) %>%
  expand(nesting(ID), nesting(Gr2)) %>% 
  left_join(df) %>%
  mutate_at(vars(var1:var2), replace_na, 0)  %>% 
  arrange(Gr1, Gr2, ID) %>% 
  select(names(df))
# A tibble: 20 x 5
# Groups:   Gr1 [2]
#   Gr1   Gr2   ID     var1  var2
#   <chr> <chr> <chr> <dbl> <dbl>
# 1 x     x1    xa    -0.56 -1.07
# 2 x     x1    xb     0     0   
# 3 x     x1    xc     1.56 -1.03
# 4 x     x1    xd     0.07 -0.73
# 5 x     x1    xe     0     0   
# 6 x     x2    xa     1.72 -1.69
# 7 x     x2    xb     0.46  0.84
# 8 x     x2    xc     0     0   
# 9 x     x2    xd    -0.69 -1.14
#10 x     x2    xe    -0.45  1.25
#11 y     y1    ya     1.22  0.43
#12 y     y1    yb     0.36 -0.3 
#13 y     y1    yc     0     0   
#14 y     y1    yd     0     0   
#15 y     y1    ye    -0.56  0.82
#16 y     y2    ya     1.79  0.69
#17 y     y2    yb     0.5   0.55
#18 y     y2    yc    -1.97 -0.06
#19 y     y2    yd     0.7  -0.31
#20 y     y2    ye    -0.47 -0.38
person akrun    schedule 04.06.2019