[原]數據科學教程:R語言與DataFrame[2016版]

data frame

什麼是DataFrame

引用 r-tutor上的定義:css

DataFrame 是一個表格或者相似二維數組的結構,它的各行表示一個實例,各列表示一個變量。

沒錯,DataFrame就是相似於Excel表格和MySQL數據庫同樣是一個結構化的數據體。而這種結構化的數據體是當代數據流編程中的中流砥柱,幾乎全部先進算法的載體都是DataFrame,好比如今咱們耳熟能詳的邏輯迴歸算法、貝葉斯算法、支持向量機算法、XGBoost算法等等都創建在這個數據流編程的基礎之上,咱們能夠在R、Python、Scala等函數式編程中找到他們的身影。html

R中的DataFrame數據流編程

參考前文 [[原]基於RStudio Webinars的統計報告Web化與工程化實踐總結](https://segmentfault.com/a/11...、數據清洗、數據處理、數據可視化以及數據建模五個模塊。git

數據讀取 readr/httr/DBI

readr

readr簡化了咱們讀取多種格式表格型數據的方法,包括分割文件withread_delim(),read_csv()read_tsv()read_csv2()、固定寬度文件讀取的read_fwf()read_table()以及read_log()來讀取Web日誌文件。在參數配置方面是和原生的read.xxx()函數族是看齊的。github

readr是利用C++和RCpp編寫的,因此執行的速度是至關快的,不過相對於直接用C語言寫的data.table::fread()就稍微慢大概1.2-2倍左右。在實際使用中,data.talbe::fread()的讀取速度能夠比原生的read.csv有3-10倍的提高速度。算法

httr

httr是一個高級的網絡請求庫,相似於Python中的Tornado和Requests,除了提供基本的Restful接口設計功能,好比GET(), HEAD(), PATCH(), PUT(), DELETE()POST(),還提供了OAuth的調用,好比oauth1.0_token()oauth2.0_token()。並且httr還提供了諸如session、cookie、SSL、header、proxy、timeoutd等更太高級管理功能。固然你能夠用它來作簡單的爬蟲應用,若是須要更高級的爬蟲,咱們須要投入rvest的懷抱來支持諸如xpath等高級爬蟲特性。sql

DBI

DBI是一個爲R與數據庫通信的數據庫接口。至關於Java裏面的DAO,Python裏的Torndb和Tornlite,方便多種關係型數據庫的SQL請求。其中最亮眼的是,R中的DataFrame和數據庫以前能夠以整個數據框插入的形式插入數據而不須要再拼接SQL語句。數據庫

如下是一個官方文檔的示例:apache

library(DBI)
# 建立一個臨時內存的 RSQLite 數據庫
con <- dbConnect(RSQLite::SQLite(), dbname = ":memory:")

dbListTables(con)
# 直接插入整個數據框到數據庫中
dbWriteTable(con, "mtcars", mtcars)
dbListTables(con)

dbListFields(con, "mtcars")
dbReadTable(con, "mtcars")

# 你能夠獲取全部結果:
res <- dbSendQuery(con, "SELECT * FROM mtcars WHERE cyl = 4")
dbFetch(res)
dbClearResult(res)

# 或者一次取一塊
res <- dbSendQuery(con, "SELECT * FROM mtcars WHERE cyl = 4")
while(!dbHasCompleted(res)){
  chunk <- dbFetch(res, n = 5)
  print(nrow(chunk))
}
dbClearResult(res)

dbDisconnect(con)

clipboard.png

數據清洗 tidyr/jsonlite

tidyr

tidyr是一個數據清洗的新包,正在取代reshape2spreadsheets等包。清潔的數據在數據處理的後續流程中十分重要,好比數據變化(dplyr),可視化(ggplot2/ggvis)以及數據建模等。tidyr主要提供了一個相似Excel中數據透視表(pivot table)的功能,提供gatherspread函數將數據在長格式和寬格式之間相互轉化,應用在好比稀疏矩陣和稠密矩陣之間的轉化。此外,separateunion方法提供了數據分組拆分、合併的功能,應用在nominal數據的轉化上。編程

jsonlite

相似於Python中的json庫,參考前文 [[原]數據流編程教程:R語言與非結構化數據共舞](https://segmentfault.com/a/11...,咱們能夠知道jsonlite是一個標準的json轉化庫,依賴於jsonlite咱們能夠自由地在JSON和DataFrame之間相互轉化。json

數據處理 dplyr/rlist/purrr

dplyr

dplyr包是如今數據流編程的核心,同時支持主流的管道操做 %>%,主要的數據處理方法包括:

  • 高級查詢操做:

select(): 按列變量選擇
filter(): 按行名稱分片
slice(): 按行索引分片
mutate(): 在原數據集最後一列追加一些數據集
summarise(): 每組聚合爲一個小數量的彙總統計,一般結合gruop_by()使用
arrange(): 按行排序

  • 關聯表查詢

inner_join(x, y): 匹配 x + y
left_join(x, y): 全部 x + 匹配 y
semi_join(x, y): 全部 x 在 y 中匹配的部分
anti_join(x, y): 全部 x 在 y 中不匹配的部分

  • 集合操做

intersect(x, y): x 和 y 的交集(按行)
union(x, y): x 和 y 的並集(按行)
setdiff(x, y): x 和 y 的補集 (在x中不在y中)

更多詳細操做能夠參考由SupStats翻譯的 數據再加工速查表,比Python的老鼠書直觀不少。

rlist

參考前文 數據流編程教程:R語言與非結構化數據共舞,咱們知道,區別於dplyr包,rlist包是針對非結構化數據處理而生的,也對以list爲核心的數據結構提供了相似DataFrame的高級查詢、管道操做等等方法。

purrr

purrr向Scala這樣的具備高級類型系統的函數式編程語言學習,爲data frame的操做提供更多的函數式編程方法,好比map、lambda表達式。此外,purrr引入了靜態類型,來解決原生的apply函數族類型系統不穩定的狀況。

我遇到過一個很是頭疼的apply函數的問題:apply內的表達式計算結果不一致。

# 原來表達式是這樣的,可是返回的計算結果不對:
# x1,x2,x3都是一個含有NA值的一個10x10的矩陣
apply(x1*x2-x1*x3,1,sum,na.rm=T)

因而改爲分步計算才能獲得正確答案。

t1 <- apply(x1 * x2,1,sum,na.rm=T)
t2 <- apply(x1 * x3,1,sum,na.rm=T)
t3 <- t1 - t2

若是使用purrr包就能夠很好的解決這一問題。參考 Wisdom's Quintessence: Purrr package for R is good for performance 的例子:

library(purrr)
 
mtcars %>%
  split(.$cyl) %>%
  map(~ lm(mpg ~ wt, data = .)) %>%
  map(summary) %>%
  map_dbl("r.squared")

具體使用能夠參考Rstudio Blog:purrr 0.2.0

數據可視化 ggplot2/ggvis

ggplot2

ggplot2 是一個加強的數據可視化R包,幫助咱們輕鬆建立使人驚歎的多層圖形。它的設計理念相似於PhotoShop,具體參數包含設計對象、藝術渲染、統計量、尺寸調整、座標系統、分片顯示、位置調整、動畫效果等等。

更多操做能夠查看ggplot2與數據可視化速查表官方文檔

實戰能夠參考R Graphics Cookbook一書

ggvis

ggvis是吸取了ggplot2vega以及d3的精華,目標旨在配合shiny打造動態可交互的可視化組件。ggvis最明顯的區別就是在做圖時直接支持%>%的管道操做,好比:

diamonds %>% ggvis(~carat, ~price, fill=~clarity) %>% layer_points(opacity:=1/2)

ggplot2與ggvis的關係相似於plyr與dplyr的關係,都是一種演化過程。

數據建模 broom

broom

在機器學習的本質其實就是各類姿式的迴歸,而在R中的各類迴歸分析每每不會返回一個整齊的data frame 結果。好比

lmfit <- lm(mpg ~ wt, mtcars)
lmfit
## 
## Call:
## lm(formula = mpg ~ wt, data = mtcars)
## 
## Coefficients:
## (Intercept)           wt  
##      37.285       -5.344

這時候broom包就派上用場了,直接將統計結果轉化爲data frame格式:

library(broom)
tidy(lmfit)
##          term  estimate std.error statistic      p.value
## 1 (Intercept) 37.285126  1.877627 19.857575 8.241799e-19
## 2          wt -5.344472  0.559101 -9.559044 1.293959e-10

augment()函數返回data frame格式的s其全部他參數結果

head(augment(lmfit))
##           .rownames  mpg    wt  .fitted   .se.fit     .resid       .hat    .sigma      .cooksd  .std.resid
## 1         Mazda RX4 21.0 2.620 23.28261 0.6335798 -2.2826106 0.04326896  3.067494 1.327407e-02 -0.76616765
## 2     Mazda RX4 Wag 21.0 2.875 21.91977 0.5714319 -0.9197704 0.03519677  3.093068 1.723963e-03 -0.30743051
## 3        Datsun 710 22.8 2.320 24.88595 0.7359177 -2.0859521 0.05837573  3.072127 1.543937e-02 -0.70575249
## 4    Hornet 4 Drive 21.4 3.215 20.10265 0.5384424  1.2973499 0.03125017  3.088268 3.020558e-03  0.43275114
## 5 Hornet Sportabout 18.7 3.440 18.90014 0.5526562 -0.2001440 0.03292182  3.097722 7.599578e-05 -0.06681879
## 6           Valiant 18.1 3.460 18.79325 0.5552829 -0.6932545 0.03323551  3.095184 9.210650e-04 -0.23148309

glance()函數,返回data frame格式的部分參數結果

glance(lmfit)
##   r.squared adj.r.squared    sigma statistic      p.value df    logLik
## 1 0.7528328     0.7445939 3.045882  91.37533 1.293959e-10  2 -80.01471
##        AIC      BIC deviance df.residual
## 1 166.0294 170.4266 278.3219          30

DataFrame優化

data.table

衆所周知,data.frame的幾個缺點有:

  1. 大數據集打印緩慢
  2. 內部搜索緩慢
  3. 語法複雜
  4. 缺少內部的聚合操做

針對這幾個問題,data.table應運而生。data.table完美兼容data.frame,這意味着以前對data.frame的操做咱們能夠徹底保留,而且支持更多方便的數據操做方法。

data.table還參考了NoSQL中流行的Key-Value形式,引入了setkey()函數,爲數據框設置關鍵字索引。

值得一提的是data.table引入了全新的索引形式,大大簡化了data frame的分片形式,提供接近於原生矩陣的操做方式並直接利用C語言構造底層,保證操做的速度。

對比操做

對比data.table 和 dplyr 的操做:

操做 data.table dplyr
按行分片 DT[1:2,] DF[1:2,]
按列分片 DT[,1:2,with=False] DF[,1:2]
分組summarise DT[, sum(y), by=z] DF %>% group_by(z) %>% summarise(sum(y))
分組mutate DT[, y := cumsum(y), by=z] ans <- DF %>% group_by(z) %>% mutate(y = cumsum(y))
篩選後分組彙總 DT[x > 2, sum(y), by=z] DF %>% filter(x>2) %>% group_by(z) %>% summarise(sum(y))
篩選後分組更新 DT[x > 2, y := cumsum(y), by=z] ans <- DF %>% group_by(z) %>% mutate(y = replace(y, which(x>2), cumsum(y)))
分組後按條件彙總 DT[, if(any(x > 5L)){y[1L]-y[2L]}else{y[2L], by=z]} DF %>% group_by(z) %>% summarise(if (any(x > 5L)) y[1L]-y[2L] else y[2L])
apply函數族
操做 data.table dplyr
分組擴展各list DT[, (cols) := lapply(.SD, sum), by=z] ans <- DF %>% group_by(z) %>% mutate_each(funs(sum))
分組彙總各list DT[, lapply(.SD, sum), by=z] DF %>% group_by(z) %>% summarise_each(funs(sum))
分組彙總各list DT[, c(lapply(.SD, sum),lapply(.SD, mean)), by=z] DF %>% group_by(z) %>% summarise_each(funs(sum, mean))
分組彙總各list DT[, c(.N, lapply(.SD, sum)), by=z] DF %>% group_by(z) %>% summarise_each(funs(n(), mean))
join 操做
setkey(DT1, x, y)
操做 data.table dplyr
通常join DT1[DT2] left_join(DT2, DT1)
擇列join DT1[DT2, .(z, i.mul)] left_join(select(DT2, x,y,mul), select(DT1, x,y,z))
聚合join DT1[DT2, .(sum(z)*i.mul), by=.EACHI] DF1 %>% group_by(x, y) %>% summarise(z=sum(z)) %>% inner_join(DF2) %>% mutate(z = z*mul) %>% select(-mul)
更新join DT1[DT2, z := cumsum(z)*i.mul, by=.EACHI] join and group by + mutate
滾動join DT1[DT2, roll = -Inf] /
其餘變量控制輸出 DT1[DT2, mult = "first"] /
拼接操做
操做 data.table dplyr
分組再分list聚合 DT[, list(x[1], y[1]), by=z] DF %>% group_by(z) %>% summarise(x[1], y[1])
分組再分list拼接 DT[, list(x[1:2], y[1]), by=z] DF %>% group_by(z) %>% do(data.frame(.$x[1:2], .$y[1]))
分組取分位數聚合 DT[, quantile(x, 0.25), by=z] DF %>% group_by(z) %>% summarise(quantile(x, 0.25))
分組取分位數拼接 DT[, quantile(x, c(0.25, 0.75)), by=z] DF %>% group_by(z) %>% do(data.frame(quantile(.$x, c(0.25, 0.75))))
分組分list聚合拼接 DT[, as.list(summary(x)), by=z] DF %>% group_by(z) %>% do(data.frame(as.list(summary(.$x))))

更多操做詳情可查看data.table速查表

DataFrame可視化

DT

DT包是謝溢輝老師的大做,爲data frame數據提供了很是好的可視化功能,而且提供了篩選、分頁、排序、搜索等數據查詢操做。

library(DT)
datatable(iris)

datatable

此外,DT包還提供了大量的UI定製的功能,對html、css和js進行深度定製。好比:

m = matrix(c(
  '<b>Bold</b>', '<em>Emphasize</em>', '<a href="http://rstudio.com">RStudio</a>',
  '<a href="#" onclick="alert(\'Hello World\');">Hello</a>'
), 2)
colnames(m) = c('<span style="color:red">Column 1</span>', '<em>Column 2</em>')
datatable(m)  # 默認 escape = TRUE

datatable(m, escape = FALSE)

raw_matrix %>%
DT::datatable(options = list(pageLength = 30, dom = 'tip')) %>% 
  DT::formatStyle(columns = c("A","B")
    background = styleColorBar(c(0, max(raw_matrix,na.rm = TRUE)), 'steelblue'),
    backgroundSize = '100% 50%',
    backgroundRepeat = 'no-repeat',
    backgroundPosition = 'center')

分佈式DataFrame

sparklyr

sparklyr是 rstudio 公司爲連接spark 和dataframe 編寫的一套分佈式數據處理框架,用一個統一的跨引擎API簡化了多數據源的分析操做,進一步將data frame底層的分佈式傻瓜化。

在R中使用sparklyr::spark_apply,咱們不須要修改以前任何的代碼,而且繞過Hadoop的絕對限制,就可讓data frame格式的數據,自動得到分佈式處理的能力!

# 建立 spark連接 管理器來運行Spark引擎
sc <- sparklyr::spark_connect(master = "local",

version = "2.2.0",
method = "livy",
config = sparklyr::config())



# 經過SQL讀取spark數據
ddf <- DBI::dbGetQuery(sc, "select * from mtcars")

/*Basic Stats*/
# 返回行/列的值
ncol(ddf)
nrow(ddf)

# 在 DF 上進行運行標準彙總
summary(ddf)

更多具體操做能夠參考官方指南

各數據處理框架性能對比圖

DataFrame在R、Python和Spark三者中的聯繫

操做 R Python Spark
base Pandas spark SQL
讀取csv read.csv() read_csv() spark-csv
計數 nrow() pandasDF.count() sparkDF.count()
分片 head(data,5) pandasDF.head(5) sparkDF.show(5)
推斷類型 自動推斷 自動推斷 默認爲string類型
標準差計算中的NaN處理 視爲NA 自動排除 視爲NaN
特徵工程 dplyr::mutate() pandasDF['new'] sparkDF.withColumn()

DataFrame 之我見

  1. 處理數據的第一語言仍是 SQL語句,由於SQL是DSL,這樣就對使用者沒有Python或者R的要求,也方便與DBA的維護。在R中可使用 sqldf 經過SQL直接操做DataFrame,在Python中可使用 pysqldf
  2. 處理數據的第二語言則是 tidyverse 或者 pandas,使用這樣的鏈式調用方法能夠提高數據流的處理效率,規避一些原生SQL在不一樣數據庫中執行狀況不一樣或者可讀性較差的問題。
  3. 處理數據的第三語言則是 data.table 或者 scala,使用這樣高性能的方法能夠在關鍵步驟提高數據處理效率到極致,不過會犧牲一部分維護性。

參考資料

做爲分享主義者(sharism),本人全部互聯網發佈的圖文均聽從CC版權,轉載請保留做者信息並註明做者 Harry Zhu 的 FinanceR專欄: https://segmentfault.com/blog...,若是涉及源代碼請註明GitHub地址: https://github.com/harryprince。微信號: harryzhustudio 商業使用請聯繫做者。
相關文章
相關標籤/搜索