事先不知道返回列的分组和汇总

时间:2016-11-08 12:43:21

标签: r dplyr grouping dynamic-columns summarization

我有一个数据帧--say x - 它提供一个函数,该函数根据列x $ id的值返回一个子集。

此子集y包含y $ room列,其中包含不同的值组合,具体取决于x $ id值。

然后用tidyr传播子集,y $ room的值成为列 然后,生成的扩展df --say ext_y--必须按列y_ext $ visit进行分组,并且应通过特殊函数计算剩余列的汇总统计信息。

显而易见的问题是这些列事先是未知的,因此无法通过函数中的名称来定义。

当涉及group_by时,使用列的索引而不是名称的替代方法似乎不适用于dplyr。

您是否有任何想法可以解决这个问题?

数据框有几千行,所以我只给你一瞥:

       > tail(y)
           id visit        room value
     11940 14     2 living room    19
     11941 14     2 living room    16
     11942 14     2 living room    15
     11943 14     2 living room    22
     11944 14     2 living room    25
     11945 14     2 living room    20

     > unique(x$id)
    [1]  14  20  41  44  46  54  64  74 104 106
     > unique(x$visit)
    [1] 0 1 2
     > unique(x$room)
     [1] "bedroom"      "living room"  "family  room" "study room"   "den"         
     [6] "tv room"      "office"       "hall"         "kitchen"      "dining room" 
     > summary(x$value)
         Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
        2.000    2.750    7.875   17.410   16.000 1775.000 

对于给定的id,tidyr的spread()仅返回x中房间值的子集。例如。对于id = 54:

  > y<- out
  > y$row <- 1 : nrow(y)
  > y_ext <- spread(y, room, value)
  > head(y_ext)
       id visit row bedroom family  room living room
     1 14     0   1    6.00           NA          NA
     2 14     0   2    6.00           NA          NA
     3 14     0   3    2.75           NA          NA
     4 14     0   4    2.75           NA          NA
     5 14     0   5    2.75           NA          NA
     6 14     0   6    2.75           NA          NA

现在,我必须编写一个函数,按访问对结果进行分组,并按以下格式汇总为每个组返回的列:

         visit    bedroom    family room   living room
      1   0         NA            2.79         3.25
      2   1         NA             NA          4.53
      3   2         4.19           3.77        NA

正如我上面提到的,我事先并不知道将为给定的id返回哪些列,这使问题复杂化。当然是捷径 检查并找出每个id返回哪些列,然后创建一个 如果结构将每个id引导到适当的代码,但这不是很优雅,我担心。

希望这有助于您更好地了解情况。

1 个答案:

答案 0 :(得分:1)

好吧,这对我来说很有趣,我自己制作了一些样本数据:

nSamples <- 50

allRooms <-
  c("Living", "Dining", "Bedroom", "Master", "Family", "Garage", "Office")

set.seed(8675309)

df <-
  data_frame(
    id = sample(1:5, nSamples, TRUE)
    , visit = sample(1:3, nSamples, TRUE)
    , room = sample(allRooms, nSamples, TRUE)
    , value = round(rnorm(nSamples, 20, 5))
  )

我看待它的方式,有三种方法,按合理性的升序排列。第一个选择是遵循您的基本布局。在这里,我按df分割id,按指示进行传播,然后使用summarise_all进行求和,无需明确识别房间名称。

df %>%
  split(.$id) %>%
  lapply(function(x){
    x %>%
      select(-id) %>%
      mutate(row = 1:n()) %>%
      spread(room, value) %>%
      select(-row) %>%
      group_by(visit) %>%
      summarise_all(sum, na.rm = TRUE)
  })

返回以下内容(注释唯一列):

$`1`
# A tibble: 3 × 6
  visit Bedroom Dining Garage Master Office
  <int>   <dbl>  <dbl>  <dbl>  <dbl>  <dbl>
1     1       0     27     27      0      0
2     2      22     19      0     20     23
3     3       0      0      0     27      0

$`2`
# A tibble: 3 × 6
  visit Bedroom Dining Family Living Office
  <int>   <dbl>  <dbl>  <dbl>  <dbl>  <dbl>
1     1      15      0      0      0     17
2     2       0     14     42     30      0
3     3      15     13     18      0     20

$`3`
# A tibble: 3 × 6
  visit Bedroom Dining Living Master Office
  <int>   <dbl>  <dbl>  <dbl>  <dbl>  <dbl>
1     1      24      0     36      0     28
2     2       0      0     15     30      0
3     3       0     25     21      0     15

$`4`
# A tibble: 3 × 7
  visit Bedroom Dining Garage Living Master Office
  <int>   <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>
1     1       0      0     23     20      0     24
2     2       0     28     22      0      0      0
3     3      24      0     36      0     16      0

$`5`
# A tibble: 3 × 8
  visit Bedroom Dining Family Garage Living Master Office
  <int>   <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>
1     1      23      0      0     21      0     16      0
2     2      44     14     41      0     26      0     18
3     3      21     19      0      0     25     19      0

但是,因为您必须添加行以使spread正常工作(没有它,所以没有唯一条目),spread实际上没有帮助。如果你先进行总结,你可以更容易地得到同样的东西,如下:

df %>%
  split(.$id) %>%
  lapply(function(x){
    x %>%
      select(-id) %>%
      group_by(visit, room) %>%
      summarise(Sum = sum(value)) %>%
      spread(room, Sum, 0)
  })

请注意,由于0参数的最后0,它为没有访问的会议室提供了fill。如果您希望返回NA,则可以保留默认值。

最后,目前还不清楚为什么要首先单独进行此操作。在一个大的group_by中完成所有这一切可能更有意义,并在事后处理所需的缺失。也就是说,获得相同摘要的代码要少得多。

df %>%
  group_by(id, visit, room) %>%
  summarise(sum = sum(value)) %>%
  spread(room, sum)

给出

      id visit Bedroom Dining Family Garage Living Master Office
*  <int> <int>   <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>
1      1     1      NA     27     NA     27     NA     NA     NA
2      1     2      22     19     NA     NA     NA     20     23
3      1     3      NA     NA     NA     NA     NA     27     NA
4      2     1      15     NA     NA     NA     NA     NA     17
5      2     2      NA     14     42     NA     30     NA     NA
6      2     3      15     13     18     NA     NA     NA     20
7      3     1      24     NA     NA     NA     36     NA     28
8      3     2      NA     NA     NA     NA     15     30     NA
9      3     3      NA     25     NA     NA     21     NA     15
10     4     1      NA     NA     NA     23     20     NA     24
11     4     2      NA     28     NA     22     NA     NA     NA
12     4     3      24     NA     NA     36     NA     16     NA
13     5     1      23     NA     NA     21     NA     16     NA
14     5     2      44     14     41     NA     26     NA     18
15     5     3      21     19     NA     NA     25     19     NA

如果您想要过滤到只有一个id,请在事后使用filter,然后删除包含所有NA条目的列。 (注意,你可能会保存一次输出,然后为每个感兴趣的id传递一次最后两行,例如打印时)

df %>%
  group_by(id, visit, room) %>%
  summarise(sum = sum(value)) %>%
  spread(room, sum) %>%
  filter(id == 1) %>%
  select_if(function(col) mean(is.na(col)) != 1)

给出

     id visit Bedroom Dining Garage Master Office
  <int> <int>   <dbl>  <dbl>  <dbl>  <dbl>  <dbl>
1     1     1      NA     27     27     NA     NA
2     1     2      22     19     NA     20     23
3     1     3      NA     NA     NA     27     NA