【问题标题】:Replace conditional statements替换条件语句
【发布时间】:2015-06-12 06:56:42
【问题描述】:

我有一个包含年份、日期和温度测量值 (TMEAN) 的数据表 (DT):

YEAR    DATE    TMEAN
2010    2010-01-01  -5
2010    2010-01-02  -9
2010    2010-01-03  -6
2010    2010-01-04  0.1
2010    2010-01-05  -0.5
2010    2010-01-06  1
2010    2010-01-07  1.6
2010    2010-01-08  8
2010    2010-01-09  6
2010    2010-01-10  3
2010    2010-01-11  5
2010    2010-01-12  3
2011    2011-01-01  -3
2011    2011-01-02  -5.4
2011    2011-01-03  -3.6
2011    2011-01-04  0.06
2011    2011-01-05  -0.3
2011    2011-01-06  0.6
2011    2011-01-07  0.96
2011    2011-01-08  4.8
2011    2011-01-09  3.6
2011    2011-01-10  1.8
2011    2011-01-11  3
2011    2011-01-12  1.8

对于每一年,我需要获得至少连续 5 天测量结果为阳性的第一天...

一个想法是:

for (y in min(DT$YEAR):max(DT$YEAR)) {
    for (i in 1:nrow(DT)) {
DT$test <- ifelse(DT[i, TMEAN] > 0 & DT[i+1, TMEAN] > 0 & DT[i+2, TMEAN] > 0 & DT[i+3, TMEAN] > 0 & DT[i+4, TMEAN] > 0, 1, 0)
    }
}

DT2 <- DT[test == 1, ][, list(START = min(DATE)), by = .(YEAR)]

但它超级慢(而且不那么优雅!)。

如何替换 for 和 ifelse 的多次使用?

【问题讨论】:

  • 请提供DT的可重现示例。
  • 简单地说,你应该真正开始写DT$test &lt;- ifelse(DT[i, TMEAN] &gt; 0 &amp; DT[i+1, TMEAN] &gt; 0 &amp; DT[i+2, TMEAN] &gt; 0 &amp; DT[i+3, TMEAN] &gt; 0 &amp; DT[i+4, TMEAN] &gt; 0, 1, 0) 作为一个函数,然后使用lapply。然后不要使用外部for,而是尝试为条件创建一个真/假值,然后将其合并到您的函数中。一般来说,尽可能在 r 中摆脱 for 循环并利用矢量化。
  • 感谢您的提示...我正试图朝着这些方向前进以提高我的 R 计算效率(以及使用 data.table 进行二进制搜索)
  • @user2165907 在示例中,数据是有序的。您的原始数据集中是这种情况吗?
  • 是的,我还处理更多因素(例如车站)...我为每个因素组合进行操作

标签: r data.table


【解决方案1】:

使用“data.table”的开发版本,即。 v1.9.5,我们可以在逻辑条件TMEAN &gt;0 by 'YEAR' 上使用rleid 创建一个'ind' 列,将数据集的nrows 大于4 和TMEAN 的正值(.SD[.N &gt;4 &amp; TMEAN &gt;0])通过'ind'和“年”。按 YEAR (.SD[1L]) 获取第一行并将 'ind' 列分配为 NULL。

 library(data.table)#v1.9.5+
 DT[, ind:= rleid(TMEAN>0) ,YEAR][, .SD[.N>4 & TMEAN>0] ,
             list(ind,YEAR)][,.SD[1L] , YEAR][, ind:=NULL][]
 #   YEAR       DATE TMEAN
 #1: 2010 2010-01-06   1.0
 #2: 2011 2011-01-06   0.6

如果 'DATE' 没有排序,我们可以使用 which.min(DATE) 代替 .SD[1L]

 DT[, ind:= rleid(TMEAN>0) ,YEAR][, .SD[.N>4 & TMEAN>0] ,
    list(ind, YEAR)][, .SD[which.min(DATE)], YEAR][, ind:=NULL][]

注意:安装devel版本的说明是here

我们也可以使用来自base Rrle

  DT[,  ind:=inverse.rle(within.list(rle(TMEAN >0), 
        values <- seq_along(values))), YEAR][,
       .SD[ .N >4 & TMEAN >0], list(ind, YEAR)][, 
       .SD[which.min(DATE)], YEAR][, ind:=NULL][]

 #  YEAR       DATE TMEAN
 #1: 2010 2010-01-06   1.0
 #2: 2011 2011-01-06   0.6

如果是@VLC 帖子中显示的第 5 天

 DT[,  ind:=inverse.rle(within.list(rle(TMEAN >0), 
       values <- seq_along(values))), YEAR][,
       .SD[ .N >4 & TMEAN >0], list(ind, YEAR)][
       order(DATE), .SD[5L], YEAR][,ind:=NULL][]
 #   YEAR       DATE TMEAN
 #1: 2010 2010-01-10   3.0
 #2: 2011 2011-01-10   1.8

【讨论】:

  • 很好的解释(代码不是那么直观!)。是否可以避免使用 rleid(至少在“稳定”版本中没有实现它的时候)?
【解决方案2】:

首先是一个数据集:

set.seed(1)
dataset <- data.frame(
  date = seq(as.Date("2011/1/1"), as.Date("2014/1/31"), "day"),
  year = format(date, "%Y"),
  tmean = runif(length(date), -10, 35)
)

然后我们可以定义一个函数,它接受两个参数(一个包含你的温度的向量和一个定义连续天数的数字),主要基于rle 函数:

getFirstDay <- function(x,d){
  a1 <- rle(sign(x))
  a2 <- which(a1$lengths >= d & a1$values == 1)
  a3 <- rep(0, length(x))
  if(length(a2) != 0) a3[(d + sum(a1$lengths[seq_len(a2[1] - 1)])] <- 1
  a3      
}

我将使用plyr 中的ddply函数来提取每年的日期,但您也可以将它与data.table 一起使用:

library(plyr)
dataset2 <- ddply(dataset, .(year), mutate, theDay = getFirstDay(tmean, 5))
subset(dataset2, dataset2$theDay == 1)

#           date year     tmean theDay
# 17   2011-01-17 2011 22.292833      1
# 372  2012-01-07 2012 15.297955      1
# 761  2013-01-30 2013 24.971524      1
# 1102 2014-01-06 2014  1.419521      1

使用您的数据集:

dataset2 <- ddply(DT, .(YEAR), mutate, theDay = getFirstDay(TMEAN, 5))
subset(dataset2, dataset2$theDay == 1)
#    YEAR       DATE TMEAN theDay
# 10 2010 2010-01-10   3.0      1
# 22 2011 2011-01-10   1.8      1

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-07-31
    • 1970-01-01
    • 2015-05-12
    • 1970-01-01
    • 2018-11-18
    • 2019-09-15
    • 2011-05-04
    • 2012-07-16
    相关资源
    最近更新 更多