【问题标题】:How to use dplyr to find unique entries in the previous rows如何使用 dplyr 在前几行中查找唯一条目
【发布时间】:2017-09-10 15:27:54
【问题描述】:

我有一个很长的数据框,或多或少具有以下结构:

df <- data.frame(
dates = c("2011-10-01","2011-10-01","2011-10-01","2011-10-02","2011-10-03","2011-10-05","2011-10-06","2011-10-06"),
ids = c("A","A","B","C","D","A","E","D"),
values = c(10,1,25,2,5,10,4,1))

> df
       dates ids values
1 2011-10-01   A     10
2 2011-10-01   A      1
3 2011-10-01   B     25
4 2011-10-02   C      2
5 2011-10-03   D      5
6 2011-10-05   A     10
7 2011-10-06   E      4
8 2011-10-06   D      1

我想得到以下输出:

       dates   unique_ids sum_values
1 2011-10-01            2         36
2 2011-10-02            3         38
3 2011-10-03            4         43
4 2011-10-04            4         43
5 2011-10-05            4         53
6 2011-10-06            5         58

即对于每个日期,unique_ids 给出了与较早日期对应的唯一 ID 的数量,而 sum_values 给出了与较早日期对应的值的总和。

我绝对想避免循环,因为原始 df 太大了。所以我在考虑使用 dplyr。

我知道如何获取 sum_value

df %>%
group_by(dates) %>%
summarize(sum_values_daily = sum(values)) %>%
mutate(sum_values = cumsum(sum_values_daily)) %>%
select(dates, sum_values)

我不知道如何获取 unique_ids 列。

有什么想法吗?

【问题讨论】:

  • df %&gt;% group_by(dates) %&gt;% summarise(unique_ids = n_distinct(ids), sum_values = sum(values))
  • 嗨,Ronak,你的建议不是想要的结果,我想要某个日期的 uniques_ids 之前所有日期的唯一 id 的数量,以及对应于之前的值的总和日期。
  • 请仔细检查您的预期输出,我认为它不正确。
  • mtoto 我认为它是正确的,但没有明确解释。 id 的唯一编号是 2,并且 2011-10-01 的所有值的总和是 36,但您不仅要添加唯一 id #s 的值。
  • @mtoto 所需的输出是正确的。我不想要一个简单的 df %>% group_by(date) %>% summarise(unique_ids=n_distinct(ids), sum_values = sum(values))

标签: r dplyr


【解决方案1】:

因为您正在尝试计算不同的ids 的数量跨组,首先我们需要定义一个布尔列,它允许我们仅对唯一的进行求和em> 值。

其次,您希望在预期输出中包含原始df 中缺失的日期,因此我们还需要使用完整的日期序列执行right_join。我在这里假设您的dates 列已经属于Date 类。这将产生 NA 值,我们 replace 通过 0

最后,我们计算unique_idssum_valuescumsum

library(dplyr)

df %>% mutate(unique_ids = !duplicated(ids)) %>%
        group_by(dates) %>%
        summarise(unique_ids = sum(unique_ids),
                  sum_values = sum(values)) %>%
        right_join(data.frame(dates = seq(min(df$date), 
                                          max(df$dates), 
                                          by = 1))) %>%
        mutate_each(funs(replace(., is.na(.), 0)), -dates)  %>%
        mutate_each(funs(cumsum), -dates)
#       dates unique_ids sum_values
#      <date>      <dbl>      <dbl>
#1 2011-10-01          2         36
#2 2011-10-02          3         38
#3 2011-10-03          4         43
#4 2011-10-04          4         43
#5 2011-10-05          4         53
#6 2011-10-06          5         58

【讨论】:

    【解决方案2】:

    作为替代方案,这里有一个data.table 解决方案。为清楚起见,我将展示一个三行版本,尽管这些行可以链接成一行。

    library(data.table)
    # convert to data.table and make dates a Data data type
    setDT(df)[, "dates" := as.Date(dates)]
    # merge on the daily values (missing 10-04 in original data)
    # convert NAs to 0 for missing dates, calculate cumulative sums of unique ID and values
    df <- df[.(seq.Date(min(dates), max(dates), by="day")), on="dates",
             .(dates, values=cumsum(ifelse(is.na(values), 0, values)),
               unique_ids=cumsum(!duplicated(ids) & !is.na(ids)))]
    # aggregate by date, saving the max of unique ID and value
    df <- df[, .(unique_ids=max(unique_ids), sum_values=max(values)), by=dates]
    

    返回

    df 
            dates unique_ids sum_values
    1: 2011-10-01          2         36
    2: 2011-10-02          3         38
    3: 2011-10-03          4         43
    4: 2011-10-04          4         43
    5: 2011-10-05          4         53
    6: 2011-10-06          5         58
    

    【讨论】:

    • @mtoto 感谢您抓住另一个 NA... 关于第二点,您部分正确。例如,当使用:= 时,如上面的setDT(df)[, "dates" := as.Date(dates)],data.table 通过引用分配。但是,在大多数其他操作中,它会进行复制(另一个例外是set)。要看到这一点,请尝试我的代码的前两行,第二行中没有 df &lt;-。然后在控制台中打印df。您会注意到缺少 10-04。
    【解决方案3】:
    library(dplyr)
    library(purrr)
    df %>% 
        mutate(dates = as.Date(dates), ids = as.character(ids)) %>% 
        group_by(dates) %>%
        summarise(ids = list(unique(ids)), values = sum(values)) %>%
        merge(data.frame(dates = seq.Date(min(.$dates), max(.$dates), "day")), all.y = TRUE) %>% 
        transmute(
            dates,
            uniqe_ids = map_int(accumulate(ids, ~unique(c(.x, .y))), length),
            sum_values = accumulate(values, ~sum(.x, .y, na.rm = TRUE))
        )
    

    第一部分,mutategroup_by 然后summarise 我猜,很容易理解:一些准备工作,比我们每天收集唯一的 id 和 sum 值。结果是:

    # A tibble: 5 × 3
           dates       ids values
          <date>    <list>  <dbl>
    1 2011-10-01 <chr [2]>     36
    2 2011-10-02 <chr [1]>      2
    3 2011-10-03 <chr [1]>      5
    4 2011-10-05 <chr [1]>     10
    5 2011-10-06 <chr [2]>      5
    

    然后我们将其与data.frame(dates = seq.Date(min(.$dates), max(.$dates), "day")) 合并以填补可能的日期空白。

    现在我们需要在 idsvalues 变量上累积地从上到下遍历。 对于ids,我们首先使用purrr::accumulate~unique(c(.x, .y)) 函数。这意味着我们从ids 的第一个单元格开始,然后逐步连接它(c),以下单元格仅保留唯一值。所以在我们的例子中,这将评估为:

    [[1]]
    [1] "A" "B"
    
    [[2]]
    [1] "A" "B" "C"
    
    [[3]]
    [1] "A" "B" "C" "D"
    
    [[4]]
    [1] "A" "B" "C" "D"
    
    [[5]]
    [1] "A" "B" "C" "D" "E"
    

    但我们只需要知道不同 id 的数量,因此我们在 purrr::map_int 的帮助下映射 length 函数。

    对于sum_values,我们计算累积和(我们不能使用cumsum,因为合并后可能会有NA)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-03
      • 2011-08-31
      相关资源
      最近更新 更多