Неожиданный выход из совокупности

Во время экспериментов с aggregate для другого вопроса здесь я столкнулся с довольно странным результатом. Я не могу понять, почему и интересно, что то, что я делаю, совершенно неверно.

Предположим, что у меня есть data.frame:

df <- structure(list(V1 = c(1L, 2L, 1L, 2L, 3L, 1L), 
                     V2 = c(2L, 3L, 2L, 3L, 4L, 2L), 
                     V3 = c(3L, 4L, 3L, 4L, 5L, 3L), 
                     V4 = c(4L, 5L, 4L, 5L, 6L, 4L)), 
                  .Names = c("V1", "V2", "V3", "V4"), 
        row.names = c(NA, -6L), class = "data.frame")
> df
#   V1 V2 V3 V4
# 1  1  2  3  4
# 2  2  3  4  5
# 3  1  2  3  4
# 4  2  3  4  5
# 5  3  4  5  6
# 6  1  2  3  4

Теперь, если я хочу вывести data.frame с уникальными строками с дополнительным столбцом, указывающим их частоту в df. Для этого примера

#   V1 V2 V3 V4 x
# 1  1  2  3  4 3
# 2  2  3  4  5 2
# 3  3  4  5  6 1

Я получил этот результат, используя aggregate, поэкспериментируя следующим образом:

> aggregate(do.call(paste, df), by=df, print)

# [1] "1 2 3 4" "1 2 3 4" "1 2 3 4"
# [1] "2 3 4 5" "2 3 4 5"
# [1] "3 4 5 6"
#   V1 V2 V3 V4                         x
# 1  1  2  3  4 1 2 3 4, 1 2 3 4, 1 2 3 4
# 2  2  3  4  5          2 3 4 5, 2 3 4 5
# 3  3  4  5  6                   3 4 5 6

Итак, это дало мне вставную строку. Итак, если бы я использовал length вместо print, он должен дать мне количество таких вхождений, что является желаемым результатом, который был в этом случае (как показано ниже).

> aggregate(do.call(paste, df), by=df, length)
#   V1 V2 V3 V4 x
# 1  1  2  3  4 3
# 2  2  3  4  5 2
# 3  3  4  5  6 1

И это, казалось, сработало. Однако, когда размеры data.frame составляют 4 * 2500, выход data.frame равен 1 * 2501 вместо 4 * 2501 (все строки уникальны, поэтому частота равна 1).

> df <- as.data.frame(matrix(sample(1:3, 1e4, replace = TRUE), nrow=4))
> o <- aggregate(do.call(paste, df), by=df, length)
> dim(o)
# [1]    1 2501

Я тестировал с меньшими кадрами data.frames только с уникальными строками, и он дает правильный вывод (например, изменение nrow=40). Однако, когда размеры матрицы увеличиваются, это, похоже, не работает. И я просто не могу понять, что происходит не так! Любые идеи?

Ответ 1

Проблема здесь в том, как aggregate.data.frame() определяет группы.

В aggregate.data.frame() существует цикл, который формирует переменную группировки grp. В этом цикле grp изменяется/обновляется с помощью:

grp <- grp * nlevels(ind) + (as.integer(ind) - 1L)

Проблема с вашим примером, если один раз by преобразуется в факторы, и цикл перешел все эти факторы, в вашем примере grp заканчивается:

Browse[2]> grp
[1] Inf Inf Inf Inf

По существу, обновление цикла обновляло значения grp до числа, неотличимого от Inf.

Сделав это, aggregate.data.frame() позже делает это

y <- y[match(sort(unique(grp)), grp, 0L), , drop = FALSE]

и здесь более ранняя проблема теперь проявляется как

dim(y[match(sort(unique(grp)), grp, 0L), , drop = FALSE])

потому что

match(sort(unique(grp)), grp, 0L)

явно возвращает только 1:

> match(sort(unique(grp)), grp, 0L)
[1] 1

поскольку существует только одно уникальное значение grp.