日期区间和数据操作

3

我是R语言的新用户,遇到了些问题,我的数据看起来像这样:

dates        temp
01/31/2011    40
01/30/2011    34
01/29/2011    30
01/28/2011    52
01/27/2011    39
01/26/2011    37
...
01/01/2011    31

我希望能获取温度低于40度的数据,包括开始和结束日期以及持续天数。例如:
from         to           days
01/29/2011   01/30/2011     2
01/26/2011   01/27/2011     2

我尝试过用difftime,但没有成功,也许使用一个函数会更好。

非常感谢任何帮助。

3个回答

3
我会这样做。我将在此处使用data.table
df <- read.table(header=TRUE, text="dates        temp
01/31/2011    40
01/30/2011    34
01/29/2011    30
01/28/2011    52
01/27/2011    39
01/26/2011    37", stringsAsFactors=FALSE)

require(data.table)
dt <- data.table(df)
dt <- dt[, `:=`(date.form = as.Date(dates, format="%m/%d/%Y"), 
          id = cumsum(as.numeric(temp >= 40)))][temp < 40]
dt[, list(from=min(date.form), to=max(date.form), count=.N), by=id]

#    id       from         to count
# 1:  1 2011-01-29 2011-01-30     2
# 2:  2 2011-01-26 2011-01-27     2

首先,需要创建一个包含将dates列转换为Date格式的列。然后,创建另一个名为id的列,找到temp >= 40的位置,并使用它来创建在两个temp>=40之间的值的组。也就是说,如果你有c(40, 34, 30, 52, 39, 37),那么你需要c(1,1,1,2,2,2),也就是在>= 40的两个值之间的所有内容必须属于同一组(34, 30 -> 1 和 39, 37 -> 2)。完成这一步之后,需要删除temp >= 40的条目。

然后,可以按此组进行拆分,然后取minmaxlength(.)(默认存储在.N中)。


非常感谢,这个想法非常聪明。我不知道为什么,但它说找不到 ":="!我需要加载某个包吗? - Marco
这个程序相关的包是 data.table,你需要先安装它。括号中的反引号 := 是必须的,必须包含在 <tick>:=<tick>(...) 中。 - Arun

3

虽然不如Arun的data.table优雅,但这里提供了一个base解决方案。

DF <- read.table(text = "dates        temp\n01/31/2011    40\n01/30/2011    34\n01/29/2011    30\n01/28/2011    52\n01/27/2011    39\n01/26/2011    37", 
    header = TRUE, stringsAsFactors = FALSE)

DF$dates <- as.POSIXct(DF$dates, format = "%m/%d/%Y")
DF <- DF[order(DF$dates), ]
DF$ID <- cumsum(DF$temp >= 40)
DF2 <- DF[DF$temp < 40, ]

# Explanation split : split DF2 by DF2$ID 
# lapply : apply function on each list element given by split
# rbind : bind all the data together

do.call(rbind, lapply(split(DF2, DF2$ID), function(x) 
            data.frame(from = min(x$dates),  
                       to = max(x$dates), 
                       count = length(x$dates))))
##         from         to count
## 0 2011-01-26 2011-01-27     2
## 1 2011-01-29 2011-01-30     2

另一个好主意!谢谢帮忙,我加了 decreasing=TRUE 因为我想要日期以相反的方式显示。 - Marco

3

首先读取数据。使用read.zoo函数可以一行代码搞定包括数据重新排序和日期转换成"Date"格式等许多细节问题。如果z是生成的zoo对象,则coredata(z)提供温度信息,time(z)则提供对应的日期。

Lines <- "
dates        temp
01/31/2011    40
01/30/2011    34
01/29/2011    30
01/28/2011    52
01/27/2011    39
01/26/2011    37
"

library(zoo)
z <- read.zoo(text = Lines, header = TRUE, format = "%m/%d/%Y")

所有的核心都在于使用rle,它可以计算出lengthsvalues,通过这些我们可以得到所有的量:
tt <- time(z)
with(rle(coredata(z) < 40), {
   to <- cumsum(lengths)[values]
   lengths <- lengths[values]
   from <- to - lengths + 1
   data.frame(from = tt[from], to = tt[to], days = lengths)
})

使用展示的输入数据的前6行,我们得到:
       from          to   days
1 2011-01-26 2011-01-27      2
2 2011-01-29 2011-01-30      2

实际上,我尝试过使用RLE,但是我做不到,谢谢 :) - Marco

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接