我对* apply系列函数表现得相当不错,而且我最近学会了使用do.call("rbind", by(...
作为tapply
的包装器。我正在使用大型数据集(Compustat),我有一个函数(见下文),它生成一个新的滞后变量列,我稍后将其附加到主数据框df
。
我的问题是非常慢。我创建了大约24个滞后变量,并且此函数中的处理大约需要1.5小时,因为数据集中有350,000多个公司年度观察值。
任何人都可以帮助提高此功能的速度,而不会丢失我认为合适的方面:
#' lag vector of unknown size (for do.call-rbind-by: using datadate to track)
lag.vec <- function(x){
x <- x[order(x$datadate), ] # sort data into ascending by date
var <- x[,2] # the specific variable name in data.frame x hereby ignored
var.name <- paste(names(x)[2], "lag", sep = '.') # keep variable name
if(length(var)>1){ # no lagging if single observation
lagged <- c(NA, var[1:(length(var)-1)])
datelag <- c(x$datadate[1], x$datadate[1:(length(x$datadate) - 1)])
datediff <- x$datadate - datelag
y <- data.frame(x$datadate, datediff, lagged) # join lagged variable and difference in YYYYMMDD data
y$lagged[y$datediff >= 20000 & !is.na(y$datediff)] <- NA # 2 or more full years difference
y <- y[, c('x.datadate', 'lagged')]
names(y) <- c("datadate", var.name)
} else { y <- c(x$datadate[1], NA); names(y) <- c("datadate", var.name) }
return(y)
}
然后我在一个命令中单独为每个要为其生成滞后系列的变量调用此函数(这里我使用ni
变量作为示例):
ni_lag <- do.call('rbind', by(df[ , c('datadate', 'ni')], df$gvkey, lag.vec))
其中gvkey
是特定公司的ID号,datadate
是YYYYMMDD
形式的8位整数。
当我使用更简单的函数时,这种方法要快得多:
lag.vec.seq <- function(x){#' lag vector when all data points are present, in order
if(length(x)>1){
y <- c(NA, x[1:(length(x)-1)])
} else {y <- NA}
return(y)
}
与tapply
命令一起使用
ni_lag <- as.vector(unlist(tapply(df$ni, df$gvkey, lag.vec.seq)))
正如您所看到的,主要区别在于tapply
方法不包含任何datadate
信息,因此该函数假设所有数据都是连续的(即,没有丢失的年份)数据帧)。由于我知道缺少年份,因此我构建了do.call
- by
函数来解释这一点。
一些注意事项:
1)函数中的第一个order
命令可能是不必要的,因为我的数据是由gvkey
和datadate
提前排序的(例如df <- df[order(df$gvkey, df$datadate), ]
)。但是,当我使用像这样的函数式编程时,我总是有点害怕R
弄乱我的行排序。这是毫无根据的恐惧吗?
2)识别减慢处理速度的因素将非常有帮助。它是变量的重命名吗?在函数中创建一个新的数据框?或者do.call
与by
的{{1}}通常比tapply
慢得多吗?
谢谢!