【问题标题】:How to create a conditional dummy in R?如何在R中创建一个条件假人?
【发布时间】:2016-05-10 00:55:49
【问题描述】:

我有一个包含每日温度观察的时间序列数据的数据框。我需要创建一个虚拟变量来计算温度高于 5C 阈值的每一天。这本身很容易,但存在一个附加条件:仅在连续十天超过阈值后才开始计数。这是一个示例数据框:

df <- data.frame(date = seq(365), 
         temp = -30 + 0.65*seq(365) - 0.0018*seq(365)^2 + rnorm(365))

我想我已经完成了,但是我喜欢的循环太多了。这就是我所做的:

df$dummyUnconditional <- 0
df$dummyHead <- 0
df$dummyTail <- 0

for(i in 1:nrow(df)){
    if(df$temp[i] > 5){
        df$dummyUnconditional[i] <- 1
    }
}

for(i in 1:(nrow(df)-9)){
    if(sum(df$dummyUnconditional[i:(i+9)]) == 10){
        df$dummyHead[i] <- 1
    }
}

for(i in 9:nrow(df)){
    if(sum(df$dummyUnconditional[(i-9):i]) == 10){
        df$dummyTail[i] <- 1
    }
}

df$dummyConditional <- ifelse(df$dummyHead == 1 | df$dummyTail == 1, 1, 0)

谁能提出更简单的方法来做到这一点?

【问题讨论】:

    标签: r loops dataframe


    【解决方案1】:

    这是使用 rle 的基本 R 选项:

    df$dummy <- with(rle(df$temp > 5), rep(as.integer(values & lengths >= 10), lengths))
    

    一些解释: 该任务是运行长度编码 (rle) 函数 imo 的经典用例。我们首先检查temp 的值是否大于5(创建一个逻辑向量)并将rle 应用于该向量,结果是:

    > rle(df$temp > 5)
    #Run Length Encoding
    #  lengths: int [1:7] 66 1 1 225 2 1 69
    #  values : logi [1:7] FALSE TRUE FALSE TRUE FALSE TRUE ...
    

    现在我们要找出valuesTRUE(即temp 大于5)并且同时lengths 大于10(即至少连续十个temp)的情况值大于 5)。我们通过运行来做到这一点:

    values & lengths >= 10
    

    最后,由于我们要返回与nrow(df) 相同长度的向量,因此我们使用rep(..., lengths)as.integer 来返回1/0 而不是TRUE/FALSE

    【讨论】:

    • 其他两个建议也有效,但这是最简单的,如果存在的话,我倾向于使用 R-base 解决方案。谢谢!
    • 您可能需要仔细检查此解决方案。我在第 67-75 天得到了虚拟值 = 1,尽管这些天不是连续 10 天 > 5 度温度运行的一部分
    • @JHowIX,你能提供一个例子吗?请注意,示例数据使用rnorm 没有设置种子,因此解决方案不必相同(因为示例数据可能不同)
    • @docendodiscimus - 是的,我不确定我希望如何传达样本数据。但是,在同一个数据帧上运行您的解决方案和我的解决方案,它们会产生不同的结果,这意味着您或我的解决方案都不正确。如果它是我的,我想知道,所以我可以纠正它。
    • rle R 必须是万能锤。 :-)
    【解决方案2】:

    我认为您可以结合使用简单的 ifelse 和 zoo package 中的滚动应用功能来实现您正在寻找的东西。最后一步只涉及填充结果以说明前 N-1 天没有足够的信息来填充窗口。

    library(zoo)
    
    df <- data.frame(date = seq(365), 
                     temp = -30 + 0.65*seq(365) - 0.0018*seq(365)^2 + rnorm(365))
    
    df$above5 <- ifelse(df$temp > 5, 1, 0)
    temp <- rollapply(df$above5, 10, sum)
    df$conseq <- c(rep(0, 9),temp)
    

    【讨论】:

    • 用简单的sum 替换function(x) { sum(x) } ?
    • 建议这样写:df2 &lt;- transform(transform(df, uncond = temp &gt; 5), head = rollsum(uncond, 10, align = "left", fill = 0) == 10, tail = rollsum(uncond, 10, align = "right", fill = 0) == 10) + 0
    【解决方案3】:

    我会这样做:

    set.seed(42)
    df <- data.frame(date = seq(365), 
                     temp = -30 + 0.65*seq(365) - 0.0018*seq(365)^2 + rnorm(365))
    thr <- 5
    df$dum <- 0
    
    #find first 10 consecutive values above threshold
    test1 <- filter(df$temp > thr, rep(1,10), sides = 1) == 10L
    test1[1:9] <- FALSE
    n <- which(cumsum(test1) == 1L)
    
    #count days above threshold after that
    df$dum[(n+1):nrow(df)] <- cumsum(df$temp[(n+1):nrow(df)] > thr)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-09-22
      • 2021-01-14
      • 2021-12-08
      • 1970-01-01
      • 2021-11-08
      • 2019-01-14
      • 2016-01-02
      相关资源
      最近更新 更多