【问题标题】:Insert midnight entry into Log将午夜条目插入日志
【发布时间】:2013-01-15 11:36:07
【问题描述】:

我们正在监控 3 个进程 A、B 和 C,它们始终处于 X、Y 或 Z 级。协议会记录进程何时更改级别。

df = read.csv(tc <- textConnection('Time1,Process1,Level1
2013-01-09 18:00:34,A,X
2013-01-09 18:00:34,B,Y
2013-01-09 18:00:34,C,X
2013-01-09 22:00:59,A,Z
2013-01-10 00:10:38,A,X
2013-01-10 18:38:35,B,Z
2013-01-11 05:03:11,A,Z
2013-01-11 11:09:10,C,Y
2013-01-11 12:01:18,A,Off
2013-01-11 12:01:18,B,Off
2013-01-11 12:01:18,C,Off
'),header=TRUE)
close.connection(tc) 
df$Time1 = as.POSIXct(df$Time1)

监控于 2013 年 1 月 9 日 18:00:34 开始,并于 2013 年 1 月 11 日 12:01:18 关闭。在 2013-01-09 18:00:34 和 2013-01-09 22:00:59 之间,进程 A 处于 X 级,在 2013-01-09 22:00:59 和 2013-01-10 00:10 之间: 38 进程 A 处于 Z 级。

出于图表的目的,我们希望将每个进程在每个午夜的最后一级和一级状态插入到协议中:

2013-01-09 23:59:59,A,Z
2013-01-10 00:00:00,A,Z
2013-01-10 23:59:59,A,X
2013-01-11 00:00:00,A,X

2013-01-09 23:59:59,B,Y
2013-01-10 00:00:00,B,Y
2013-01-10 23:59:59,B,Z
2013-01-11 00:00:00,B,Z

2013-01-09 23:59:59,C,X
2013-01-10 00:00:00,C,X
2013-01-10 23:59:59,C,X
2013-01-11 00:00:00,C,X

可以假设在 23:59:59 和 00:00:00 之间日志中没有事件。最后,协议会在插入后按 Time1 排序(这个我们可以自己弄清楚)。非常感谢任何指导!

【问题讨论】:

    标签: r datetime logging


    【解决方案1】:

    (+1) 相当复杂和有趣的任务。我想我有答案。我将尝试在这里解释该方法。我希望这是有道理的。这里有两个棘手的地方。我的解决方案使用data.table

    First: 我发现首先构造你需要的输出的前两列更容易。这是在代码的第一部分完成的,如下所示:

    require(data.table)
    dates <- unique(as.character(strptime(as.character(df$Time1), "%Y-%m-%d")))
    dates <- dates[1:(length(dates)-1)]
    dates <- strptime(paste(dates, "23:59:59"), "%Y-%m-%d %H:%M:%S")
    dates <- sort(c(dates, dates+1))
    Time <- rep(dates, length(levels(df$Process1)))
    Process <- rep(levels(df$Process1), each=length(dates))
    dt.out <- data.table(Time=as.POSIXct(Time), Process=Process)
    # data.table outputs crazy values if not converted using as.POSIXct..?!
    

    通过查看每一行代码的作用应该很容易理解。我希望它可以扩展到其他场景。

    Second: 第二位同样棘手,但可以使用data.table 在一行中完成。花了一段时间才弄清楚,但它很棒!

    dt <- data.table(df, key="Process1") # convert input data.frame to data.table
    out <- dt.out[, dt[J(Process)]$Level1[max(which(dt[J(Process)]$Time1 < Time))], 
                by = c("Process", "Time")]
    
    > out
    
        Process                Time V1
     1:       A 2013-01-09 23:59:59  Z
     2:       A 2013-01-10 00:00:00  Z
     3:       A 2013-01-10 23:59:59  X
     4:       A 2013-01-11 00:00:00  X
     5:       B 2013-01-09 23:59:59  Y
     6:       B 2013-01-10 00:00:00  Y
     7:       B 2013-01-10 23:59:59  Z
     8:       B 2013-01-11 00:00:00  Z
     9:       C 2013-01-09 23:59:59  X
    10:       C 2013-01-10 00:00:00  X
    11:       C 2013-01-10 23:59:59  X
    12:       C 2013-01-11 00:00:00  X
    

    让我把这两行分成几部分来解释发生了什么。

    在第一行中,我们将set key 用于dt 作为Process1。这允许VERY fast 按列Process1 过滤数据。即dt["A"]等价于df[df$Process1 == "A"],但前者速度极快。

    在第二行中,发生了很多事情。我们已经创建了dt.out,需要输出的前两列。剩下的就是第三列。查看by = c("Process", "Time") 行的最后一部分。在这里,我们用这两个变量分割data.table dt.out。对于每个拆分data.table,我们应用dt[J(Process)]$Level1[max(which(dt[J(Process)]$Time1 &lt; Time))],它基本上从Process过滤的data.table中的所有当前Time1值中挑选出Time的maximum index并使用这个最大索引返回对应的Levels1值。

    希望这会有所帮助。

    【讨论】:

    • +1 但这感觉像是 roll=TRUE 的工作,例如只是dt[dt.out,roll=TRUE],其中dtdt.out 都由'Process,Time' 键入。没有测试,但差不多?
    • 谢谢@Arun。我从你的帖子中学到了很多,也很享受这个问题。我按照您的建议实现了First:,并为Second: 使用了for-loop。不漂亮,但在大型数据集上,它似乎比您的解决方案更快。我会多玩一点data.table
    • @Arun,感谢您的评估。不幸的是,共享数据会很困难。在 2.5 天内大约有 3,000 行,大约 30 个流程和 10 个级别。越来越多。一些进程的活动比其他进程多得多。 df 已排序的事实有利于 for 循环。让我明天或接下来的几天再次使用您的解决方案......
    • 如果您遇到错误,很乐意帮助 Arun,但范围界定可能会有些棘手。 roll=TRUE 应该是一个显着的加速,因为就目前的答案而言,j 正在为 dt.out 的每个 1 行组进行评估,iiuc。这是每次对j 中的两个[.data.table 调用的新调用,以及相关的开销、参数检查等。用一个roll=TRUE 连接替换它将是一个批量连接。 i 不必为 roll 加入键入密钥,但如果它也有助于加快速度。
    • @Arun:我在周五离开了项目,无法再访问数据。最终的脚本可能并不完全漂亮,但它运行良好。我仍然有兴趣进一步探索data.table 包,但我现在的优先事项已经转移,我将像你一样首先制作一个新数据集。再次,我非常感谢您对这个问题表现出的兴趣!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-13
    • 1970-01-01
    相关资源
    最近更新 更多