Re: [問題] 處理資料 效能詢問

作者: celestialgod (天)   2016-07-08 09:44:17
※ 引述《spiderway (spiderway)》之銘言:
: ※ 引述《spiderway (spiderway)》之銘言:
: : - 問題: 當你想要問問題時,請使用這個類別
: : [問題類型]:
: : 效能諮詢(我想讓R 跑更快)
: : [軟體熟悉度]:
: : 入門(寫過其他程式,只是對語法不熟悉)
: : [問題敘述]:
: : 大家好,
: : 最近因為需要要處理一些資料,雖然有寫出要的東西,但是實在是跑太慢了,感覺要跑個
: : 幾年= =,一定是我寫法不好,想來這問一下各位大大該怎個改才對,並學習一下
: : 麻煩各位大大給予小魯指導,感激不盡!!!
: : 程式:
: : 讀取了全部CSV(10萬多個)存在一個大大的list裡面,要做的大概是把每一個list裡面的
: : 資料都做同一件事,就是每5筆整理成1筆,然後append在一起,如下
: : 1 2 3 4 5 6 1 2 3 ....
: : 1 6/1 a b 12 1 2 1 6/1 a b 12 1 2 13 1 3 14 1 4 15 1 5 16 1 6
: : 2 6/1 a b 13 1 3
: : 3 6/1 a b 14 1 4 變成1筆>>>
: : 4 6/1 a b 15 1 5
: : 5 6/1 a b 16 1 6
: : [程式範例]:
: : http://pastie.org/10898589
: : [版本]
: : R version 3.3.0 (2016-05-03)
: : Platform: x86_64-pc-linux-gnu (64-bit)
: : Running under: Ubuntu 14.04.4 LTS
: : [關鍵字]:
: 解說一下我的資料型態
: CSV檔10萬個,每一個大概有2000~3000多列不等,但一定是5個倍數
: 但是他的資料都是5列一組,所以我才會希望第一篇說得讓他5個整理成一列
: 前面會有V1~V3分別是 時間和AB兩個屬性,前五筆都是一樣的,V4~V6是我要處理的。
: 我上次是選C板大的教學使用,但是因為之前都不常用過dply那些,不太會用
: 用到csv_append之後是沒問題,但是在data那邊是個list我想要的是一個值
: 一行,所以最後的資料希望是像這樣,最後存成一個csv,因為我之後還要在處理一個類
: 似的資料,只有後面不一樣,再合併在一起。
: V1 V2 V3 V4 ... V18
: 時間1 A1 B1 (整理的那5 rows合併的)1
: 時間2 A2 B2 (整理的那5 rows合併的)2
: .
: .
: .
: 我現在面臨的問題,
: 1,do.call(rbind,csv_append)大概下午跑到隔天早上,但至少跑好了XD
: 2,data那邊是list我不太理解是不是能夠直接轉
: 2,我後來有想到辦法,我發現我都只會這種處理方法...
: http://pastie.org/10901154
: 是可以把每一個都轉成我要的之後在做do.call rbind
: 在請大大幫忙了 謝謝~~~~~~
: 心得:謝謝各位大大的幫忙,雖然我很多function都看不太懂= =
: 而且發現好像apply系列很好用欸,之前都一直覺得用for就好了,真的遇到資料量變大差
: 別真多...,之後應該好好找時間重讀一下apply系列和dply那些
我就用三千筆測試就好,下面是三種不同方法:
好讀版:http://pastebin.com/GPgR4FSc
library(pipeR)
library(plyr)
library(dplyr)
library(tidyr)
# data generation (csv_files就是你的ldf)
num_csv <- 3e3
num_xlvls <- 30
num_ylvls <- 30
timePoints <- expand.grid(paste0("a", 1:num_xlvls),
paste0("b", 1:num_ylvls),
stringsAsFactors = FALSE)
csv_files <- lapply(1:num_csv, function(i){
dat <- sample(1:nrow(timePoints), sample(400:600, 1)) %>>%
rep(each = 5) %>>% (timePoints[., ]) %>>%
modifyList(setNames(lapply(1:3, function(j){
sample(1:100, nrow(.), TRUE)
}), paste0("Var", 3:5))) %>>% tbl_df
})
# 整併 + 分開
st <- proc.time()
outRes1 <- lapply(csv_files, function(subdf){
nest(subdf, -Var1, -Var2) %>>%
mutate(data = lapply(data, function(x){
as.matrix(x) %>>% t %>>% as.vector %>>% paste(collapse = ";")
})) %>>% unnest(data) %>>% separate(data, paste0("V", 1:15)) %>>%
mutate_each(funs(as.integer(.)), -Var1, -Var2)
}) %>>% bind_rows
proc.time() - st
# user system elapsed
# 263.93 0.02 265.86
st <- proc.time()
outRes2 <- lapply(csv_files, function(subdf){
subdf %>>% gather(vars, values, -Var1, -Var2) %>>%
group_by(Var1, Var2) %>>%
summarise(tmp = paste(values, collapse = ";")) %>>% ungroup %>>%
separate(tmp, paste0("V", 1:15)) %>>%
mutate_each(funs(as.integer(.)), -Var1, -Var2)
}) %>>% bind_rows
proc.time() - st
# user system elapsed
# 70.01 0.00 70.25
library(data.table)
st <- proc.time()
outRes3 <- lapply(csv_files, function(subdf){
subdf %>>% data.table %>>% melt(c("Var1", "Var2")) %>>%
`[`( , list(tmp = paste(value, collapse = ";")), by = c("Var1", "Var2"))%>>%
`[`( , `:=`(paste0("V", 1:15), tstrsplit(tmp, ";"))) %>>%
`[`( , tmp := NULL) %>>%
`[`( , `:=`(paste0("V", 1:15), lapply(.SD, as.integer)), .SDcols = V1:V15)
}) %>>% rbindlist
proc.time() - st
# user system elapsed
# 37.35 1.30 38.16
第一個方法只是把我原本方法做一個修正,但是nest跟unnest實在太花時間
而且沒有必要,所以就有了第二個版本直接把資料直接做合併的動作
雖然快了四倍,但是速度還可以更快,所以就有了第三個版本
(測了一下記憶體使用量已經很低)
我最近開始往回走,雖然dplyr好用,但是data.table在處理資料還是有他的優勢
在第三個方法可以看到,硬是比dplyr的解法快了一倍,但是相對寫起來就沒那麼漂亮
這個是code style跟效能的取捨(攤手
另外,我一萬個csv就會出問題了... 記憶體真沒那麼大...
如果你記憶體真的有很大在考慮這樣做,沒那麼大可以考慮分段讀取
分段寫入時,在append就好,不過之後讀進來又很麻煩(攤手
以上,敬請參考~~~
OS: 難得的颱風假阿~~~ 大家請注意自身安全!!
7/8 11:00補上data.table把V1~V15轉成integer

Links booklink

Contact Us: admin [ a t ] ucptt.com