【问题标题】:Dplyr or data.table consolidate consecutive rows within grouped data based on value in another columnDplyr 或 data.table 根据另一列中的值合并分组数据中的连续行
【发布时间】:2016-04-28 19:14:02
【问题描述】:

我的数据如下所示:

ID  CLASS   START   END
100 GA  3-Jan-15    1-Feb-15
100 G   1-Feb-15    22-Feb-15
100 GA  28-Feb-15   17-Mar-15
100 G   1-Apr-15    8-Apr-15
100 G   10-Apr-15   18-Apr-15
200 FA  3-Jan-14    1-Feb-14
200 FA  1-Feb-14    22-Feb-14
200 G   28-Feb-14   15-Mar-14
200 F   1-Apr-14    20-Apr-14

这是数据:

df <- structure(list(ID = c(100L, 100L, 100L, 100L, 100L, 200L, 200L, 
200L, 200L), CLASS = structure(c(4L, 3L, 4L, 3L, 3L, 2L, 2L, 
3L, 1L), .Label = c("F", "FA", "G", "GA"), class = "factor"), 
START = structure(c(9L, 4L, 7L, 2L, 5L, 8L, 3L, 6L, 1L), .Label = c("1-Apr-14", 
"1-Apr-15", "1-Feb-14", "1-Feb-15", "10-Apr-15", "28-Feb-14", 
"28-Feb-15", "3-Jan-14", "3-Jan-15"), class = "factor"), 
END = structure(c(2L, 8L, 4L, 9L, 5L, 1L, 7L, 3L, 6L), .Label = c("1-Feb-14", 
"1-Feb-15", "15-Mar-14", "17-Mar-15", "18-Apr-15", "20-Apr-14", 
"22-Feb-14", "22-Feb-15", "8-Apr-15"), class = "factor")), .Names = c("ID", 
"CLASS", "START", "END"), class = "data.frame", row.names = c(NA, 
-9L))

我想按 ID 列对数据进行分组,然后在 CLASS 列中合并任何连续出现的相同值(按开始日期排序),同时选择最短开始日期和最长结束日期。所以对于 ID 号 100,只有一个实例“G”类是连续的,所以我想将这两行合并为具有 min(START) 和 max(END) 日期的单行。这是一个简单的示例,但在实际数据中,有时需要合并多个连续行。

我尝试了 group_by 然后使用某种排名,但这似乎没有奏效。关于如何解决这个问题的任何建议?这也是我第一次在 SO 上发帖,所以我希望这个问题有意义。

结果应如下所示:

ID  CLASS   START   END
100 GA  3-Jan-15    1-Feb-15
100 G   1-Feb-15    22-Feb-15
100 GA  28-Feb-15   17-Mar-15
100 G   1-Apr-15    18-Apr-15
200 FA  3-Jan-14    22-Feb-14
200 G   28-Feb-14   15-Mar-14
200 F   1-Apr-14    20-Apr-14

【问题讨论】:

  • ID 200 中的两个FAs 不应该也合并一下吗?
  • @CactusWoman 没错!我将编辑表格。

标签: r data.table dplyr


【解决方案1】:

这是一个选项,使用data.table::rleid 为相同的IDCLASS 的运行创建一个ID:

# make START and END Date class for easier manipulation
df <- df %>% mutate(START = as.Date(START, '%d-%b-%y'), 
                    END = as.Date(END, '%d-%b-%y'))
# More concise alternative: 
# df <- df %>% mutate_each(funs(as.Date(., '%d-%b-%y')), START, END)

# group and make rleid as mentioned above
df %>% group_by(ID, CLASS, rleid = data.table::rleid(ID, CLASS)) %>% 
    # collapse with summarise, replacing START and END with their min and max for each group
    summarise(START = min(START), END = max(END)) %>% 
    # clean up arrangement and get rid of added rleid column
    ungroup() %>% arrange(rleid) %>% select(-rleid)

# Source: local data frame [7 x 4]
# 
#      ID  CLASS      START        END
#   (int) (fctr)     (date)     (date)
# 1   100     GA 2015-01-03 2015-02-01
# 2   100      G 2015-02-01 2015-02-22
# 3   100     GA 2015-02-28 2015-03-17
# 4   100      G 2015-04-01 2015-04-18
# 5   200     FA 2014-01-03 2014-02-22
# 6   200      G 2014-02-28 2014-03-15
# 7   200      F 2014-04-01 2014-04-20

这是纯粹的 data.table 类比:

library(data.table)
setDT(df)
datecols = c("START","END")
df[, (datecols) := lapply(.SD, as.IDate, format = '%d-%b-%y'), .SDcols = datecols]

df[, .(START = START[1L], END = END[.N]), by=.(ID, CLASS, r = rleid(ID, CLASS))][, r := NULL][]

【讨论】:

  • 很好,尽管我会覆盖格式错误的日期列,而不是只在链的一部分中进行转换。我的意思是 df = df %&gt;% mutate_each(funs(. %&gt;% as.Date(format='%d-%b-%y')), START, END) 在其他东西之前。
  • @Frank 是的,我追求的是可读性而不是简洁。我认为最简洁的方法是将mutate 替换为mutate_each(funs(as.Date(., '%d-%b-%y')), START, END)。 (当然,欢迎对 data.table 进行编辑!)
  • @Alistair Nice data.table 解决方案。但请注意,日期转换仅适用于英语语言环境。我必须在我的系统上使用Sys.setlocale("LC_ALL","English")%b 转换规范需要当前语言环境中的月份名称。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-02-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多