【R語言】必學包之dplyr包
轉自:https://blog.csdn.net/wltom1985/article/details/54973811
R包dplyr可用於處理R內部或者外部的結構化數據,相較於plyr包,dplyr專注接受dataframe對象, 大幅提高了速度,並且提供了更穩健的數據庫接口。同時,dplyr包可用於操作Spark的dataframe。本文只是基礎的dplyr包學習筆記,所以並不會討論一些高級應用,或者與data.table包的性能比較。
1. 數據集類型轉換
tbl_df()可用於將過長過大的數據集轉換爲顯示更友好的 tbl_df 類型。使用dplyr包處理數據前,建議先將數據集轉換爲tbl對象。
語法 : tbl_df(data)
舉例 1:
- #data.frame類型數據集
- class(mtcars)
- #轉換爲tbl_df類型
- ds <- tbl_df(mtcars)
- #轉換爲data.frame類型
- df <- as.data.frame(ds)
filter() 和slice()函數可以按給定的邏輯條件篩選出符合要求的子數據集, 類似於 base::subset() 函數,但代碼更加簡潔, 同時也支持對同一對象的任意個條件組合(表示AND時要使用&或者直接使用逗號),返回與.data相同類型的對象。原數據集行名稱會被過濾掉。
語法 : filter(.data, ...)
舉例 1:
- #過濾出cyl == 8的行
- filter(mtcars, cyl == 8)
- filter(mtcars, cyl < 6)
- #過濾出cyl < 6 並且 vs == 1的行
- filter(mtcars, cyl < 6 & vs == 1)
- filter(mtcars, cyl < 6, vs == 1)
- #過濾出cyl < 6 或者 vs == 1的行
- filter(mtcars, cyl < 6 | vs == 1)
- #過濾出cyl 爲4或6的行
- filter(mtcars, cyl %in% c(4, 6))
slice() 函數通過行號選取數據。
舉例 2:
- #選取第一行數據
- slice(mtcars, 1L)
- filter(mtcars, row_number() == 1L)
- #選取最後一行數據
- slice(mtcars, n())
- filter(mtcars, row_number() == n())
- #選取第5行到最後一行所有數據
- slice(mtcars, 5:n())
- filter(mtcars, between(row_number(), 5, n()))
arrange()按給定的列名依次對行進行排序,類似於base::order()函數。默認是按照升序排序,對列名加 desc() 可實現倒序排序。原數據集行名稱會被過濾掉。
語法 : arrange(.data, ...)
舉例1:
- #以cyl和disp聯合升序排序
- arrange(mtcars, cyl, disp)
- #以disp降序排序
- arrange(mtcars, desc(disp))
select()用列名作參數來選擇子數據集。dplyr包中提供了些特殊功能的函數與select函數結合使用, 用於篩選變量,包括starts_with,ends_with,contains,matches,one_of,num_range和everything等。用於重命名時,select()只保留參數中給定的列,rename()保留所有的列,只對給定的列重新命名。原數據集行名稱會被過濾掉。
語法 : select(.data, ...)
舉例 1:
- iris <- tbl_df(iris)
- #選取變量名前綴包含Petal的列
- select(iris, starts_with("Petal"))
- #選取變量名前綴不包含Petal的列
- select(iris, -starts_with("Petal"))
- #選取變量名後綴包含Width的列
- select(iris, ends_with("Width"))
- #選取變量名後綴不包含Width的列
- select(iris, -ends_with("Width"))
- #選取變量名中包含etal的列
- select(iris, contains("etal"))
- #選取變量名中不包含etal的列
- select(iris, -contains("etal"))
- #正則表達式匹配,返回變量名中包含t的列
- select(iris, matches(".t."))
- #正則表達式匹配,返回變量名中不包含t的列
- select(iris, -matches(".t."))
- #直接選取列
- select(iris, Petal.Length, Petal.Width)
- #返回除Petal.Length和Petal.Width之外的所有列
- select(iris, -Petal.Length, -Petal.Width)
- #使用冒號連接列名,選擇多個列
- select(iris, Sepal.Length:Petal.Width)
- #選擇字符向量中的列,select中不能直接使用字符向量篩選,需要使用one_of函數
- vars <- c("Petal.Length", "Petal.Width")
- select(iris, one_of(vars))
- #返回指定字符向量之外的列
- select(iris, -one_of(vars))
- #返回所有列,一般調整數據集中變量順序時使用
- select(iris, everything())
- #調整列順序,把Species列放到最前面
- select(iris, Species, everything())
- df <- as.data.frame(matrix(runif(100), nrow = 10))
- df <- tbl_df(df[c(3, 4, 7, 1, 9, 8, 5, 2, 6, 10)])
- #選擇V4,V5,V6三列
- select(df, V4:V6)
- select(df, num_range("V", 4:6))
舉例 3:
- #重命名列Petal.Length,返回子數據集只包含重命名的列
- select(iris, petal_length = Petal.Length)
- #重命名所有以Petal爲前綴的列,返回子數據集只包含重命名的列
- select(iris, petal = starts_with("Petal"))
- #重命名列Petal.Length,返回全部列
- rename(iris, petal_length = Petal.Length)
mutate()和transmute()函數對已有列進行數據運算並添加爲新列,類似於base::transform() 函數, 不同的是可以在同一語句中對剛增添加的列進行操作。mutate()返回的結果集會保留原有變量,transmute()只返回擴展的新變量。原數據集行名稱會被過濾掉。
語法 :mutate(.data, ...) transmute(.data, ...)
舉例 1:
- #添加新列wt_kg和wt_t,在同一語句中可以使用剛添加的列
- mutate(mtcars, wt_kg = wt * 453.592, wt_t = wt_kg / 1000)
- #計算新列wt_kg和wt_t,返回對象中只包含新列
- transmute(mtcars, wt_kg = wt * 453.592, wt_t = wt_kg / 1000)
distinct()用於對輸入的tbl進行去重,返回無重複的行,類似於 base::unique() 函數,但是處理速度更快。原數據集行名稱會被過濾掉。
語法 :distinct(.data, ..., .keep_all = FALSE)
舉例 1:
- df <- data.frame(
- x = sample(10, 100, rep = TRUE),
- y = sample(10, 100, rep = TRUE)
- )
- #以全部兩個變量去重,返回去重後的行數
- nrow(distinct(df))
- nrow(distinct(df, x, y))
- #以變量x去重,只返回去重後的x值
- distinct(df, x)
- #以變量y去重,只返回去重後的y值
- distinct(df, y)
- #以變量x去重,返回所有變量
- distinct(df, x, .keep_all = TRUE)
- #以變量y去重,返回所有變量
- distinct(df, y, .keep_all = TRUE)
- #對變量運算後的結果去重
- distinct(df, diff = abs(x - y))
對數據框調用函數進行彙總操作, 返回一維的結果。返回多維結果時會報如下錯誤:
Error: expecting result of length one, got : 2
原數據集行名稱會被過濾掉。
語法 :summarise(.data, ...)
- #返回數據框中變量disp的均值
- summarise(mtcars, mean(disp))
- #返回數據框中變量disp的標準差
- summarise(mtcars, sd(disp))
- #返回數據框中變量disp的最大值及最小值
- summarise(mtcars, max(disp), min(disp))
- #返回數據框mtcars的行數
- summarise(mtcars, n())
- #返回unique的gear數
- summarise(mtcars, n_distinct(gear))
- #返回disp的第一個值
- summarise(mtcars, first(disp))
- #返回disp的最後個值
- summarise(mtcars, last(disp))
抽樣函數,sample_n()隨機抽取指定數目的樣本,sample_frac()隨機抽取指定百分比的樣本,默認都爲不放回抽樣,通過設置replacement = TRUE可改爲放回抽樣,可以用於實現Bootstrap抽樣。
語法 :sample_n(tbl, size, replace = FALSE, weight = NULL, .env = parent.frame())
舉例 1:
- #隨機無重複的取10行數據
- sample_n(mtcars, 10)
- #隨機有重複的取50行數據
- sample_n(mtcars, 50, replace = TRUE)
- #隨機無重複的以mpg值做權重取10行數據
- sample_n(mtcars, 10, weight = mpg)
舉例 2:
- #默認size=1,相當於對全部數據無重複重新抽樣
- sample_frac(mtcars)
- #隨機無重複的取10%的數據
- sample_frac(mtcars, 0.1)
- #隨機有重複的取總行數1.5倍的數據
- sample_frac(mtcars, 1.5, replace = TRUE)
- #隨機無重複的以1/mpg值做權重取10%的數據
- sample_frac(mtcars, 0.1, weight = 1 / mpg)
9. 分組: group
group_by()用於對數據集按照給定變量分組,返回分組後的數據集。對返回後的數據集使用以上介紹的函數時,會自動的對分組數據操作。
語法 :group_by(.data, ..., add = FALSE)
舉例 1:
- #使用變量cyl對mtcars分組,返回分組後數據集
- by_cyl <- group_by(mtcars, cyl)
- #返回每個分組中最大disp所在的行
- filter(by_cyl, disp == max(disp))
- #返回每個分組中變量名包含d的列,始終返回分組列cyl
- select(by_cyl, contains("d"))
- #使用mpg對每個分組排序
- arrange(by_cyl, mpg)
- #對每個分組無重複的取2行記錄
- sample_n(by_cyl, 2)
- #使用變量cyl對mtcars分組,然後對分組後數據集使用聚合函數
- by_cyl <- group_by(mtcars, cyl)
- #返回每個分組的記錄數
- summarise(by_cyl, n())
- #求每個分組中disp和hp的均值
- summarise(by_cyl, mean(disp), mean(hp))
- #返回每個分組中唯一的gear的值
- summarise(by_cyl, n_distinct(gear))
- #返回每個分組第一個和最後一個disp值
- summarise(by_cyl, first(disp))
- summarise(by_cyl, last(disp))
- #返回每個分組中最小的disp值
- summarise(by_cyl, min(disp))
- summarise(arrange(by_cyl, disp), min(disp))
- #返回每個分組中最大的disp值
- summarise(by_cyl, max(disp))
- summarise(arrange(by_cyl, disp), max(disp))
- #返回每個分組中disp第二個值
- summarise(by_cyl, nth(disp,2))
- #使用cyl對數據框分組
- grouped <- group_by(mtcars, cyl)
- #獲取分組數據集所使用的分組變量
- groups(grouped)
- #ungroup從數據框中移除組合信息,因此返回的分組變量爲NULL
- groups(ungroup(grouped))
返回分組後,每條記錄的分組id。
舉例 4:
- #返回每條記錄所在分組id組成的向量
- group_indices(mtcars, cyl)
n_groups(x)
group_size用於返回每個分組的記錄數,n_groups返回分成的組數。
舉例 5:
- by_cyl <- group_by(mtcars, cyl)
- #返回每個分組記錄數組成的向量
- group_size(by_cyl)
- summarise(by_cyl, n())
- table(mtcars$cyl)
- #返回所分的組數
- n_groups(by_cyl)
- length(group_size(by_cyl))
對數據集的每個分組計數,類似於base:: table()函數。其中count已經過group_by分組,而tally需要對數據集調用group_by後對分組數據計數。
語法 :tally(x, wt, sort = FALSE)
count(x, ..., wt =NULL, sort = FALSE)
舉例 6:
- #使用count對分組計數,數據已按變量分組
- count(mtcars, cyl)
- #設置sort=TRUE,對分組計數按降序排序
- count(mtcars, cyl, sort = TRUE)
- #使用tally對分組計數,需要使用group_by分組
- tally(group_by(mtcars, cyl))
- #使用summarise對分組計數
- summarise(group_by(mtcars, cyl), n())
- #按cyl分組,並對分組數據計算變量的gear的和
- count(mtcars, cyl, wt = gear)
- tally(group_by(mtcars, cyl), wt = gear)
數據框中經常需要將多個表進行連接操作, 如左連接、右連接、內連接等,dplyr包也提供了數據集的連接操作,類似於 base::merge() 函數。語法如下:
#內連接,合併數據僅保留匹配的記錄
inner_join(x,y, by = NULL, copy = FALSE, suffix = c(".x", ".y"), ...)
#左連接,向數據集x中加入匹配的數據集y記錄
left_join(x,y, by = NULL, copy = FALSE, suffix = c(".x", ".y"), ...)
#右連接,向數據集y中加入匹配的數據集x記錄
right_join(x,y, by = NULL, copy = FALSE, suffix = c(".x", ".y"), ...)
#全連接,合併數據保留所有記錄,所有行
full_join(x,y, by = NULL, copy = FALSE, suffix = c(".x", ".y"), ...)
#返回能夠與y表匹配的x表所有記錄
semi_join(x,y, by = NULL, copy = FALSE, ...)
#返回無法與y表匹配的x表的所有記錄
anti_join(x, y, by = NULL, copy = FALSE, ...) by設置兩個數據集用於匹配的字段名,默認使用全部同名字段進行匹配,如果兩個數據集需要匹配的字段名不同,可以直接用等號指定匹配的字段名,如, by = c("a" = "b"),表示用x.a和y.b進行匹配。如果兩個數據集來自不同的數據源,copy設置爲TRUE時,會把數據集y的數據複製到數據集x中,出於性能上的考慮,需要謹慎設置copy參數爲TRUE。合併後的數據集中同名變量,會自動添加suffix中設置的後綴加以區分。
舉例 1:
- df1 = data.frame(CustomerId=c(1:6), sex = c("f", "m", "f", "f", "m", "m"), Product=c(rep("Toaster",3), rep("Radio",3)))
- df2 = data.frame(CustomerId=c(2,4,6,7),sex = c( "m", "f", "m", "f"), State=c(rep("Alabama",3), rep("Ohio",1)))
- #內連接,默認使用"CustomerId"和"sex"連接
- inner_join(df1, df2)
- #左連接,默認使用"CustomerId"和"sex"連接
- left_join(df1, df2)
- #右連接,默認使用"CustomerId"和"sex"連接
- right_join(df1, df2)
- #全連接,默認使用"CustomerId"和"sex"連接
- full_join(df1, df2)
- #內連接,使用"CustomerId"連接,同名字段sex會自動添加後綴
- inner_join(df1, df2, by = c("CustomerId" = "CustomerId"))
- #以CustomerId連接,返回df1中與df2匹配的記錄
- semi_join(df1, df2, by = c("CustomerId" = "CustomerId"))
- #以CustomerId和sex連接,返回df1中與df2不匹配的記錄
- anti_join(df1, df2)
dplyr也提供了集合操作函數,實際上是對base包中的集合操作的重寫,但是對數據框和其它表格形式的數據操作更加高效。語法如下:
#取兩個集合的交集
intersect(x,y, ...)
#取兩個集合的並集,並進行去重
union(x,y, ...)
#取兩個集合的並集,不去重
union_all(x,y, ...)
#取兩個集合的差集
setdiff(x,y, ...)
#判斷兩個集合是否相等
setequal(x, y, ...) 舉例 1:
- mtcars$model <- rownames(mtcars)
- first <- mtcars[1:20, ]
- second <- mtcars[10:32, ]
- #取兩個集合的交集
- intersect(first, second)
- #取兩個集合的並集,並去重
- union(first, second)
- #取兩個集合的差集,返回first中存在但second中不存在的記錄
- setdiff(first, second)
- #取兩個集合的交集,返回second中存在但first中不存在的記錄
- setdiff(second, first)
- #取兩個集合的交集, 不去重
- union_all(first, second)
- #判斷兩個集合是否相等,返回TRUE
- setequal(mtcars, mtcars[32:1, ])
12. 數據合併: bind
dplyr包中也提供了按行/列合併數據集的函數,合併的對象爲數據框,也可以是能夠轉換爲數據框的列表。按行合併函數bind_rows()通過列名進行匹配,不匹配的值使用NA替代,類似於base:: rbind()函數。按列合併函數bind_cols()通過行號匹配,因此合併的數據框必須有相同的行數,函數類似於base:: cbind()函數。原數據集行名稱會被過濾掉。語法如下:#按行合併,.id添加新列用於指明合併後每條數據來自的源數據框
bind_rows(...,.id = NULL)
#按列合併
bind_cols(...)
#合併數據集
combine(...) 舉例 1:
- one <- mtcars[1:4, ]
- two <- mtcars[11:14, ]
- #按行合併數據框one和two
- bind_rows(one, two)
- #按行合併元素爲數據框的列表
- bind_rows(list(one, two))
- #按行合併數據框,生成id列指明數據來自的源數據框,id列的值使用數字代替
- bind_rows(list(one, two), .id = "id")
- #按行合併數據框,生成id列指明數據來自的源數據框,id列的值爲數據框名
- bind_rows(list(a = one, b = two), .id = "id")
- #按列合併數據框one和two
- bind_cols(one, two)
- bind_cols(list(one, two))
- #合併數據框,列名不匹配,因此使用NA替代,使用rbind直接報錯
- bind_rows(data.frame(x = 1:3), data.frame(y = 1:4))
- #合併因子
- f1 <- factor("a")
- f2 <- factor("b")
- c(f1, f2)
- unlist(list(f1, f2))
- #因子level不同,強制轉換爲字符型
- combine(f1, f2)
- combine(list(f1, f2))
dplyr包也提供了更加嚴格的條件操作語句,if_else函數類似於base::ifelse(),不同的是true和false對應的值必須要有相同的類型,這樣使得輸出類型更容易預測,因此相對而言執行效率更高。
語法 :if_else(condition,true, false, missing = NULL)
missing值用於替代缺失值。
舉例 1:
- x <- c(-5:5, NA)
- #替換所有小於0的元素爲NA,爲了保持類型一致,因此使用NA_integer_
- if_else(x < 0, NA_integer_, x)
- #使用字符串missing替換原數據中的NA元素
- if_else(x < 0, "negative", "positive", "missing")
- #if_else不支持類型不一致,但是ifelse可以
- ifelse(x < 0, "negative", 1)
- x <- factor(sample(letters[1:5], 10, replace = TRUE))
- #if_else會保留原有數據類型
- if_else(x %in% c("a", "b", "c"), x, factor(NA))
- ifelse(x %in% c("a", "b", "c"), x, factor(NA))
case_when語句類似於if/else語句。表達式使用“~”連接,左值LHS爲條件語句用於判斷滿足條件的元素,右值爲具有相同類型的替換值,用於替換滿足條件的元素。
語法 :case_when(...)
舉例 3:
- #順序執行各語句對原向量進行替換,因此越普遍的條件需放在最後
- x <- 1:50
- case_when(
- x %% 35 == 0 ~ "fizz buzz",
- x %% 5 == 0 ~ "fizz",
- x %% 7 == 0 ~ "buzz",
- TRUE ~ as.character(x)
- )
dplyr也提供了對數據庫的連接和操作函數,目前僅支持sqlite, mysql,postgresql以及google bigquery。dplyr可把R代碼自動轉換爲SQL語句,然後在數據庫上執行以獲取數據。實際的處理過程中,所有的R代碼並不是立即執行,而是在實際獲取數據的時候,一次性在數據庫中執行。下面以sqlite數據庫爲例。
創建和連接數據庫: src_sqlite(path, create = FALSE)
當create爲FALSE(默認),path必須爲已存在的數據庫路徑和全名,爲TRUE,會根據設置的path創建sqlite數據庫。
舉例 1:
- #在默認工作路勁下創建sqlite數據庫
- my_db <- src_sqlite("dplyrdb.db", create = TRUE)
- <span style="font-size:13.3333px;"><span style="font-family:'Courier New';background-color:rgb(255,255,255);"> </span><span style="background-color:rgb(204,204,204);">列出數據源x中所有的表</span></span><span style="background-color:rgb(204,204,204);font-family:'Courier New';font-size:13.3333px;">:<span style="font-size:13.3333px;">src_tbls(x)</span></span>
舉例 2:
- #目前數據庫中還沒有表
- src_tbls(my_db)
導入數據到創建的數據庫中並創建相應的表,如果未給出表名則使用傳入的data frame名稱,導入時可以通過indexes參數給創建的表添加索引, copy_to同時會執行ANALYZE命令以保證表具有最新的統計信息並且執行相應的查詢優化。
導入數據到遠程數據源:copy_to(dest, df, name =deparse(substitute(df)), temporary, indexes,...)
舉例 3:
- library(nycflights13)
- #導入flights數據到數據庫中,並創建相應的索引
- flights_sqlite <- copy_to(my_db, flights, temporary = FALSE, indexes = list(c("year", "month", "day"), "carrier", "tailnum"))
- #已存在表flights
- src_tbls(my_db)
tbl可用於與源數據源(src)中的數據(from)建立連接,from可以是表名或者是SQL語句返回的數據。
與數據庫建立連接: tbl(src, from, ...)
舉例 4:
- #查詢數據庫中表數據,直接給出表名
- tb.flight <- tbl(my_db, 'flights')
- #查詢數據庫中表數據,使用SQL語句返回數據
- tb.flight2 <- tbl(my_db, sql("SELECT * FROM flights"))
- #操作數據庫中數據,語句並沒有被實際執行,只有顯式獲取數據時纔會執行
- c1 <- filter(tb.flight, year == 2013, month == 1, day == 1)
- c2 <- select(c1, year, month, day, carrier, dep_delay, air_time, distance)
- c3 <- mutate(c2, speed = distance / air_time * 60)
- c4 <- arrange(c3, year, month, day, carrier)
在未顯式獲取數據時,所有的操作只是生成tbl_sql對象,可以通過以下操作獲取返回相應的SQL語句以及執行計劃。
語法: show_query(x)
explain(x, ...)
舉例 6:
- #返回對象c4對應的SQL語句
- show_query(c4)
- #返回對象c4對應的SQL語句以及執行計劃
- explain(c4)
對於lazy操作的這種機制,數據操作實際並沒有真正的執行查詢,如果需要返回數據結果,可以用以下的函數強制執行查詢並返回結果。
#強制執行查詢,並返回tbl_df對象到R
collect(x, ...)
#強制執行查詢,並在源數據庫中創建臨時表存儲結果
compute(x, name = random_table_name(),temporary = TRUE,
unique_indexes = list(), indexes = list(),...)
#不強制執行查詢,拆分複雜的tbl對象,以便添加額外的約束
collapse(x, ...) 舉例 7:
- #執行c4查詢,返回對象到R
- tbl_dfight <- collect(c4)
- #執行查詢並在數據庫中創建臨時表,通過src_tbls可查詢到新建的temp表
- compute(c4, name = 'temp_flights')
- src_tbls(my_db)
- #實際並沒有執行查詢,仍可用show_query返回對應的SQL語句
- remote <- collapse(c4)
- show_query(remote)