【问题标题】:For loop - select time window from day columnFor 循环 - 从日期列中选择时间窗口
【发布时间】:2016-11-18 13:04:37
【问题描述】:

我需要调整一个代码,该代码与我的数据框完美配合(但使用另一个设置),以便从 Day 列中选择 2 天的时间窗口。特别是我对前 1 天 day0 感兴趣(即 i - 1 和 i,其中 i 是感兴趣的日期),并且必须将包含在 Count 列中的 (i - 1) 值添加到第 0 天(i ) 列数。

这是我的数据框的示例:

df <- read.table(text = "
        Station   Day           Count
    1    33012  12448               4
    2    35004  12448               4
    3    35008  12448               4
    4    37006  12448               4
    5    21009   4835               3
    6    24005   4835               3
    7    27001   4835               3
    8    25005  12447               3
    9    29001  12447               3
    10   29002  12447               3
    11   29002  12446               3
    12   30001  12446               3
    13   31002  12446               3
    14   47007   4834               2
    15   49002   4834               2
    16   47004  12445               1
    17   51001  12449               1
    18   51003   4832               1
    19   52004   4836               1", header = TRUE)

我的输出应该是:

           Station    Day           Count
        1    33012  12448               7
        2    35004  12448               7
        3    35008  12448               7
        4    37006  12448               7
        5    21009   4835               5
        6    24005   4835               5
        7    27001   4835               5
        8    29002  12446               4
        9    30001  12446               4
        10   31002  12446               4
        11   51001  12449               1
        12   51003   4832               1
        13   52004   4836               1
        14   25005  12447               0
        15   29001  12447               0
        16   29002  12447               0
        17   47007   4834               0
        18   49002   4834               0
        19   47004  12445               0

我正在尝试这段代码,但它不适用于我的真实数据框:

for (i in unique(df$Day)) {
    temp <- df$Count[df$Day == i]  
    if(length(temp > 0)) {  
    condition1 <- df$Day == i - 1   
    if (any(condition1)) {
       df$Count[df$Day == i] <- mean(df$Count[condition1]) + df$Count[df$Day == i]
       df$Count[condition1] <- 0
            }
         }
}

代码看起来正确且有意义,但我的输出不正确。

谁能帮帮我?


@aichao 代码运行良好。

如果我想考虑前 30 天(即第 30 天、第 29 天、第 28 天、....、第 1 天、第 0 天),是否有任何快速的方法来代替创建 30 个 if 语句(条件)?

再次感谢@aichao 的帮助。

【问题讨论】:

  • 您可以查看roll_apply 或rollmean
  • 为什么 12447 = 0?不应该是6吗?
  • 12447 = 0 因为我会从上到下考虑这一天。一旦考虑到第 -1 天(即取 Count 中的值并将其添加到第 0 天计数中),我将为它们分配一个 0 值。所以,按顺序,12448 - 12447 和 12446 - 12445。
  • @user181187:您的更新既不是按Count 也不是按Day 排序的。在我回答的最后一行,我将结果按Count 降序排列。您可以忽略print 并将生成的数据框df 设置为新的数据框dfSort。要按Count 排序,然后是Day,按降序排列,请使用dfSort &lt;- df[order(df$Count, df$Day, decreasing=TRUE),] 而不仅仅是dfSort &lt;- df[order(df$Count, decreasing=TRUE),]。对于升序,只需删除 decreasing=TRUE 参数。让我知道这是否清楚。

标签: r for-loop time dataframe window


【解决方案1】:

以下对您提供的示例数据执行您想要的操作

for (i in unique(df$Day)) {
  temp <- df$Count[df$Day == i]
  if (any(temp > 0)) {
    condition1 <- df$Day == i - 1
    condition1[which(df$Day == i - 1) < max(which(df$Day == i))] <- FALSE
    if (any(condition1)) {
      df$Count[df$Day == i] <- mean(df$Count[condition1]) + df$Count[df$Day == i]
      df$Count[condition1] <- 0
    }
  }
}
print(df[order(df$Count, decreasing = TRUE),])
##   Station   Day Count
##1    33012 12448     7
##2    35004 12448     7
##3    35008 12448     7
##4    37006 12448     7
##5    21009  4835     5
##6    24005  4835     5
##7    27001  4835     5
##11   29002 12446     4
##12   30001 12446     4
##13   31002 12446     4
##17   51001 12449     1
##18   51003  4832     1
##19   52004  4836     1
##8    25005 12447     0
##9    29001 12447     0
##10   29002 12447     0
##14   47007  4834     0
##15   49002  4834     0
##16   47004 12445     0

从您的评论中可以看出,您的实施中缺少的一个关键要求是,在确定前一天及其计数时,仅考虑数据框下方的天数(以行为单位)。也就是说,您正在处理数据框行,就好像它们按时间排序一样,而不是将Day 列中的值视为时间排序。因此,对于df$Day = 12449,没有前一天需要考虑,因为所有带有df$Day = 12448 的行都在它之前。因此,df$Day = 12449 的 Count 仍为 1,更重要的是,所有具有 df$Day = 12448 的行的 Counts 在处理 df$Day = 12449 后不会被清零。

为了实现这一点,我们需要进一步过滤condition1,以便我们将df$Day == i - 1(前一天)在df$Day == i(感兴趣的日期)使用的最高行之前的所有行设置为FALSE线

condition1[which(df$Day == i - 1) < max(which(df$Day == i))] <- FALSE

请注意,此解决方案假定数据框中 Day 列的相同值与示例数据中的行块一样集中在一起。否则,您的 for 循环 unique(df$Day) 需要完全重新考虑并替换为行上的循环,以便跟踪数据框中感兴趣日期的当前行。

此外,您的代码中存在一个小错误

if(length(temp > 0)) {

目的是检查是否有任何行的Count 大于0 在感兴趣的日子。但是,R 中的条件运算符是矢量化的,因此temp &gt; 0 返回一个与输入temp 长度相同的布尔向量。因此,length(temp &gt; 0) 将始终返回一个正数,除非temp 本身的长度为0(即为空)。为了得到你想要的,该行更改为

if(any(temp > 0)) {

更新:关于前几天的新要求

解决新要求的最简单方法是将if (any(temp &gt; 0)) {...} 块中的代码主体放入一个函数中,将其命名为accumulate.mean.count,然后使用sapply 将此函数应用于前几天的集合。修改如下:

accumulate.mean.count <- function(this.day, lag) {
  condition1 <- df$Day == this.day - lag
  condition1[which(df$Day == this.day - lag) < max(which(df$Day == this.day))] <- FALSE
  if (any(condition1)) {
    df$Count[df$Day == this.day] <<- mean(df$Count[condition1]) + df$Count[df$Day == this.day]
    df$Count[condition1] <<- 0
  }
}

lags <- seq_len(30)

for (i in unique(df$Day)) {
  temp <- df$Count[df$Day == i]
  if (any(temp > 0)) {
    sapply(lags, accumulate.mean.count, this.day=i)
  }
}

print(df[order(df$Count, decreasing = TRUE),])

注意事项:

  1. lag 是当天之前(即滞后)的天数。 lag = 1 表示前一天,lag = 2 表示前两天,以此类推。lags 是这些的集合。这里,lags &lt;- seq_len(30) 是从1 到30 的序列,在其上应用accumulate.mean.count,这就是你想要的。请参阅 this 以了解有关 *apply 系列 R 函数的出色概述。请注意,lags 不必是序列,而只是整数的集合,例如前一天、前 5 天和前 10 天的 c(1, 5, 10)。如果您想在未来几天滚动,它甚至不必为正数,但不应为零。

  2. 由于lexical scoping rule of R,设置df$Count,这是accumulate.mean.count范围之外的变量,在函数accumulate.mean.count内需要&lt;&lt;-而不是&lt;-。请参阅this 以获得解释,并注意其中提到的使用&lt;&lt;- 的危险。

我没有足够的数据来测试lags &lt;- seq_len(30),但是对于seq_len(1),我恢复了原始结果,对于seq_len(2),我得到了

##   Station   Day Count
##1    33012 12448    10
##2    35004 12448    10
##3    35008 12448    10
##4    37006 12448    10
##5    21009  4835     5
##6    24005  4835     5
##7    27001  4835     5
##16   47004 12445     1
##17   51001 12449     1
##18   51003  4832     1
##19   52004  4836     1
##8    25005 12447     0
##9    29001 12447     0
##10   29002 12447     0
##11   29002 12446     0
##12   30001 12446     0
##13   31002 12446     0
##14   47007  4834     0
##15   49002  4834     0

我相信这是你想要的。

【讨论】:

  • 感谢@aichao,调整效果很好。如果我想考虑前 30 天(即第 30 天、第 29 天、第 28 天、....、第 1 天、第 0 天),是否有任何快速的方法可以做到这一点,而不是创建 30 if 语句(条件)?
  • @user181187:很抱歉没有尽快回复。我是否理解您想在当前循环中嵌套一个循环以在几天内处理“滞后”?也就是说,我们仍然在外循环中从上到下进行,并且在确定前几天的集合及其计数(内循环)时只考虑数据框(行)更下方的日子?
  • 嗨@aichao,谢谢。是的,该过程与第 -1 天(您的答案)相同,我现在询问是否可以在 1 条件下压缩第 0 天前 30 天的时滞,而不是创建 30 个不同的 if 语句(条件)。
  • 衷心感谢。我希望和你一样和R一样好。
  • @user181187:不客气。如果对此有任何其他问题,请告诉我。
猜你喜欢
  • 1970-01-01
  • 2010-12-17
  • 2019-04-04
  • 1970-01-01
  • 1970-01-01
  • 2013-10-02
  • 2011-11-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多