[爆卦]r group by用法是什麼?優點缺點精華區懶人包

為什麼這篇r group by用法鄉民發文收入到精華區:因為在r group by用法這個討論話題中,有許多相關的文章在討論,這篇最有參考價值!作者celestialgod (攸藍)看板R_Language標題[心得] 資料整理套件介紹-第二章...



這章重點放在dplyr

plyr與dplyr有不少函數是重疊的

不過都會以dplyr為主

plyr跟dplyr有一些名字不同,但功能相似的函數

我會一併介紹

先列一下這章要介紹的函數 (沒標註的就是來自dplyr)

A. 基本整理的函數:arragnge, filter, mutate, select, group_by, summarise, n

B. 增併rownames為變數:add_rownames, plyr:::name_rows

C. list to data.frame:as_data_frame

D. by var 合併函數:join, plyr:::join, data.table:::merge, base:::merge

E. col/row 合併函數:bind_rows, data.table:::rbindlist, bind_cols

F. 取唯一列:distinct, data.table:::unique

G. 列行運算:rowwise, plyr:::colwise

H. 值映射(對應修改):plyr:::mapvalues, plyr:::revalue

I. 其他函數:summarise_each, mutate_each

J. 特殊函數:plyr:::here


1. 基本整理函數

arrange: 根據你選定的變數做排列 (可以是多個變數)

filter: 根據你設定的條件做row 篩選(or selection)

mutate: 根據你給定的值賦予新變數,或是變更舊變數

select: 根據給定的變數名稱做選擇,也可以做刪除變數

group_by: 根據給定變數做group,以銜接summarise

summarise: 資料整併

n: 計算資料個數


用一個簡單例子來展示用法:

` R
set.seed(100)
(dt = data.table(V1 = rpois(5, 3),
V2 = sample(c("g1", "g2"), 5, 1), V3 = rnorm(5)))
# V1 V2 V3
# 1: 2 g1 0.3186301
# 2: 2 g2 -0.5817907
# 3: 3 g1 0.7145327
# 4: 1 g2 -0.8252594
# 5: 3 g1 -0.3598621

dt %>% arrange(V1, V2, V3)
# V1 V2 V3
# 1: 1 g2 -0.8252594
# 2: 2 g1 0.3186301
# 3: 2 g2 -0.5817907
# 4: 3 g1 -0.3598621
# 5: 3 g1 0.7145327

dt %>% filter(V1 <= 2, V3 < 0)
# V1 V2 V3
# 1: 2 g2 -0.5817907
# 2: 1 g2 -0.8252594

dt %>% mutate(V5 = V1 * V3, V6 = substr(V2, 2, 2),
V7 = round(V3), V8 = 1L, V3 = V3 **2)
# V1 V2 V3 V5 V6 V7 V8
# 1: 2 g1 0.1015251 0.6372602 1 0 1
# 2: 2 g2 0.3384804 -1.1635814 2 -1 1
# 3: 3 g1 0.5105570 2.1435981 1 1 1
# 4: 1 g2 0.6810531 -0.8252594 2 -1 1
# 5: 3 g1 0.1295008 -1.0795864 1 0 1

dt %>% select(V1, V2)
# V1 V2
# 1: 2 g1
# 2: 2 g2
# 3: 3 g1
# 4: 1 g2
# 5: 3 g1

dt %>% group_by(V2) %>% summarise(size_g = n(), m_V3 = mean(V3),
s_V1 = sum(V1))
# V2 size_g m_V3 s_V1
# 1 g1 3 0.2244336 8
# 2 g2 2 -0.7035251 3
`

上面的例子是一些簡單運用的範例

先介紹一下tbl_df, tbl_dt的class

tbl_df跟tbl_dt只會列出一部分的資料

做操作時比較不會因為太多資料的輸出造成當機

要更改列出的資料量,可以這樣做

` R
set.seed(100)
(dt = data.table(V1 = rpois(50, 3), V2 = sample(c("g1", "g2"), 50, 1),
V3 = rnorm(50))) %>% tbl_dt(FALSE)
# V1 V2 V3
# 1 2 g1 -0.4470622
# 2 2 g1 -1.7385979
# 3 3 g1 0.1788648
# 4 1 g1 1.8974657
# 5 3 g2 -2.2719255
# 6 3 g1 0.9804641
# 7 4 g1 -1.3988256
# 8 2 g1 1.8248724
# 9 3 g2 1.3812987
# 10 1 g1 -0.8388519
# .. .. .. ...

print(dt, n = 5)
# V1 V2 V3
# 1 2 g1 -0.4470622
# 2 2 g1 -1.7385979
# 3 3 g1 0.1788648
# 4 1 g1 1.8974657
# 5 3 g2 -2.2719255
# .. .. .. ...
`

再介紹一些這些函數的其他用法

` R
dt = data.table(V1 = rpois(20, 3), V2 = sample(c("g1", "g2"), 20, 1),
V3 = rnorm(20), V4 = rgamma(20, 5, 3))
# 你可以直接用一個你想要使用的變數放入,不須先立變數
dt %>% arrange(V1*V3, V3)
dt %>% filter(abs(V1*V3) > 1)
# desc是dplyr的函數提供反向排列
dt %>% arrange(V1)
dt %>% arrange(desc(V1))
# 兩種做變數刪除的方式 (我偏好第二種)
dt %>% mutate(V4 = NULL)
dt %>% select(-V4)
# select 還有提供各種特別函數於select中使用
dt %>% select(starts_with("V"))
dt %>% select(ends_with("1"))
dt %>% select(contains("2"))
dt %>% select(matches("\\w\\d"))
dt %>% select(num_range("V", 1:2))
`

2. 增併rownames為變數

如標題所示,直接看範例

` R
dat = data.frame(A = 1:5, row.names = paste0("City_", LETTERS[1:5]))
dat %>% name_rows
dat %>% add_rownames
dat %>% add_rownames("city") # add_rownames可以改成你要的名稱
`
3. list to data.frame

as_data_frame提供比as.data.frame有效率的轉換方法

我之前也沒用過,不過看到manual寫到這個函數,就忍不住想分享一下

不過這個函數強迫list的element要有name,使用上要注意一下

` R
library(microbenchmark)
dat_list = lapply(rep(1e6, 200), rnorm)
names(dat_list) <- paste0("A", 1:200)
microbenchmark(
as_data_frame(dat_list),
as.data.frame(dat_list)
)
#Unit: milliseconds
# expr min lq mean median uq
# as_data_frame(dat_list) 1.22642 1.281156 1.418296 1.311944 1.339027
# as.data.frame(dat_list) 19.83196 20.199147 21.397833 20.350524 21.143335
# expr max neval
# as_data_frame(dat_list) 6.957693 100
# as.data.frame(dat_list) 33.307182 100
`
看起來是沒差很多啦(汗顏,可能資料不夠大

4. by var 合併函數

先介紹base的merge,這個函數是用來合併兩個data.frame

除了input的兩個data.frame,還有其他五個input (其他input之後再提)

a. by - 合併根據的變數
b. by.x - 合併根據的變數 於第一個data.frame的名稱
c. by.y - 合併根據的變數 於第二個data.frame的名稱
d. all.x - 是否保留來自第一個data.frame的values
e. all.y - 是否保留來自第一個data.frame的values

註:還有一個input是 all 可以一次控制all.x跟all.y

我用簡單的範例去介紹這幾個選項

` R
## 產生資料
set.seed(75)
(x = data.frame(cat1 = sample(c("A", "B", NA), 5, 1),
cat2 = sample(c(1, 2, NA), 5, 1), v = rpois(5, 3),
stringsAsFactors = FALSE))
# cat1 cat2 v
# 1 A 1 4
# 2 A 2 3
# 3 <NA> NA 4
# 4 B NA 4
# 5 A 1 4
(y = data.frame(cat1 = sample(c("A", "B", NA), 5, 1),
cat2 = sample(c(1, 2, NA), 5, 1), v = rpois(5, 3),
stringsAsFactors = FALSE))
# cat1 cat2 v
# 1 A 2 1
# 2 A 1 8
# 3 <NA> NA 2
# 4 B 2 5
# 5 <NA> 1 3

## 兩個data.frame的資料都不保留 (預設值)
merge(x, y, by = c("cat1","cat2"), all.x = FALSE, all.y = FALSE)
# cat1 cat2 v.x v.y
# 1 A 1 4 8
# 2 A 1 4 8
# 3 A 2 3 1
# 4 <NA> NA 4 2

## 保留第一個data.frame的全部資料
merge(x, y, by = c("cat1","cat2"), all.x = TRUE, all.y = FALSE)
# cat1 cat2 v.x v.y
# 1 A 1 4 8
# 2 A 1 4 8
# 3 A 2 3 1
# 4 B NA 4 NA
# 5 <NA> NA 4 2

## 保留第二個data.frame的全部資料
merge(x, y, by = c("cat1","cat2"), all.x = FALSE, all.y = TRUE)
# cat1 cat2 v.x v.y
# 1 A 1 4 8
# 2 A 1 4 8
# 3 A 2 3 1
# 4 B 2 NA 5
# 5 <NA> 1 NA 3
# 6 <NA> NA 4 2

## 保留兩個data.frame全部的資料
merge(x, y, by = c("cat1","cat2"), all.x = TRUE, all.y = TRUE)
# cat1 cat2 v.x v.y
# 1 A 1 4 8
# 2 A 1 4 8
# 3 A 2 3 1
# 4 B 2 NA 5
# 5 B NA 4 NA
# 6 <NA> 1 NA 3
# 7 <NA> NA 4 2
`

all.x跟all.y這四種組合分別對應到dplyr的四種join

a. inner_join - merge(..., all.x = FALSE, all.y = FALSE)
b. left_join - merge(..., all.x = TRUE , all.y = FALSE)
c. right_join - merge(..., all.x = FASLE, all.y = TRUE)
d. full_join - merge(..., all.x = TRUE , all.y = TRUE)

但是merge跟dplyr的join還是有些微不同

dplyr的join不會去比對by variable都是NA的情況

給一個例子就好

` R
inner_join(x, y, by = c("cat1","cat2"))
# cat1 cat2 v.x v.y
# 1 A 1 4 8
# 2 A 2 3 1
# 3 <NA> NA 4 2
# 4 A 1 4 8
`

PS: If you use dplyr 0.4.1, there is something wrong. You're gonna find the

output do not contain the line: 3 <NA> NA 4 2. Please update your dplyr

to 0.4.2 or higher version.


至於plyr:::join就沒有這個問題

` R
join(x, y, by = c("cat1","cat2"), 'inner')
# cat1 cat2 v v
# 1 A 1 4 8
# 2 A 2 3 1
# 3 <NA> NA 4 2
# 4 A 1 4 8
`
plyr:::join用法其實大同小異,它是用type去控制join方式

最後是data.table:::merge

` R
setDT(x)
setDT(y)
merge(x, y, by = c("cat1","cat2"))
# cat1 cat2 v.x v.y
# 1: NA NA 4 2
# 2: A 1 4 8
# 3: A 1 4 8
# 4: A 2 3 1
`
其實用法跟merge一模一樣,不贅述


介紹完by, all.x, 跟all.y之後,我們來介紹by.x跟by.y

用一個簡單例子:

` R
set.seed(75)
x = data.frame(cat1 = sample(c("A", "B", NA), 5, 1),
cat2 = sample(c(1, 2, NA), 5, 1), v = rpois(5, 3),
stringsAsFactors = FALSE)
y = data.frame(cat3 = sample(c("A", "B", NA), 5, 1),
cat4 = sample(c(1, 2, NA), 5, 1), v = rpois(5, 3),
stringsAsFactors = FALSE)
merge(x, y, by.x = c("cat1","cat2"), by.y = c("cat3","cat4"))
# cat1 cat2 v.x v.y
# 1 A 1 4 8
# 2 A 1 4 8
# 3 A 2 3 1
# 4 <NA> NA 4 2
`
我想這個例子已經很好說明了by.x跟by.y了

接著是再dplyr怎麼做?

` R
inner_join(x, y, by = c("cat1" = "cat3", "cat2" = "cat4"))
# cat1 cat2 v.x v.y
# 1 A 1 4 8
# 2 A 2 3 1
# 3 A 1 4 8
`

至於plyr:::join跟data.table:::merge就沒有支援這種功能了


dplyr還提供兩種join: semi_join跟anti_join

簡單說明一下,semi_join就是只保留第一個data.frame變數的inner_join

anti_join則semi_join沒有配對的組合

這兩個有興趣再去玩玩看,這裡就不提供例子了


最後是一個實際問題

我如果要merge超過三個的df怎麼辦?

可以參考一下 #1LaHm_aH (R_Language)

這裡完整介紹一下使用這幾個套件要怎麼解決

` R
DF_list = replicate(5, data.frame(cat1 = sample(c("A", "B"), 5, 1),
cat2 = sample(c(1, 2), 5, 1), v = rnorm(5)), simplify = FALSE)
# 下列兩種會變成橫表,每一個data.frame的v都會保留
Reduce(function(x, y) merge(x, y, by = c("cat1","cat2"), all=TRUE), DF_list)
Reduce(function(x, y) full_join(x, y, by = c("cat1","cat2")), DF_list)
# 只保留第一個data.frame的值
join_all(DF_list, by = c("cat1","cat2"), type = "full")
# 直表,保留全部的v,等同於全部做rbind
join_all(DF_list, by = c("cat1","cat2", "v"), type = "full")
`

其實這樣每一個方法的結果都會很混亂,非常不建議,除非你知道你目標是什麼


5. col/row 合併函數

bind_rows跟rbindlist其實就是在做 do.call(rbind, .)或是 Reduce(rbind, .)

只是這兩個function更加有效率

如果還不懂do.call(rbind, .)跟Reduce(rbind, .)再做什麼

剛好可以利用這個機會去弄懂他們在幹嘛

` R
DF_list = replicate(5, data.frame(cat1 = sample(c("A", "B"), 5, 1),
cat2 = sample(c(1, 2), 5, 1), v = rnorm(5)), simplify = FALSE)
bind_rows(DF_list)
rbindlist(DF_list)
`

bind_cols等同於 do.call(cbind, .)

`
DT_list = lapply(1:5, function(x) data.table(rnorm(5)) %>%
setnames(paste0("V", x)))
bind_cols(DT_list)
`

還有一半,我們留到下一章再討論。

[關鍵字]: dplyr

--
※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 123.205.27.107
※ 文章網址: https://www.ptt.cc/bbs/R_Language/M.1437545676.A.A4B.html
MADNUG: 第一章還沒研究完,第二章又出來了>"< 感謝推推 07/22 14:48
這種東西越拖越不想寫啊,想說早點寫完XDDD

第三章可能要拖到晚上了,現在有點累
kokolotl: 感謝~~ 07/22 22:12
squallscer: 推實用!dplyr愛好者+1 07/22 23:07
※ 編輯: celestialgod (123.205.27.107), 08/12/2015 21:07:23

你可能也想看看

搜尋相關網站