суммировать для всех других значений на группу в dplyr - PullRequest
6 голосов
/ 20 октября 2019

У меня есть набор данных с индивидуальными решениями, принятыми в группах. Для каждого человека мне нужна совокупная (скажем, сумма) всех решений членов его / ее группы. Допустим, данные выглядят так:

set.seed(123)
group_id <- c(sapply(seq(1, 3), rep, times = 3))
person_id <- rep(seq(1,3),3)
decision <- sample(1:10, 9, replace=T)
df <-data.frame(group_id, person_id, decision)
df

Результат:

  group_id person_id decision
1        1         1        3
2        1         2        8
3        1         3        5
4        2         1        9
5        2         2       10
6        2         3        1
7        3         1        6
8        3         2        9
9        3         3        6

И мне нужно произвести что-то подобное:

  group_id person_id decision others_decision
1        1         1        3 13
2        1         2        8  8
3        1         3        5 11

Так что дляНа каждый элемент группы я получаю всех остальных членов той же группы и что-то делаю (сумма). Я могу сделать это всего лишь с помощью цикла for, но он кажется уродливым и неэффективным. Есть ли лучшие решения?

ОБНОВЛЕНИЕ:

Вот решение, которое я нашел до сих пор, извините за уродство:

df$other_decision=unlist(by(df, 1:nrow(df), function(row) {
  df %>% filter(group_id==row$group_id, person_id!=row$person_id) %>% summarize(sum(decision))
}
  ))
df

Ответы [ 3 ]

3 голосов
/ 21 октября 2019

Вы можете сделать:

df %>%
 inner_join(df, by = c("group_id" = "group_id")) %>%
 filter(person_id.x != person_id.y) %>%
 group_by(group_id, person_id = person_id.x) %>%
 summarise(decision = first(decision.x),
           others_decison = sum(decision.y))

  group_id person_id decision others_decison
     <int>     <int>    <int>          <int>
1        1         1        3             13
2        1         2        8              8
3        1         3        5             11
4        2         1        9             11
5        2         2       10             10
6        2         3        1             19
7        3         1        6             15
8        3         2        9             12
9        3         3        6             15

В зависимости от вашего фактического набора данных (его размера), он может стать довольно сложным в вычислительном отношении, поскольку включает в себя внутреннее соединение.

Другая возможность, не связанная свнутреннее соединение может быть:

df %>% 
 group_by(group_id) %>% 
 mutate(others_decison = list(decision),
        rowid = 1:n()) %>%
 ungroup() %>%
 rowwise() %>%
 mutate(others_decison = sum(unlist(others_decison)[-rowid])) %>%
 ungroup() %>%
 select(-rowid)
2 голосов
/ 21 октября 2019

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

library(dplyr)

my_summarise <- function(x, FUN, ...) {
  sapply(seq_along(x), function(y)
    FUN(x[-y], ...))
} 

df %>%
  group_by(group_id) %>%
  mutate(dsum = my_summarise(decision, sum),
         dmean = my_summarise(decision, mean),
         dmax = my_summarise(decision, max))

# A tibble: 9 x 6
# Groups:   group_id [3]
  group_id person_id decision  dsum dmean  dmax
     <int>     <int>    <int> <int> <dbl> <int>
1        1         1        3    13   6.5     8
2        1         2        8     8   4       5
3        1         3        5    11   5.5     8
4        2         1        9    11   5.5    10
5        2         2       10    10   5       9
6        2         3        1    19   9.5    10
7        3         1        6    15   7.5     9
8        3         2        9    12   6       6
9        3         3        6    15   7.5     9
1 голос
/ 21 октября 2019

Вот несколько методов:

library(data.table)
dt <- as.data.table(df)

# don't update original dt
dt[dt, on = .(group_id), allow.cartesian = T
   ][person_id != i.person_id,
     .(decison = first(i.decision), others = sum(decision)),
     by = .(group_id, person_id = i.person_id)]

#update the original dt way 1
dt[,
   others_decision := .SD[.SD, on = .(group_id), allow.cartesian = T
                          ][person_id != i.person_id, sum(decision), by = .(group_id,i.person_id)]$V1
   ]

#update the original dt way 2
dt1[, 
   others_decision := dt[group_id == .BY[[1]] & person_id != .BY[[2]], sum(decision)],
   by = .(group_id, person_id)]

Первые две основные вещи - это более или менее подход @ tmfmnk, но через data.table. Последнее более интуитивно для меня, но, вероятно, самое медленное.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...