精華區beta R_Language 關於我們 聯絡資訊
data.table包含的東西很多 但是很多東西都可以被plyr, dplyr的function取代 所以data.table很多function,我都不太熟 這裡簡單介紹一下data.table 如果你想要了解更多,請自行去看manual 要了解data.table,我們可以先從package的description來看 "Fast aggregation of large data (e.g. 100GB in RAM), fast ordered joins, fast add/modify/delete of columns by group using no copies at all, list columns and a fast file reader (fread). Offers a natural and flexible syntax, for faster development." 簡單翻譯一下,大資料(例如,記憶體中大小為100GB的資料)的在不創建複本下,根據 類別(group)變數進行快速整合、排列、合併、增加/修改/刪除行資料等動作。... 重點就在不創建複本,因為R修改data.frame時,會先複製一次再修改, 然後傳回複本,因此,會浪費不少記憶體,而且很容易拖累速度,因此, data.table提供這方面更有效率的操作。 (這方面的速度比較可以參考#1LeXNCKV (R_Language) [分享] 資料數據處理修改) 1. data.table 這個函數基本上data.frame使用差不多,而且data.frame的參數都可以放進 像是很常用到的stringsAsFactors,只是data.table預設是FALSE, 這點跟data.frame不同,使用上需要注意,範例如下: ` R t = data.table(a = LETTERS[1:3]) str(t) # Classes ‘data.table’ and 'data.frame': 3 obs. of 1 variable: # $ a: chr "A" "B" "C" # - attr(*, ".internal.selfref")=<externalptr> t2 = data.frame(a = LETTERS[1:3]) str(t2) # 'data.frame': 3 obs. of 1 variable: # $ a: Factor w/ 3 levels "A","B","C": 1 2 3 ` 第二個差異是data.table不包含rownames, 在轉換data.frame到data.table時,要注意這點 這裡先提供快速把rownames變成變數的function,plyr的name_rows 附註一條:data.table都包含data.frame的class 可以用在data.frame的方法都可以在data.table上實現 但是data.table還多了一個引數 "key",我對它的解讀是一種索引的概念 而透過索引的動作都會被加速。 key可以是一個變數,也可以是多個變數,這點看個人使用。 再來,就是data.table的'[',這部分跟data.frame不太一樣 所以需要特別說明,但是這部分,我自己也不是很熟悉,我只能大概講過 a. 我們很常在data.frame做取多行的動作,在data.table是不可行的,舉例: ` R vars = data.frame(X = rnorm(3), Y = rnorm(3), Z = rnorm(3)) vars[,1:2] # X Y # 1 -0.5677575 2.1831285 # 2 -0.7161529 0.3714633 # 3 1.2665120 0.7837508 vars_dt = data.table(vars) vars_dt[,1:2] # [1] 1 2 ` 但是你想這麼做,怎麼辦? 加上with=FALSE就好了,或是用list包住column name ` R vars_dt[,1:2,with=FALSE] # X Y # 1: -0.5677575 2.1831285 # 2: -0.7161529 0.3714633 # 3: 1.2665120 0.7837508 vars_dt[j=list(X, Y)] # X Y # 1: -0.5677575 2.1831285 # 2: -0.7161529 0.3714633 # 3: 1.2665120 0.7837508 ` 剩下像是by, .SD, .SDcols, ':='等自行?data.table查看吧 data.table的部分就先說明到這,接下來,講一些相關的function b. setkey: 改變key的值, setnames: 改變column name,但是一樣不製造複本 c. copy: 製造data.table的複本 d. setDF: 在不製作複本下,把data.table的class改為data.frame 舉例: ` R DT = data.table(X = rnorm(3), Y = rnorm(3)) str(DT) # Classes ‘data.table’ and 'data.frame': 3 obs. of 2 variables: # $ X: num -1.3738 0.167 -0.0578 # $ Y: num 0.487 1.728 0.646 # - attr(*, ".internal.selfref")=<externalptr> setDF(DT) str(DT) # 'data.frame': 3 obs. of 2 variables: # $ X: num -1.3738 0.167 -0.0578 # $ Y: num 0.487 1.728 0.646 DT = data.table(X = rnorm(3), Y = rnorm(3)) tracemem(DT) # [1] "<0000000006A1BE28>" setDF(DT) # 沒有複製的動作 DF = data.frame(DT) retracemem(DF, retracemem(DT)) # tracemem[<0000000006A1BE28> -> 0x00000000061ec928]: ## 記憶體位置就發生改變了,就複製了DT一次 ` 這部分可能不太懂,不過沒關係,記住一點,要轉成data.frame用setDF就好 e. setDT: setDF的反向 f. duplicated, unique duplicated提供一個跟data.table列數相等長度的邏輯值向量, TRUE代表前面有一樣的列,FALSE代表沒有 unique則是留下沒有重複的列,舉例來說: ` R set.seed(100) DT = data.table(A = rbinom(5, 1, 0.5), B = rbinom(5, 1, 0.5)) # A B # 1: 0 0 # 2: 0 1 # 3: 1 0 # 4: 0 1 # 5: 0 0 duplicated(DT) # [1] FALSE FALSE FALSE TRUE TRUE unique(DT) # A B # 1: 0 0 # 2: 0 1 # 3: 1 0 DT[!duplicated(DT)] # A B # 1: 0 0 # 2: 0 1 # 3: 1 0 ` 不過unique還有更多功能,它可以選擇變數做unique,舉例來說: ` R unique(DT, by = "A") # A B # 1: 0 0 # 2: 1 0 unique(DT, by = "B") # A B # 1: 0 0 # 2: 0 1 ` 順便一提,dplyr的distinct,如果你input的class是data.table 它就是用unique做的 ` R library(dplyr) distinct(DT) # A B # 1: 0 0 # 2: 0 1 # 3: 1 0 ` 你如果想看distinct怎麼做,可以在R上面打dplyr:::distinct_.data.table > dplyr:::distinct_.data.table function (.data, ..., .dots) { dist <- distinct_vars(.data, ..., .dots = .dots) if (length(dist$vars) == 0) { unique(dist$data) } else { unique(dist$data, by = dist$vars) } } 之後提到distinct,我們再來講distinct 其他相關function像是subset, setcolorder, setorder (setorderv) 對這三個function有興趣,再去看manual,不贅述 這三個對應到dplyr的filter, select, arrange,之後我們會再提到這些 g. transform: 改變column的屬性、值等,舉例來說: ` R DT = data.table(a = 1:3, b = 2:4, c = LETTERS[1:3]) DT2 = copy(DT) DT[, b := b**2] DT2 %<>% transform(b = b**2) all.equal(DT, DT2) # TRUE DT %<>% transform(c = as.factor(c)) str(DT) # Classes ‘data.table’ and 'data.frame': 3 obs. of 3 variables: # $ a: int 1 2 3 # $ b: num 4 9 16 # $ c: Factor w/ 3 levels "A","B","C": 1 2 3 # - attr(*, ".internal.selfref")=<externalptr> ` h. set: 用來變更特定column,某些列的值,舉個簡單的例子 ` R DT = data.table(a = 1:3, b = 2:4) DT2 = copy(DT) DT[, b := 1] set(DT2,, "b", value = 1) all.equal(DT, DT2) # TRUE ` 一般來說都用'['來做,但是你如果需要用到for再來完成,再用set 還有一個function是 J,這裡就不提了,一樣請洽manual 最後,還有一個operator,':=',它是用來擴增data.table的column, 同樣,也不創造複本,這樣可以更快的增加column 那如果刪除怎麼辦?還記得前面學過 DT[, list('X', 'Y')],就用這個 再來,我們講一些data.table中其他function 2. fread 功能可以用來取代read.table, read.csv 它可以用多種separate去分割columns,然後讀入R 而且讀入速度比read.table, read.csv快很多 但是注意,不規則的檔案會讀入失敗 這裡提幾個參數: a. sep: column跟column之間的分隔,如果是csv就是',', 如果是tab separated values就是'\t' b. na.strings: 視作NA的字串,它可以是一個vector c. stringsAsFactors:是否要把字串轉成factor,預設是否 d. colClasses:各行的classes,可以自行設定 我愛用fread還有一個原因,第一個input可以直接放我要讀的字串, 但是read.table需要經過其他的方式,有點麻煩(我懶得記,其實沒記過) 舉例來說 ` R text = "a b 1 2 3 4" DT = fread(text) setDF(DT) # 轉成data.frame,前面學過,還記得嗎? DF = read.table(header = TRUE, text = text) # text format DF2 = read.table(textConnection(text), header = TRUE) # file format all.equal(DT, DF) # TRUE all.equal(DT, DF2) # TRUE ` fread很適合拿來讀大資料,所以有必要把table輸出成text 用文字方式處理時,讀入就變得很方便,可見 #1LegOjwB (R_Language) 3. dcast.data.table 這個function,需要先`require(reshape2)` 有人可能會問reshape2就有dcast為啥要用dcast.data.table 原因很簡單,因為dcast.data.table快更多!! 速度直接?dcast.data.table下面例子就有,直接來簡介怎麼用 第一個input是data.table,第二個是給一個公式 舉例來說,如果公式是 Y ~ X,Y的元素會展開在列,X就會在行 學過統計的話,應該是contingency table (列聯表) 或是熟悉EXCEL,知道樞紐分析表,它其實就是樞紐分析表 Y就是列聯表中的列變數,X就是行變數 製作列聯表也可以說它的應用之一 第三個input是加總函數,你如果有相同類別的X, Y 它會把相同類別的值用這個函數做加總,預設是length 先用一個簡單例子來說明 ` R set.seed(100) DT = expand.grid(LETTERS[1:2], LETTERS[3:4]) %>% data.table %>% setnames(c("col1","col2")) %>% rbind(., .) %>% '['(,values := rpois(8,2)) DT # col1 col2 values # 1: A C 1 # 2: B C 1 # 3: A D 2 # 4: B D 0 # 5: A C 2 # 6: B C 2 # 7: A D 3 # 8: B D 1 dcast.data.table(DT, col1~col2) # col1 C D # 1: A 2 2 # 2: B 2 2 dcast.data.table(DT, col1~col2, sum) # col1 C D # 1: A 3 5 # 2: B 3 1 ` 產生資料的函數、operator,我們都講過了,往前找找看 我們專注到第一個dcast,dcast.data.table(DT, col1~col2) 可以看的出來 col1就在列,col2就在行展開,然後計算col1, col2有相同類別的length 第二個dcast就是把有相同的類別,把values做總和 但是,我們怎麼知道它加總的是values 它會告訴你自動找尋data.table,然後選定values做為加總的column 至於改法就是修改value.var這個input,舉例來說 ` R DT[, values2 := rpois(8, 3)] dcast.data.table(DT, col1~col2, sum, value.var = "values") # col1 C D # 1: A 3 5 # 2: B 3 1 dcast.data.table(DT, col1~col2, sum, value.var = "values2") # col1 C D # 1: A 5 7 # 2: B 3 9 DT[, col3 := rep(LETTERS[5:6],,,4)] dcast.data.table(DT, col1+col2~col3, sum, value.var = "values") # col1 col2 E F # 1: A C 1 2 # 2: A D 2 3 # 3: B C 1 2 # 4: B D 0 1 ` dcast.data.table說明到此,我給兩個應用的例子 可以自己先練習一下,跟我對答案這樣 ` R ## example 1 # 我們有一個樣本,是統計每個人抽菸與否 以及 是否有得到肺癌 # 請幫忙製作列聯表 (抱歉,統計的最常用例子XDDDD) # data generation set.seed(100) n = 1e4 DT = data.table(smoke = rbinom(n, 1, 0.3), lungCancer = rbinom(n,1,0.05)) (table = dcast.data.table(DT, smoke ~ lungCancer)) # smoke 0 1 # 1: 0 6690 362 # 2: 1 2801 147 # 改成文字 DT %<>% transform(smoke = as.character(smoke), lungCancer = as.character(lungCancer)) DT[smoke == "0", smoke := "non-smoking"] DT[smoke == "1", smoke := "smoking"] DT[lungCancer == "0", lungCancer := "non-cancer"] DT[lungCancer == "1", lungCancer := "cancer"] (table = dcast.data.table(DT, smoke ~ lungCancer)) # smoke cancer non-cancer # 1: non-smoking 306 6739 # 2: smoking 145 2810 ## 備註更改部分可以用plyr:::mapvalues做,比較簡單 ## 用迴圈可以這樣做: set.seed(100) n = 1e4 DT2 = data.table(smoke = rbinom(n, 1, 0.3), lungCancer = rbinom(n,1,0.05)) (table = dcast.data.table(DT, smoke ~ lungCancer)) DT2 %<>% transform(smoke = as.character(smoke), lungCancer = as.character(lungCancer)) smoking = c("non-smoking", "smoking") cancer = c("non-cancer", "cancer") for (k in 0:1){ set(DT2, which(DT$smoke == as.character(k)), "smoke", value = smoking[k+1]) set(DT2, which(DT$lungCancer == as.character(k)), "lungCancer", value = cancer[k+1]) } all.equal(DT, DT2) # TRUE # example 2 # 我們有很多學生的成績資料,我們有學生的班級 # 我們想做一個簡單的表格去看各班的成績表現,成績以區間表示 # 像是這樣: # 班級1 班級2 班級3 # 1-10 # 11-20 # 21-30 n = 1e3 DT = data.table(class = paste0("class_", 1:3) %>% sample(n, TRUE), grades = sample(1:100, n, TRUE)) DT[, grade_group := cut(grades, c(0, seq(9, 89, by=10), 100))] (table = dcast.data.table(DT, grade_group ~ class, mean, value.var = "grades")) # grade_group class_1 class_2 class_3 # 1: (0,10] 29 28 33 # 2: (10,20] 31 37 30 # 3: (20,30] 36 44 45 # 4: (30,40] 38 31 38 # 5: (40,50] 39 33 31 # 6: (50,60] 37 32 32 # 7: (60,70] 35 26 25 # 8: (70,80] 31 32 32 # 9: (80,90] 40 26 26 # 10: (90,100] 32 35 36 # 把grade_group改一下 DT %<>% transform(grade_group = as.character(grade_group)) %>% '['(, lb := gsub("\\((\\d*),\\d*\\]", "\\1", grade_group)) %>% transform(lb = as.numeric(lb)) %>% transform(grade_group = paste(lb+1, lb+10, sep="-")) (table = dcast.data.table(DT, grade_group ~ class, length, value.var = "grades")) # grade_group class_1 class_2 class_3 # 1: 1-10 34 39 43 # 2: 11-20 30 39 27 # 3: 21-30 30 33 34 # 4: 31-40 34 36 38 # 5: 41-50 35 31 37 # 6: 51-60 23 26 29 # 7: 61-70 40 36 34 # 8: 71-80 22 34 44 # 9: 81-90 35 33 37 # 10: 91-100 30 26 31 ` 還剩下 melt 跟 merge,我們就留到下一章再繼續吧 下一章重點會放在tidyr跟dplyr [關鍵字]: data.table, reshape2 -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 123.205.27.107 ※ 文章網址: https://www.ptt.cc/bbs/R_Language/M.1437467101.A.E6D.html