В R неожиданный результат от использования group_by () и summarize () в dplyr

Я не совсем понимаю, как некоторые группы и сводки построены в R с использованием пакета dplyr.

В воспроизводимом примере ниже я пытаюсь сначала сгруппировать по (PN, GOT, HID), чтобы подсчитать отдельные экземпляры PC1. Затем я перегруппировываюсь по (PN, GOT), чтобы суммировать отдельные экземпляры PC1 на основе второй группировки. Этот процесс, похоже, работает для общих сумм, за исключением того, что для среднего (TC) я получаю среднее значение всего кадра данных, когда я ожидал бы увидеть средние по группировкам (PN, GOT). Что мне не хватает, чтобы получить эти средства (PN, GOT), не теряя при этом суммы на PC1, который я построил? Я был бы признателен за объяснение того, где я здесь ошибаюсь.

PN<- c("Mazda","Mazda","Datsun","Hornet","Hornet","Valiant","Duster","Merc","Merc","Merc","Merc","Merc",
       "Merc","Merc","Fiat","Honda","Toyota","Toyota","Dodge","AMC","Fiat")
GOT<- c("A","A","B","C","C","A","D","B","B","B","B","B","B","B","A","D","B","B","C","E","A")
HID<- c("Mazda_H1","Mazda_H1","Datsus_H1","Hornet_H1","Hornet_H2","Valiant_H1","Duster_H1","Merc_H1","Merc_H1","Merc_H1",
        "Merc_H2","Merc_H2","Merc_H3","Merc_H4","Fiat_H1","Honda_H1","Toyota_H1","Toyota_H2","Dodge_H1","AMC_H1","Fiat_H1")
PIC<- c("BB","BB","BB","BB","AA","AA","AA","BA","BA","BA",
        "AA","BB","BB","BB","BB","AA","AA","AA","BA","BA","BA")
TC <- c(110,110,93,175,175,105,245,62,62,62,62,62,62,62,33,52,97,97,150,150,33)
Int <- c(16.46,17.02,18.61,19.44,17.02,20.22,15.84,20.00,22.90,18.30,18.90,
         17.40,17.60,18.00,19.47,18.52,19.90,20.01,16.87,17.30,18.90)
PC1<- c("", "","G1","C1","","G1","", "G1","G1","C1","C1","","","","Z1","Z1","Z1","Z1","","","G1")

df<-data.frame(PN,GOT,HID,PIC,TC,Int,PC1)

df

df%>% filter(PC1!="") %>%
  group_by(PN, GOT, HID) %>%
  summarize(new = n_distinct(PC1)) %>%
  group_by(PN, GOT) %>%
  mutate(TOT_new = sum(new),
            meanTC = mean(TC))

Я думаю, что ответ, который я ищу, выглядит примерно так:

       PN    GOT        HID   TOT_new meanTC
   <fctr> <fctr>     <fctr>   <int>  <dbl>
1  Datsun      B  Datsus_H1     1     93
2    Fiat      A    Fiat_H1     2     33
3   Honda      D   Honda_H1     1     52
4  Hornet      C  Hornet_H1     1    175
5    Merc      B    Merc_H1     3     62
6  Toyota      B  Toyota_H1     2     97
7 Valiant      A Valiant_H1     1    105

или, по крайней мере, это:

       PN    GOT        HID   new TOT_new meanTC
   <fctr> <fctr>     <fctr> <int>   <int>  <dbl>
1  Datsun      B  Datsus_H1     1       1     93
2    Fiat      A    Fiat_H1     2       2     33
3   Honda      D   Honda_H1     1       1     52
4  Hornet      C  Hornet_H1     1       1    175
5    Merc      B    Merc_H1     2       3     62
6    Merc      B    Merc_H2     1       3     62
7  Toyota      B  Toyota_H1     1       2     97
8  Toyota      B  Toyota_H2     1       2     97
9 Valiant      A Valiant_H1     1       1    105

person val    schedule 27.02.2017    source источник
comment
Когда вы впервые group_by(PN,GOT,HID) и подводите итоги, несгруппированные переменные теряются. Если вы пройдете через summarize только первые несколько команд, вы увидите, что происходит. Возможно, вам следует сделать отдельные группы / сводные каналы и left_join результаты? (Было бы полезно, если бы вы предоставили ожидаемый результат.)   -  person r2evans    schedule 27.02.2017
comment
@ r2evans: в ближайшее время я опубликую ожидаемый результат - спасибо. Есть ли способ сохранить или отозвать потерянные переменные для случаев, когда нужно переходить между различными сводками?   -  person val    schedule 27.02.2017
comment
Вот что я пытаюсь выяснить. Ничего не видя, я буду придерживаться своего первого предложения о нескольких каналах (сгруппируйте / суммируйте df один раз, затем сделайте полностью отдельную группу / суммируйте еще раз df, затем объедините их вместе).   -  person r2evans    schedule 27.02.2017


Ответы (3)


Как прокомментировал @ r2evans, вы получаете глобальное среднее значение потому, что столбец TC опускается на первом этапе суммирования. Помимо опции join, предложенной в комментарии, вы также можете передать информацию столбца TC на первом этапе сводки, вычислив две промежуточные переменные:

df %>% filter(PC1 != "") %>%

    group_by(PN, GOT, HID) %>%
    # create two columns with the sum and length of TC in each group which you can use later
    # for average calculation
    summarize(new = n_distinct(PC1), n = n(), TC_sum = sum(TC)) %>%

    group_by(PN, GOT) %>%
    summarise(TOT_new = sum(new), meanTC = sum(TC_sum)/sum(n))

# Source: local data frame [7 x 4]
# Groups: PN [?]

#       PN    GOT TOT_new meanTC
#   <fctr> <fctr>   <int>  <dbl>
#1  Datsun      B       1     93
#2    Fiat      A       2     33
#3   Honda      D       1     52
#4  Hornet      C       1    175
#5    Merc      B       3     62
#6  Toyota      B       2     97
#7 Valiant      A       1    105
person Psidom    schedule 27.02.2017
comment
Мне это нравится больше, чем двойной конвейер ... он работает со статистикой, относящейся к sum и sum, хотя вам могут потребоваться альтернативные решения, когда агрегаты не работают (например, median). - person r2evans; 27.02.2017
comment
@ r2evans Верно, в таком случае. Вероятно, нет простого способа сделать это без соединения. - person Psidom; 27.02.2017
comment
@Psidom: Разве это не группировка по PN и GOT. Почему в этих столбцах могут быть дубликаты (например, Merc и Toyota) - эти два должны были быть сгруппированы в один с одним значением в каждом из TOT_new и meanTC. Кажется, что для этого нужно изменить mutate () на summarize (). - person val; 27.02.2017
comment
да. Если вам не нужен промежуточный столбец new, лучше использовать summarize, что также делает код короче. - person Psidom; 27.02.2017

Мы также можем использовать data.table. Преобразуйте data.frame в data.table (setDT(df)), укажите логическое условие в 'i' (PC1 != ""), сгруппированное по 'PN', 'GOT', 'HID', мы получим length из uniqueelements 'PC1 (' new '), количество элементов в группе (.N) и sum из' TC ', затем сгруппированные по' PN ',' GOT ', мы назначаем sum из' new 'и отношение sum из' TC_sum 'с sum от' n 'до' TOT_new 'и' meanTC '. Назначьте ненужные столбцы NULL

library(data.table)
setDT(df)[PC1 != "", .(new = uniqueN(PC1), n = .N, TC_sum = sum(TC)) ,.(PN, GOT, HID)
       ][, c("TOT_new", "meanTC") := .(sum(new), sum(TC_sum)/sum(n)) ,.(PN, GOT)
         ][, c("n", "TC_sum") := NULL][]
#        PN GOT        HID new TOT_new meanTC
#1:  Datsun   B  Datsus_H1   1       1     93
#2:  Hornet   C  Hornet_H1   1       1    175
#3: Valiant   A Valiant_H1   1       1    105
#4:    Merc   B    Merc_H1   2       3     62
#5:    Merc   B    Merc_H2   1       3     62
#6:    Fiat   A    Fiat_H1   2       2     33
#7:   Honda   D   Honda_H1   1       1     52
#8:  Toyota   B  Toyota_H1   1       2     97
#9:  Toyota   B  Toyota_H2   1       2     97
person akrun    schedule 27.02.2017
comment
можно ли использовать этот метод для возврата исходных столбцов df, которые не были введены сразу в начале? Ваше объяснение мне понятно, спасибо. - person val; 27.02.2017
comment
@val Вы можете выполнить соединение с этим набором данных. Если вы заметили набор данных, в нем всего 9 строк, тогда как в исходном наборе данных их больше. Итак, это зависит от того, какие значения вы хотите, чтобы он был на выходе - person akrun; 27.02.2017

Это один из способов заставить его работать, основанный на некоторых комментариях выше. Но это выглядит лишним.

df%>% filter(PC1!="") %>%
  group_by(PN, GOT, HID) %>%
  summarize(new = n_distinct(PC1),
            meanTC = mean(TC)) %>%
  group_by(PN, GOT) %>%
  mutate(TOT_new = sum(new),
         meanTC = mean(meanTC))%>%
  select(-HID)

       PN    GOT   new meanTC TOT_new
   <fctr> <fctr> <int>  <dbl>   <int>
1  Datsun      B     1     93       1
2    Fiat      A     2     33       2
3   Honda      D     1     52       1
4  Hornet      C     1    175       1
5    Merc      B     2     62       3
6    Merc      B     1     62       3
7  Toyota      B     1     97       2
8  Toyota      B     1     97       2
9 Valiant      A     1    105       1
person val    schedule 27.02.2017