• 沒有找到結果。

R軟體套件介紹 : data.table

N/A
N/A
Protected

Academic year: 2021

Share "R軟體套件介紹 : data.table"

Copied!
15
0
0

加載中.... (立即查看全文)

全文

(1)

1

R 軟體套件介紹 : data.table

王博賢 副統計分析師 在現在這個資料龐大且雜亂的時代,R 本身的函數也漸漸的越來越跟不上這 個時代,一些基本的函數使用起來變得很沒有效率,甚至在資料量大的時候根本無 法使用,因此漸漸的有人發展了其他的package 來幫助我們進行資料清理,像是常 見 的 “dplyr” 。 而 近幾 年 有 另 外 一 個 package 崛 起 , 那 就 是 今天 要 介 紹的 data.table。 首先要使用時,記得要先下載 package,下載後記得要呼叫他。語法如下 install.packages("data.table") library(data.table)  讀取及輸出資料 第一個要跟大家介紹的功能是輸出資料,在傳統的 R 中寫出資料常用的語法 是使用“write.csv”,但在輸出的資料很大時,write.csv 會需要很長的時 間,甚至可能就當機了。而 data.table 提供了更快速的方法,他的語法為 “fwrite”,下面我們來測試兩種方法輸出資料時所需的時間。程式碼如下 : #生成 5000*5000 的資料 n <- 5000 dt.test <- data.table(matrix(rep(1,(n*n)),nrow=n)) #fwrite ptm <- proc.time() fwrite(dt.test,"test.csv") dt.time <- proc.time() - ptm #write.csv ptm <- proc.time() write.csv(dt.test,"test.csv") base.time <- proc.time() - ptm

(2)

臺北醫學大學生物統計研究中心 eNews 第 34 期 2019/12

2

cat("fwrite use time :",dt.time[1], "write.csv use time :",base.time[1])

而得到的結果如下圖: 我們可以從上面的結果來看,在輸出 5000*5000 (大約 47.7 MB)的資料時, fwrite 幾乎是瞬間就完成了,而 write.csv 則花了 19.83 秒以上,可想而知在 現在資料動不動就是好幾 GB 甚至 TB ,如果我們使用 write.csv 那可能輸出資 料就要等上好幾個小時以上。 接著要跟大家介紹的是讀取資料,在一般常見的語法為“read.csv”而在 data.table 中提供另一種讀取資料的語法為“fread”,下面就讓我們來看兩著 的效率,程式碼如下 : ptm <- proc.time() dt1 <- fread("test.csv") dt.time <- proc.time() - ptm #write.csv ptm <- proc.time() dt2 <- read.csv("test.csv") base.time <- proc.time() - ptm cat("fread use time :",dt.time[1], "read.csv use time :",base.time[1])

(3)

3 結果如下 : 我們試著把剛寫出的資料讀進來,我們可以看到在 47.7 MB 的資料時,一樣的 fread 的效率遠遠的超越了 write.csv ,在資料量大時,他們的差異只會越來 越大,因此建議大家在讀取大型資料時,使用 fread 他可以大幅度的減少您所需 等待時間。 在利用 R 處理大型的資料時,最常遇到的問題是記憶體不足,導致根本無 法讀取資料,而在這個問題上 fread 也有提供一些特別的功能,那就是他提供 我們可以控制我們要 a. 跳過幾筆資料,b. 一次要讀取幾筆的資料,有這兩種功 能後,我們就可以分段處理資料,先讓資料量變小,以方便我們進行後續的分 析。語法如下 data1 <- fread("http://biostat.tmu.edu.tw/attachment/download.php?atta chment_id=94") data1 #分段讀取 data2 <- fread("http://biostat.tmu.edu.tw/attachment/download.php?atta chment_id=94",nrows = 10 ,skip = 64000) data2 結果如下 :

(4)

臺北醫學大學生物統計研究中心 eNews 第 34 期 2019/12 4 我們可以從 data1 的結果看到資料共有 64489 筆,而當我控制 nrows = 10 (讀進來的筆數),skip = 6000 (略過的筆數) ,我們發現資料指讀取 64000 到 64009 共只有 10 筆資料,可能您會覺得很奇怪為什麼跳過 64000 ,他是從 64000 筆讀取,那是因為第一列的欄位名稱也算是一列,且您可以發現 data2 的部分並沒有欄位名稱,因此在使用上要注意,因為跳過了首列,所以並不會知 道資料的欄位名稱,所以要在自己補上去。  data.table 的基本介紹 接著是 data.table 的基本介紹 ,下面我們使用的範例資料為 CVD_all 網址為為 http://biostat.tmu.edu.tw/attachment/download.php?attachment_id=94

(5)

5 語法如下: data1 <- fread("http://biostat.tmu.edu.tw/attachment/download.php?atta chment_id=94") data2 <- read.csv("http://biostat.tmu.edu.tw/attachment/download.php?a ttachment_id=94") data1 data2 結果如下 : 我們可以看到,data.table 在顯示資料方面做得很好,我們不需要預設我 要看前幾列,他自然會幫我們顯示前五筆及最後五筆,而有用過 data.frame 的 人,應該都知道如果沒有控制顯示的筆數,他會顯示一長串,這樣很難幫助我們 快速的看一下資料。這就是 data.table 的一個好處。 在我們利用 fread 讀取資料時,讀進來的資料自然就為 data.table 的形 式,而如果您利用其他方法讀去資料,或是其他的資料型態的資料,要轉換為 data.table 時,那您只需使用 data.table() 即可,語法如下 : str(data2) data2.dt <- data.table(data2) str(data2.dt) 結果如下 :

(6)

臺北醫學大學生物統計研究中心 eNews 第 34 期 2019/12 6 我們可以看到資料的型態從 data.frame 轉換成了 data.table。 接著就要準備進入我們的正題了,下面為 data.table 整體的概念 : DT[i, j, by] i j by

where | order by select | update group by 第一個位置 i : 主要是用來下篩選資料的條件,或是排序用。 第二個位置 j : 主要是用來篩選欄位,或是更新欄位。 第三個位置 by : 依照 group 去做事情。  data.table 資料選取及篩選 首先跟大家介紹如何篩選資料,語法如下: #data.table data1[心血管疾病 == 0 & 年齡 > 50] #data.frame

data2[data2$心血管疾病 == 0 & data2$年齡 > 50,]

(7)

7 我們可以看到,使用 data.table 篩選資料時,我們不必像一般 data.frame 一直使用 “data2$” ,我們只需要直接寫欄位名稱,且這次我們再一次看到 data.table 的好處,經過篩選後,我們可以很快用肉眼看出滿足“心血管疾病 是 0 且 年齡 >50”的有 19034 筆,假設您使用的是 data.frame,他只會把所 有的資料印出來,最後面再加個 …. ,沒有辦法直接讓我們看出資料的筆數。 接著介紹欄位的篩選,假設今天我們要選擇 “心血管疾病”,語法如下 #data.table data1[1:10,心血管疾病] #data.frame data2[1:10,"心血管疾病"] data1[,list(心血管疾病)] data1[,.(心血管疾病)] #selct column note ---- a <- "心血管疾病"

data1[,a,with=F]

(8)

臺北醫學大學生物統計研究中心 eNews 第 34 期 2019/12 8 從最上面結果我們可以看到,在 data.table 中選擇欄位一樣直接輸入欄位 名稱即可,並不用像 data.frame 一樣再加上 “ ” ,而在 data.table 中您可 以使用 “list()” 或是 “.()” 來保持 data.table 的樣式而非直接是 vector。 而在某些狀況時,例如寫 for 迴圈時,我們可能是使用變數代替變數名 稱,假設 a <- “心血管疾病” ,我們要用時要在後面加上 “with = F” 這 個指令可以幫助我們轉換回 data.frame 的語法,這樣我們就可以讀取資料,但 要注意的是,用這種方法得到的結果,只能是 data.table ,假設您要的是 vector 則要在後面加上[[1]]就可以了。主要是因為他本身也是 list 的資料型 態,因此我們可以使用此方法得到 vector。 而假設今天我要選取多個欄位,語法如下

(9)

9 #data frame dt2 <- data2[,c("ID","年齡","收縮壓","舒張壓")] #data table dt1 <- data1[,.(ID,年齡,收縮壓,舒張壓)] dt1 結果如下 : data.table 可以直接用 .() ,加上欄位名稱即可,不需要再加上 c() 以及 “ ”。 而 data.table 另一個好處為我們可以同時篩選資料,並且更改欄位名稱, 假設我今天要篩選出前 10 筆,並只要 ID 及 年齡,且年齡要改名為 age 時, 我只需要短短一句即可 ,語法如下 :

#select and rename

dt1[1:10,.(ID,age=年齡)]

結果如下 :

(10)

臺北醫學大學生物統計研究中心 eNews 第 34 期 2019/12 10 “>=50”,不是的為 “<50”,那語法如下 : #data.frame age.group <- ifelse(dt2$年齡 >= 50,">=50","<50") dt2 <- cbind(dt2,age.group) dt2[1:5,] #data.tab;e dt1[,age.group:=ifelse(dt2$年齡 >= 50,">=50","<50")] dt1 結果如下 : 從上面看到,如果您是使用原本的 data.frame,你可能就需要用到 3 個步 驟才能完成,但如果使用 data.table 則可以用一個步驟就完成他。在 data.table 中新增欄位我們只需要使用 “:=” 即可在最後面新增一個變數。  data.table 的計算 data.table 在計算及聚合上,也提供了快速且方便的方法,假設今天我要計 算收縮壓的平均值,語法如下: dt1[,mean(收縮壓,na.rm=T)] 結果如下 : 假設今天我們要的是同時計算平均數及標準差,那一樣我們只需用一行指令

(11)

11 即可,語法如下 : dt1[,.(mean(收縮壓,na.rm=T), sd(收縮壓,na.rm=T))] 結果為 : 因為在 data.table 中,假設您沒有為欄位命名,他認定會使 V1,V2…來為 欄位命名,假設今天你想在計算的同時給定欄位名稱的話,語法如下 : dt1[,.(mean = mean(收縮壓,na.rm=T), sd = sd(收縮壓,na.rm=T))] 結果為 : 從上面我們可以看到,我們只需要加上 “欄位名稱 = function(x)”,就 可以快速的得到聚合好的資訊,且名稱也為我們所需要的。 接著一樣的我們在計算的同時,我們也可以針對資料進行篩選。假設今天我 們只想知道 age.group 為 “>=50” 這群人的收縮壓之平均值及標準差,語法 如下

dt1[age.group == ">=50",.(mean = mean(收縮壓,na.rm=T), sd = sd(收縮壓,na.rm=T), N = .N)] 結果如下 : 從上面結果來看我們只需要在 i 的位置增加篩選條件即可。因此我們只需 短短一行指令,就可以同時完成資料的篩選及計算,大幅度簡短了所需撰寫的程 式碼及閱讀性。 接著跟大家介紹 “by” 的用法,回到上面那個例子,我們只針對

(12)

臺北醫學大學生物統計研究中心 eNews 第 34 期 2019/12 12 age.group 為 “>=50” 的進行計算,但通常我們會想同時知道的是 ”>=50” 及 “<50” 兩群的資訊,而這時我們就可以使用 by 來幫助我們計算,語法如 下 : dt1[,.(mean = mean(收縮壓,na.rm=T), sd = sd(收縮壓,na.rm=T), N = .N),by=age.group] dt1[,.(mean = mean(收縮壓,na.rm=T), sd = sd(收縮壓,na.rm=T), N = .N),by=.(年齡>=50)] 結果如下 : 從上面我們可以看到只需要在原本的程式碼後面加上 “by = 分組變數 “ 即可,或是也可以用“by =邏輯判斷”,data.table 就可以幫我們進行計 算。 一樣的在進行分組計算時,我們可以先對資料進行篩選。假設現在我們不想 看到 NA 這群的結果,那我之需要在前面 i 的位置先排除掉 age.group 為 NA 的資料即可,語法如下 : dt1[!is.na(age.group),.(mean = mean(收縮壓,na.rm=T), sd = sd(收縮壓,na.rm=T), N = .N),by=age.group] 結果為 : 當然在很多情況下,我們的分組變數不會只有一個,加設今天我要再加上性

(13)

13 別做為分組變數,語法如下 dt1[,gender:=data1$性別] dt1[!is.na(age.group),.(mean = mean(收縮壓,na.rm=T), sd = sd(收縮壓,na.rm=T), N = .N),by = .(age.group,gender)] 結果如下 : 因為我們剛在選擇欄位時,並沒有選擇到性別,因此我們先利用前面提到的 “:=” 為 dt1 新增一欄為 gender,裡面的內容為原本檔案中的性別。接著我們 只需要在 by 的地方在新增上 gender 即可。 接著聚合完後,我們有時還會對資料進行篩選或排序,在 data.table 中我 們可以使用 [ ] 接著對輸出的結果做動作。 假設今天聚合完後,我要篩選出 mean 小於 130 後再對 mean 做遞減的排 序,那語法如下 : dt1[!is.na(age.group),.(mean = mean(收縮壓,na.rm=T), sd = sd(收縮壓,na.rm=T), N = .N),by = .(age.group,gender)][mean < 130] [order(-mean)] 結果如下 : 接著最後跟大家介紹“.SD”,在 data.table 中 “.SD” 代表著整個資 料,以下面例子為大家介紹,語法如下: dt2 <- data1[,.(收縮壓,舒張壓,空腹血糖,高密度脂蛋白,三酸甘油酯)] dt2 dt2[,lapply(.SD,mean,na.rm=T)] 結果如下:

(14)

臺北醫學大學生物統計研究中心 eNews 第 34 期 2019/12 14 首先我們先從原先的資料中,選擇 收縮壓、舒張壓、空腹血糖、高密度脂 蛋白、三酸甘油酯 5 個數值變數。接著我們想要知道這五個變數各自的平均值, 此時我們只需要在 j 的位置中,使用 lapply(.SD,mean,na.rm=T) 即可,其中 的 .SD 就表示 dt2 整個資料集。如果對 lapply 不了解的,可以使用 ?lapply 語法來查看他。 接著帶大家看“.SD”實際應用的例子,假設今天我要對 dt1 中的收縮壓 及 舒張壓 做標準化的動作,並且是在原本的資料集中進行新增,語法如下 : dt1[, c("SBP", "DBP") := lapply(.SD,scale),.SDcols = c("收縮壓","舒張 壓")] dt1 結果如下: 一樣我們可以使用 “:=” 一次新增兩個以上的欄位,只是前面就要以傳統 vector 的形式來撰寫,接著一樣使用 lapply(.SD,scale) 來做計算,但這次我 們並非要對整個 dt1 做計算,因此我們可以在後面加上 “.SDcols = “ 來選擇 我們所需的欄位即可。 今天跟大家簡單介紹了 data.table 這個 package 的一些內容,提供大家在

(15)

15 資料處理上的另一種方法。他不只快速,且簡潔易讀,且這個 package 還有很多 不錯的 function,如果大家有興趣,可以仔細去讀他的說明檔,相信會有很多的 收穫。 參考資料 : 1. https://cran.r-project.org/web/packages/data.table/vignettes/datatable-intro.html 2. https://cran.r-project.org/web/packages/data.table/data.table.pdf

參考文獻

相關文件

&#34;Extensions to the k-Means Algorithm for Clustering Large Data Sets with Categorical Values,&#34; Data Mining and Knowledge Discovery, Vol. “Density-Based Clustering in

A clever and simplifying strategy: pairing up all the rays coming through the slit and then finding what conditions cause the waves of the rays in each pair to cancel each other.

Korea, Ministry of Education &amp; Human Resources Development, &#34;Organization of the Curriculum and Time Allotment standards,&#34;. http://www.moe.go.kr/en/down/curriculum-3.pdf

[r]

 for…迴圈自初值開始判斷 &lt;條件判斷&gt; 是否為 true,若為 true 則執行 for 迴圈內的敘述,並依 &lt;增量值&gt;,每次增 加 (或減少) 指定的增量值,直至 &lt;條件判斷&gt;

http://www.edb.gov.hk/attachment/en/edu-system/special/overview/factsheet/special-edu/spschc16-17.pdf 新高中學制在

õT¤_ .â·&lt;íËju, Data Access Component Module 2FíŠ?. âÀÓ“, ©ø_ method úk’eé query v,

[r]