R數據處理|基礎篇(二)

先用幾個問題檢驗一下你是否需要看這篇文章

  • dplyr包中如何批量篩選變數做匯總計算,知不知道有summarise_all at 這類函數
  • reshape2包中的融合重鑄和分組計算有什麼關聯
  • tidyr 包的使用
  • Hadley 大神

本文介紹數據處理上的其他方面,和上一篇文章合在一起就可以組成處理數據的一個完整的系統。

本文目錄如下

  • 數據框合併
    • 拼接合併
    • merge合併
  • 計算並增加行列
  • 匯總計算
  • 分組計算
  • 融合重鑄
  • 融合重鑄的應用
  • 拆分合併列

載入包

library(dplyr) # 高速處理數據,取代R自帶的一些函數,代碼簡單易記library(tidyr) # 提供一些其他功能library(reshape2)

本文使用這三個包較多,如果對這幾個包不再了解,建議先看一看

dplyr包5個主要函數

tidyr包中四個主要函數

reshape2包中的融合-重鑄兩個函數(或R語言實戰書)

數據框合併

拼接合併

列相同的兩個數據框可以直接縱向接在一起,行相同的兩個數據框也可以橫向接在一起。

name1 <- c("Bob","Mary","Jane","Kim")name2 <- c("Bob","Mary","Kim","Jane")weight <- c(60,65,45,55)height <- c(170,165,140,135)birth <- c("1990-1","1980-2","1995-5","1996-4")accept <- c("no","ok","ok","no")df1 <- data.frame(name1,weight,height)df2 <- data.frame(name2,birth,accept)# 合併rbind(df1,df1[c(2,4),]) # 行合併,需要列數、列名相同cbind(df1,df2) # 列合併,需要行數相同data.frame(df1,df2) # 建立數據框,達到相同的效果# dplyr包中高效合併bind_rows(df1,df1[c(2,4),]) bind_cols(df1,df2)

merge合併

當兩個數據框中有一列表示相同的含義,其他列不同時,可以按照這一列merge起來。

比如拿到兩張表,分別對應兩個數據框,一個記錄了公司每個員工的體重,一個記錄了公司每個員工的身高,他們有相同的姓名一列,我們就可以把兩個數據框融合成為一個帶有姓名、身高、體重的數據框。

有的人可能會問,為什麼不直接橫向拼在一起,再把相同的名字列去掉?是因為如果兩個數據框的名字順序不一樣,或者名字不是完全相同,這樣做就不行了。我們來看下面的例子

先創建數據框

name1 <- c("Bob","Mary","Jane","Kim","Smith")weight <- c(60,65,45,55,60)name2 <- c("Bob","Mary","Kim","Jane","Eric")height <- c(170,165,140,135,170)df11 <- data.frame(name1,weight,stringsAsFactors=F) # 加這個參數是防止字元串自動轉化為因子df33 <- data.frame(name1,height,stringsAsFactors=F)df22 <- data.frame(name2,height,stringsAsFactors=F) # 成員與前面不完全一樣

先使用基礎的merge函數

merge(df11,df33) # 自動根據相同的列名匹配merge(df11,df22,by.x="name1",by.y="name2") # 沒有相同的列名則指定根據這兩列融合# 上面默認保留了df11和df22共有的行merge(df11,df22,by.x="name1",by.y="name2",all=T) # 保留所有出現過的行,沒有的顯示NAmerge(df11,df22,by.x="name1",by.y="name2",all.x=T) # 保留所有x的行merge(df11,df22,by.x="name1",by.y="name2",all.y=T) # 保留所有y的行

下面使用dplyr包

inner_join(df11,df33) # 自動根據相同的列名匹配 full_join(df11,df22,by=c("name1"="name2"))left_join(df11,df22,by=c("name1"="name2")) # 只保留前者的行right_join(df11,df22,by=c("name1"="name2")) # 只保留後者的行semi_join(df11,df22,by=c("name1"="name2")) # 保留共有的行,同時只返回前者的列anti_join(df11,df22,by=c("name1"="name2")) # 返回後者沒有的前者的行,同時只返回前者的列

計算並增加行列

創建數據框

name1 <- c("Bob","Mary","Jane","Kim")weight <- c(60,65,45,55)height <- c(170,165,140,135)df1 <- data.frame(name1,weight,height)

R基礎函數

df2 <- transform(df1,BMI=weight/height^2) # 第一種方法df2df1$BMI <- df1$weight/df1$height^2 # 第二種方法, 每一步都要$,很麻煩df1

使用dplyr包中的函數

mutate(df1,BMI=weight/height^2)

匯總計算

apply(df1[,-1],2,mean) # R基礎函數

dplyr包中的summarise系列函數

summarise(df1,arv_weight=mean(weight),arv_height=mean(height))

上面這種方式是一般人都會用的,但是如果沒有看過dplyr包的文檔,就不知道還有summarise_all等函數。當需要對每列都進行計算時,或者選擇某一些列計算,只是summarise一個一個指定就會非常麻煩。下面我們介紹一些批量篩選計算的函數

為了更好地說明問題,我們改一下數據框

name1 <- c("Bob","Mary","Jane","Kim")weight <- c(60,65,45,55)height <- c(170,165,140,135)weta <- 1:4df1 <- data.frame(name1,weight,height,weta)

summarise_all 和 summarise_if

對所有列進行計算或者根據列的數據類型選擇計算

summarise(df1,avg_weight=mean(weight),avg_height=mean(height)) # 很麻煩地每個都指定summarise_all(df1[-1],mean) # 對選出來的所有列都進行計算summarise_if(df1,is.numeric,mean) # 檢驗出所有是數值的列,全部求均值

summarise_at配合vars的用法

篩選出符合條件的列名對應的列

summarise_at(df1,vars(weight,height,weta),mean) # 配合vars函數,一次選擇多列summarise_at(df1,vars(weight:weta),mean) # 從哪到哪u <- c("weight","height")summarise_at(df1,vars(one_of(u)),mean) # 可以接字元串向量summarise_at(df1,u,mean) # 也可以直接接字元串向量summarise_at(df1,u,mean,trim=1) # mean的參數可以接在後面summarise_at(df1,vars(contains("eig")),mean) # 匹配含有的summarise_at(df1,vars(matches(".t.")),mean) # 使用正則表達式summarise_at(df1,vars(starts_with("w")),mean) # 匹配以此為開頭的summarise_at(df1,vars(ends_with("ht")),mean) # 匹配以此為結尾的summarise_at(df1[,-1],vars(everything()),mean) # 選擇所有列

funs的用法

summarise_all(df1[,-1],funs(mean,sum))summarise_all(df1[,-1],funs(sum(.*2))) # 數據用.表示summarise_all(df1[,-1],funs(medi=median)) # 指定得到的列後綴名summarise_all(df1[,-1],funs("in"=median)) # 或者加引號mutate_all(df1[,-1],funs(.^2))

結合使用

summarise_if(df1,is.numeric,funs(mean,sum))summarise_at(df1,vars(ends_with("t")),funs(mean,sum))

分組計算

首先創建我們需要的數據集

name1 <- c("Bob","Mary","Jane","Kim")weight <- c(60,65,45,55)height <- c(170,165,140,135)accept <- c("no","ok","ok","no")df <- data.frame(name1,weight,height,accept)

R基礎函數

tapply(df$height,df$accept,mean) # 使用tapply函數,按照accept分類with(df,{ # 使用aggregate函數 aggregate(height,by=list(accept),FUN=mean)})

使用dplyr包中的函數

group_df <- group_by(df,accept)summarise(group_df,arv_height=mean(height),count=n()) # 其中n()是查數的意思# 使用擴展函數summarise_all(group_df[,-1],mean)summarise_if(group_df,is.numeric,mean)summarise_at(group_df,vars(contains("eigh")),funs(sum,mean))

dplyr包中的主要功能我們就講到這裡,除了以上功能,這個包還改寫了很多基礎函數,使其使用更方便,運行速度更快,感興趣的讀者可以查看幫助文檔自行學習。

融合重鑄

說到融合重鑄,就不得不使用這張經典的圖(來自《R語言實戰》),這張圖清晰的展現了融合重鑄到底做了什麼

這裡我們使用reshape2包和tidyr包分別完成融合重鑄功能。

reshape2

上面這張圖對應的代碼就是reshape2包中的函數

names(airquality) <- tolower(names(airquality))View(airquality)aqm <- melt(airquality, id=c("month", "day"), na.rm=TRUE) # 除了month和day兩列,其他列摞起來,為了等長,m和d列循環對齊dcast(aqm, day + variable ~ month) # 保持day和variable不變,month中的元素分類映射到列上去dcast(aqm, variable + day ~ month) # 換一下順序,重複的variable連在一起,對應不一樣的day,這樣的方式排列dcast(aqm, day ~ variable + month) # 只保留day列# 加入計算dcast(aqm, day ~ month, mean) # 沒出現的那個變數被平均掉了# dcast 和 acast區別dcast(aqm, variable + month ~ day) acast(aqm, variable + month ~ day) # acast和dcast的功能基本上相同,只是dcast會把分組信息作為一列或幾列顯示,而acast會將其作為行名acast(aqm, day ~ month, mean) # 保留的列作為合併在一起作為列名acast(aqm, variable ~ month ~ day) # acast 多出來的功能,生成一個三維數組,按照day的值分成31個矩陣

tidyr包

有融合和重鑄的函數,但是在重鑄方面功能弱一些

aqg <- gather(airquality,group,value,ozone:temp) # 融合,和reshape2的不同在於輸入的是被轉換的列spread(aqg,group,value) # 還原spread(aqg,month,value) # 輸入要被轉化到列名的列和值,好像一次只能轉化一列作為列名

融合重鑄的應用

讓我們把融合重鑄計算實現的功能和分組計算做一下對比。

假設我們拿到的數據是下面這個樣子的。按照月日,分不同組別匯總在一起的兩列變數值

df <- mutate(aqm, newvalue = value+rnorm(2,0,50))colnames(df) <- c("month","day","group","value1","value2")View(df) # 我們可以看一看現在拿到的數據

我們首先實現如下功能

  • 根據group分組計算兩個value的均值
  • 根據month和group分組計算兩個value的均值
  • 根據month和group分組計算每組個數

使用分組計算來實現

# 根據group分組計算兩個value的均值df_grp1 <- group_by(df,group)summarise_at(df_grp1,vars(value1,value2),mean)# 根據month和group分組計算兩個value的均值df_grp2 <- group_by(df,month,group)summarise_at(df_grp2,vars(value1,value2),mean)# 根據month和group分組計算每組個數summarise(df_grp2,count=n())

使用融合重鑄來實現

df_melt <- melt(df,id=c("month","day","group"))# 根據group分組計算兩個value的均值dcast(df_melt, group ~ variable, mean)# 根據month和group分組計算兩個value的均值dcast(df_melt, month + group ~ variable, mean)# 根據month和group分組計算每組個數dcast(df_melt, month + group ~ variable, length)

從上面來看,實現分組計算的功能二者沒有太大區別,畢竟這就是分組計算本身在做的事情,不過當需要按照多種分組來計算時,融合重鑄不需要重新設置組別,顯然會更方便。

不過融合重鑄也要注意一點,如果數據列數非常多,而且有的是字元串,或者有的列不是你需要計算的,要事先只提取出你需要的,再融合重鑄。

下面我們說一說融合重鑄擅長的部分

  • 如果要不區分value1和value2,算整體按照month和group分組後的均值
  • 上面計算結果值是一個矩陣,想要把數值用一列表示
  • 按照月份拆分成多個矩陣,每一個矩陣表示group和日期的對應

# 如果要不區分value1和value2,算整體按照month和group分組後的均值(mg <- dcast(df_melt, month ~ group , mean))# 上面計算結果值是一個矩陣,想要用一列表示melt(mg, id="month")# 按照月份拆分成多個矩陣,每一個矩陣表示group和日期的對應u <- acast(df_melt, group ~ day ~ month) # 使用acast返回一個三維數組

拆分合併列

我們現在要把一列這種類型」1990-1」的數據拆分成兩列」1990」和」1」這樣的數據,使用tidyr包中的separate函數

library(tidyr)name1 <- c("Bob","Mary","Jane","Kim")birth <- c("1990-1","1980-2","1995-5","1996-4")df <- data.frame(name1, birth)(df1 <- separate(df,birth,into=c("year","month"),sep="-"))separate_rows(df,birth,sep="-") # 拆分完放在一列裡面顯示# 其實separate_rows相當於使用separate之後進行了融合,再更換一下順序separate(df,birth,into=c("year","month"),sep="-") %>% gather(group,birth,year:month)

tidyr包中很多函數都是成對出現的,unite 函數是和separate函數反向的函數,可以將它得到的結果還原

unite(df1,birth,year,month,sep="-")

到這裡,我們就把數據處理的整體框架講述了一遍,主要針對數據的計算與變形,覆蓋了dplyr tidyr reshape2包的使用方法。本專題下一篇,我們會使用data.table包來重新完成這整個框架

專欄信息

專欄主頁:Data Analysis

專欄目錄:目錄

文末彩蛋

本期介紹Hadley大神。他的主頁

Hadley Wickham 是 Rstudio 首席數據科學家,他寫了很多非常著名的R包,為R語言做出了非常大貢獻。rstudio網站上列出了一些非常著名的R包,我們會發現有一大半都是Hadley寫的。他還是幾本書的作者,本人讀過三本,均受益匪淺。

他的R包

  • 最著名的當然是 ggplot2 作圖包了,有些人認為這是自己不想離開R轉向python的唯一原因。用簡單的代碼做出精美的圖,強大的功能,專門為統計分析作圖提供便捷,確實讓人愛不釋手。不過這個包學起來卻不是那麼容易,本專欄之後也會開出專題專門講這個包。
  • 數據分析包,也就是本專欄主要介紹的 reshape2 dplyr tidyr 包,這些包可以高效處理數據,代碼簡潔,功能強大。而且可以和 ggplot2 一起使用,形成了一整個處理數據並可視化的生態。
  • 比基礎方法更簡潔高效的 時間處理包 lubridate 包、字元串處理 stringr包 讀取數據 readr 包 readxl 包 haven 包
  • 除此之外,為了幫助完善R語言的生態,幫助其他人更加便捷地開發R包,他還寫了 devtools 等包

他的書

  • Advanced R 想要深入理解R語言可以讀這本書,讀過之後會發現之前遇到的很多無法解決的問題都豁然開朗。本書已經有了中文版,《r語言高級程序設計》

  • R for Data Science 這是Hadley的新書,本書結合他的 數據分析和ggplot2包 ,講述了數據分析的理念和流程,讀完可以對統計分析有更深入的了解,也可以更靈活地使用他開發的R包

  • R packages 本書我還沒有讀過,不做介紹。上面三本都有英文版免費放在網站上

  • ggplot2: elegant graphics for data analysis 這本書主要講述 ggplot2 包的涉及理念及用法,想要更熟練掌握 ggplot2 包的話建議閱讀

推薦閱讀:

R Markdown 簡介
【乾貨收藏】Python面試指南大全
《利用數據改進醫療質量指南》
若當時你也在泰坦尼克號上,能否活下來嗎?

TAG:R编程语言 | 数据分析 | 统计 |