条件总和取决于其他数据框列

时间:2012-10-02 18:55:00

标签: r

假设您有一个由以下命令生成的数据框:

date <- seq(as.Date("2012-09-01"), Sys.Date(), 1)
id <- rep(c("a","b","c","d"), 8)
bdate <- seq(as.Date("2012-08-01"), as.Date("2012-11-01"), 1)[sample(1:32, 32)]

# The end date should be random but greater than the begin date. However, I set it to 15 days later for simplicity.
edate <- bdate + 15

value <- seq(1, 1000, 1)[sample(1:1000, 32)]
dfa <- data.frame(id, value, bdate, edate)
names(dfa) <- c("ID", "Value", "Begin.Date", "End.Date")

目标是通过以下方式按ID(即“a”,“b”或“c”)对所有观察结果进行求和:

Date        a   b   c
2012-08-01  XXX YYY ZZZ
2012-08-02  XXX YYY ZZZ
2012-08-03  XXX YYY ZZZ

值XXX,YYY和ZZZ表示所有观察的总和,其中“Date”列上的日期落在原始数据框上的dfa $ Begin.Date和dfa $ End.Date之间,用于每个ID。 / p>

我目前的解决方案对于大型数据集几乎没用,所以我想知道是否有更快的方法可以做到这一点。

我目前的剧本:

# Create additional data frame
dfb <- data.frame(seq(as.Date("2012-08-01"), as.Date("2012-11-01"), 1))
names(dfb)[1] <- "Date"

# Variable for unique IDs
nid <- unique(dfa$ID)

# Number of total IDs
tid <- length(nid)

for (i in c(1:tid))
{
sums <- vapply(dfb$Date, function(x)
{
temp <- subset(dfa, dfa$ID == nid[i])
temp <- subset(temp, temp$Begin.Date < x & temp$End.Date > x)
res <- sum(temp$Value)
res
}, FUN.VALUE = 0.1
)
dfb[1+i] <- sums
}

# Change column names to ID
names(dfb) <- c("Date", as.character(nid))

编辑:我在下面发布了一个更有效的方法来回答这个问题。但是,我接受了马修的答案,因为它让我走上了正确的道路。

4 个答案:

答案 0 :(得分:3)

感谢@Matthew Dowle,我发现如何使用data.table包更有效地完成这项工作。

# Fire up the bad boy
library(data.table)

# Create the data table with original data
value <- seq(1, 1000, 1)[sample(1:1000, 32)]
dt <- data.table(id, value, bdate, edate)
setnames(dt, names(dt), c("id", "value", "begin", "end"))

# For each pair of id and value, create a row for each day. (i.e., the first line:
# a  928  2012-08-11  2012-08-26
# will now be 15 lines. The first two columns are repeated over 15 different dates.
dt <- dt[, seq(begin[1], (last(end) - 1), by="days"), by = list(id, value)]
setnames(dt, names(dt), c(names(dt)[1:2], "date"))
setkey(dt)

# Sum each pair of id and value over the dates column
dt <- dt[, sum(value), by = list(id, date)]
setnames(dt, names(dt), c(names(dt)[1:2], "value"))
setkey(dt, date, id)

# Define the time span you would like on your final table
timespan <- dt[, seq(as.Date("2012-07-25"), max(date), by = "day")]

# Now just cross reference the time span with your actual data
setkey(dt, id, date)
dt <- dt[CJ(unique(id), timespan), ]
setnames(dt, names(dt), c(names(dt)[1:2], "value"))
setkey(dt, date)

的Ta-DA !!

现在,按照我原来想要的顺序重新排列表格:

Date        a   b   c
2012-08-01  XXX YYY ZZZ
2012-08-02  XXX YYY ZZZ
2012-08-03  XXX YYY ZZZ

只需使用reshape2包中的dcast。

那么你们都在想什么?棒极了吧?

答案 1 :(得分:2)

有趣。这似乎与这个问题非常相似:

Splitting irregular time series into regular monthly averages

这有帮助吗?就像你的问题一样,一个技巧可以是使用begin包中的roll=TRUE来加入流行的data.table。特别是因为你声明你有大型数据集。

答案 2 :(得分:0)

我会做以下事情。通过检查所需日期是否在Begin.DateEnd.Date之间,确定原始数据集的第一个子集。然后,只需使用简单的table函数即可获得'a''d'的频率。

mydate <- as.Date("2012-08-25")  # take Aug 25, 2012 as an example
ind <- (dfa$Begin.Date <= mydate) & (dfa$End.Date >= mydate)
temp <- subset(dfa, ind)
out <- table(temp$ID)

答案 3 :(得分:-1)

我不知道这是否更快(没有对其进行基准测试),对于特别大的数据,它可能会创建一个太大的中间数据集,但无论如何我都会提供它。

也可以设置考虑的日期范围(根据对此答案的评论请求)。

library("plyr")
library("reshape2")

earliest.date <- as.Date("2007-01-01")
latest.date <- as.Date("2012-11-01")

dfa.long <- adply(dfa, 1, function(DF) {
  if(DF$End.Date >= earliest.date & DF$Begin.Date <= latest.date) {
    data.frame(Date=seq(max(DF$Begin.Date, earliest.date), 
                        min(DF$End.Date, latest.date), 
                        1))
  }
})

dfb <- ddply(dfa.long, .(Date, ID), summarise, sum=sum(Value))
dfb <- dcast(dfb, Date~ID, value.var="sum", fill=0)

dfa.long是一个数据集,每个日期在开始/结束范围内重复每一行(也限制在最早/最晚的日期范围内)。然后,可以直接按日期和ID进行汇总,并使用dcast中的reshape2将其转换为您想要的宽格式。

相关问题