【问题标题】:Efficient use of functions on long data.frames in R在 R 中有效地使用长数据帧上的函数
【发布时间】:2013-09-27 15:36:07
【问题描述】:

我有一个长数据框,其中包含来自桅杆的气象数据。它包含同时在不同高度(data$z)对不同参数(风速、风向、气温等,data$param)进行的观测(data$value

我正在尝试通过$time 有效地对该数据进行切片,然后将函数应用于收集的所有数据。通常函数一次应用于单个$param(即,我将不同的函数应用于风速而不是应用于气温)。

目前的做法

我目前的方法是使用data.frameddply

如果我想获取所有的风速数据,我运行这个:

# find good data ----
df <- data[((data$param == "wind speed") &
                  !is.na(data$value)),]

然后我使用ddply()df 上运行我的函数:

df.tav <- ddply(df,
               .(time),
               function(x) {
                      y <-data.frame(V1 = sum(x$value) + sum(x$z),
                                     V2 = sum(x$value) / sum(x$z))
                      return(y)
                    })

通常 V1 和 V2 是对其他函数的调用。这些只是例子。不过,我确实需要对同一数据运行多个函数。

问题

我目前的方法非常很慢。我没有对它进行基准测试,但是它足够慢,我可以去喝杯咖啡,然后在处理一年的数据之前回来。

我有订单(一百个)要处理的塔,每个都有一年的数据和 10-12 个高度,所以我正在寻找更快的东西。

数据样本

data <-  structure(list(time = structure(c(1262304600, 1262304600, 1262304600, 
1262304600, 1262304600, 1262304600, 1262304600, 1262304600, 1262304600, 
1262304600, 1262304600, 1262304600, 1262304600, 1262304600, 1262304600, 
1262304600, 1262304600, 1262304600, 1262304600, 1262304600, 1262304600, 
1262304600, 1262304600, 1262304600, 1262304600, 1262304600, 1262304600, 
1262304600, 1262304600, 1262304600, 1262304600, 1262304600, 1262304600, 
1262305200, 1262305200, 1262305200, 1262305200, 1262305200, 1262305200, 
1262305200), class = c("POSIXct", "POSIXt"), tzone = ""), z = c(0, 
0, 0, 100, 100, 100, 120, 120, 120, 140, 140, 140, 160, 160, 
160, 180, 180, 180, 200, 200, 200, 40, 40, 40, 50, 50, 50, 60, 
60, 60, 80, 80, 80, 0, 0, 0, 100, 100, 100, 120), param = c("temperature", 
"humidity", "barometric pressure", "wind direction", "turbulence", 
"wind speed", "wind direction", "turbulence", "wind speed", "wind direction", 
"turbulence", "wind speed", "wind direction", "turbulence", "wind speed", 
"wind direction", "turbulence", "wind speed", "wind direction", 
"turbulence", "wind speed", "wind direction", "turbulence", "wind speed", 
"wind direction", "turbulence", "wind speed", "wind direction", 
"turbulence", "wind speed", "wind direction", "turbulence", "wind speed", 
"temperature", "barometric pressure", "humidity", "wind direction", 
"wind speed", "turbulence", "wind direction"), value = c(-2.5, 
41, 816.9, 248.4, 0.11, 4.63, 249.8, 0.28, 4.37, 255.5, 0.32, 
4.35, 252.4, 0.77, 5.08, 248.4, 0.65, 3.88, 313, 0.94, 6.35, 
250.9, 0.1, 4.75, 253.3, 0.11, 4.68, 255.8, 0.1, 4.78, 254.9, 
0.11, 4.7, -3.3, 816.9, 42, 253.2, 2.18, 0.27, 229.5)), .Names = c("time", 
"z", "param", "value"), row.names = c(NA, 40L), class = "data.frame")

【问题讨论】:

  • 为什么不用.(param, time) 而不是.(time)?您可以使用data[!is.na(data$value),] 一次删除NAs。
  • 我不想对所有数据应用相同的函数。在这种情况下,我只需对风速数据应用一些东西。
  • 让您的函数读取param 列并采取相应措施。

标签: r


【解决方案1】:

使用data.table:

library(data.table)
dt = data.table(data)

setkey(dt, param)  # sort by param to look it up fast

dt[J('wind speed')][!is.na(value),
                    list(sum(value) + sum(z), sum(value)/sum(z)),
                    by = time]
#                  time      V1         V2
#1: 2009-12-31 18:10:00 1177.57 0.04209735
#2: 2009-12-31 18:20:00  102.18 0.02180000

如果您想为每个参数应用不同的函数,这里有一个更统一的方法。

# make dt smaller because I'm lazy
dt = dt[param %in% c('wind direction', 'wind speed')]

# now let's start - create another data.table
# that will have param and corresponding function
fns = data.table(p = c('wind direction', 'wind speed'),
                 fn = c(quote(sum(value) + sum(z)), quote(sum(value) / sum(z))),
                 key = 'p')
fns
                p     fn
1: wind direction <call>    # the fn column contains functions
2:     wind speed <call>    # i.e. this is getting fancy!

# now we can evaluate different functions for different params,
# sliced by param and time
dt[!is.na(value), {param; eval(fns[J(param)]$fn[[1]], .SD)},
   by = list(param, time)]
#            param                time           V1
#1: wind direction 2009-12-31 18:10:00 3.712400e+03
#2: wind direction 2009-12-31 18:20:00 7.027000e+02
#3:     wind speed 2009-12-31 18:10:00 4.209735e-02
#4:     wind speed 2009-12-31 18:20:00 2.180000e-02

附:我认为我必须在eval 之前以某种方式使用param 才能使eval 工作这一事实是一个错误。


更新:截至version 1.8.11,此错误已得到修复,以下工作正常:

dt[!is.na(value), eval(fns[J(param)]$fn[[1]], .SD), by = list(param, time)]

【讨论】:

  • 如果函数不是sum(),这将如何工作,但我需要传入值和 z - 例如myFunction(value,z)?我只是把那个电话放在list()吗?
  • @AndyClifton 只写myFunction(value, z)?
  • 第二种方法很有趣,但在可读性的限制(对我而言)。我使用了第一个,使用list(V1 = myFunction1(value,z), V2 = myFunction2(value,z))。加速大约是 100 倍。
【解决方案2】:

使用 dplyr。它仍在开发中,但比 plyr 快得多:

# devtools::install_github(dplyr)
library(dplyr)

windspeed <- subset(data, param == "wind speed")
daily <- group_by(windspeed, time)

summarise(daily, V1 = sum(value) + sum(z), V2 = sum(value) / sum(z))

dplyr 的另一个优点是您可以使用数据表作为后端,而无需了解 data.table 的特殊语法:

library(data.table)
daily_dt <- group_by(data.table(windspeed), time)
summarise(daily_dt, V1 = sum(value) + sum(z), V2 = sum(value) / sum(z))

(带有数据帧的 dplyr 比 plyr 快 20-100 倍,带有 data.table 的 dplyr 大约快 10 倍)。 dplyr 远不如 data.table 简洁,但它对数据分析的每个主要任务都有一个功能,我发现这使代码更容易理解——你的速度几乎可以将一系列 dplyr 操作读给其他人,并且让他们了解发生了什么。

如果您想对每个变量进行不同的汇总,我建议您将数据结构更改为“tidy”:

library(reshape2)
data_tidy <- dcast(data, ... ~ param)

daily_tidy <- group_by(data_tidy, time)
summarise(daily_tidy, 
  mean.pressure = mean(`barometric pressure`, na.rm = TRUE),
  sd.turbulence = sd(`barometric pressure`, na.rm = TRUE)
)

【讨论】:

  • 嗨@hadley。您在推文中提到的 11 多个功能以及您所映射到的主要任务是什么?我在 dplyr 主页上看到了 5 个:select()、filter()、mutate()、summarise() 和arrange()。当我查看group_by(data.table(windspeed), time) 时,我觉得很困惑,因为那里没有聚合。 group_by 是否拆分数据?或者它是否设置了密钥?
  • 顺便说一句,要大声读出 data.table 查询,您说“从 DT 子集 i 然后执行 jby 分组”。实际上只有 3 个简单的参数:DT[i,j,by]。 SQL 人员可能更容易点击。
  • @MatthewDowle group_by 在概念上类似于设置密钥 - 您描述的是您想要“按”一个组进行分析(它被转换为带有 sql 后端的 GROUP BY)。其他动词是 group by,和(当前)四个连接。
  • @MatthewDowle 我发现很难将您的阅读建议应用于另一个答案中的示例,尤其是。使用 eval 等。我还发现很难确定正确的数据表语法,因为[ 内部和外部的操作行为不同。
  • 你在这里不公平。 @eddi 回答的那一部分很复杂,因为他保持数据范围广泛并展示了拉伸 data.table;例如,他创建了一个包含函数的 data.table 并为上帝的缘故查找它们。你不是在比较喜欢和喜欢。顺便说一句,您不需要在 subset 中处理 NA 或在每个 sum 调用中添加 na.rm 吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-30
  • 2015-02-13
  • 2022-01-04
  • 2021-03-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多