數據分析項目中大多數的時間都用在了準備數據上,一個典型的項目80%的精力都花在分析而進行的發現、清洗和準備數據上。只有不到5%的精力用於分析(剩下的時間都耗在了寫報告上面)。
合併數據集數據分析中最常見的一個障礙是將存儲在兩個不同地方的數據組合到一起。
粘貼數據結構R提供了幾個函數可以將多個數據結構粘貼成一個數據結構。
pastepaste函數可以將多個字符型向量連接成一個向量,默認向量的值是用空格分隔的,我們可以通過sep參數指定分隔符號,而collapse參數可以用來指定這些值之間的連接符號。
x <- c("a", "b", "c", "d", "e")y <- c("A", "B", "C", "E", "F")
# 默認paste(x, y)## [1] "a A" "b B" "c C" "d E" "e F"
# 使用自定義分隔符
paste(x, y, sep = ",")## [1] "a,A" "b,B" "c,C" "d,E" "e,F"
# 指定連接符
paste(x, y, sep = "-", collapse = "#")## [1] "a-A#b-B#c-C#d-E#e-F"另外,使用簡化版的paste0函數隻提供連接參數,沒有分隔。
paste0(x, y, collapse = "#")## [1] "aA#bB#cC#dE#eF"rbind 和 cbindrbind與cbind函數分別可以以增加行或列的形式將幾個對象(矩陣或者數據框)合併起來。你可以將它想像為以垂直或者水平地將兩張表拼在一起。
mergemerge(x, y, by = , by.x = , by.y = , ...)x與y指定用於合併的數據框,by對應x和y共有的列名,後面by.x與by.y用於分別指定用於合併的列名。
數據轉換數據框中常用的更改變量的函數是transform,它定義如下:
這個函數首先要指定一個數據框,跟著是一系列的表達式,表達式中的變量是數據框中的變量,transform函數會完成每個表達式中的計算,然後返回最終的數據框。
head(mtcars)## mpg cyl disp hp drat wt qsec vs am gear carb## Mazda RX4 21.0 6 160 110 3.90 2.62 16.5 0 1 4 4## Mazda RX4 Wag 21.0 6 160 110 3.90 2.88 17.0 0 1 4 4## Datsun 710 22.8 4 108 93 3.85 2.32 18.6 1 1 4 1## Hornet 4 Drive 21.4 6 258 110 3.08 3.21 19.4 1 0 3 1## Hornet Sportabout 18.7 8 360 175 3.15 3.44 17.0 0 0 3 2## Valiant 18.1 6 225 105 2.76 3.46 20.2 1 0 3 1mtcars.transformed <- transform(mtcars, newVar = disp / hp)mtcars.transformed## mpg cyl disp hp drat wt qsec vs am gear carb## Mazda RX4 21.0 6 160.0 110 3.90 2.62 16.5 0 1 4 4## Mazda RX4 Wag 21.0 6 160.0 110 3.90 2.88 17.0 0 1 4 4## Datsun 710 22.8 4 108.0 93 3.85 2.32 18.6 1 1 4 1## Hornet 4 Drive 21.4 6 258.0 110 3.08 3.21 19.4 1 0 3 1## Hornet Sportabout 18.7 8 360.0 175 3.15 3.44 17.0 0 0 3 2## Valiant 18.1 6 225.0 105 2.76 3.46 20.2 1 0 3 1## Duster 360 14.3 8 360.0 245 3.21 3.57 15.8 0 0 3 4## Merc 240D 24.4 4 146.7 62 3.69 3.19 20.0 1 0 4 2## Merc 230 22.8 4 140.8 95 3.92 3.15 22.9 1 0 4 2## Merc 280 19.2 6 167.6 123 3.92 3.44 18.3 1 0 4 4## Merc 280C 17.8 6 167.6 123 3.92 3.44 18.9 1 0 4 4## Merc 450SE 16.4 8 275.8 180 3.07 4.07 17.4 0 0 3 3## Merc 450SL 17.3 8 275.8 180 3.07 3.73 17.6 0 0 3 3## Merc 450SLC 15.2 8 275.8 180 3.07 3.78 18.0 0 0 3 3## Cadillac Fleetwood 10.4 8 472.0 205 2.93 5.25 18.0 0 0 3 4## Lincoln Continental 10.4 8 460.0 215 3.00 5.42 17.8 0 0 3 4## newVar## Mazda RX4 1.455## Mazda RX4 Wag 1.455## Datsun 710 1.161## Hornet 4 Drive 2.345## Hornet Sportabout 2.057## Valiant 2.143## Duster 360 1.469## Merc 240D 2.366## Merc 230 1.482## Merc 280 1.363## Merc 280C 1.363## Merc 450SE 1.532## Merc 450SL 1.532## Merc 450SLC 1.532## Cadillac Fleetwood 2.302## Lincoln Continental 2.140## [到達getOption("max.print") -- 略過16行]]對對象的每個元素進行函數運算apply函數簇該內容參考【r<-高級|理論】apply,lapply,sapply用法探索[1]學習
plyr軟體包apply函數眾多,參數也有些不同,幸運的是,我們可以使用plyr包來避免這些函數的細節。
plyr包包含了12個命名與其功能有邏輯關聯的函數,用於將某個函數運行在某個R對象上,並且返回結果。每個函數的輸入都是一個數組、數據框或者列表,輸出也都是一個數組、數據框或者列表,或者什麼都不輸出。
輸入輸出矩陣輸出數據框輸出列表不輸出數組aaplyadplyalplya_ply數據框daplyddplydlplyd_ply列表laplyldplyllplyl_ply所有的函數都接受下面的參數。
參數描述默認值.data輸入的數據對象
.fun要運行的函數NULL.progress進度條類型(用create_progress構建);選項可包括「none」,「text」,「tk」和「win」「none」.expand若.data是一個數據框,則該參數控制輸出如何擴展;.expand=TRUE表示1維輸出,.expand=FALSE表示n維輸出TRUE.parallel指定是否並行地運行函數(通過foreach)FALSE…其他傳遞給.fun的參數舉幾個例子:
library(plyr)d <- data.frame(x=1:5, y=6:10)# (1)輸入為列表,輸出也為列表lapply(d, function(x) 2 ^ x)## $x## [1] 2 4 8 16 32## ## $y## [1] 64 128 256 512 1024
# 等價命令是llplyllply(.data=d, .fun=function(x) 2^x)## $x## [1] 2 4 8 16 32## ## $y## [1] 64 128 256 512 1024
# (2) 輸入為矩陣,輸出為列表x = as.matrix(d)apply(X = x, MARGIN = 1, FUN=paste, collapse=",")## [1] "1,6" "2,7" "3,8" "4,9" "5,10"
# 等價命令aaply(.data = x, .margins = 1, .fun=paste, collapse=",")## 1 2 3 4 5 ## "1,6" "2,7" "3,8" "4,9" "5,10"
# (3)輸入為數據框,輸出為矩陣t(sapply(d, FUN=function(x) 2 ^ x))## [,1] [,2] [,3] [,4] [,5]## x 2 4 8 16 32## y 64 128 256 512 1024# 等價命令aaply(.data=as.matrix(d), .margins = 2, .fun=function(x) 2 ^ x)## ## X1 1 2 3 4 5## x 2 4 8 16 32## y 64 128 256 512 1024數據分段shingleShingle對象是因子對象的連續性泛化,一個Shingle對象包括一個數字向量和一組間隔,各個間隔允許重疊,這種結構十分類似於屋簷上的瓦片結構。Shingle對象廣泛應用於lattice包,它允許我們輕鬆地把條件或者分組變量作為連續變量使用。
lattice::shingle(x, intervals = unique(x))## ## Data:## [1] 1 2 3 4 5 6 7 8 9 10## ## Intervals:## min max count## 1 1 6 6## 2 2 7 6## 3 3 8 6## 4 4 9 6## 5 5 10 6## ## Overlap between adjacent intervals:## [1] 5 5 5 5intervals參數用來指定在什麼地方分割箱子,你可以用一個數值向量來指定分割的位置,也可以使用一個兩列的矩陣,每一列表示一個特定的間距。equal.count函數可以用來創建一個shingle,每個箱子有相同個數的觀測值:
lattice::equal.count(x)## ## Data:## [1] 1 2 3 4 5 6 7 8 9 10## ## Intervals:## min max count## 1 0.5 3.5 3## 2 1.5 4.5 3## 3 3.5 6.5 3## 4 4.5 7.5 3## 5 6.5 9.5 3## 6 7.5 10.5 3## ## Overlap between adjacent intervals:## [1] 2 1 2 1 2Cutcut函數可以很方便地將一個連續性變量切割成很多個小片段。輸入是一個數值向量,輸出是一個因子,因子的每個水平對應輸入向量的每個區間範圍。
cut(x, breaks, labels = NULL, include.lowest = FALSE, right=TRUE, dig.lab=3,ordered_result = FALSE, ...)例如,假設我們想要統計平均擊球數據在某個範圍內的選手的數量,可以使用cut函數與table函數:
# 讀入示例數據library(nutshell)## 載入需要的程輯包:nutshell.bbdb## 載入需要的程輯包:nutshell.audioscrobblerdata("batting.2008")# 首先在數據框中加入擊球平均值batting.2008.AB <- transform(batting.2008, AVG = H / AB)
# 選擇100 AB以上的球員(為了統計顯著性)batting.2008.over100AB <- subset(batting.2008.AB, subset = (AB > 100))
# 把結果分為10份battingavg.2008.bins <- cut(batting.2008.over100AB$AVG, breaks = 10)
table(battingavg.2008.bins)## battingavg.2008.bins## (0.137,0.163] (0.163,0.189] (0.189,0.215] (0.215,0.24] (0.24,0.266] ## 4 6 24 67 121 ## (0.266,0.292] (0.292,0.318] (0.318,0.344] (0.344,0.37] (0.37,0.396] ## 132 70 11 5 2利用分組變量合併對象有時候,我們可能想要將幾個相似的對象(向量或者數據框)合併成一個數據框,數據框中有一列用來表示數據的來源。lattice包中的make.groups函數可以實現這個功能:
library(lattice)make.groups(...)例如我們將下面不同的向量合成一個數據框:
hat.sizes <- seq(from = 6.25, to = 7.75, by = .25)pants.sizes <- c(30:34, 36, 38, 40)shoe.sizes <- seq(from=7, to=12)lattice::make.groups(hat.sizes, pants.sizes, shoe.sizes)## data which## hat.sizes1 6.25 hat.sizes## hat.sizes2 6.50 hat.sizes## hat.sizes3 6.75 hat.sizes## hat.sizes4 7.00 hat.sizes## hat.sizes5 7.25 hat.sizes## hat.sizes6 7.50 hat.sizes## hat.sizes7 7.75 hat.sizes## pants.sizes1 30.00 pants.sizes## pants.sizes2 31.00 pants.sizes## pants.sizes3 32.00 pants.sizes## pants.sizes4 33.00 pants.sizes## pants.sizes5 34.00 pants.sizes## pants.sizes6 36.00 pants.sizes## pants.sizes7 38.00 pants.sizes## pants.sizes8 40.00 pants.sizes## shoe.sizes1 7.00 shoe.sizes## shoe.sizes2 8.00 shoe.sizes## shoe.sizes3 9.00 shoe.sizes## shoe.sizes4 10.00 shoe.sizes## shoe.sizes5 11.00 shoe.sizes## shoe.sizes6 12.00 shoe.sizes隨機抽樣有時候數據太多,或者出於統計或計算性能的原因,你想要將數據隨機分為幾部分構建模型(通常分為訓練集、測試集和評估集)。
最簡單地方法就是使用sample函數,它可以對一個向量做隨機抽樣。
sample(x, size, replace = FALSE, prob = NULL)當對數據框做sample操作時,實際返回的是列的隨機抽樣結果,而不是行。因為數據框是向量的列表,sample實際抽樣的是這個列表的元素。所以要注意一下。
對於觀察結果做行的隨機抽樣,需要使用sample函數創建一組行號的抽樣結果,然後再使用索引選取這些行號所對應的行。比如我們隨機抽樣batting.2008數據集的5條記錄:
batting.2008[sample(1:nrow(batting.2008), 5), ]## nameLast nameFirst weight height bats throws debut birthYear## 478 Rodriguez Francisco 175 72 R R 2002-09-18 1982## 350 Seanez Rudy 185 70 R R 1989-09-07 1968## 1019 Francisco Ben 190 73 R R 2007-05-01 1981## 1011 Musser Neal 235 73 L L 2007-04-21 1980## 327 Riske David 180 74 R R 1999-08-14 1976## playerID yearID stint teamID lgID G G_batting AB R H 2B 3B HR## 478 rodrifr03 2008 1 LAA AL 76 4 0 0 0 0 0 0## 350 seaneru01 2008 1 PHI NL 42 41 0 0 0 0 0 0## 1019 francbe01 2008 1 CLE AL 121 121 447 65 119 32 0 15## 1011 mussene01 2008 1 KCA AL 1 0 0 0 0 0 0 0## 327 riskeda01 2008 1 MIL NL 45 44 1 0 0 0 0 0## RBI SB CS BB SO IBB HBP SH SF GIDP G_old## 478 0 0 0 0 0 0 0 0 0 0 4## 350 0 0 0 0 0 0 0 0 0 0 41## 1019 54 4 3 40 86 0 6 2 4 10 121## 1011 0 0 0 0 0 0 0 0 0 0 1## 327 0 0 0 0 0 0 0 0 0 0 44還可以使用這種技術做更複雜的隨機抽樣,比如你想要隨機統計3個對的情況,可以這樣:
batting.2008$teamID <- as.factor(batting.2008$teamID)levels(batting.2008$teamID)## [1] "ARI" "ATL" "BAL" "BOS" "CHA" "CHN" "CIN" "CLE" "COL" "DET" "FLO"## [12] "HOU" "KCA" "LAA" "LAN" "MIL" "MIN" "NYA" "NYN" "OAK" "PHI" "PIT"## [23] "SDN" "SEA" "SFN" "SLN" "TBA" "TEX" "TOR" "WAS"
# 抽樣例子sample(levels(batting.2008$teamID), 3)## [1] "BAL" "SLN" "SEA"# 使用例子batting.2008.3teams <- batting.2008[is.element(batting.2008$teamID, sample(levels(batting.2008$teamID),3)), ]batting.2008.3teams## nameLast nameFirst weight height bats throws debut birthYear## 6 Ardoin Danny 218 72 R R 2000-08-02 1974## 9 Aurilia Rich 170 72 R R 1995-09-06 1971## 19 Beimel Joe 201 74 L L 2001-04-08 1977## 24 Bennett Gary 190 72 R R 1995-09-24 1972## 27 Berroa Angel 175 71 R R 2001-09-18 1978## 30 Blake Casey 200 74 R R 1999-08-14 1973## playerID yearID stint teamID lgID G G_batting AB R H 2B 3B HR## 6 ardoida01 2008 1 LAN NL 24 24 51 3 12 1 0 1## 9 aurilri01 2008 1 SFN NL 140 140 407 33 115 21 1 10## 19 beimejo01 2008 1 LAN NL 71 69 0 0 0 0 0 0## 24 bennega01 2008 1 LAN NL 10 10 21 1 4 1 0 1## 27 berroan01 2008 1 LAN NL 84 84 226 26 52 13 1 1## 30 blakeca01 2008 2 LAN NL 58 58 211 25 53 12 1 10## RBI SB CS BB SO IBB HBP SH SF GIDP G_old## 6 4 1 0 2 10 0 1 0 0 2 24## 9 52 1 1 30 56 4 1 0 2 11 140## 19 0 0 0 0 0 0 0 0 0 0 69## 24 4 0 0 2 0 0 0 0 0 1 10## 27 16 0 0 20 41 4 4 6 0 13 84## 30 23 1 0 16 52 5 4 0 2 9 58## [到達getOption("max.print") -- 略過134行]]這個函數對於數據的各種複雜抽樣非常方便,但你可能還需要用到更複雜的抽樣方式,比如分層抽樣、整群抽樣、最大熵抽樣,這些方法都可以在sampling包中找到。
匯總函數tapply與aggregatetapply函數用於向量的匯總分析,是一個非常靈活的函數。可以設置對向量X的某個子集做匯總,也可以指定匯總函數:
tapply(X, INDEX, FUN = , ..., simplify = )比如計算各隊本壘打的總數:
tapply(X=batting.2008$HR, INDEX=list(batting.2008$teamID), FUN=sum)## ARI ATL BAL BOS CHA CHN CIN CLE COL DET FLO HOU KCA LAA LAN MIL MIN NYA ## 159 130 172 173 235 184 187 171 160 200 208 167 120 159 137 198 111 180 ## NYN OAK PHI PIT SDN SEA SFN SLN TBA TEX TOR WAS ## 172 125 214 153 154 124 94 174 180 194 126 117也可以使用返回多個值的函數,例如fivenum計算各個聯盟球員擊球平均數:
tapply(batting.2008$H/batting.2008$AB, INDEX=list(batting.2008$lgID), fivenum)## $AL## [1] 0.000 0.176 0.249 0.283 1.000## ## $NL## [1] 0.0000 0.0952 0.2173 0.2680 1.0000plyr包中沒有tapply的等價功能。
by是和tapply有密切關係的一個函數,區別在於by是用於數據框的。下面是一個例子:
by(batting.2008[, c("H", "2B", "3B", "HR")], INDICES = list(batting.2008$lgID, batting.2008$bats), FUN=mean)## Warning in mean.default(data[x, , drop = FALSE], ...): 參數不是數值也不是邏## 輯值:回覆NA
## Warning in mean.default(data[x, , drop = FALSE], ...): 參數不是數值也不是邏## 輯值:回覆NA
## Warning in mean.default(data[x, , drop = FALSE], ...): 參數不是數值也不是邏## 輯值:回覆NA
## Warning in mean.default(data[x, , drop = FALSE], ...): 參數不是數值也不是邏## 輯值:回覆NA
## Warning in mean.default(data[x, , drop = FALSE], ...): 參數不是數值也不是邏## 輯值:回覆NA
## Warning in mean.default(data[x, , drop = FALSE], ...): 參數不是數值也不是邏## 輯值:回覆NA## : AL## : B## [1] NA## - ## : NL## : B## [1] NA## - ## : AL## : L## [1] NA## - ## : NL## : L## [1] NA## - ## : AL## : R## [1] NA## - ## : NL## : R## [1] NA另一個用於數據匯總的函數是aggregate:
aggregate(x, by, FUN, ...)也可以用於時間序列,參數略有不同。
下面看一個按球隊統計擊球數的例子:
aggregate(x=batting.2008[, c("AB", "H", "BB", "2B", "3B", "HR")], by=list(batting.2008$teamID), FUN=sum)## Group.1 AB H BB 2B 3B HR## 1 ARI 5409 1355 587 318 47 159## 2 ATL 5604 1514 618 316 33 130## 3 BAL 5559 1486 533 322 30 172## 4 BOS 5596 1565 646 353 33 173## 5 CHA 5553 1458 540 296 13 235## 6 CHN 5588 1552 636 329 21 184## 7 CIN 5465 1351 560 269 24 187## 8 CLE 5543 1455 560 339 22 171## 9 COL 5557 1462 570 310 28 160## 10 DET 5641 1529 572 293 41 200## 11 FLO 5499 1397 543 302 28 208## 12 HOU 5451 1432 449 284 22 167## 13 KCA 5608 1507 392 303 28 120## 14 LAA 5540 1486 481 274 25 159## 15 LAN 5506 1455 543 271 29 137## 16 MIL 5535 1398 550 324 35 198## 17 MIN 5641 1572 529 298 49 111## 18 NYA 5572 1512 535 289 20 180## 19 NYN 5606 1491 619 274 38 172## 20 OAK 5451 1318 574 270 23 125## 21 PHI 5509 1407 586 291 36 214## 22 PIT 5628 1454 474 314 21 153## 23 SDN 5568 1390 518 264 27 154## 24 SEA 5643 1498 417 285 20 124## 25 SFN 5543 1452 452 311 37 94## 26 SLN 5636 1585 577 283 26 174## 27 TBA 5541 1443 626 284 37 180## 28 TEX 5728 1619 595 376 35 194## [到達getOption("max.print") -- 略過2行]]計數使用tabulate與table函數。
reshape包另起一文單獨寫下,不要將內置的reshape函數與reshape包混淆。
來源:《R核心技術手冊》
參考資料[1]【r<-高級|理論】apply,lapply,sapply用法探索: https://www.jianshu.com/p/9bca3555b06c