агрегат (R) ведет себя по-разному для, очевидно, идентичных задач - PullRequest
3 голосов
/ 19 июня 2019

Я несколько дней бился головой об кирпичную стену по этому вопросу;Интересно, может ли кто-нибудь увидеть, что не так с моим кодом, или сказать мне, если я пропускаю что-то очевидное.

У меня есть этот data.frame, где большинство столбцов являются векторами, числовыми или символьными, и один столбецэто список символьных векторов:

t0g2 <- structure(list(P = c(1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 
4, 4, 5, 5, 5, 5), ID = c(8, 10, 7, 9, 5, 2, 3, 4, 8, 9, 1, 2, 
8, 1, 4, 10, 4, 10, 2, 7), SC = c("A", "D", "A", "B", "B", "A", 
"A", "E", "A", "B", "D", "A", "A", "D", "E", "D", "E", "D", "A", 
"A"), FP = list(`40,41,37,8,11` = c("40", "41", "37", "8", "11"
), `49,28,16,41` = c("49", "28", "16", "41"), `15,49` = c("15", 
"49"), `27,12,20,35,45` = c("27", "12", "20", "35", "45"), `1,34,43,37` = c("1", 
"34", "43", "37"), `41,7,30,2,34,43` = c("41", "7", "30", "2", 
"34", "43"), `22,35,31,10,3` = c("22", "35", "31", "10", "3"), 
    `29,6,15` = c("29", "6", "15"), `40,41,37,8,11` = c("40", 
    "41", "37", "8", "11"), `27,12,20,35,45` = c("27", "12", 
    "20", "35", "45"), `10,49,28` = c("10", "49", "28"), `41,7,30,2,34,43` = c("41", 
    "7", "30", "2", "34", "43"), `40,41,37,8,11` = c("40", "41", 
    "37", "8", "11"), `10,49,28` = c("10", "49", "28"), `29,6,15` = c("29", 
    "6", "15"), `49,28,16,41` = c("49", "28", "16", "41"), `29,6,15` = c("29", 
    "6", "15"), `49,28,16,41` = c("49", "28", "16", "41"), `41,7,30,2,34,43` = c("41", 
    "7", "30", "2", "34", "43"), `15,49` = c("15", "49"))), class = "data.frame", row.names = c("8", 
"10", "7", "9", "5", "2", "3", "4", "81", "91", "1", "21", "82", 
"11", "41", "101", "42", "102", "22", "71"))

Я хочу агрегировать его по одному из столбцов, а функция для других столбцов - просто объединение уникальных значений.[Да, я знаю, что это можно сделать со многими специальными пакетами, но мне нужно сделать это с базой R].

Это прекрасно работает, если я выберу числовой столбец "ID" в качестве столбца для агрегирования:

aggregate(x=t0g2[, !(colnames(t0g2) %in% c("ID"))], by=list(ID=t0g2[["ID"]]), 
          FUN=function(y) unique(unlist(y)))
#  ID       P SC                   FP
#1  1    3, 4  D           10, 49, 28
#2  2 2, 3, 5  A 41, 7, 30, 2, 34, 43
#3  3       2  A    22, 35, 31, 10, 3
#4  4 2, 4, 5  E            29, 6, 15
#5  5       2  B        1, 34, 43, 37
#6  7    1, 5  A               15, 49
#7  8 1, 3, 4  A    40, 41, 37, 8, 11
#8  9    1, 3  B   27, 12, 20, 35, 45
#9 10 1, 4, 5  D       49, 28, 16, 41

или с символьным столбцом "SC":

aggregate(x=t0g2[, !(colnames(t0g2) %in% c("SC"))], by=list(SC=t0g2[["SC"]]), 
          FUN=function(y) unique(unlist(y)))
#  SC             P         ID                                                             FP
#1  A 1, 2, 3, 4, 5 8, 7, 2, 3 40, 41, 37, 8, 11, 15, 49, 7, 30, 2, 34, 43, 22, 35, 31, 10, 3
#2  B       1, 2, 3       9, 5                              27, 12, 20, 35, 45, 1, 34, 43, 37
#3  D    1, 3, 4, 5      10, 1                                             49, 28, 16, 41, 10
#4  E       2, 4, 5          4                                                      29, 6, 15

Однако, если я попробую с "P", который, насколько я знаю, является просто еще одним числовым столбцом, этовот что я получаю:

aggregate(x=t0g2[, !(colnames(t0g2) %in% c("P"))], by=list(P=t0g2[["P"]]), 
          FUN=function(y) unique(unlist(y)))
#   P ID.1 ID.2 ID.3 ID.4 SC.1 SC.2 SC.3                                                                  FP
#1  1    8   10    7    9    A    D    B               40, 41, 37, 8, 11, 49, 28, 16, 15, 27, 12, 20, 35, 45
#2  2    5    2    3    4    B    A    E           1, 34, 43, 37, 41, 7, 30, 2, 22, 35, 31, 10, 3, 29, 6, 15
#3  3    8    9    1    2    A    B    D 40, 41, 37, 8, 11, 27, 12, 20, 35, 45, 10, 49, 28, 7, 30, 2, 34, 43
#4  4    8    1    4   10    A    D    E                        40, 41, 37, 8, 11, 10, 49, 28, 29, 6, 15, 16
#5  5    4   10    2    7    E    D    A                         29, 6, 15, 49, 28, 16, 41, 7, 30, 2, 34, 43

Кто-нибудь знает, что происходит, почему это происходит?Буквально сходя с ума с этим материалом ...


РЕДАКТИРОВАТЬ : добавление примера желаемого результата агрегирования по "P", как этого требует jay.sf.

#  P          ID      SC                                                                  FP
#1 1 8, 10, 7, 9 A, D, B               40, 41, 37, 8, 11, 49, 28, 16, 15, 27, 12, 20, 35, 45
#2 2  5, 2, 3, 4 B, A, E           1, 34, 43, 37, 41, 7, 30, 2, 22, 35, 31, 10, 3, 29, 6, 15
#3 3  8, 9, 1, 2 A, B, D 40, 41, 37, 8, 11, 27, 12, 20, 35, 45, 10, 49, 28, 7, 30, 2, 34, 43
#4 4 8, 1, 4, 10 A, D, E                        40, 41, 37, 8, 11, 10, 49, 28, 29, 6, 15, 16
#5 5 4, 10, 2, 7 E, D, A                         29, 6, 15, 49, 28, 16, 41, 7, 30, 2, 34, 43

На самом деле я обнаружил, что, установив simplify=F в совокупности, он работает так, как я хочу.
Надеюсь, это не будет иметь неприятных последствий.


РЕДАКТИРОВАТЬ 2 : это вызвало обратную реакцию ...

Я не хочу, чтобы все мои столбцы становились списками, даже если они могут быть векторами, но с simplify = F они становятся списками:

sapply(aggregate(x=t0g2[,!(colnames(t0g2) %in% c("P"))],by=list(P=t0g2[["P"]]),FUN=function(y) unique(unlist(y)), simplify = F),class)
#        P        ID        SC        FP 
#"numeric"    "list"    "list"    "list" 

sapply(aggregate(x=t0g2[,!(colnames(t0g2) %in% c("ID"))],by=list(ID=t0g2[["ID"]]),FUN=function(y) unique(unlist(y)), simplify = T),class)
#         ID           P          SC          FP 
#  "numeric"      "list" "character"      "list" 

sapply(aggregate(x=t0g2[,!(colnames(t0g2) %in% c("ID"))],by=list(ID=t0g2[["ID"]]),FUN=function(y) unique(unlist(y)), simplify = F),class)
#       ID         P        SC        FP 
#"numeric"    "list"    "list"    "list" 

Так что у меня до сих пор нет решения ...: (


РЕДАКТИРОВАТЬ 3 : возможно, жизнеспособное (хотя и довольно неуклюжее) решение?

t0g2_by_ID <- aggregate(x=t0g2[,!(colnames(t0g2) %in% c("ID"))],by=list(ID=t0g2[["ID"]]),FUN=function(y) unique(unlist(y)), simplify = F)

sapply(t0g2_by_ID,class)
#       ID         P        SC        FP 
#"numeric"    "list"    "list"    "list" 

for (i in 1:NCOL(t0g2_by_ID)) {y = t0g2_by_ID[,i]; if ((class(y) == "list") & (length(y) == length(unlist(y)))) {t0g2_by_ID[,i] <- unlist(y)} }

sapply(t0g2_by_ID,class)
#       ID           P          SC          FP 
#"numeric"      "list" "character"      "list" 

Я пытался уклониться от неэлегического цикла, используя sapply, но затем любая операция cbind возвращается к списку data.frame.

Это лучшее, что я могу придуматьс.

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

Ответы [ 2 ]

1 голос
/ 19 июня 2019

aggregate, очевидно, пытается дать матрицу, где это возможно.См. Этот пример:

# data
n <- 10
df <- data.frame(id= rep(1:2, each= n/2),
             value= 1:n)

length(unique(df$value[df$id == 1])) == length(unique(df$value[df$id == 2]))
TRUE

Здесь длина уникальности одинакова для каждого значения идентификатора, поэтому агрегат предоставляет матрицу

aggregate(x= df[, "value"], by=list(id=df[, "id"]), 
      FUN=function(y) unique(unlist(y)))
   id x.1 x.2 x.3 x.4 x.5
1  1   1   2   3   4   5
2  2   6   7   8   9  10

Теперь мы изменим данные так, чтобы длина уникальности для каждого идентификаторане равно

df$value[2] <- 1
length(unique(df$value[df$id == 1])) == length(unique(df$value[df$id == 2]))
FALSE

В этом случае мы получаем вывод со значениями, разделенными ,:

aggregate(x= df[, "value"], by=list(id=df[, "id"]), 
      FUN=function(y) unique(unlist(y)))
  id              x
1  1     1, 3, 4, 5
2  2 6, 7, 8, 9, 10

В вашем случае у вас есть для каждого значения P ровно 4 уникальных значения IDи точно 3 уникальных значения SC, следовательно, совокупность показывает эти результаты в виде матрицы.Это не верно для FP: здесь агрегат не может предоставить матрицу, следовательно, мы получаем значения, разделенные ,

0 голосов
/ 19 июня 2019

aggregate имеет аргумент simplify, который по умолчанию равен TRUE, что означает, что он пытается упростить вектор или матрицу, когда это возможно.Все группы в P имеют n = 4, поэтому ваши агрегированные данные упрощаются до матрицы.Просто установите simpflify = FALSE, чтобы изменить это поведение:

aggregate(x=t0g2[, !(colnames(t0g2) %in% c("P"))], by=list(P=t0g2[["P"]]), 
          FUN=function(y) unique(unlist(y)), simplify = F)

#### OUTPUT ####

  P          ID      SC                                                                  FP
1 1 8, 10, 7, 9 A, D, B               40, 41, 37, 8, 11, 49, 28, 16, 15, 27, 12, 20, 35, 45
2 2  5, 2, 3, 4 B, A, E           1, 34, 43, 37, 41, 7, 30, 2, 22, 35, 31, 10, 3, 29, 6, 15
3 3  8, 9, 1, 2 A, B, D 40, 41, 37, 8, 11, 27, 12, 20, 35, 45, 10, 49, 28, 7, 30, 2, 34, 43
4 4 8, 1, 4, 10 A, D, E                        40, 41, 37, 8, 11, 10, 49, 28, 29, 6, 15, 16
5 5 4, 10, 2, 7 E, D, A                         29, 6, 15, 49, 28, 16, 41, 7, 30, 2, 34, 43
...