【问题标题】:Alternative to for loop and indexing?替代 for 循环和索引?
【发布时间】:2013-10-12 15:33:52
【问题描述】:

我有一个包含 3 列的大型数据集,Order、Discharge、Date(数字)。每个订单有 20 年的每日放电值,可以超过 100。

> head(dat)
      Order Discharge date
         1   0.04712 6574
         2   0.05108 6574
         3   0.00000 6574
         4   0.00000 6574
         5   3.54100 6574
         6   3.61500 6574

对于给定的订单 x,我想用该日期在 x+1 和 x-1 的平均排放量替换排放量值。我一直在使用 for 循环和索引以粗略的方式执行此操作,但处理需要一个多小时。我知道必须有更好的方法。

    x <- 4
    for(i in min(dat[,3]):max(dat[,3]))
    dat[,2][dat[,3] == i & dat[,1] == x ] <- 
    mean(c(dat[,2][dat[,3] == i & dat[,1] == x + 1], 
    dat[,2][dat[,3] == i & dat[,1] == x - 1]))

> head(dat)
    Order Discharge date
       1   0.04712 6574
       2   0.05108 6574
       3   0.00000 6574
       4   1.77050 6574
       5   3.54100 6574
       6   3.61500 6574

在订单 4 中,日期 6574 的排放已被替换为 1.77050。它有效,但速度慢得离谱。

我应该指定我不需要对每个订单进行此计算,而只需选择少数几个(总共 117 个订单中的 8 个)。根据答案,我有以下内容。

    dat$NewDischarge <- by(dat$Discharge,dat$date,function(x) 
    colMeans(cbind(c(x[-1],NA), x, 
    c(NA, x[-length(x)])), na.rm=T)) 

我正在尝试找出一种方法,仍然只计算选择订单的值,并陷入 for 循环和日期和订单索引的常规中。

【问题讨论】:

  • 您选择x的标准是什么?是否有一个外部循环来确定您没有向我们展示的x?这可能是相关的。
  • Order 表示河流中测量流量的站点。 X-1 和 X+1 本质上是上游站和下游站。我想取上游和下游站点一天的平均排放量来申请当天的中间站点。我已经单独识别了这些 x(有 8 个),但我可以将它们放在一个向量中并将其包含在函数中。

标签: r for-loop indexing


【解决方案1】:

我会按照以下方式进行:

  1. 确保Order 是一个因素。
  2. 对于每个订单,您现在有一个子问题:
    1. date对子数据帧进行排序。
    2. 每个Discharge-mean 都可以“矢量化”生成为: colMeans(cbind(c(Discharge[-1], NA), Discharge, c(NA, Discharge[-length(Discharge)])))
  3. 子问题可以通过简单的for循环或函数by来处理。我更喜欢by
  4. 您的数据已重新排列,但您可以轻松地重新排序。

对于第 2.2 点,用一个简单的向量想象(或尝试一下),看看cbind 操作的效果。它还迫使您考虑极限情况;第一个和最后一个 Discharge-value 是如何计算的(没有之前或之后的日期)。

【讨论】:

  • 这是一个很好的开始。我应该指定我不需要对每个订单进行此计算,而只需选择少数几个(总共 117 个订单中只有 8 个)。根据您的回答,我有以下几点。 'dat$NewDischarge
  • 这可能行不通。 by 很少返回要插入 data.frame 的向量。 by 的第二个参数按日期拆分数据 - 在这种情况下,您取的是几个订单的平均值。由于您只有 8 个订单,您可以这样做: dat
【解决方案2】:

有几种方法可以解决您的特定困境,但是当遇到缓慢的for 循环时要问的基本问题是,“如何使用矢量化来替换此循环?” (好吧,也许您应该先问“我应该...吗?”。)在您的情况下,您正在循环遍历日期,但没有必要明确地这样做,因为只需抓住 dat$Order==x 将隐式抓取所有日期。

您发布的数据集只有一个日期,但我可以生成一些假数据来说明:

generate.data <- function(n.order, n.date){
  dat <- expand.grid(Order=seq_len(n.order), date=seq_len(n.date))
  dat$Discharge <- rlnorm(n.order * n.date)
  dat[, c("Order", "Discharge", "date")]
}

dat <- generate.data(10, 5)

head(dat)
#   Order Discharge date
# 1     1 2.1925563    1
# 2     2 0.4093022    1
# 3     3 2.5525497    1
# 4     4 1.9274013    1
# 5     5 1.1941986    1
# 6     6 1.2407451    1
tail(dat)
#    Order Discharge date
# 45     5 1.4344575    5
# 46     6 0.5757580    5
# 47     7 0.4986190    5
# 48     8 1.2076292    5
# 49     9 0.3724899    5
# 50    10 0.8288401    5

这是dat$Order==4 在所有日期的所有行:

dat[dat$Order==4, ]
#    Order Discharge date
# 4      4 1.9274013    1
# 14     4 3.5319072    2
# 24     4 0.2374532    3
# 34     4 0.4549798    4
# 44     4 0.7654059    5

您可以只使用Discharge 列,您将获得作业的左侧:

dat[dat$Order==4, ]$Discharge
# [1] 1.9274013 3.5319072 0.2374532 0.4549798 0.7654059

现在您只需要右侧,它有两个组件:x-1 放电和x+1 放电。您可以像抓住x 放电一样抓住这些:

dat[dat$Order==4-1, ]$Discharge
# [1] 2.5525497 1.9143963 0.2800546 8.3627810 7.8577635
dat[dat$Order==4+1, ]$Discharge
# [1] 1.1941986 4.6076114 0.3963693 0.4190957 1.4344575

要获得新值,您需要平行均值。 R 没有pmean 函数,但你可以cbind 这些并取rowMeans

rowMeans(cbind(dat[dat$Order==4-1, ]$Discharge, dat[dat$Order==4+1, ]$Discharge))
# [1] 1.8733741 3.2610039 0.3382119 4.3909383 4.6461105

所以,最后你有:

dat[dat$Order==4, ]$Discharge <- rowMeans(cbind(dat[dat$Order==4-1, ]$Discharge,
                                                dat[dat$Order==4+1, ]$Discharge))

您甚至可以使用 %in% 使这项工作适用于您的所有 x 值。

请注意,这假设您的数据是有序的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-20
    • 2021-07-12
    • 1970-01-01
    • 1970-01-01
    • 2012-09-27
    • 2022-01-22
    相关资源
    最近更新 更多