2015-06-08 46 views
2

我想通過使用data.table作爲參數使用data.table的行應用函數。我目前正在使用apply建議here使用列作爲參數在data.table中使用行應用函數

但是,我的data.table是7列2700萬行,所以申請操作需要很長時間,當我在許多輸入文件遞歸運行時,作業佔用所有可用RAM(32Gb)。我很可能多次複製data.table,儘管我不確定這一點。

我希望能幫助這個代碼更高效的存儲,因爲每個輸入文件將是〜7千行〜7千行,並且有30個輸入文件要處理。我確信使用apply的行會減慢整個代碼的速度,所以更具內存效率或使用向量化函數的替代方法可能會是更好的選擇。

我在試圖編寫一個向量化函數時遇到了很多麻煩,它使用4列作爲參數,並使用data.table逐行操作。我的示例代碼中的應用解決方案有效,但速度很慢。一種替代方法我試過是:

cols=c("C","T","A","G") 
func1<-function(x)x[max1(x)] 
datU[,high1a:=func1(cols),by=1:nrow(datU)] 

但第6行督data.table輸出看起來像這樣的:

Cycle Tab ID colA colB colC colG high1 high1a 
1 0 45513 -233.781 -84.087 -3.141 3740.916 3740.916 colC 
2 0 45513 -103.561 -347.382 2900.866 357.071 2900.866 colC 
3 0 45513 153.383 4036.636 353.479 -42.736 4036.636 colC 
4 0 45513 -147.941 28.994 4354.994 384.945 4354.994 colC 
5 0 45513 -89.719 -504.643 1298.476 131.32 1298.476 colC 
6 0 45513 -250.11 -30.862 1877.049 -184.772 1877.049 colC 

下面是一個使用應用該作品在我的代碼(它產生的高溫1列以上),但過於緩慢和內存密集型:

#Get input files from top directory, searching through all subdirectories 
    file_list <- list.files(pattern = "*.test.txt", recursive=TRUE, full.names=TRUE) 

#Make a loop to recursively read files from subdirectories, determine highest and second highest values in specified columns, create new column with those values 

    savelist=NULL 
    for (i in file_list) { 

    datU <- fread(i) 
    name=dirname(i) 

    #Compute highest and second highest for each row (cols 4,5,6,7) and the difference between highest and second highest values 
    maxn <- function(n) function(x) order(x, decreasing = TRUE)[n] 
    max1 <- maxn(1) 
    max2 <- maxn(2) 
    colNum=c(4,5,6,7) 
    datU[,high1:=apply(datU[,colNum,with=FALSE],1,function(x)x[max1(x)])]) 
    datU[,high2:=apply(datU[,colNum,with=FALSE],1,function(x)x[max2(x)])] 
    datU[,difference:=high1-high2,by=1:nrow(datU)] 
    datU[,folder:=name] 
    savelist[[i]]<-datU 

} 

#Create loop to iterate over folders and output data 

sigout=NULL 
for (i in savelist) { 

    # Do some stuff to manipulate data frames, then merge them for output 
setkey(i,Cycle,folder) 
Sums1<-i[,sum(colA,colB,colC,colD),by=list(Cycle,folder)] 
MeanTot<-Sums[,round(mean(V1),3),by=list(Cycle,folder)] 
MeanTotsd<-Sums[,round(sd(V1),3),by=list(Cycle,folder)] 
Meandiff<-i[,list(meandiff=mean(difference)),by=list(Cycle,folder)] 
Meandiffsd<-i[,list(meandiff=sd(difference)),by=list(Cycle,folder)] 

df1out<-merge(MeanTot,MeanTotsd,by=list(Cycle,folder)) 
df2out<-merge(Meandiff,Meandiffsd,by=list(Cycle,folder)) 
sigout<-merge(df1out,df2out) 

#Output values 
write.table(sigout,"Sigout.txt",append=TRUE,quote=FALSE,sep=",",row.names=FALSE,col.names=TRUE) 
} 

我喜歡關於替代的功能應用,這將使我的最高和次高值,每一行對C一些例子第4,5,6,7列可以通過索引或列名來識別。

謝謝!

回答

2

你可以做這樣的事情:

DF <- read.table(text = " Cycle Tab ID colA colB colC colG high1 high1a 
1 0 45513 -233.781 -84.087 -3.141 3740.916 3740.916 colC 
       2 0 45513 -103.561 -347.382 2900.866 357.071 2900.866 colC 
       3 0 45513 153.383 4036.636 353.479 -42.736 4036.636 colC 
       4 0 45513 -147.941 28.994 4354.994 384.945 4354.994 colC 
       5 0 45513 -89.719 -504.643 1298.476 131.32 1298.476 colC 
       6 0 45513 -250.11 -30.862 1877.049 -184.772 1877.049 colC", header = TRUE) 

library(data.table) 
setDT(DF) 

maxTwo <- function(x) { 
    ind <- length(x) - (1:0) #the index is equal for all rows, 
          #so it could be made a function parameter 
          #for better efficiency 
    as.list(sort.int(x, partial = ind)[ind]) #partial sorting 
} 

DF[, paste0("max", 1:2) := maxTwo(unlist(.SD)), 
    by = seq_len(nrow(DF)), .SDcols = 4:7] 
DF[, diffMax := max2 - max1] 

# Cycle Tab ID  colA  colB  colC  colG high1 high1a max1  max2 diffMax 
#1:  1 0 45513 -233.781 -84.087 -3.141 3740.916 3740.916 colC -3.141 3740.916 3744.057 
#2:  2 0 45513 -103.561 -347.382 2900.866 357.071 2900.866 colC 357.071 2900.866 2543.795 
#3:  3 0 45513 153.383 4036.636 353.479 -42.736 4036.636 colC 353.479 4036.636 3683.157 
#4:  4 0 45513 -147.941 28.994 4354.994 384.945 4354.994 colC 384.945 4354.994 3970.049 
#5:  5 0 45513 -89.719 -504.643 1298.476 131.320 1298.476 colC 131.320 1298.476 1167.156 
#6:  6 0 45513 -250.110 -30.862 1877.049 -184.772 1877.049 colC -30.862 1877.049 1907.911 

但是,你仍然可以遍歷行,這意味着nrow調用該函數。您可以嘗試使用Rcpp在編譯代碼中執行循環。

+0

羅蘭,謝謝你的幫助!我能夠成功地在2〜3000萬行輸入文件上使用您的代碼。但是,這段代碼在運行時使用了大約14Gb的RAM。是否有可能對諾羅打電話是什麼東西吃掉所有的內存?我擔心如果我在30個輸入文件上試試這個,我仍然會用完RAM。我會盡快開始,讓你知道它是如何發展的。 – krisadastra

+0

當我嘗試使用我的30個輸入文件運行代碼時,我遇到了內存分配錯誤:'錯誤:無法分配大小爲310的向量。8 Mb 此外:警告消息: 1:在forderv(byval,sort = FALSE,retGrp = TRUE): 達到32675Mb的總分配:see help(memory.size)'看起來這個代碼不是內存有效,因爲我需要它。 savelist對象只有14個元素,而不是30個,但已經是31.1Gb的大小。 – krisadastra

1

取決於你想如何處理重複,例如如果你沒有他們或想將它們組合在一起,你可以這樣做:

d = data.table(a = 1:4, b = 4:1, c = c(2,1,1,4)) 
# a b c 
#1: 1 4 2 
#2: 2 3 1 
#3: 3 2 1 
#4: 4 1 4 

high1 = do.call(pmax, d) 
#[1] 4 3 3 4 
high2 = do.call(pmax, d * (d != high1)) 
#[1] 2 2 2 1 

否則,你可能只是添加一些抖動出你的精度範圍(我選擇了大量以保持可見):

d.jitter = d + runif(nrow(d) * ncol(d), 0, 1e-4) 
#   a  b  c 
#1: 1.000044 4.000090 2.000008 
#2: 2.000076 3.000029 1.000034 
#3: 3.000007 2.000029 1.000036 
#4: 4.000001 1.000069 4.000041 

high1.j = do.call(pmax, d.jitter) 
high2 = do.call(pmax, d * (d.jitter != high1.j)) 
#[1] 2 2 2 4 

翻譯相關.SD.SDcols語義留作一個簡單的練習給讀者。

+0

如果你有負數(你可能需要做一些額外的加/減)(提示:使用'pmin')。 – eddi