【问题标题】:Can I do multiple split sapply operations on a large data frame?我可以对大型数据框执行多个拆分 sapply 操作吗?
【发布时间】:2013-04-29 17:50:39
【问题描述】:

我已经为此工作了几个小时,但似乎找不到解决方案。我有一个非常大的数据框(超过 150 万行),我想做一个相当具体的操作。首先,我的数据是这样的:

STATION       DATE      Precip
COOP 310     -7788        .24
COOP 310     -7788        .15
COOP 310     -6654        .59
COOP 310     -6654        .10
COOP 499     -7122        .64
COOP 499     -7122        .36
COOP 499     -7122        .14
COOP 499     -2350        .11
COOP 499     -2350        .85

我有一个气象站 ID (STATION)、一个 UNIX 纪元形式的日期 (DATE) 和降水值(下雨时的 15 分钟数据间隔)。我一直在尝试做的是确定每个站点每天下雨的每日降雨量总和。所需的输出如下所示:

STATION       DATE        24-hour_PRECIP
COOP 310     -7788        0.39
COOP 310     -6654        0.69
COOP 499     -7122        1.14
COOP 499     -2350        0.96

我认为这实质上意味着执行两次SPLIT 操作,一次根据相同的 STATION 值拆分所有数据,然后再次根据相同的 DATE 值。理论上,此输出将通过SAPPLY 操作运行,将SUM 函数应用于每个唯一日期/站点集中的数据集。我的方法(虽然错误):

数据框名称为“dfhour”:

sp1<-split(dfhour$Precip,dfhour$STATION)

我可以对这些数据执行 sapply 函数,但我想在使用 sapply 之前进一步拆分它。我知道做类似的事情

sapply(split(split(dfhour$Precip, dfhour$STATION),dfhour$DATE),FUN=sum)

不会工作,因为split 函数的输出是一个列表,而下一个split 函数将无法接受一个列表作为参数。有人对这个问题有任何指导吗?还有哪些其他功能可以帮助我到达需要去的地方?

【问题讨论】:

  • 虽然data.table 可能是解决您问题的正确方法,但我建议您也学习tapply,它将sapplysplit 合并为一个步骤,并且可以分成两个或多个同时考虑因素,例如tapply(data$Precip, list(data$STATION, data$DATE), sum)

标签: r split aggregate lapply sapply


【解决方案1】:

我认为您只是在寻找aggregate。如果您的 data.frame 被命名为“mydf”:

> aggregate(Precip ~ ., mydf, sum)
   STATION  DATE Precip
1 COOP 310 -7788   0.39
2 COOP 499 -7122   1.14
3 COOP 310 -6654   0.69
4 COOP 499 -2350   0.96

不过,从数据的大小来看,您可能希望改用 data.table

> library(data.table)
data.table 1.8.8  For help type: help("data.table")
> DT <- data.table(mydf, key = "STATION,DATE")
> DT[, list(Precip = sum(Precip)), by = key(DT)]
    STATION  DATE Precip
1: COOP 310 -7788   0.39
2: COOP 310 -6654   0.69
3: COOP 499 -7122   1.14
4: COOP 499 -2350   0.96

更新,根据 cmets 中的讨论

假设您的数据如下(注意重复的日期,但在不同的站点):

mydf <- structure(list(STATION = c("COOP 310", "COOP 310", "COOP 310",                 
     "COOP 310", "COOP 499", "COOP 499", "COOP 499", "COOP 499", "COOP 499",            
     "COOP 499", "COOP 499"), DATE = c(-7788L, -7788L, -6654L, -6654L,                  
     -7122L, -7122L, -7122L, -2350L, -2350L, -7788L, -7788L), Precip = c(0.24,          
     0.15, 0.59, 0.1, 0.64, 0.36, 0.14, 0.11, 0.85, 0.35, 0.17)), .Names = c("STATION", 
     "DATE", "Precip"), row.names = c(NA, 11L), class = "data.frame")
mydf
#     STATION  DATE Precip
# 1  COOP 310 -7788   0.24
# 2  COOP 310 -7788   0.15
# 3  COOP 310 -6654   0.59
# 4  COOP 310 -6654   0.10
# 5  COOP 499 -7122   0.64
# 6  COOP 499 -7122   0.36
# 7  COOP 499 -7122   0.14
# 8  COOP 499 -2350   0.11
# 9  COOP 499 -2350   0.85
# 10 COOP 499 -7788   0.35
# 11 COOP 499 -7788   0.17

提出的两种备选方案都会为“STATION”和“DATE”的组合生成总和。这是data.table的过程和结果:

DT <- data.table(mydf, key = "STATION,DATE")
DT[, list(Precip = sum(Precip)), by = key(DT)]
#     STATION  DATE Precip
# 1: COOP 310 -7788   0.39
# 2: COOP 310 -6654   0.69
# 3: COOP 499 -7788   0.52
# 4: COOP 499 -7122   1.14
# 5: COOP 499 -2350   0.96

【讨论】:

  • 这似乎是一个可能的答案,但一个问题可能会使它复杂化:在我的百万行数据中,DATE 值最终将被不同的 STATION 复制。如果 1995 年 6 月 17 日在第 1 站和第 2 站下雨,则此聚合函数会将降水值合并为一个 SUM,而不是保持 DATE*STATION 值唯一。
  • 我可以根据站名聚合而不做求和函数吗?只需根据电台对它们进行分组,然后将它们发送到 AGGREGATE 函数,该函数按 datexstation 求和?
  • @user2332986,我来晚了,所以现在解读你的后续问题对我来说很慢。对不起!您能否用一个说明您预见的问题案例的最小示例来更新您的问题?
  • 对于两个数据点可能存在这种情况:(1) A 站在 1985 年 6 月 5 日记录了 0.55 毫米的降雨(UNIX 时间戳为 5634)。 B 站也可能在 1985 年 6 月 5 日记录了 0.35 毫米的降雨(UNIX 时间戳也是 5634)。我不想将这两个值包含在同一个 SUM 实例中,因为它们位于不同的站点,即使它们同时发生。这有意义吗?
  • @user2332986,那么你误解了上面的输出。这将使用电台 日期的唯一组合。他们不会在同一个sum
【解决方案2】:

“超过 150 万行”结合简单的拆分应用组合表明 data.table 是解决您问题的完美工具。

我想你会想要这样的东西:

DT[,sum(Precip),by="STATION,DATE"]

其中DT 是您的data.framedata.table 形式。

【讨论】:

  • @AnandaMahto 是的,你做到了 :-)
【解决方案3】:

您不需要嵌套的splits。您只需要提供一个捕获交叉级别的“拆分”参数,可能使用interaction 函数。

tapply( statfrm$Precip, interaction(statfrm$STATION, statfrm$DATE) , sum) 
#----------------
COOP-310.-7788 COOP-499.-7788 COOP-310.-7122 COOP-499.-7122 COOP-310.-6654 
          0.39             NA             NA           1.14           0.69 
COOP-499.-6654 COOP-310.-2350 COOP-499.-2350 
            NA             NA           0.96 

您还可以使用 split-sapply 策略来获得类似的答案,并且在您的案例中,零值可能比您使用 tapply 获得的 NA 更合适:

 sapply(split(statfrm$Precip, interaction(statfrm$STATION, statfrm$DATE) ), sum) 
#-------
COOP-310.-7788 COOP-499.-7788 COOP-310.-7122 COOP-499.-7122 COOP-310.-6654 
          0.39           0.00           0.00           1.14           0.69 
COOP-499.-6654 COOP-310.-2350 COOP-499.-2350 
          0.00           0.00           0.96 

就这个向量的显示而言,我有时将 as.matrix 包裹在一个向量周围以显示“向下”:

as.matrix(sapply(split(statfrm$Precip, interaction(statfrm$STATION, statfrm$DATE) ), sum))
#_________________
               [,1]
COOP-310.-7788 0.39
COOP-499.-7788 0.00
COOP-310.-7122 0.00
COOP-499.-7122 1.14
COOP-310.-6654 0.69
COOP-499.-6654 0.00
COOP-310.-2350 0.00
COOP-499.-2350 0.96

【讨论】:

  • sapply 属于“数字”类,对吗?如何将其转换为可用于进一步操作的东西?说类似 column1: STATION, column2: DATE, column 3: sapply SUM 的输出。再次感谢您的回答,假设我能克服最后一个障碍,它工作得很好。
  • aggregate函数以这种方式提供结果。 interaction 函数创建一维向量,因此可能无法满足您的目标。
猜你喜欢
  • 2021-09-01
  • 2015-05-03
  • 1970-01-01
  • 2012-07-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-23
  • 1970-01-01
相关资源
最近更新 更多