【发布时间】:2014-04-18 09:30:34
【问题描述】:
受this post 的启发,我尝试使用嵌套的 ddply 语句来验证我的数据集。但是,我遇到了性能问题,每次运行代码需要将近一个小时(300,000 公司年。
这不一定是问题(因为我不需要经常重新运行),但我想知道如何提高它的性能或以不同的方式学习它。
我在这里遇到的问题是,我需要根据一些规则来验证(不平衡的)时间序列数据集。
示例数据集如下:
dat <- data.frame (
FirmID = c(rep("a",10),rep("b",10),"c",rep("d",10)),
Year = c(rep(c(2000:2004,2006:2010),2),2000,c(2000:2004,2006:2010)),
Random1 = rep("test",31),
Random2 = rep("test2",31),
Assets = rpois(31,3),
Sales = rpois(31,3)
)
dat$Assets[c(1,11)] <- NA
dat$Sales[c(2,11)] <- NA
dat$Assets[21] <- NA
dat$Sales[21] <- NA
我需要的第一个测试是每一行的数据是否完整。这个 sn-p 测试是否有任何必需的列是 NA,如果所有值都有效则返回 OK:
require(plyr)
RequiredVariables <- c("Assets", "Sales")
ValidateT0 <- ddply(dat, .(FirmID,Year),
function(dat) AnyNA = ifelse(sum(is.na(dat[,names(dat) %in% RequiredVariables]))==0,"OK",NA))
dat <- merge(dat,ValidateT0)
dat <- rename(dat, c("V1"="ValidRow")) # Somehow the variable name was wrong?
dat
这将返回以下数据集。
FirmID Year Assets Sales ValidRow
1 a 2000 NA 2 <NA>
2 a 2001 1 NA <NA>
3 a 2002 5 3 OK
4 a 2003 5 3 OK
5 a 2004 1 6 OK
6 a 2006 3 4 OK
7 a 2007 3 0 OK
8 a 2008 4 3 OK
9 a 2009 5 3 OK
10 a 2010 3 4 OK
11 b 2000 NA NA <NA>
12 b 2001 4 3 OK
13 b 2002 5 1 OK
14 b 2003 1 4 OK
15 b 2004 4 2 OK
16 b 2006 6 2 OK
17 b 2007 3 3 OK
18 b 2008 2 4 OK
19 b 2009 7 6 OK
20 b 2010 3 5 OK
21 c 2000 NA NA <NA>
22 d 2000 0 2 OK
23 d 2001 4 1 OK
24 d 2002 3 4 OK
25 d 2003 4 0 OK
26 d 2004 3 6 OK
27 d 2006 6 4 OK
28 d 2007 7 0 OK
29 d 2008 6 2 OK
30 d 2009 4 6 OK
31 d 2010 0 1 OK
然后,对于每一年,我指定三个(相对)期间,我需要数据进行单独分析(我正在研究收购,我需要 T-2、T-1 和 T +1、T+2 等):
AcqPeriod <- c(-2, -1, 1, 2)
TargetPeriod <- c(-3, -2, -1)
LogitPeriod <- c(-2, -1)
现在我想验证每一行是否可以在我的一个分析中使用,这就是嵌套 ddply 的用武之地:
ValidatePeriods <- ddply(dat, .(FirmID),
function(datc) adply(datc, 1,
function(x) data.frame(
AsAcquirerOK =
sum(!is.na(subset(datc, Year %in%(x$Year+AcqPeriod))$ValidRow))==length(AcqPeriod),
AsTargetOK =
sum(!is.na(subset(datc, Year %in% (x$Year+TargetPeriod))$ValidRow))==length(TargetPeriod),
AsLogitOK =
sum(!is.na(subset(datc, Year %in% (x$Year+LogitPeriod))$ValidRow))==length(LogitPeriod)
)
)
)
ValidatePeriods
这段代码虽然难以阅读,但以直观的方式工作,因为我能够在几行代码中准确指定我需要的内容。它测试每个公司年度是否存在指定期间的所有行(==length(period) 部分)并通过 !is.na 在先前生成的“ValidRow”列上包含有效值。
它返回的正是我所需要的:
FirmID Year Assets Sales ValidRow AsAcquirerOK AsTargetOK AsLogitOK
1 a 2000 NA 6 <NA> FALSE FALSE FALSE
2 a 2001 1 NA <NA> FALSE FALSE FALSE
3 a 2002 3 3 OK FALSE FALSE FALSE
4 a 2003 4 0 OK FALSE FALSE FALSE
5 a 2004 5 3 OK FALSE FALSE TRUE
6 a 2006 1 6 OK FALSE FALSE FALSE
7 a 2007 3 3 OK FALSE FALSE FALSE
8 a 2008 1 2 OK TRUE FALSE TRUE
9 a 2009 1 0 OK FALSE TRUE TRUE
10 a 2010 2 0 OK FALSE TRUE TRUE
11 b 2000 NA NA <NA> FALSE FALSE FALSE
12 b 2001 2 0 OK FALSE FALSE FALSE
13 b 2002 5 2 OK FALSE FALSE FALSE
14 b 2003 4 2 OK FALSE FALSE TRUE
15 b 2004 1 4 OK FALSE TRUE TRUE
16 b 2006 4 3 OK FALSE FALSE FALSE
17 b 2007 3 2 OK FALSE FALSE FALSE
18 b 2008 4 1 OK TRUE FALSE TRUE
19 b 2009 2 2 OK FALSE TRUE TRUE
20 b 2010 3 3 OK FALSE TRUE TRUE
但是,如前所述,在包含 300,000 个公司年的数据集上,此函数需要大约 52 分钟。
我尝试结合 data.tables 的速度,但我相对不确定应该如何做。我定义了以下函数,以便快速将 T-1... (_Tm1) 或 T+1.. (_Tp1) 列添加到我的表中。 :
AddTimeSeriesCols <- function(data=dt, Periods=c(-1), keys=c("FirmID","Year")){
require(data.table)
require(stringr)
dt <- data.table(data)
setkeyv(dt, cols=keys)
dtFinal <- copy(dt) # Duplicate dt to add columns to
for (i in Periods){
StartColumn <- length(names(dt))+1 # First Column to Rename
Tm <- data.table(transform(dt, Year=Year-i)) # Create lagged dataset
setkey(Tm, FirmID,Year) #
dtCurrent<-merge(dt, Tm, by = c("FirmID","Year"), all.x = TRUE) # Join with T-/+x
OldNames <- names(dtCurrent)[StartColumn:length(names(dtCurrent))] # Define old names to change
ifelse(i < 0, middle <- "m",ifelse(i>0,middle <- "p",middle <-"")) # Define middle part in Suffix
Suffix <- paste("_","T",middle,abs(i), sep="") # Define Suffix, Tm1 for T(-1), Tp1 for T(+1)
NewNames <- str_c(str_sub(OldNames,1,-3),Suffix) # Generate new names
setnames(dtCurrent,OldNames, NewNames) # Rename data table
KeepKey <- 1:(length(names(dt))-length(NewNames)) # I only want the lagged values
KeepNew <- StartColumn:length(names(dtCurrent)) # & keys of the original dt when merging
dtCurrent <- dtCurrent[,j=c(KeepKey,KeepNew), with=FALSE] # Data Table with original FirmYear + lagged values
dtFinal <- merge(dtFinal,dtCurrent, by = c("FirmID","Year")) # Append to a separate copy in order to reuse original dataframe.
}
return(dtFinal)
}
它返回一个包含添加(滞后)列的数据表,并在整个(300k 行)数据集上运行近 2 秒。它负责加入相关的滞后年份并以一致的方式命名变量(_Tm1 表示 T-1,_Tp1 表示 T+1 等):
>AddTimeSeriesCols(data=dat,c(-3, -2, -1))
FirmID Year Assets Sales RowOK Assets_Tm3 Sales_Tm3 RowOK_Tm3 Assets_Tm2 Sales_Tm2 RowOK_Tm2 Assets_Tm1 Sales_Tm1 RowOK_Tm1
1: a 2000 NA 1 NA NA NA NA NA NA NA NA NA NA
2: a 2001 3 NA NA NA NA NA NA NA NA NA 1 NA
3: a 2002 4 3 OK NA NA NA NA 1 NA 3 NA NA
4: a 2003 1 1 OK NA 1 NA 3 NA NA 4 3 OK
5: a 2004 2 0 OK 3 NA NA 4 3 OK 1 1 OK
6: a 2006 5 5 OK 1 1 OK 2 0 OK NA NA NA
7: a 2007 2 4 OK 2 0 OK NA NA NA 5 5 OK
8: a 2008 4 2 OK NA NA NA 5 5 OK 2 4 OK
9: a 2009 2 1 OK 5 5 OK 2 4 OK 4 2 OK
10: a 2010 5 2 OK 2 4 OK 4 2 OK 2 1 OK
11: b 2000 NA NA NA NA NA NA NA NA NA NA NA NA
12: b 2001 3 6 OK NA NA NA NA NA NA NA NA NA
13: b 2002 1 3 OK NA NA NA NA NA NA 3 6 OK
14: b 2003 4 5 OK NA NA NA 3 6 OK 1 3 OK
15: b 2004 0 3 OK 3 6 OK 1 3 OK 4 5 OK
16: b 2006 3 3 OK 4 5 OK 0 3 OK NA NA NA
17: b 2007 2 5 OK 0 3 OK NA NA NA 3 3 OK
18: b 2008 4 3 OK NA NA NA 3 3 OK 2 5 OK
19: b 2009 3 4 OK 3 3 OK 2 5 OK 4 3 OK
20: b 2010 5 1 OK 2 5 OK 4 3 OK 3 4 OK
从技术上讲,这可以让我更快地进行类似的验证,但需要使用非常不同的语法进行验证(我必须命名特定的列,而不是放入 c(-3, -2, - 1)等)
我的问题在这里:
- 在 ValidateT0 sn-p 中:为什么我的变量没有正确命名,导致我添加了重命名行?
- 我应该如何提高 ddply 选项的速度,还是应该放弃这些嵌套的 ddply 函数?
- 如何使用 c(-3, -2, -1) 格式的输入来编写一个测试我的验证规则的 sn-p?或者我如何在新创建的具有滞后值的数据表中有效地引用这些列(由我的函数创建)?
感谢您的帮助,Stackoverflow 在教我如何在没有任何编程知识的情况下进行此类分析方面非常有帮助。
编辑:添加了更真实的数据集。
【问题讨论】:
-
你有没有在同一年两次为同一个
FirmId? -
我不知道,FirmYear 是独一无二的。
-
好的,你的“周期”呢?它们总是像上面的例子一样间隔一年,还是你可以有一个周期,例如
c(-5, -3, -1)? -
我在最小年份和最大年份之间人为地插入了所有缺失的年份(作为 NA),我更愿意学习如何在不这样做的情况下执行此操作,以防止在原始数据集上插入任何内容.它们并非都具有相同的最小/最大年份。有些公司只有 1985 年,有些公司有 1985-2012 年,有些公司有 1986-1990 年。抱歉,您指的是别的东西:时期 c(-3,-2,-1) 很可能是每年和连续的。不过,我再次希望能够做到这一点,而不会将自己限制在一年的时间间隔内。
-
好的,如果您确实将自己限制为 1 年间隔,您可以通过例如对于你的
TargetPeriod,计算到今年为止连续多少年,然后简单地将这个数字与 3 进行比较 -cumsum和diff将帮助你(你可以在这些方面找到大量问题)
标签: r time-series data.table