《探索性數據分析》翻譯
距離上次聽完課已經一個多月的時間了,面對翻譯這個「工程」確實一個頭等難題,先不說翻譯如何。Hadley Wickham 大牛的論文理論性太強,真的沒有毅力看下去,找了好久,最終決定翻譯《R for Data Science》入門級的書籍中的一章。不免其中有多處翻譯錯誤和不當,請各位看官批評吐槽。原文鏈接R for Data Science。翻譯的好處是,之前不懂的懂了,懂的加深了理解,但是這個真的是很耗時間啊。
7 探索性數據分析7.1 介紹 本章將展示如何使用可視化和轉換以系統的方式來探索數據,這項任務統計學家稱為探索性數據分析,或者簡稱EDA。EDA是一個迭代循環。你將了解到:1.對你的數據質疑。2.通過可視化、轉換和建模數據來探究答案。
3.利用你所學到的東西來精鍊你的問題和/或提出新的問題。 EDA不是一個有嚴格的規則的正式的流程。更重要的是,EDA是一種思維方式。在EDA的最初階段,你應該可以自由地調查每一個發生在你身上的想法。其中一些想法將會得到解決,有些將會是死胡同。隨著你的探索的繼續,你將會在一些特別有成效的領域裡工作,最終你將會寫出並與他人分享。 EDA是任何數據分析的重要組成部分,即使問題是計算機產生的,你也總是需要調查數據的質量。數據清理只是EDA的一個應用:即是為了達到你的數據是否符合你的期望的目的。要進行數據清理,你需要部署EDA的所有工具:可視化、轉換和建模。7.1.1預備知識 在這一章里,我們會結合你所學到dplyr 和ggplot2的函數,用於互動式地提問,用數據解答,然後再問新的問題。library(tidyverse)7.2 問題「沒有常規的統計問題,只有可以質疑的統計規律。」——大衛·考克斯「一個模糊的同時近似正確的答案,遠遠好於一個精準的回答錯了問題。」——約翰·圖基EDA的目標是對數據的理解。最簡單的方法就是用問題作為工具來指導你的研究。當你問一個問題時,這個問題會把你的注意力集中在你的數據集的一個特定部分,並幫助你決定要做哪些圖形、模型或轉換。
EDA從根本上來說是一個創造性的過程。和大多數創造性的過程一樣,關鍵是問高質量的問題才能產生大量的問題。在分析開始時,很難提出一些有啟發性的問題,因為你不知道你的數據集里包含了什麼。另一方面,你問的每一個新問題都會讓你接觸到數據的一個新方面,並增加一個新的發現。如果你繼續研究每一個基於你發現新的問題,你可以很快深入到最有趣的部分數據開發同時產生一系列發人深省的有用的發現。應該問哪些問題來指導你的研究,這是沒有規則的。然而,兩種類型的問題對於在數據分析總是有用的。你可以把這些問題籠統地說成:1.在變數中發生了什麼類型的變化?2.變數之間有什麼類型的協變?本章剩下的部分將討論這兩個問題。我將解釋變異和共變是什麼,我將向你們展示幾種回答每個問題的方法。為了讓討論更簡單,我們來定義一些術語:變數,是可以測量的數量、質量或屬性。值,是一個變數在測量時的狀態。一個變數的值可能會在測試的過程中產生變化。觀察報告,是一組在相似條件下進行的測量(通常在同一時間和同一對象上觀察到的所有測量值)。一個觀察報告將包含幾個值,每個值都與一個不同的變數相關聯。我有時會把觀察作為數據點。表格數據是一組值,每個值都與一個變數和一個觀察值相關聯。如果每個值都放在自己的「單元格」中,每個變數在自己的列中,並且在自己的行中進行觀察,那麼表格數據就會很整潔。到目前為止,您所看到的所有數據都是整潔的。在現實生活中,大多數數據都不整潔,所以我們會在整理數據時再次回到數據清理的思路中。7.3變化
變化是指變數的值在測量的過程中的變化趨勢。在現實生活中你可以很容易地看到變化;如果你測量任何連續變數兩次,你就會得到兩個不同的結果。這是對的,即使你測量的量是恆定的,就像光速一樣。您的每一個測量值都會包含一個小的誤差,這個誤差會隨測量的測量而變化。不同的測量對象,即使確定的變數也可以不同 (如眼睛顏色不同的人),或不同的時間(例如,電子的能量水平在不同的時刻)。每個變數都有自己的變異模式,可以揭示有趣的信息。理解該模式的最佳方法是將變數的值的分布可視化。7.3.1可視化分布如何使一個變數的進行可視化分布取決於這個變數是離散的還是連續的。如果一個變數只能取一小部分的值,那麼它是離散的。在R中,離散變數通常被保存為因子或字元向量。要展示離散變數的分布,請使用條形圖:Ggplot(data = diamonds) +Geom_bar(mapping = aes(x = cut))柱的高度顯示了每一個x值的觀察值。diamonds %>% count(cut)#> # A tibble: 5 × 2#> cut n#> <ord> <int>#> 1 Fair 1610#> 2 Good 4906#> 3 Very Good 12082#> 4 Premium 13791#> 5 Ideal 21551
如果一個變數可以取任意一組無限的有序值,那麼它是連續的。數字和日期時間是連續變數的兩個例子。為了展示一個連續變數的分布,使用直方圖:
ggplot(data = diamonds) + geom_histogram(mapping = aes(x = carat), binwidth = 0.5)
可以通過dplyr::count()函數和ggplot2::cut_width()函數共同來計算
diamonds %>% count(cut_width(carat, 0.5))#> # A tibble: 11 × 2#> `cut_width(carat, 0.5)` n#> <fctr> <int>#> 1 [-0.25,0.25] 785#> 2 (0.25,0.75] 29498#> 3 (0.75,1.25] 15977#> 4 (1.25,1.75] 5313#> 5 (1.75,2.25] 2002#> 6 (2.25,2.75] 322#> # ... with 5 more rows
柱狀圖將x軸分成等間距的柱形,然後使用柱狀圖的高度來顯示每個柱形的採樣點數量。在上面的圖中,最高的柱子顯示,近30000個的採樣點在0.25克拉和0.75克拉之間。
可以使用binwidth參數設置一個直方圖柱形的寬度,該參數是用x變數的單位來度量的。應該嘗試使用不同的binwidths的值來製作直方圖,不同binwidths可以揭示不同的模式。例如,當我們放大選取更小的binwidths同時僅統計小於3克拉的鑽石,上面的圖就是這樣的。
smaller <- diamonds %>% filter(carat < 3) ggplot(data = smaller, mapping = aes(x = carat)) + geom_histogram(binwidth = 0.1)
如果需要在同一張圖中疊加多個直方圖時,建議使用geom_freqpoly() 函數來代替 geom_histogram()。geom_freqpoly()可以得出和geom_histogram()相同的計算結果,但是它可以使用折線來替代直方圖。它比直方圖更加直觀。
ggplot(data = smaller, mapping = aes(x = carat, colour = cut)) + geom_freqpoly(binwidth = 0.1)
現在你可以將變數可視化,你能從你的圖形中找到什麼?同時你能依據你的圖形提出什麼更深層次的問題?我將最有價值的可以從圖形中得到的信息和進一步可以提出的問題整理成一個清單。這個關鍵依賴於你的好奇心(你想要更深入的學習什麼?)和你的懷疑的態度(你是如何被帶入歧途的?)。
7.3.2 典型變數在條形圖和柱狀圖中,高的柱子表示更頻繁出現的值,而矮的柱子表示不頻繁出現的值。在沒有柱子的區間表示在數據裡邊沒有出現這個區間的變數。為了把這個信息轉換成有用的問題,尋找一些額外的東西:哪些變數最頻繁的出現?為什麼?哪些變數最罕見出現?為什麼?這和你的預期是否一致?你是否發現非常規的圖形?這能說明什麼?舉個例子,下面的直方圖暗示了一些有意思的問題:為什麼磚石重量都分屬於整個克拉值中?為什麼在一小部分的值出現很頻繁?為什麼磚石重量在峰值的右邊的值要大於左邊的值?
為什麼沒有大於3克拉的磚石?ggplot(data = smaller, mapping = aes(x = carat)) + geom_histogram(binwidth = 0.01)
ggplot(data = faithful, mapping = aes(x = eruptions)) + geom_histogram(binwidth = 0.25)
上面的許多問題會加深你對變數之間的關係的認識。比如,能否得出一個變數的值可以解釋另一個變數的變化。我們將在很快學習它。
7.3.3異常變數離群的觀察值是異常變數,即一些數據點似乎在圖形里不太和諧。一些離群的數據甚至是完全錯誤的,另外一些離群的數據可以得到新的科學領域。當你有海量的數據時,離群數據有時候很難在圖形里看出來。例如,用y變數的關於磚石分布的圖形中,由於x軸寬度的限制了唯一可以證明離群變數的條件。
ggplot(diamonds) + geom_histogram(mapping = aes(x = y), binwidth = 0.5)
有很多在非常矮小的柱形中的觀察值,我們無法看到它們(除非你非常仔細觀察接近0的值,才有可能發現它)。為了容易的發現這些異常變數,我們需要使用coord_cartesian()函數來放大y軸的值.
ggplot(diamonds) + geom_histogram(mapping = aes(x = y), binwidth = 0.5) + coord_cartesian(ylim = c(0, 50))
在0, ~30, 和 ~60之間,有部分異常數據,我們用dplyr來把他們剔除。
unusual <- diamonds %>% filter(y < 3 | y > 20) %>% select(price, x, y, z) %>% arrange(y)unusual#> # A tibble: 9 × 4#> price x y z#> <int> <dbl> <dbl> <dbl>#> 1 5139 0.00 0.0 0.00#> 2 6381 0.00 0.0 0.00#> 3 12800 0.00 0.0 0.00#> 4 15686 0.00 0.0 0.00#> 5 18034 0.00 0.0 0.00#> 6 2130 0.00 0.0 0.00#> 7 2130 0.00 0.0 0.00#> 8 2075 5.15 31.8 5.12#> 9 12210 8.09 58.9 8.06
y變數測量的是這些鑽石的三個維度中的一個,我們知道鑽石的寬度不能是0,所以這些值一定是不正確的。我們也懷疑測量值為32毫米和59毫米是很難讓人信服的,因為這些鑽石在一英寸長,不應該花費數十萬美元。
在沒有異常值的情況下重複的分析是一項很好的實踐。如果它們對結果的影響很小,並且你不知道它們為什麼存在,那麼用缺失的值替換它們是合理的,然後繼續前進。然而,如果它們對你的結果有很大的影響,你就不應該毫無理由地放棄它們。你需要找出是什麼導致了他們(如數據輸入錯誤)並記錄你刪除他們所帶來的損失。7.4 缺失值如果你需要在數據組裡邊異常變數,繼續下面的分析,你有兩個方法:1.將含有除特殊值的整行去除diamonds2 <- diamonds %>% filter(between(y, 3, 20))
不建議這個方法,因為某個單一的值是異常的,不代表整行都是有問題的。另外,在數據量不大的情況下,有可能這種方法會把全部數據移除,這樣就沒有數據可以使用了。
2.除第一種方法外,推薦使用缺失值來替代異常變數。最簡單的方法是使用mutate()函數來替換需要更改的變數。可以使用ifelse()函數將異常數據修改為NAdiamonds2 <- diamonds %>% mutate(y = ifelse(y < 3 | y > 20, NA, y))
ifelse()函數有三個參數。第一個test是一個邏輯矢量,結果是放在第二個參數裡邊的,如果邏輯為正確,結果是yes的值,如果邏輯錯誤,結果是no的值。
在R語言中,ggplot2函數認為缺失值永遠不會在毫無提示的情況下丟棄。缺失值不是很容易的展現在圖表中,因為ggplot2不會將它們囊括其中,但是會在告警中說明它們被移除了。ggplot(data = diamonds2, mapping = aes(x = x, y = y)) + geom_point()#> Warning: Removed 9 rows containing missing values (geom_point).ggplot(data = diamonds2, mapping = aes(x = x, y = y)) + geom_point()#> Warning: Removed 9 rows containing missing values (geom_point).為了解決這個告警,需要增加設置na.rm = TRUEggplot(data = diamonds2, mapping = aes(x = x, y = y)) + geom_point(na.rm = TRUE)
在一些情況下,你需要知道在有缺失值的觀察結果和僅有有記錄值得觀察有何異同。例如,在nycflights13::flights數據中,缺失值dep_time表示這個航班已經取消。因此,你可能需要將按照正常時間起飛和航班和已經取消的航班進行比較。你可以使用is.na()函數來進行這個操作:
但是這個圖標也不是特別好的,因為沒有取消的航班數目大於取消航班數。在下節我們將繼續研究這種比較的改進方法。7.5協變正如變化是描述變數自身的行為,協變是描述變數之間的行為。協變是指兩個或兩個以上的變數共同變化的相關性的趨勢。最好的方法展現協變是將多個變數在圖表中展現出來。具體怎麼做需要依據涉及到的變數的特性。7.5.1離散變數和連續變數
通常情況下,如前文多次所述圖例,將一個連續的變數拆解成離散變數進行分散式的可視化。這種情況下,geom_freqpoly()函數就無法實現,因為它的高度表示是計數值。這意味著,一個組的值大大小於其他的值,在圖形中就很難觀察到。例如,我們來看下磚石的價格和它的質量的關係:ggplot(data = diamonds, mapping = aes(x = price)) + geom_freqpoly(mapping = aes(colour = cut), binwidth = 500)這個圖標就很難看出不同的數量,因為他們相差太大。ggplot(diamonds) + geom_bar(mapping = aes(x = cut))
為了使這種比較更明顯,我們需要更改y軸的值。用density來代替計數。
ggplot(data = diamonds, mapping = aes(x = price, y = ..density..)) + geom_freqpoly(mapping = aes(colour = cut), binwidth = 500)
在每個盒子里有一根線,貫穿除了離群點的最大和最小值。
讓我們來觀察下,在使用geom_boxplot()函數來展示價格和樣式的關係。
ggplot(data = diamonds, mapping = aes(x = cut, y = price)) + geom_boxplot()boxplot可以更加簡潔的展示無法在分散式圖例的一些信息,因為我們可以更加容易的進行比較分析。它更直觀的說明了高質量的磚石的平均價格要比低質量的價格低。在練習中,你將嘗試去分析為什麼。Cut是一個有序因子。例如,Fair的質量比good差,good比very good差。許多練習變數沒有這個天然的有序關係,因此,你需要對他們重新排序。一個方法就是使用reorder()函數進行排序。例如,在mpg數據組中取出class變數,你可能想知道不同型號的在高速的油耗。ggplot(data = mpg, mapping = aes(x = class, y = hwy)) + geom_boxplot()
ggplot(data = mpg) + geom_boxplot(mapping = aes(x = reorder(class, hwy, FUN = median), y = hwy))
如果變數含有很長的名字,geom_boxplot() 函數可以將圖標轉置,使用coord_flip()函數。
ggplot(data = mpg) + geom_boxplot(mapping = aes(x = reorder(class, hwy, FUN = median), y = hwy)) + coord_flip()
7.5.2兩個離散變數
為了可視化離散變數的協變關係,需要計算每種組合的觀察值的數目。一個方法是使用ggplot(data = diamonds) + geom_count(mapping = aes(x = cut, y = color))
圖表中圓形的大小可以展示每種組合的觀察的數目。協變關係將出現特殊的x軸和特殊的y軸中。
另一種方法是使用dplyr函數diamonds %>% count(color, cut)#> Source: local data frame [35 x 3]#> Groups: color [?]#> #> color cut n#> <ord> <ord> <int>#> 1 D Fair 163#> 2 D Good 662#> 3 D Very Good 1513#> 4 D Premium 1603#> 5 D Ideal 2834#> 6 E Fair 224#> # ... with 29 more rows
然後用geom_tile() 函數來展示。
diamonds %>% count(color, cut) %>% ggplot(mapping = aes(x = color, y = cut)) + geom_tile(mapping = aes(fill = n))
這個離散變數是沒有排序的,需要使用離散的包函數來同時將行和列排序,這樣會更加清楚的看到圖表所揭示的問題。在更大的圖表中,需要使用d3heatmap 或者 heatmaply 包來創建表格。
7.5.3兩個連續變數你已經看到了一個可以可視化在兩個連續變數的協變關係的非常好的方法,即使用geom_point()函數。例如,下表展示了磚石的大小和價格的指數關係。ggplot(data = diamonds) + geom_point(mapping = aes(x = carat, y = price))
散點圖在大量數據的圖標中變的不是特別有效,因為,大量的點疊加起來,變成了上圖中的一片黑色區域。你已經知道了一種方法去解決這個問題,用alpha參數去區分數值的透明度。
ggplot(data = diamonds) + geom_point(mapping = aes(x = carat, y = price), alpha = 1 / 100)
但是使用透明度的方法在面臨極大的數據組時也會存在一定困難。另外一個解決方法是使用bin參數。之前,已經使用了geom_histogram() 和geom_freqpoly()把數組放入容器中。現在,我們學習使用geom_bin2d() 和geom_hex() 去放入兩個不同的容器中。
geom_bin2d() 和geom_hex() 將坐標軸分為第二個容器,使用不同的顏色來區分有多少點在容器中。geom_bin2d() 創建了句型的容器,而geom_hex() 創建了六邊形的容器。你需要安裝hexbin包來運行他們。ggplot(data = smaller) + geom_bin2d(mapping = aes(x = carat, y = price))# install.packages("hexbin")ggplot(data = smaller) + geom_hex(mapping = aes(x = carat, y = price))
另一個策略是將連續變數看成離散變數來分析。然後您可以使用一個已經學過的將一個離散變數和連續變數進行可視化的方法來分析。例如,可以先將磚石的大小放入容器,然後為每個組,顯示一個箱線圖:
ggplot(data = smaller, mapping = aes(x = carat, y = price)) + geom_boxplot(mapping = aes(group = cut_width(carat, 0.1)))
如上圖所述,cut_width(x, width)函數是將x軸按照寬度放入不同的容器。默認情況下,boxplot不管有多少採樣點,看起來基本一樣(去除離群的數據),因此就很難來對不同的採樣點的數據求和。一個方法是使用varwidth = TRUE的方法來讓boxplot圖標成比例的。
另一種方法是將近似的採樣點放入一個容器中。使用cut_number()函數。ggplot(data = smaller, mapping = aes(x = carat, y = price)) + geom_boxplot(mapping = aes(group = cut_number(carat, 20)))
7.6模式和模型
在數據中的模式提供了數據之間關係的線索。如果在兩個變數存在系統級的關係,那麼在數據中就會出現一種模式。如果你識別出一種模式,問下面幾個問題:這種模式是湊巧的嗎?(隨機出現的?)通過模式如何分析變數之間的關係?其他的變數對數據的關係有何影響?如果單獨觀察一個數據的子組,這種關係是否改變?一個關於老忠實噴泉散點圖中,噴泉長度和間隔時間有一種模式:等待時間越久得到更長的噴發。這個散點圖也展示了下面兩個組的數據。ggplot(data = faithful) + geom_point(mapping = aes(x = eruptions, y = waiting))
散點圖提供了一個數據分析更加有用的工具,原因是他揭示了變數的協變關係。如果你認為變數是一種隨機的現象,那麼協變關係是可以降低隨機性的現象。如果兩個變數是公變的,你可以使用一個變數來更好的預測另一個變數。如果共變關係是一種因果關係,那麼你就可以用一個數量來控制另一個數據。
模型是提取數據模式的工具。就拿磚石的數據來舉例子。我們很難理解樣式和價格的關係,由於樣式和重量,重量和價格是緊密聯繫起來的。那麼使用一種模型來去除價格和重量的強關係後,我們可以發現剩餘變數的微妙關係。下面的代碼使用一種模型來由重量來預測價格,然後計算出它的合理性(預測值和真實值得差異)。一旦磚石重量的影響被移除後,我們可以得到一種磚石價格的視角。library(modelr)mod <- lm(log(price) ~ log(carat), data = diamonds)diamonds2 <- diamonds %>% add_residuals(mod) %>% mutate(resid = exp(resid))ggplot(data = diamonds2) + geom_point(mapping = aes(x = carat, y = resid))
一旦你已經刪除了重量和價格之間的強關係,你可以看到你希望樣式和價格之間的關係:相對於它們的大小,更好質量的鑽石更昂貴。
ggplot(data = diamonds2) + geom_boxplot(mapping = aes(x = cut, y = resid))
推薦閱讀:
※大數據怎麼搞?
※大數據在金融領域是如何應用的?
※【徵集】讓我們一起玩#別人的#數據研究人體
※天雲大數據,如何在Hadoop基礎設施上玩出人工智慧新花樣
※人大的CDA數據分析師培訓以及考試怎麼樣?
TAG:大数据 |