我对 *apply 系列函数已经相当熟悉了,而且我最近学会了do.call("rbind", by(...
使用tapply
. 我正在处理一个大型数据集(Compustat),并且我有一个函数(见下文),它生成一个新的滞后变量列,我稍后将其附加到主数据框df
。
我的问题是它非常慢。我创建了大约两打滞后变量,这个函数中的处理大约需要 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
是 8 位整数,形式为YYYYMMDD
。
当我使用更简单的函数时,这种方法要快得多:
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
withby
通常(很多)比 慢tapply
?
谢谢!