先用幾個問題檢驗一下你是否需要看這篇文章
在使用R做數據分析的一個完整的過程包括數據的獲取,數據的前期處理,之後才是使用「整齊」的數據來套用模型得出結論。本專題旨在系統地講述使用R語言完成前期的數據處理,英文叫tidy data,將「髒」數據洗乾淨。比如處理掉一些會影響結果準確度的數據,提取出我們想要的內容,或將整個數據集或多個數據集融合、改變成我們想要的形式。這個過程往往是數據處理上最繁榮瑣碎的過程,可能在這裡耗費的時間要過多真正 fit model 所需要的時間。
除了通用的處理之外,本專題還會專門針對缺失值處理、時間處理、讀寫數據等方面,單獨進行介紹。
在數據處理上,R語言本身自帶這一些操作方法,但是有三個主要的缺陷
用法不夠簡潔易記
在處理大量數據時,速度太慢
完成有些需求過於麻煩,或者無法達成。
但是這是R的開源好處就體現出來了,用戶開發了幾個高效處理數據的R包,它們不僅提高了運行速度,而且代碼簡明易記,如果熟練掌握,可以成為處理數據的一大利器。
主要的大數據處理包可以分為兩個派別
本專題講述時,將前者和R自帶基礎函數一起講述,data.table包會單獨開出一個部分來講。
本文使用R語言進行數據處理,不僅包括R自帶的函數,而且包括其他高效處理數據的R包。這個部分共兩篇文章,使用基礎函數和Hadley寫的一系列數據處理包,來完成整個數據處理的基本需求。
本文目錄如下
數據框建立
提取
根據坐標和行列名提取單個數值
提取整行或整列
根據邏輯值判斷提取
提取的衍生物(為什麼這麼說會在後面介紹)
增添
本文只需要使用dplyr包
library(dplyr) # 高速處理數據,取代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)
rownames(df1) <- letters[1:4] # 賦予行名
df2 <- data.frame(name2,birth,accept)
上面是通過建立向量再組合來創建數據框,日常使用中還有兩種常見獲得數據框的方法
提取看起來簡單,但是方法卻多而雜亂,在這裡我將做一個總結
從大方向上來看是分為三個角度
根據坐標位置來提取
根據行名列名來提取
使用邏輯值判斷提取
一二兩种放在一起說,都是使用[ ]實現提取,[ ]中的內容分為以下5種
[ ]中放兩個維度的坐標、或者行名和列名,提取單個點或多個點
[ ]中放一個兩列的矩陣,可以把每行看成一個坐標x y,用於提取多個點,點的個數和矩陣的行數相等
兩個維度中只指定一個維度(兩個參數中有一個是空著的,即逗號一邊什麼都不加),提取行或列
只有一位維度,即[ ]中只有一個數或者列名,提取列(提取列比行多這種方法)
使用$指定列名提取列(這也是提取列多出來的方法)
下面有這幾種提取方法的實例,看完實例,我會拓展一些更深層次的數據結構。
# 通過[]選擇坐標,確定點
df1[2,3]
# 使用向量確定多個點
df1[c(2,4),c(1,3)]
# 通過[]選擇行列名
df1["a","weight"]
df1["a",2] # 混搭
# 通過矩陣提取多個點,一列指定行,一列指定列
df1[cbind(c(1,2,1),3:1)]
df1[cbind(c("a","b"), c("weight","height"))]
# 通過[]少選擇一個維度,整行整列地選擇(這是比選擇行多的一種方法)
df1[2,] # 取行
df1[,3] # 取列
df1[c(2,3),] # 取多行
df1[,c(2,3)] # 取多列
df1["a",]
df1[,"weight"]
df1[3] # 只接一個數字,選擇列
df1[c(2,3)] # 使用向量則選擇多列
df1$weight # 通過$選擇列名名字來選擇列
# 列名放在[]中選取列
df1["weight"]
df1[c("weight","height")] # 使用向量選擇多列
select(df2,accept) # dplyr包中函數,等價於 df2$accept
在選擇行和列上有必要進一步說明,關於取出來的仍是數據框還是向量的問題
這是提取數據是否同時進行降維的問題。即使是只有一列的數據框,它仍是二維的,只有變成向量才算是一維的。上面涉及到的多種提取行列的方法,默認得到的結果是數據框還是向量是不一樣的,但是二者之間是有辦法相互轉化的。
# 取行和取列默認不一樣
class(df1[,3]) # "numeric"
class(df1[2,]) # "data.frame"
# 另外三種
class(df1[3]) # "data.frame"
class(df1["weight"]) # "data.frame"
class(df1$weight) # "numeric"
上面輸出的兩種結果中
對於取兩個坐標[ ]的方法來說,drop參數可以調整輸出結果
# drop=T是降維的意思
class(df1[,3,drop=F]) # "data.frame"
class(df1[2,,drop=T]) # "list"
# 取行用drop也不能轉化為向量,那就用unlist
class(unlist(df1[2,])) # "numeric"
對於只接受一個維度參數的方法來說[[ ]]相當於$,可以實現降維
class(df1[["weight"]]) # "numeric"
class(df1[[2]]) # "numeric"
# 注意:[[]]不能作用到選取多列的情況上,因為多列談不上降維
df1[[c("weight","height")]] # 報錯
df1[[1:3]] # 報錯
其他方面
1.關於[[ ]]和$等關於降維方面的使用,讀者可以自行在list和matrix上試。使用下面一條命令查看幫助文檔
help(`[`)
2.模糊匹配
df1$wei
df1$weig <- 1:4
df1$wei
是否支持模糊匹配也可以用參數exact調整
df1[["hei"]] # 默認不支持模糊匹配
df1[["hei",exact=F]] # 支持
df1["hei"]; df1["hei",exact=F] # 單個[]加了也不支持
模糊匹配在函數參數使用上也有體現
seq(1,10,length.out=10)
seq(1,10,len=10)
3.如果指定的數字不是整數,會自動向下取整
(i <- 3.999999999) # 返回4,因為當前保留的位數不夠
# 如果想要改變保留位數,可以用options(digits = 10)修改
# 不過內存中存儲的還是3.999999999
(1:5)[i] # 3
通過邏輯值判斷選取
df2[df2$accept=="no"|name2=="Bob",]
subset(df2,accept=="no")
filter(df2,accept=="no"|name2=="Bob")
上面這種通過判斷來選取的方式,有一個背後的原理,我們拿[]來說,這是通過邏輯值向量進行的選取
df2$accept=="no"|name2=="Bob"
df2[df2$accept=="no"|name2=="Bob",]
df2[c(T,F,F,T),]
知道了這個原理之後,我們就可以更加靈活地提取數據,因為[ ]中放置的不一定必須是本數據框中的變量,只要是等長邏輯值向量就可以了(注意最後的逗號不要忘記寫就好)
df2[df1$weight>50,]
我們使用過程中可能涉及到的,修改數據框,按照行或列進行排序,刪除某行某列,插入某行某列。這些如果都單獨來學,會感覺命令非常多、非常亂,但如果看成是提取的衍生物,則不需要花多少精力就能掌握。因為這些其實都是使用了提取的方法,只是各自加入了自己的原理,就包裝成了另外一種東西。我們一項一項來看:
修改
這裡修改數據框其實只是把需要修改的地方用提取的方法提取出來,再賦個值,就實現了對原數據框的更改,基本上沒有什麼新東西。
df1[2,3] <- 160;df1
df2$accept[df2$accept=="ok"] <- "yes"
df2 # 數據框發生了改變
我們會發現,這裡每次使用數據框中的一列時,都要使用$,非常麻煩,可以使用within函數避免這樣的麻煩
df2 <- data.frame(name2,birth,accept,stringsAsFactors=F)
within(df2,{accept[accept=="ok"]<-"yes"
name2[name2=="Bob"]<-"BOB"})
當其他時候使用數據框中的列進行計算時,如果不想用$,可以使用attach函數
df2 <- data.frame(name2,birth,accept,stringsAsFactors=F)
attach(df2)
2*weight
detach(df2)
用這種方法一定要注意最後detach,否則會搞亂命名空間,由於容易忘記,一般不推薦這種方法,我們還可以使用with函數
with(df2,{a <- weight*2
a^3})
刪除
不想要哪行,就在哪行用負數,其實相當於提取了其他行,組成了一個新的數據框。根據這個原理,使用邏輯值向量也可以實現刪除行列。
df1[-c(2,3),]
df1[,c(T,F,T)]
排序
R語言使用基礎函數處理向量、數據框等,都不能直接對其本身修改,無論是改變一個值,還是排序,刪除某一列,得到的都不再是原來的數據框了。所以排序其實就是一個按順序提取的過程。
R中自帶的方法是使用order函數,生成大小順序的索引
df1_order_row <- df1[order(df1$weight),]
# 我們可以拆分來看
order(df1$weight) # 3 4 1 2
# 上面的結果意味著第三個是最小的,最先被提取出來,放在第一行,接下來是第4個第二小……
# 先按體重,再按身高排序(當體重一樣時)
df1_order_row <- df1[order(c(df1$weight,df1$height)),]
# 按照列名對列排序也是一樣
df1_order_col <- df1[,order(colnames(df1))]
# 使用dplyr包對行更方便、高效地處理
arrange(df1,weight,height)
插入
由於上面說到的特性,R語言沒有辦法在原有向量或數據框中插入內容,唯一的方法就是按照要插入的位置,將數據框分開,即提取出dfa和dfb,再用二者和要加入的內容拼接。這種方法非常繁瑣,尤其是當要在很多地方插入的情況下,本人目前沒有找到更好的方法,如果讀者有好的方法可以在評論區留言。
下一節,我們會介紹以下內容數據框合併
計算並增加行列
匯總計算
分組計算
融合重鑄
融合重鑄的應用
拆分合併列
1.想要快速入門dplyr包,可以看網上的教程,都大同小異,很多都是轉載的同一篇,這裡隨便貼一個 http://blog.163.com/zzz216@yeah/blog/static/16255468420147179438149/
2.一般看完上面的這種教程,就能處理大部分問題了,如果想更系統地了解這個包(dplyr),可以看一看官方幫助文檔。說實話,有很多特別好的功能都不為人所知。
專欄信息專欄主頁:Data Analysis https://zhuanlan.zhihu.com/Data-AnalysisR專欄目錄:目錄 https://zhuanlan.zhihu.com/p/25780082
文末彩蛋這次講管道操作,一種數據處理中非常便捷的使用方法,主要基於 magrittr 包,裡面有如下幾個函數
%>% %T>% %$% %<>%
最常用的是第一個 %>% ,如果你載入Hadley的包,就自動可以使用這個函數,不用另外加載magrittr包,如果想了解其他函數再載入這個包,查看文檔來學習,或者看這裡連結 http://blog.fens.me/r-magrittr/
管道操作除了這個包,還有 pipeR 包,這是另外一個系統,有興趣的可以去學一下,這裡簡要介紹一下 %>% 函數
library(dplyr)
name2 <- c("Bob","Mary","Kim","Jane")
weight <- c(60,65,45,55)
height <- c(170,165,140,135)
accept <- c("no","ok","ok","no")
df <- data.frame(name2,weight,height,accept)
# 下面兩個操作等價
select(df,weight:accept)
df %>% select(weight:accept)
所以 %>% 的作用在於把前面的內容放到後面函數中,作為第一個參數。使用這個符號的好處有
我們可以對比一下操作
df
unlist()
plot(colMeans(matrix(unlist(2*select(df,starts_with("w"))),
nrow=2)))
w <- select(df,starts_with("w"))
v <- unlist(w*2)
m <- matrix(v,nrow=2)
plot(colMeans(m))
以上三塊內容實現的是相同的目的,我們通過管道函數從前往後讀一下
拿到數據集,先提取首字母是w的列,乘2,轉化為向量,使用這個向量創建一個2行的矩陣,對矩陣每一列求均值,最後畫個圖我們在再去看一下第二種,括號一層套一層,是不是可讀性差了很多?而且之後如果想加操作還要再往外套
第三種則可讀性提高了一些(但是還是沒有從前往後讀順暢),但是中間加了很多沒有必要的變量,代碼就會很冗餘。
以上是最基本也是最常用的方法,下面展示一些其他用法,更深入的可以去看包的幫助文檔
df$weight %>% {c(min(.), mean(.), max(.))} %>% floor
2 %>% head(df,.)
a <- df %>% .$name2 %>% grep("a",.)
Dwzb , R語言中文社區專欄作者,廈門大學統計專業學生。
知乎專欄:Data Analysis
https://zhuanlan.zhihu.com/Data-AnalysisR
回復 R R語言快速入門免費視頻
回復 統計 統計方法及其在R中的實現
回復 用戶畫像 民生銀行客戶畫像搭建與應用
回復 大數據 大數據系列免費視頻教程
回復 可視化 利用R語言做數據可視化
回復 數據挖掘 數據挖掘算法原理解釋與應用
回復 機器學習 R&Python機器學習入門