R: добавление в строки нулей на основе значений в нескольких столбцах

Я пытаюсь добавить строки в R data.frame. Вот пример data.frame "foo":

A   B   C   D
1   1   1   200
1   1   2   50
1   1   3   15
1   2   1   150
1   2   4   50
1   3   1   300
2   1   2   40
2   1   4   90
2   3   2   80

Для каждого A существует 3 возможных значения B, а для каждого B существует 4 возможных значения C. Однако начальный df содержит только ненулевые значения D. Я хотел бы манипулировать df так, чтобы нули включены как для B, так и для C. Таким образом, df будет показывать 0 в D для любого значения B / C, равного 0. Я видел вопросы, которые касаются этого с одним столбцом, но не мог найти вопрос, касающийся этого с несколькими столбцы. Окончательный df будет выглядеть так:

A   B   C   D
1   1   1   200
1   1   2   50
1   1   3   15
1   1   4   0
1   2   1   150
1   2   2   0
1   2   3   0
1   2   4   50
1   3   1   300
1   3   2   0
1   3   3   0
1   3   4   0
2   1   1   0
2   1   2   40
2   1   3   0
2   1   4   90
2   2   1   0
2   2   2   0
2   2   3   0
2   2   4   0
2   3   1   0
2   3   2   80
2   3   3   0
2   3   4   0

Сначала я попытался создать фиктивный фрейм данных, который затем объединился с исходным df, но что-то не работает. Вот текущий код, который, как я знаю, неверен, потому что этот код генерирует строки только на основе A. Я думаю, что хочу создать фиктивный кадр на основе A и B, но я не знаю, как - может ли здесь работать функция if/else ?:

# create dummy df
dummy <- as.data.frame(
  cbind(
    sort(rep(unique(foo$A), 12)),
    rep(1:3,length(unique(foo$A)))))
colnames(dummy) <- c("A","B")
foo$A <- as.numeric(foo$A)
foo$B <- as.numeric(foo$C)

# merge with foo
mergedummy <- merge(dummy,foo,all.x=T)

Любое понимание очень ценится - спасибо!


person ucsbcoding    schedule 17.10.2014    source источник


Ответы (3)


Один лайнер:

merge(dat, data.frame(table(dat[1:3]))[-4],all.y=TRUE)

#   A B C   D
#1  1 1 1 200
#2  1 1 2  50
#3  1 1 3  15
#4  1 1 4  NA
#...

Или, может быть, менее сложно:

out <- data.frame(xtabs(D ~ ., data=dat))
out[do.call(order,out[1:3]),]

#   A B C Freq
#1  1 1 1  200
#7  1 1 2   50
#13 1 1 3   15
#19 1 1 4    0
#...

Где dat:

dat <- structure(list(A = c(1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L), B = c(1L, 
1L, 1L, 2L, 2L, 3L, 1L, 1L, 3L), C = c(1L, 2L, 3L, 1L, 4L, 1L, 
2L, 4L, 2L), D = c(200L, 50L, 15L, 150L, 50L, 300L, 40L, 90L, 
80L)), .Names = c("A", "B", "C", "D"), class = "data.frame", row.names = c(NA, 
-9L))
person thelatemail    schedule 17.10.2014
comment
Спасибо за это! Ваша первая однострочная строка работает, а затем я добавил дополнительную строку df[is.na(df) <- 0, чтобы заменить NA на 0. Большое спасибо. - person ucsbcoding; 17.10.2014

Я создал фрейм основных данных, который включает все комбинации A, B и C, как вы описываете в ожидаемом результате. Затем я объединяю основной фрейм данных и ваш фрейм данных. Наконец, я заменил NA на 0.

master <- data.frame(A = rep(1:2, each = 12),
                     B = rep(1:3, each = 4),
                     C = rep(1:4, times = 6))

library(dplyr)

master %>%
    left_join(., mydf) %>%
    mutate(D = ifelse(D %in% NA, 0, D))

#   A B C   D
#1  1 1 1 200
#2  1 1 2  50
#3  1 1 3  15
#4  1 1 4   0
#5  1 2 1 150
#6  1 2 2   0
#7  1 2 3   0
#8  1 2 4  50
#9  1 3 1 300
#10 1 3 2   0
#11 1 3 3   0
#12 1 3 4   0
#13 2 1 1   0
#14 2 1 2  40
#15 2 1 3   0
#16 2 1 4  90
#17 2 2 1   0
#18 2 2 2   0
#19 2 2 3   0
#20 2 2 4   0
#21 2 3 1   0
#22 2 3 2  80
#23 2 3 3   0
#24 2 3 4   0
person jazzurro    schedule 17.10.2014

Вот одно из решений:

foo <- merge(expand.grid(lapply(foo[,1:3], unique)), foo, all=TRUE, sort=TRUE)
foo[is.na(foo)] <- 0
person Karolis Koncevičius    schedule 17.10.2014