【问题标题】:Grouping and summarising when the columns returned are not known in advance事先不知道返回的列时的分组和汇总
【发布时间】:2016-11-08 12:43:20
【问题描述】:

我有一个数据框 --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 返回的列,然后创建一个 if 结构将每个 id 定向到适当的代码,但恐怕这不是很优雅。

希望这有助于为您提供更好的图片。

【问题讨论】:

  • 欢迎来到 Stack Overflow!请阅读有关how to ask a good question 的信息以及如何提供reproducible example。这将使其他人更容易帮助您。
  • 习惯上不告诉我们问题,而是给出一个示例数据集......这有助于解决问题。如果没有……是否总是选择相同数量的列?名称中是否有任何模式可用于例如按功能选择?有没有常见的场景?通过以上所有方法修改您的问题,我们将看看我们能做些什么。
  • 感谢您的反馈。我添加了代码和数据。我希望这将帮助您更好地理解问题。如果我可以进一步改进这个问题,请告诉我。此外,如果可能的话,提高问题的评分,这样我以后在提出新问题时就不会遇到问题。
  • 这看起来是一个非常有趣的潜在问题。但是,仍然没有足够的信息来构建任何东西。有什么变化?数据集中实际存在什么?你需要输出包含什么?查看here,了解有关在 R 中制作可重现示例的更多信息。

标签: r dplyr grouping dynamic-columns summarization


【解决方案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))
  )

在我看来,有三种方法,按合理性升序排列。第一种选择是遵循您的基本布局。在这里,我将dfid 分开,按照指示进行传播,然后使用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,因为fill 参数的最后一个0。如果您希望返回 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

【讨论】:

  • 马克感谢您的详尽回答。当我运行您的代码时,我会收到一些错误消息。当我运行第一部分时,我收到错误消息“n() 中的错误:不应直接调用此函数”。当我运行第二部分和第三部分时,我收到错误消息“错误:输入中不存在键列'房间'。”。此错误出现在汇总子句之后。顺便说一句,我发布了一个标题为:“使用参考类范例在 R 中定义一个类”的问题。如果您可以看看是否可以提供答案,是否有可能?
  • 看来我错过了包括我设置样本数量以包含在我为您制作的可重现数据中的部分。在我包含它之后(现在在问题中),其他一切都按预期运行。您是否尝试单独运行各个部分?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-11-10
  • 2019-09-29
  • 2021-10-18
  • 2021-01-20
  • 1970-01-01
  • 2018-07-28
  • 2014-05-05
相关资源
最近更新 更多