在R中执行此表转换的更快方法

时间:2014-09-30 15:53:38

标签: r

我有一个简单的股票价格表,我们称之为“价格”,#34;沿着顶部的代码和沿着边的日期。

我想创建一个新表," price_norm,"它将每个价格除以第一个价格。例如,第一列可能如下所示:

price     price_norm
20.00     1.000
21.00     1.050
21.00     1.050
20.00     1.000
20.50     1.025

...等

皱纹是有些股票一直没有定价回到第一个日期,所以他们的专栏在"价格"从一串NA开始 - 在这种情况下,它们应该除以第一个非NA值,而不是第一行中的值。

可能还有一些股票没有任何价格,其中每个价值都是NA;在这种情况下,price_norm中的相应列也应该都是NA。

我以一种非常缓慢的方式完成了这项工作 - 30秒或更长时间来处理。我正在寻找一种更有效的方式:

price_norm <- price

nonNAIndex <- function(z) {
  min(which(!is.na(z)))
}

for( j in colnames(price) ) {

  if(!is.na(price[nrow(price),j])) {  

    k <- nonNAIndex(price[,j])

    for( i in k:nrow(price) ) {
      price_norm[i,j] <- ( price[i,j] / price[k,j] ) 
    }
  }
}

3 个答案:

答案 0 :(得分:2)

以下听起来应该做你想做的事:

## sample data
mydf <- data.frame(v1 = c(20.00, 21.00, 21.00, 20.00, 20.50), 
                   v2 = c(NA, 20.00, 21.00, 21.00, 20.00),
                   v3 = NA)

mydf / lapply(mydf, function(x) ifelse(all(is.na(x)), NA, na.omit(x)[1]))
#      v1   v2 v3
# 1 1.000   NA NA
# 2 1.050 1.00 NA
# 3 1.050 1.05 NA
# 4 1.000 1.05 NA
# 5 1.025 1.00 NA

我刚刚创建了一个初始值列表,并将原始data.frame除以这些值。

答案 1 :(得分:0)

确保您的数据为data.frame

sapply(df, function(x) {x/ifelse(all(is.na(x)), NA, head(x[!is.na(x)],1))})

答案 2 :(得分:0)

或者您可以尝试:(使用@Ananda Mahto的数据集)

m1 <- t(mydf)
mydf/as.list(m1[cbind(1:nrow(m1),max.col(!is.na(m1), "first"))])

基准

稍微大一点的数据集

set.seed(24)
df <- as.data.frame(matrix(sample(c(NA,1:25), 1e3*2e3, replace=TRUE), ncol=2e3))
f1 <- function() df/lapply(df, function(x) ifelse(all(is.na(x)), NA, na.omit(x)[1]))
f2 <- function() sapply(df, function(x) {x/ifelse(all(is.na(x)), NA, head(x[!is.na(x)],1))})
f3 <- function() {m1 <- t(df)
                df/as.list(m1[cbind(1:nrow(m1),max.col(!is.na(m1), "first"))])}

library(microbenchmark)
microbenchmark(f1(), f2(), f3(), unit="relative", times=25L)
#Unit: relative
#expr      min       lq   median       uq      max neval
#f1() 1.213922 1.197350 1.140682 1.103249 1.351307    25
#f2() 6.318962 6.925681 7.212136 6.370824 8.080331    25
#f3() 1.000000 1.000000 1.000000 1.000000 1.000000    25