【问题标题】:How to condensed a data frame based on nearest matching times in R如何根据 R 中最近的匹配时间压缩数据帧
【发布时间】:2014-02-23 17:46:00
【问题描述】:

我有一个数据框,目前包含两个 HH:MM:SS 格式的“时间”列。我想压缩这个数据框,以便每个唯一的“id”值只有一行。我想为每个唯一的“id”值保留该行,该值的“time1”值与“time2”值最接近。但是,“time1”需要大于“time2”。

这是一个简单的例子:

> dput(df)
structure(list(id = c(1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 3L, 3L, 
3L, 3L, 4L, 4L, 4L, 4L), count = c(23L, 23L, 23L, 23L, 45L, 45L, 
45L, 45L, 67L, 67L, 67L, 67L, 88L, 88L, 88L, 88L), time1 = structure(c(1L, 
1L, 1L, 1L, 2L, 2L, 2L, 2L, 4L, 4L, 4L, 4L, 3L, 3L, 3L, 3L), .Label = c("00:13:00", 
"01:13:00", "07:18:00", "18:14:00"), class = "factor"), time2 = structure(c(4L, 
1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L), .Label = c("00:00:00", 
"06:00:00", "12:00:00", "18:00:00"), class = "factor"), afn = c(3.36, 
0.63, 1.77, 3.89, 3.36, 0.63, 1.77, 3.89, 3.36, 0.63, 1.77, 3.89, 
3.36, 0.63, 1.77, 3.89), dfn = c(201.67, 157.27, 103.55, 191.41, 
201.67, 157.27, 103.55, 191.41, 201.67, 157.27, 103.55, 191.41, 
201.67, 157.27, 103.55, 191.41)), .Names = c("id", "count", "time1", 
"time2", "afn", "dfn"), class = "data.frame", row.names = c(NA, 
-16L))

> df
   id count    time1    time2  afn    dfn
1   1    23 00:13:00 18:00:00 3.36 201.67
2   1    23 00:13:00 00:00:00 0.63 157.27
3   1    23 00:13:00 06:00:00 1.77 103.55
4   1    23 00:13:00 12:00:00 3.89 191.41
5   2    45 01:13:00 18:00:00 3.36 201.67
6   2    45 01:13:00 00:00:00 0.63 157.27
7   2    45 01:13:00 06:00:00 1.77 103.55
8   2    45 01:13:00 12:00:00 3.89 191.41
9   3    67 18:14:00 18:00:00 3.36 201.67
10  3    67 18:14:00 00:00:00 0.63 157.27
11  3    67 18:14:00 06:00:00 1.77 103.55
12  3    67 18:14:00 12:00:00 3.89 191.41
13  4    88 07:18:00 18:00:00 3.36 201.67
14  4    88 07:18:00 00:00:00 0.63 157.27
15  4    88 07:18:00 06:00:00 1.77 103.55
16  4    88 07:18:00 12:00:00 3.89 191.41

我想在上述情况下得到这个矩阵:

id  count   time1       time2       afn     dfn
1   23      00:13:00    00:00:00    0.63    157.27
2   45      01:13:00    00:00:00    0.63    157.27
3   67      18:14:00    18:00:00    3.36    201.67
4   88      07:18:00    06:00:00    1.77    103.55

我过去曾使用 ddply() 函数来压缩数据帧,但没有使用合并的匹配规则。我必须应用这是一个包含很多列的数据框(比这里给出的简单示例要多得多),所以任何关于如何做到这一点的建议都会很棒。任何帮助将不胜感激。非常感谢!

【问题讨论】:

  • 我想指出,排序表明18:00:0000:00:00 之前。

标签: r time match datetime-format


【解决方案1】:

这里有一些解决方案。

1) ave 这使用了来自 R 基础的 chron times 以及 subsetave

library(chron)

delta <- as.vector(times(df$time1) - times(df$time2))
df2 <- subset(df, delta > 0)
df2[ave(delta, df2$id, FUN = function(delta) delta == min(delta)) == 1, ]

2) dplyr 这使用 chron times 和 dplyr 包:

library(chron)
library(dplyr) 

df %.% 
   mutate(delta = as.vector(times(time1) - times(time2))) %.% 
   filter(delta > 0) %.% 
   group_by(id) %.% 
   filter(delta == min(delta)) %.% 
   select(- delta)

3) sqldf

library(sqldf)

sqldf("select *, min(strftime('%s', time1) - strftime('%s', time2)) delta
  from (select * from df where strftime('%s', time1) > strftime('%s', time2))
  group by id")[seq_along(df)]

或者可能是我们在 R 中计算 delta 然后使用 sqldf 的这种变化:

library(sqldf)
library(chron)

df2 = transform(df, delta = as.vector(times(time1) - times(time2)))

sqldf("select *, min(delta) delta
  from (select * from df2 where delta > 0)
  group by id")[-ncol(df2)]

4) 数据表

library(data.table)
library(chron)

DT <- data.table(df)
DT[, delta := times(time1) - times(time2)
 ][delta > 0
 ][, .SD[delta == min(delta)], by = id
 ][, seq_along(df), with = FALSE]

添加额外的解决方案。更正了 librarysubset 语句。小幅改进。

【讨论】:

  • @G. Grothendieck:非常感谢您提供的许多不同选项。 dplyr 很棒!只是一件小事:它应该在代码行 3 上读取 df2 &lt;- subset(df, delta &gt; 0) 而不是 subset(df2…) 吗?非常感谢您的帮助!
  • 是的,现在已经修复了。谢谢。
【解决方案2】:

这是一种使用强大的dplyr 包的方法:

library(dplyr)

(df %.%
   mutate(timeDiff = as.integer(strptime(time1, "%X") - strptime(time2, "%X")),
          posDiff = timeDiff >= 0) %.%
   filter(posDiff) %.%
   group_by(id) %.%
   filter(min(timeDiff) == timeDiff))[names(df)]

#   id count    time1    time2  afn    dfn
# 1  1    23 00:13:00 00:00:00 0.63 157.27
# 2  2    45 01:13:00 00:00:00 0.63 157.27
# 3  3    67 18:14:00 18:00:00 3.36 201.67
# 4  4    88 07:18:00 06:00:00 1.77 103.55

【讨论】:

  • @Sven:也非常感谢 dplyr 的建议。我将不得不更多地研究这个包。
【解决方案3】:

使用ddplymerge 的方法。 (假设“最近匹配时间”是difftimes 的最小绝对值)

t1 <- strptime(df$time1, "%H:%M:%S")
t2 <- strptime(df$time2, "%H:%M:%S")
df$min.diff <- abs(as.numeric(difftime(t1, t2, units='mins')))

d1 <- ddply(df, .(id), summarize, min.diff = min(min.diff))

> merge(df, d1, by = c("id", "min.diff"))
  id min.diff count    time1    time2  afn    dfn
1  1       13    23 00:13:00 00:00:00 0.63 157.27
2  2       73    45 01:13:00 00:00:00 0.63 157.27
3  3       14    67 18:14:00 18:00:00 3.36 201.67
4  4       78    88 07:18:00 06:00:00 1.77 103.55

【讨论】:

  • @朱利安:谢谢你的回答。您定义datas的代码中是否应该有额外的一行?
  • 哦,不抱歉,它确实是“df”而不是“datas”。
  • 请注意,strptime 输出包括今天的日期,所以如果碰巧 t1 设置在午夜之前,然后 t2 设置在午夜之后,那么 t1 和 t2 将具有不同的日期,而如果它们在同一天运行会有相同的日期。当然,这是极不可能的,但有可能。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-03-23
  • 2015-10-27
  • 1970-01-01
  • 2020-09-23
  • 2020-12-15
  • 2017-05-09
  • 1970-01-01
相关资源
最近更新 更多