【问题标题】:Gathering variable numbers of rows into fixed-width time intervals将可变数量的行收集到固定宽度的时间间隔中
【发布时间】:2014-08-28 05:38:25
【问题描述】:

我对 R 比较陌生,所以我可能忽略了 R 中已经存在的函数。我已经使用循环创建了一个解决方案,但我想知道是否有 R 方法可以做到这一点(到目前为止,所有我的 R 脚本中的一部分是无循环的,因为它们应该是这样的!)

数据包含一个事件的 CSV 记录。该事件具有 ISO-8601 时间戳、响应时间和响应代码(指示成功或特定失败)。我可以将此 CSV 转换为数据框,并从时间戳字符串创建一个 R 日期和时间字段。目前没问题。

然后我确定一个固定长度的时间间隔的大小,用于分析和绘图。例如,假设 5 分钟。在前 5 分钟的时间间隔内,可能有 151 个事件(数据框中的行)。在接下来的 5 分钟间隔内,可能只有 130 个事件。等等。换句话说,正在分析其日志的客户端具有不均匀的请求负载和不同的请求率。我需要做的是每 5 分钟的时间间隔,然后为该窗口内的所有事件计算平均值、最大值、第 95 个百分位数和第 99 个百分位数。

这目前工作得很好,但我已经在 for 循环中实现了它。结果是需要相当长的时间:在 i7 CPU 上超过 5 秒)分析 18K 事件并将它们转换为 177 个 5 分钟间隔。对于相同的功能,这仍然比 Splunk 快。但似乎我应该使用现有的 R 函数。

例如,这里是我的“原始”数据框的头部(数据)。好吧,无论如何,几乎是原始的:如果需要,将行反转以确保行从最旧到最新排序,并且 event.datetime 列已添加 strptime 应用于 X_time 列的结果:

DATA HEAD:
                            X_time CODE RTIME      event.datetime
18867 2014-08-20T00:00:17.971+0000    0 0.144 2014-08-20 00:00:17
18866 2014-08-20T00:00:17.973+0000    0 0.146 2014-08-20 00:00:17
18865 2014-08-20T00:00:18.156+0000    0 0.328 2014-08-20 00:00:18
18864 2014-08-20T00:00:18.177+0000    0 0.349 2014-08-20 00:00:18
18863 2014-08-20T00:00:18.264+0000 2057 0.437 2014-08-20 00:00:18
18862 2014-08-20T00:00:18.294+0000    0 0.467 2014-08-20 00:00:18

通过 dput():

DPUT DATA HEAD:
structure(list(X_time = c("2014-08-20T00:00:17.971+0000", "2014-08-20T00:00:17.973+0000", 
"2014-08-20T00:00:18.156+0000", "2014-08-20T00:00:18.177+0000", 
"2014-08-20T00:00:18.264+0000", "2014-08-20T00:00:18.294+0000"
), CODE = c(0L, 0L, 0L, 0L, 0L, 0L), RTIME = c(0.144, 
0.146, 0.328, 0.349, 0.437, 0.467), event.datetime = structure(c(1408492817.971, 
1408492817.973, 1408492818.156, 1408492818.177, 1408492818.264, 
1408492818.294), class = c("POSIXct", "POSIXt"), tzone = "UTC")), .Names = c("X_time", 
"CODE", "RTIME", "event.datetime"), row.names = c(18867L, 
18866L, 18865L, 18864L, 18863L, 18862L), class = "data.frame")

这里是统计数据的头部(stats.data),每 5 分钟时间间隔有一行包含该行的统计信息(包括对这些统计数据有贡献的原始数据行数的计数) )。在这种情况下,正在分析的原始数据中的 RTIME(响应时间,以秒为单位)列的最大值、平均值(平均值)、第 95 个百分位和第 99 个百分位:

STATS HEAD:
                Start Count   Max       Avg Perc95  Perc99
1 2014-08-20 00:00:17   151 0.545 0.3369073 0.4650 0.50200
2 2014-08-20 00:05:23   130 0.487 0.3543385 0.4630 0.47913
3 2014-08-20 00:10:28   158 0.492 0.3340190 0.4520 0.48315
4 2014-08-20 00:15:33   143 0.515 0.3547133 0.4518 0.49222
5 2014-08-20 00:20:39   115 0.469 0.3311739 0.4453 0.45616
6 2014-08-20 00:25:41   157 0.454 0.3262420 0.4440 0.45200

通过 dput():

DPUT STATS HEAD:
structure(list(Start = structure(c(1408492817.971, 1408493123.391, 
1408493428.267, 1408493733.976, 1408494039.253, 1408494341.597
), class = c("POSIXct", "POSIXt"), tzone = "UTC"), Count = c(151L, 
130L, 158L, 143L, 115L, 157L), Max = c(0.545, 0.487, 0.492, 0.515, 
0.469, 0.454), Avg = c(0.336907284768212, 0.354338461538462, 
0.334018987341772, 0.354713286713287, 0.331173913043478, 0.326242038216561
), Perc95 = c(0.465, 0.463, 0.452, 0.4518, 0.4453, 0.444), Perc99 = c(0.502, 
0.47913, 0.48315, 0.49222, 0.45616, 0.452)), .Names = c("Start", 
"Count", "Max", "Avg", "Perc95", "Perc99"), row.names = c(NA, 
6L), class = "data.frame")

我问的主要是因为这似乎是一个常见问题,并且我认为我无法找到内置的 R 解决方案是我在寻找方面的错,而不是 R 的实际缺乏。此外,包括反转行和将 ISO-8601 X_time 列转换为 R 日期/时间 event.datetime 列在内的所有其他处理都非常快,这是我将事件逐行收集到正确的固定 -宽度时间间隔,这是一个闪电般快速脚本的一个缺点。

【问题讨论】:

  • POSIXlt 对象有 cut. 和 seq. 方法。如果您发布了dput(head(stats.data)),您将获得一个有效的示例。尝试从控制台输出重建此类对象是一个真正的 PITA。

标签: r


【解决方案1】:

感谢指点。根据您的建议,我使用 seq 生成时间间隔序列,然后通过将每行适当分配到其适当时间间隔来创建一个因子。然后我使用 by 函数将每个时间间隔作为其自己的单独数据帧进行处理。但是对于这样做的其他人的一些兴趣点:

从我的(时间排序;它来自日志文件)数据中,我找到了开始和结束时间,然后使用 seq 和 POSIXlt 对象作为间隔。它工作正常,但我需要将间隔添加到结束时间,以防止结束间隔的行被剪切分类为 NA:

interval_set = seq(from=start_time, to=end_time + interval_size, by=interval_size)

data_factors = cut(data$event.datetime, interval_set, include.lowest=TRUE, right=FALSE)
data_factors = droplevels(data_factors)

在我的回调函数中,事件时间以字符串的形式返回,即使它作为适当的 R POSIXlt 对象存储在原始数据帧(所有间隔的所有行)中。没什么大不了的,只需要再次调用 strptime ,但时间字符串略有不同(实际上是默认值):

calcStats <- function(df) {
   start_time <- strptime(df[1, "event.datetime"],
                          format="%Y-%m-%d %H:%M:%S", tz="UTC")

   values <- df[,"RTIME"]
   n_values <- length(values)

   max_value <- max(values, na.rm=TRUE)
   avg_value <- mean(values, na.rm=TRUE)
   percs <- quantile(values, probs = c(0.95, 0.99), na.rm=TRUE)

   stats_df <- data.frame(
         Start=start_time,
         Count=n_values,
         Max=max_value,
         Avg=avg_value,
         Perc95=percs[1],
         Perc99=percs[2])

   return (stats_df)
}

通过 by 对回调的调用似乎需要这样:

result <- by(data, data_factors, function(arg) { return (calcStats(arg)) })

我注意到我的函数不会在没有行的任何间隔内调用。这是一种耻辱,因为该因子包含具有 NULL 数据的命名行,并且该名称可用于为该间隔创建所有 0 的显式计数。因此,针对 cut 生成的因素调用 droplevels。​​

我无法判断 by 的结果。当我打印它时我可以看到它,我可以将它输入,但我无法以编程方式将它变成任何有用的东西。谷歌搜索 R 和 by 是徒劳的。但是我可以使用可怕的 for 循环创建我的时间序列;这很好用:

result_frame <- NULL
for (i in result)
{
   if (!is.null(i))
   {
      if (is.null(result_frame))
      {
         result_frame <- i
      } else
      {
         result_frame <- rbind(result_frame, i)
      }
   }
}
ts_stats_data = ts(result_frame)

基本绘图工作正常。现在开始改进情节,然后很好地打包所有这些。

这个 for 循环实际上快如闪电;它不会影响此脚本的即时操作,因为它将 CSV 文件的 18K 行转换为一个漂亮的时间间隔图表,其中包含所有需要的统计数据,仍然在眨眼之间(不到几毫秒)。

我想我可以更新 calcStats 函数以将每个数据帧直接添加到全局可访问的数据帧并消除 for 循环。但这正在沿着收益递减的斜率下滑。

再次感谢 BondedDust 对 seq 和 cut 的引用。这就是我需要的全部灵感。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-02
    • 2014-03-08
    • 1970-01-01
    • 2022-12-07
    相关资源
    最近更新 更多