【问题标题】:Accumulate number of distinct values in a column grouped by another variable in R在 R 中由另一个变量分组的列中累积不同值的数量
【发布时间】:2016-02-02 15:19:44
【问题描述】:

我正在努力处理来自 R 中 data.frame 的数据请求,其中包含一个表格,其中包含俱乐部、他们的 ID、他们成立的年份、他们所属地区的 ID 以及他们所在的年份关闭。 data.frame dat 看起来像

club_id   founded    district_id    closed
   1       2012         1             NA
   2       2012         2            2014
   3       2013         2             NA
   4       2013         3            2014
  ...

closed 列中的NA 表示该俱乐部在当年(2016 年)仍然存在。

我想要的是每年仍然存在的俱乐部的数量,例如,这将为我提供上面的数据 sn-p:

2012   2013   2014 ...
  2      4     2   ...

我尝试使用dplyr

dat %>%
 group_by(founded) %>%
 summarise(clubs_per_year = n_distinct(club_id))

但是,这给我的只是每年新成立的俱乐部的数量,而不是每年现有俱乐部的总数。

【问题讨论】:

  • 如果一个俱乐部只存在一年,它就不会出现在你的统计中,这是你想要的吗?
  • 是的,这就是我想要的。谢谢!
  • 您是否也希望 2016 年也包含在结果中?因为一些俱乐部可能在年底之前仍然关闭。请在答案下方提供一些反馈,因为似乎每个人都得到了不同的结果。
  • 不,不应包括 2016 年。只是过去几年。
  • 好的,那就回滚到原来的版本。

标签: r dataframe dplyr


【解决方案1】:

我不确定如何使用dplyr 来实现这一点,但这里有一个可能的data.table 解决方案。这基本上是为每个俱乐部创建一个序列,同时将 NA 替换为当前年份,然后计算每年的事件数

library(data.table)
setDT(df)[, .(Year = founded:(replace(closed, is.na(closed), year(Sys.Date())) - 1L)), 
            by = club_id
          ][, .(Uniques = uniqueN(club_id)), by = Year]

#    Year Uniques
# 1: 2012       2
# 2: 2013       4
# 3: 2014       2
# 4: 2015       2

【讨论】:

    【解决方案2】:

    我尝试了一个完整的 dplyr 解决方案。攻击计划是为每个俱乐部生成一系列活跃年份,然后计算每个活跃年份的俱乐部 ID。

    首先,我们计算出每个俱乐部的最后活跃年份。

    max_year <- 2015
    years <- data_frame(
      club_id = 1:4, 
      founded = c(2012, 2012, 2013, 2013),
      closed = c(NA, 2014, NA, 2014))
    
    years <- years %>% 
      mutate(last_active = ifelse(is.na(closed), max_year, closed - 1))
    years
    #> Source: local data frame [4 x 4]
    #> 
    #>   club_id founded closed last_active
    #>     (int)   (dbl)  (dbl)       (dbl)
    #> 1       1    2012     NA        2015
    #> 2       2    2012   2014        2013
    #> 3       3    2013     NA        2015
    #> 4       4    2013   2014        2013
    

    接下来,我们创建另一个数据框,其中包含数据中每个founded-last_active 范围内每个活跃年份的一行。我们通过使用do 函数来做到这一点。 do 让我们在数据帧上计算任意函数;唯一的规则是函数需要返回一个数据框。 do 尊重分组变量,因此也会返回这些分组列。

    # Create a single-column data-frame with a sequence of values
    seq_df <- function(col_name, min, max) {
      data.frame(seq(min, max)) %>% setNames(col_name)
    }
    
    year_scheme <- years %>% 
      # Find each found-last_active pairings
      select(founded, last_active) %>% 
      distinct %>% 
      # Create a sequence of rows for each of those pairings
      group_by(founded, last_active) %>% 
      do(seq_df("active_year", .$founded, .$last_active)) %>% 
      ungroup
    year_scheme
    #> Source: local data frame [10 x 3]
    #> 
    #>    founded last_active active_year
    #>      (dbl)       (dbl)       (int)
    #> 1     2012        2013        2012
    #> 2     2012        2013        2013
    #> 3     2012        2015        2012
    #> 4     2012        2015        2013
    #> 5     2012        2015        2014
    #> 6     2012        2015        2015
    #> 7     2013        2013        2013
    #> 8     2013        2015        2013
    #> 9     2013        2015        2014
    #> 10    2013        2015        2015
    

    最后,我们可以加入表格并计算组 ID。

    full_years <- left_join(years, year_scheme)
    #> Joining by: c("founded", "last_active")
    full_years
    #> Source: local data frame [10 x 5]
    #> 
    #>    club_id founded closed last_active active_year
    #>      (int)   (dbl)  (dbl)       (dbl)       (int)
    #> 1        1    2012     NA        2015        2012
    #> 2        1    2012     NA        2015        2013
    #> 3        1    2012     NA        2015        2014
    #> 4        1    2012     NA        2015        2015
    #> 5        2    2012   2014        2014        2012
    #> 6        2    2012   2014        2014        2013
    #> 7        3    2013     NA        2015        2013
    #> 8        3    2013     NA        2015        2014
    #> 9        3    2013     NA        2015        2015
    #> 10       4    2013   2014        2014        2013
    
    # years per club
    full_years %>% count(club_id)
    #> Source: local data frame [4 x 2]
    #> 
    #>   club_id     n
    #>     (int) (int)
    #> 1       1     4
    #> 2       2     2
    #> 3       3     3
    #> 4       4     1
    
    # clubs per year
    full_years %>% count(active_year)
    #> Source: local data frame [4 x 2]
    #> 
    #>   active_year     n
    #>         (int) (int)
    #> 1        2012     2
    #> 2        2013     4
    #> 3        2014     2
    #> 4        2015     2
    

    【讨论】:

    • 结果是错误的。 2014 应该是 2,2015 也是如此
    • 一般基于 dplyr 的策略是正确的。我只是没有实现所有 OP 的假设。我更新了代码和模拟数据以匹配原始问题。
    【解决方案3】:

    此解决方案使用data.table 包中的dcast

    library(data.table)
    
    ##Example data
    DT <- data.table(club_id=1:4, founded=rep(2012:2013, each=2),
                     district_id=c(1, 2, 2, 3), closed=rep(c(NA, 2014), 2))
    
    ## Fill in NAs with current year, create row for each year the club
    ##  exists, cast to columns for each year, and get the count of clubs
    ##  per year using length function
    dcast(DT[, .(year=founded:ifelse(is.na(closed), year(Sys.Date()), closed)),
          by=club_id],  . ~ year, length, fill=0)
    
    ##    . 2012 2013 2014 2015 2016
    ## 1: .    2    4    4    2    2
    

    以下版本类似,但不计算一年内的俱乐部,除非它全年开放。谨防俱乐部在同一年开设和关闭的情况。我添加了一个在 2015 年开业和关闭的俱乐部。

    DT2 <- data.table(club_id=1:5, founded=c(rep(2012:2013, each=2), 2015),
                      district_id=c(1, 2, 2, 3, 3),
                      closed=c(rep(c(NA, 2014), 2), 2015))
    
    ## Fill in missing values with the current year
    DT2[, closed2:=ifelse(is.na(closed), year(Sys.Date()), closed)]
    
    ## Cast to columns as before, ignore cases where the club's open and
    ##  closed years match, and then subtract one from the closed year
    dcast(DT2[founded!=closed2, .(year=founded:(closed2-1)), by=club_id],
          . ~ year, length, fill=0)
    
    ##    . 2012 2013 2014 2015
    ## 1: .    2    4    2    2
    

    【讨论】:

    • 你可以编辑到dcast(DT[, .(year=founded:(ifelse(is.na(closed), year(Sys.Date()), closed) - 1L)), by = club_id], . ~ year, length, fill=0)
    • 感谢@DavidArenburg。是的,从关闭的年份中减去 1 将按照要求给出答案。但是俱乐部在同一年开张和关闭的情况呢?如果是我,我会计算俱乐部,只要它至少在一年中的一部分时间开放。换句话说,2014 年我会数 4 而不是 2。
    • 是的,我也是这么想的,可能值得问问 OP
    【解决方案4】:

    这是一个dplyr 解决方案,采用不同的方法

    注意:我几天前想出了这个问题,但我发现了n_distinct 的一个错误并报告了它;它现在已在最新的开发版本中修复。在旧版本的dplyr 中,我必须使用dplyr::n_distinct,但当前版本只需要n_distinct

    这种方法创建一个带有年份列的data.frame,并根据datdata.frame(来自OP的数据)对其进行修改

    library(dplyr)
    
    yrdf <- data.frame(year = 2012:2015) # "dat" could be used to create this as well.
    ## For each year calculate the count based on the data in 'dat'
    yrdf %>%
        group_by(year) %>%
        mutate(count = n_distinct(
                           dat$club_id[ (is.na(dat$closed) | (dat$closed > year)) & dat$founded <= year]
                           )
        ) %>%
        ungroup
    
    ##    year count
    ##   (int) (int)
    ## 1  2012     2
    ## 2  2013     4
    ## 3  2014     2
    ## 4  2015     2
    

    【讨论】:

      猜你喜欢
      • 2021-06-17
      • 1970-01-01
      • 2023-03-17
      • 2020-07-18
      • 2016-05-09
      • 2016-08-17
      • 2013-09-14
      • 1970-01-01
      相关资源
      最近更新 更多