【问题标题】:Cumulative count of observations per day with tally/dplyr, filling missing values每天使用 tally/dplyr 的累计观察次数,填充缺失值
【发布时间】:2018-09-05 11:02:38
【问题描述】:

我有每个用户的观察列表;每个用户每天可能对foo 进行多次观察。对于每个不同的日子,我想要foo 值的累积计数。这是我到目前为止得到的:

library(tidyverse)
library(lubridate)

df = tribble(
  ~user_id, ~foo, ~bar, ~created_at,
  1, "a", "b", "2018-07-30",
  1, "a", "c", "2018-07-31",
  1, "a", "c", "2018-07-31",
  1, "b", "a", "2018-08-01",
  1, "b", "c", "2018-08-02",
  1, "b", "a", "2018-08-03",
  1, "a", "a", "2018-08-03",
  2, "b", "b", "2018-07-30",
  2, "b", "c", "2018-07-31",
  2, "a", "a", "2018-08-01",
  2, "a", "a", "2018-08-01",
  2, "a", "c", "2018-08-02",
  2, "a", "c", "2018-08-02",
  2, "a", "a", "2018-08-03"
) %>% mutate_at("created_at", as_datetime)

df %>%
  mutate(cutoff_date = created_at %>% date) %>% 
  group_by(user_id, foo, cutoff_date) %>% 
  tally %>%
  mutate(foo_cnt = cumsum(n)) %>%
  select(-n) %>% 
  arrange(user_id, cutoff_date, foo)

这给了我:

   user_id foo   cutoff_date foo_cnt
     <dbl> <chr> <date>        <int>
 1      1. a     2018-07-30        1
 2      1. a     2018-07-31        3
 3      1. b     2018-08-01        1
 4      1. b     2018-08-02        2
 5      1. a     2018-08-03        4
 6      1. b     2018-08-03        3
 7      2. b     2018-07-30        1
 8      2. b     2018-07-31        2
 9      2. a     2018-08-01        2
10      2. a     2018-08-02        4
11      2. a     2018-08-03        5

太好了,所以我知道在 8 月 3 日之前,用户 1 已经看到了四次 a 和三次 b。我现在想知道,对于我的数据中出现的每个日期(我不关心丢失的日期):

  • 截至日期的特定foo观察的总数
  • 与其他观察相比的相对数量

也就是说,输出应该是:

  user_id cutoff_date foo foo_cnt foo_cnt_total foo_pct
1      1. 2018-07-30  a         1             1     100
2      1. 2018-07-30  b         0             0       0
3      1. 2018-07-31  a         3             4     100
4      1. 2018-07-31  b         0             0       0
5      1. 2018-08-01  a         3             7    87.5
6      1. 2018-08-01  b         1             1    12.5
...

在第 5 行中,这一比例为 87.5%,因为在此之前用户已经看到了七次 a 和一次 b

我知道如何到达那里,但我正在努力为数据中存在的日期包含 foo 的其他值,但没有观察到 foo。我已经查看了complete(),但我不知道如何使用它来填充剩余的值。

例如,当我添加其中任何一个时,我不会得到额外的列:

complete(nesting(user_id, foo), cutoff_date)
complete(user_id, cutoff_date, foo)

我错过了什么?


更新:我按照建议添加了ungroup,现在我也得到了每天的总数。我已经使用fillfoo 的相同值填充先前的值:

df %>%
  mutate(cutoff_date = created_at %>% date) %>% 
  group_by(user_id, foo, cutoff_date) %>%
  tally %>%
  mutate(foo_cnt = cumsum(n)) %>%
  select(-n) %>% 
  ungroup() %>% 
  complete(nesting(user_id, foo), cutoff_date) %>% 
  arrange(user_id, cutoff_date, foo) %>% 
  group_by(user_id, foo) %>% 
  fill(foo_cnt) %>% 
  ungroup() %>% 
  group_by(user_id, cutoff_date) %>% 
  mutate(foo_cnt_total = sum(foo_cnt, na.rm = TRUE))

   user_id foo   cutoff_date foo_cnt foo_cnt_total
     <dbl> <chr> <date>        <int>         <int>
 1      1. a     2018-07-30        1             1
 2      1. a     2018-07-31        3             3
 3      1. a     2018-08-01        3             4
 4      1. a     2018-08-02        3             5
 5      1. a     2018-08-03        4             7
 6      1. b     2018-07-30       NA             1
 7      1. b     2018-07-31       NA             3
 8      1. b     2018-08-01        1             4
 9      1. b     2018-08-02        2             5
10      1. b     2018-08-03        3             7

但是,b 的值不应以NA 开头。这里需要什么?

【问题讨论】:

  • complete之前使用ungroup()
  • @kath 谢谢,帮助很大!我添加了所需的fill 以继承前几天的值,我几乎到了我需要的地方。但是,我在尚未看到 b 的第一天得到 NA 值(请参阅更新的问题)。有什么想法吗?

标签: r dplyr


【解决方案1】:

您可以指定在complete-call 中使用的填充,并稍微重新排列不同步骤的顺序,获得所需的输出:

df %>%
  mutate(cutoff_date = date(created_at)) %>% 
  count(user_id, foo, cutoff_date) %>%
  complete(nesting(user_id, foo), cutoff_date, fill = list(n = 0)) %>% 
  arrange(user_id, foo, cutoff_date) %>% 
  group_by(user_id, foo) %>% 
  mutate(foo_cnt = cumsum(n)) %>%
  group_by(user_id, cutoff_date) %>% 
  mutate(foo_cnt_total = sum(foo_cnt), 
         foo_pct = 100 * foo_cnt / foo_cnt_total) %>% 
  select(-n)

# A tibble: 20 x 6
# Groups:   user_id, cutoff_date [10]
#    user_id foo   cutoff_date foo_cnt foo_cnt_total foo_pct
#      <dbl> <chr> <date>        <dbl>         <dbl>   <dbl>
#  1       1 a     2018-07-30        1             1   100  
#  2       1 a     2018-07-31        3             3   100  
#  3       1 a     2018-08-01        3             4    75  
#  4       1 a     2018-08-02        3             5    60  
#  5       1 a     2018-08-03        4             7    57.1
#  6       1 b     2018-07-30        0             1     0  
#  7       1 b     2018-07-31        0             3     0  
#  8       1 b     2018-08-01        1             4    25  
#  9       1 b     2018-08-02        2             5    40  
# 10       1 b     2018-08-03        3             7    42.9

【讨论】:

  • 嗯,当我在数据上运行它时,我得到不同的输出 - 在你的代码中,foo_cnt_total 最后只计算一次,并且不按日期分组(或累积,为此事情)。你确定你的代码和输出匹配吗?
  • 谢谢!添加mutate(foo_pct = 100 * foo_cnt / foo_cnt_total) 将给出百分比。
  • 此外,complete 调用可以执行 nesting(user_id, foo) - 否则如果我只想知道每个用户的 foo 实例,结果将是非常多余的。在我的数据集中执行此操作使我的行数从 3500 万行减少到 500,000 行。
  • 现在我明白了,为什么你的问题中有这个......因为在你的例子中两个用户都有相同的 foo 我不在乎......
  • 是的,我同意它没有被指定——只是值得注意。感谢你的宝贵时间! :)
【解决方案2】:
df = tribble(
    ~user_id, ~foo, ~bar, ~created_at,
    1, "a", "b", "2018-07-30",
    1, "a", "c", "2018-07-31",
    1, "a", "c", "2018-07-31",
    1, "b", "a", "2018-08-01",
    1, "b", "c", "2018-08-02",
    1, "b", "a", "2018-08-03",
    1, "a", "a", "2018-08-03",
    2, "b", "b", "2018-07-30",
    2, "b", "c", "2018-07-31",
    2, "a", "a", "2018-08-01",
    2, "a", "a", "2018-08-01",
    2, "a", "c", "2018-08-02",
    2, "a", "c", "2018-08-02",
    2, "a", "a", "2018-08-03"
) %>% mutate_at("created_at", as_datetime)

df %>%
    dplyr::mutate(cutoff_date = created_at %>% date) %>% 
    group_by(user_id, foo, cutoff_date) %>% 
    tally %>%
    dplyr::mutate(foo_cnt = cumsum(n)) %>%
    select(-n) %>% 
    arrange(user_id, cutoff_date, foo) %>% group_by(user_id) %>%
    complete(nesting(user_id, foo), cutoff_date, fill = list(foo_cnt = 0)) %>%
    arrange(user_id, cutoff_date, foo) %>% group_by(user_id, foo) %>%
    dplyr::mutate(foo_cnt_total = cumsum(foo_cnt)) %>% group_by(user_id, cutoff_date) %>%
    dplyr::mutate(foo_sum_del = sum(foo_cnt_total)) %>% group_by(user_id, foo, cutoff_date) %>%
    dplyr::mutate(foo_pct = foo_cnt_total/foo_sum_del*100) %>% ungroup() %>%
    select(-foo_sum_del)

结果:

# A tibble: 20 x 6
   user_id foo   cutoff_date foo_cnt foo_cnt_total foo_pct
     <dbl> <chr> <date>        <dbl>         <dbl>   <dbl>
 1       1 a     2018-07-30        1             1   100  
 2       1 b     2018-07-30        0             0     0  
 3       1 a     2018-07-31        3             4   100  
 4       1 b     2018-07-31        0             0     0  
 5       1 a     2018-08-01        0             4    80  
 6       1 b     2018-08-01        1             1    20  
 7       1 a     2018-08-02        0             4    57.1
 8       1 b     2018-08-02        2             3    42.9
 9       1 a     2018-08-03        4             8    57.1
10       1 b     2018-08-03        3             6    42.9
11       2 a     2018-07-30        0             0     0  
12       2 b     2018-07-30        1             1   100  
13       2 a     2018-07-31        0             0     0  
14       2 b     2018-07-31        2             3   100  
15       2 a     2018-08-01        2             2    40  
16       2 b     2018-08-01        0             3    60  
17       2 a     2018-08-02        4             6    66.7
18       2 b     2018-08-02        0             3    33.3
19       2 a     2018-08-03        5            11    78.6
20       2 b     2018-08-03        0             3    21.4

【讨论】:

    猜你喜欢
    • 2016-08-01
    • 1970-01-01
    • 2014-06-25
    • 2021-02-22
    • 2015-10-06
    • 1970-01-01
    • 2017-05-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多