如果一幅圖勝過(guò)千言萬(wàn)語(yǔ)尼啡,那么一幅會(huì)動(dòng)的圖呢?
需求
繪制統(tǒng)計(jì)圖形询微,是為了給誰(shuí)看崖瞭?
顯然不是給電腦看。
因?yàn)樗床欢琶矝](méi)必要看书聚。給它數(shù)據(jù)就好了。它理解起來(lái)藻雌,更準(zhǔn)確雌续。
繪制統(tǒng)計(jì)圖形,是給人看的胯杭。
可以給別人看驯杜。例如合作者、讀者做个、審稿人鸽心,或者演講時(shí)的觀眾滚局。
但更多的情況,圖也是給自己看的顽频。
為什么要畫(huà)圖藤肢?
因?yàn)槊苊苈槁榈臄?shù)字或符號(hào),遠(yuǎn)不如一幅圖像冲九,看得清楚和舒服谤草。
人類(lèi)中的大多數(shù),目前還沒(méi)有進(jìn)化出對(duì)海量原始數(shù)據(jù)莺奸,條件反射一般的理解能力丑孩。
漫長(zhǎng)的演化史上,人類(lèi)的感官只要能有效發(fā)現(xiàn)食物(包含獵物)灭贷,快速捕獲危險(xiǎn)信號(hào)(例如捕食者逼近)温学,和同類(lèi)高效交流(使用聲音、表情或肢體語(yǔ)言)就大概率可以在殘酷的自然淘汰賽里幸存下來(lái)甚疟。
不得不從財(cái)務(wù)報(bào)表這樣的密集數(shù)據(jù)里仗岖,發(fā)現(xiàn)機(jī)會(huì)和風(fēng)險(xiǎn),是最近幾百年才有的事兒览妖。
巴菲特和芒格這樣的投資大家轧拄,也許有這種超能力。
但這種能力讽膏,顯然不是所有人的標(biāo)配檩电。
對(duì)普通人來(lái)說(shuō),理解大量的數(shù)據(jù)府树,統(tǒng)計(jì)圖形很必要俐末。因此人們常說(shuō),“一幅圖勝過(guò)千言萬(wàn)語(yǔ)”奄侠。
在《如何用Python從海量文本抽取主題卓箫?》一文里,我給你展示過(guò)如何繪制主題挖掘圖形垄潮。
而《如何用Python和R對(duì)故事情節(jié)做情緒分析烹卒?》一文中,我給你介紹了如何繪制故事情緒時(shí)間序列弯洗。
如你所見(jiàn)甫题,這些圖很有用。
但是它們只是靜態(tài)的涂召。
那么,如果圖是動(dòng)態(tài)的呢敏沉?
那至少果正,它能夠給我們提供更多一個(gè)維度的信息炎码。
這種功能,真的有用嗎秋泳?
我這里給你看一個(gè)例子潦闲。
這幅動(dòng)態(tài)統(tǒng)計(jì)圖,描繪了世界不同區(qū)域迫皱,人均 GDP 和預(yù)期壽命之間的關(guān)聯(lián)歉闰。隨著左上角年份的不斷變化,你會(huì)看到幾十年來(lái)卓起,這個(gè)世界的發(fā)展變化和敬。
Hans Rosling 曾經(jīng)用類(lèi)似的數(shù)據(jù)和動(dòng)畫(huà)效果,做了非常精彩的 TED 演講戏阅。我上課的時(shí)候昼弟,不止一次拿來(lái)作為演示樣例,讓學(xué)生揣摩學(xué)習(xí)奕筐。
如果你感興趣的話(huà)舱痘,可以點(diǎn)擊這個(gè)鏈接查看視頻。
你知道嗎离赫?只需要短短10行語(yǔ)句芭逝,你也能自己繪制出這個(gè)圖形。
不過(guò)我們學(xué)東西渊胸,不宜貪多求快旬盯。
要繪制上圖,你需要了解相關(guān)的基礎(chǔ)知識(shí)蹬刷。一下子攝入很多新知瓢捉,可能造成認(rèn)知負(fù)荷,對(duì)你的學(xué)習(xí)興趣沒(méi)有益處办成。
本文中泡态,我用一個(gè)更簡(jiǎn)單的例子,給你展現(xiàn)如何用 R 繪制動(dòng)態(tài)統(tǒng)計(jì)圖迂卢。
有了它作為基礎(chǔ)某弦,結(jié)合我給你推薦的相關(guān)學(xué)習(xí)資源,你也能很快做出更為實(shí)用而克,甚至是令人驚艷的動(dòng)圖靶壮。
環(huán)境
你不需要安裝任何軟件。只需要點(diǎn)擊這個(gè)鏈接(http://t.cn/ReaP9Mk)员萍,就可以使用 R 編程環(huán)境了腾降。
等準(zhǔn)備工作完畢,你會(huì)看到碎绎,瀏覽器里面開(kāi)啟了一個(gè) RStudio 界面螃壤。
你如果時(shí)間緊迫抗果,不想輸入任何代碼,卻又想馬上看到運(yùn)行結(jié)果奸晴,可以點(diǎn)擊左上角的 File
-> Open File
冤馏,并且從出現(xiàn)的文件列表中,選擇 code.Rmd
寄啼。
你就能看見(jiàn)下圖這樣打開(kāi)該文件后的結(jié)果逮光。
Rmd
文件后綴,代表 R Markdown
墩划,是 RStudio 這個(gè) IDE 上可以使用的一種特殊的 Markdown 文件涕刚。說(shuō)它特殊,是因?yàn)槠渲械拇a段落走诞,可以直接運(yùn)行出結(jié)果副女。
界面左上方這里,有一個(gè)毛線(xiàn)球形狀的按鈕蚣旱,名稱(chēng)叫做 Knit
碑幅,點(diǎn)擊一下,它會(huì)把這個(gè) code.Rmd
文件塞绿,轉(zhuǎn)換成 HTML 沟涨,并且其中全部的代碼,都顯示出運(yùn)行結(jié)果來(lái)异吻。
如果你沒(méi)有那么急裹赴,就請(qǐng)按照我下面的說(shuō)明來(lái)操作。根據(jù)教程诀浪,一步步手動(dòng)輸入語(yǔ)句棋返。這樣更有助于你的理解,收獲會(huì)更大雷猪。
點(diǎn)擊左上角的 File
-> New File
睛竣,選擇菜單里面的第一項(xiàng) R Script
。
此時(shí)求摇,你會(huì)看到左側(cè)分欄一個(gè)空白編輯區(qū)域開(kāi)啟射沟,可以輸入語(yǔ)句了。
輸入之前与境,我們先給文件起個(gè)名字验夯。點(diǎn)擊 File
-> Save
按鈕。
在新出現(xiàn)的對(duì)話(huà)框里面摔刁,輸入 demo
挥转,回車(chē)。
好了,下面就可以輸入并運(yùn)行代碼了绑谣。
代碼
首先准潭,我們需要讀入幾個(gè)必要的軟件包:
library("tidyverse")
library("lubridate")
library("gganimate")
如果你看過(guò)我的《如何用R和API免費(fèi)獲取Web數(shù)據(jù)?》一文域仇,對(duì)于 tidyverse
應(yīng)該并不陌生。它是大神 Hadley 等人共同開(kāi)發(fā)的一系列 R 工具包合集寺擂。對(duì)我來(lái)說(shuō)暇务,它改變了之前 R 語(yǔ)言"難以學(xué)習(xí)"、"語(yǔ)法古怪"怔软、"不好使用"等刻板印象垦细。
lubridate
是用來(lái)處理時(shí)間數(shù)據(jù)的 R 軟件包。如果沒(méi)有這東西挡逼,你每次操作時(shí)間數(shù)據(jù)括改,都會(huì)麻煩許多。
gganimate
顧名思義家坎,后面我們繪制動(dòng)態(tài)圖形嘱能,需要用到。
下面看看我們這次使用的數(shù)據(jù)虱疏。
數(shù)據(jù)保存的格式是 .RData
惹骂,需要使用 load()
函數(shù)讀入。
load('carriers_jan.RData')
讀入以后做瞪,保存在其中的一個(gè)數(shù)據(jù)框變量 carriers_jan
就復(fù)活了对粪。下面我們看看其內(nèi)容:
carriers_jan
## # A tibble: 93 x 3
## mydate carrier n
## <date> <chr> <int>
## 1 2013-01-01 AA 94
## 2 2013-01-01 DL 112
## 3 2013-01-01 UA 165
## 4 2013-01-02 AA 94
## 5 2013-01-02 DL 152
## 6 2013-01-02 UA 170
## 7 2013-01-03 AA 95
## 8 2013-01-03 DL 128
## 9 2013-01-03 UA 159
## 10 2013-01-04 AA 95
## # ... with 83 more rows
我們解釋一下該數(shù)據(jù)的內(nèi)容。
這個(gè)數(shù)據(jù)實(shí)際上是從《如何用4行 R 語(yǔ)句装蓬,快速探索你的數(shù)據(jù)集著拭?》一文中的 nycflights13
數(shù)據(jù)集,通過(guò)轉(zhuǎn)換得來(lái)的牍帚。
轉(zhuǎn)換后的數(shù)據(jù)儡遮,統(tǒng)計(jì)了不同航空公司在2013年1月,每一天從紐約三大機(jī)場(chǎng)起飛航班次數(shù)履羞。
為了簡(jiǎn)便峦萎,我們?cè)谶@個(gè)數(shù)據(jù)集里,只保留了3家航空公司忆首,即:
- 美國(guó)航空(American Airlines爱榔,AA)
- 達(dá)美航空(Delta Air Lines, DL)
- 美聯(lián)航(United Airlines, UA)
下面我們挑出1月1日的數(shù)據(jù)看看:
carriers_jan %>%
filter(mydate == ymd('20130101'))
## # A tibble: 3 x 3
## mydate carrier n
## <date> <chr> <int>
## 1 2013-01-01 AA 94
## 2 2013-01-01 DL 112
## 3 2013-01-01 UA 165
可見(jiàn),這一天里糙及,美國(guó)航空起飛航班 94 架次详幽,達(dá)美 112 ,美聯(lián)航為 165 。
根據(jù)上表唇聘,我們繪制一張柱狀圖(bar chart)版姑。
橫坐標(biāo)是航空公司名稱(chēng),是分類(lèi)數(shù)據(jù)迟郎;縱坐標(biāo)是航班次數(shù)剥险,是量化數(shù)據(jù)。
carriers_jan %>%
filter(mydate == ymd('20130101')) %>%
ggplot(aes(x=carrier, y=n, fill=carrier)) +
geom_bar(stat='identity', position='identity')
如上圖所示宪肖,三家航空公司從紐約機(jī)場(chǎng)起飛次數(shù)表制,分別采用了不同顏色柱狀圖進(jìn)行了可視化。
紅色是美國(guó)航空控乾,綠色是達(dá)美航空么介,藍(lán)色是美聯(lián)航。
簡(jiǎn)單解釋一下其中的 ggplot
語(yǔ)句蜕衡。
ggplot2
也是 Hadley Wickham 的作品壤短,屬于 tidyverse
軟件包的一部分。
它將 Leland Wilkinson 提出的"繪圖語(yǔ)法"(Grammar of Graphics)在 R 語(yǔ)言上實(shí)現(xiàn)慨仿。
在《如何用 Python 和 API 收集與分析網(wǎng)絡(luò)數(shù)據(jù)久脯?》一文中,我們已經(jīng)介紹過(guò) ggplot2
的 Python 克孪馄(plotnine)桶现,所以這里就不贅述背景了。
你只要記住鼎姊,它繪制圖形的時(shí)候骡和,采用的是"分層"機(jī)制就好。
ggplot(aes(x=carrier, y=n, fill=carrier))
這一句講述映射(mapping)關(guān)系相寇,指定了把 carrier
信息投射到 x 軸慰于, n
(航班次數(shù))投射到 y 軸,用不同 carrier
類(lèi)別填充不同的色彩唤衫。
但是單單這一句婆赠,實(shí)際上是繪制不出東西來(lái)的,不信你可以嘗試執(zhí)行一下:
carriers_jan %>%
filter(mydate == ymd('20130101')) %>%
ggplot(aes(x=carrier, y=n, fill=carrier))
請(qǐng)注意這個(gè)圖里佳励, x 軸和 y 軸的設(shè)置休里,都與我們的預(yù)期一致。但是任何實(shí)質(zhì)性?xún)?nèi)容赃承,都沒(méi)有繪制出來(lái)妙黍。因?yàn)樵蹅冞€沒(méi)有告訴 ggplot ,打算畫(huà)一個(gè)什么類(lèi)別的統(tǒng)計(jì)圖形瞧剖。
這就是下一句 geom_bar(stat='identity', position='identity')
的用處拭嫁。
這句話(huà)告訴 ggplot
可免,請(qǐng)繪制柱狀圖,柱的高度按照 y 值設(shè)置做粤,對(duì)應(yīng) x 上每一個(gè)取值(航空公司名稱(chēng))浇借,分別繪制一根柱。
這張靜態(tài)圖怕品,只能告訴我們2013年1月1日這一天妇垢,紐約機(jī)場(chǎng)這3個(gè)航空公司起飛航班數(shù)量信息。
假如我們想多了解一個(gè)維度肉康,也就是把時(shí)間加進(jìn)去修己,怎么辦?
這里辦法并不唯一迎罗。
最簡(jiǎn)單的常規(guī)方法,是把三維信息壓縮到二維平面里面去片仿。
因?yàn)槲覀兛炊S圖像纹安,除了能觀察到位置區(qū)別之外,還可以辨識(shí)色彩砂豌。
利用下列語(yǔ)句厢岂,你可以把這張圖輕松做出來(lái)。
carriers_jan %>%
ggplot(aes(x=mydate, y=n, color=carrier)) +
geom_point() + geom_line()
注意阳距,這里因?yàn)槲覀儾辉侔褧r(shí)間限定在1月1日了塔粒,因此你得把 filter(mydate == ymd('20130101'))
這一句去掉橄仆,使用全部1個(gè)月的時(shí)間桐筏。否則使用時(shí)間軸就沒(méi)有意義了。
這里的 ggplot(aes(x=mydate, y=n, color=carrier))
酥筝,你應(yīng)該能觀察到跟之前的圖形間咖熟,映射關(guān)系的差別圃酵。
不同于上一幅圖,我們把 mydate
馍管,而不是 carrier
映射到了 x 軸郭赐。 y 軸的映射關(guān)系沒(méi)有變化。
我們此次不打算繪制柱狀圖了确沸,而是描繪隨時(shí)間變化趨勢(shì)捌锭,所以選用的是散點(diǎn)圖(geom_point()
)+折線(xiàn)圖(geom_line()
)。
這就意味著罗捎,再考慮柱狀圖里面的填充观谦,就不恰當(dāng)了,所以我們把 carrier
的信息宛逗,映射到顏色上去(color=carrier
)坎匿。
從這張圖里,你可以發(fā)現(xiàn)非常顯著的規(guī)律性。
假如你不想這樣壓縮信息替蔬,而希望用圖形隨時(shí)間的動(dòng)態(tài)變化告私,來(lái)體現(xiàn)附加的時(shí)間維度,該怎么辦承桥?
這時(shí)驻粟,你就需要使用 gganimate
這個(gè)動(dòng)畫(huà)包的功能了。
gganimate
目前的開(kāi)發(fā)維護(hù)者凶异,是 Thomas Lin Pedersen 蜀撑。這是他的 github 頁(yè)面地址。
他把原先的 gganimate
包接管了過(guò)來(lái)剩彬,仿照 ggplot
的風(fēng)格酷麦,對(duì)語(yǔ)法進(jìn)行了修改和補(bǔ)充,使其能夠無(wú)縫融入到 ggplot
語(yǔ)句里喉恋,很方便地調(diào)用沃饶。
因?yàn)榭梢杂脛?dòng)態(tài)體現(xiàn)時(shí)間維度,所以我們這次依然繪制柱狀圖轻黑。語(yǔ)句如下:
carriers_jan %>%
ggplot(aes(x=carrier, y=n, fill=carrier)) +
geom_bar(stat='identity', position='identity') +
transition_time(mydate)
圖動(dòng)起來(lái)了糊肤,是吧?
解釋一下語(yǔ)句氓鄙。
與之前靜態(tài)柱狀圖的區(qū)別馆揉,也是去掉了時(shí)間的限定那一句 filter(mydate == ymd('20130101'))
,以便描繪整個(gè)兒一月份的情況抖拦。
另一個(gè)顯著差別升酣,是加入了最后一行語(yǔ)句, transition_time(mydate)
态罪,這也是圖像能夠動(dòng)起來(lái)的關(guān)鍵拗踢。
根據(jù) gganimate
官方的說(shuō)明,圖形轉(zhuǎn)換可以有多個(gè)不同類(lèi)型語(yǔ)句來(lái)控制向臀。因?yàn)槲覀兦『糜?mydate
這個(gè)時(shí)間數(shù)據(jù)列巢墅,所以可以使用最自然而簡(jiǎn)單的 transition_time()
方法。
transition_time(mydate)
根據(jù)時(shí)間信息對(duì)數(shù)據(jù)框進(jìn)行切片券膀,然后分別加以展示君纫。圖像因而動(dòng)了起來(lái)。
不過(guò)芹彬,這里有個(gè)很?chē)?yán)重的問(wèn)題------你根本就看不清蓄髓,當(dāng)前的動(dòng)態(tài)結(jié)果對(duì)應(yīng)哪個(gè)時(shí)間。對(duì)不對(duì)舒帮?
咱們需要改進(jìn)一下会喝。
改進(jìn)的方法很簡(jiǎn)單:加入圖片標(biāo)題陡叠,顯示時(shí)間,并且讓標(biāo)題對(duì)應(yīng)著一起變化肢执。
修改后的代碼如下:
carriers_jan %>%
ggplot(aes(x=carrier, y=n, fill=carrier)) +
geom_bar(stat='identity', position='identity') +
transition_time(mydate) +
labs(title='{frame_time}')
這下枉阵,你一眼就可以從標(biāo)題中,看到當(dāng)前動(dòng)圖對(duì)應(yīng)的時(shí)間了预茄。
這里我們用到了 ggplot
的 labs()
函數(shù)兴溜,這個(gè)函數(shù)負(fù)責(zé)圖片的標(biāo)記設(shè)定,除了標(biāo)題以外耻陕,你還可以設(shè)置橫縱軸說(shuō)明等內(nèi)容拙徽。
我們用 title
參數(shù)設(shè)置標(biāo)題內(nèi)容。標(biāo)題需要變化诗宣,所以我們得傳入一個(gè)可以變化的量給 title
參數(shù)膘怕。
我們傳入的是 {frame_time}
,這就是我們剛才提到的召庞, gganimate
自動(dòng)切片所用的時(shí)間數(shù)據(jù)淳蔼。 傳入?yún)?shù)時(shí),不要忘了需要將其包裹在雙引號(hào)里裁眯,作為字符串類(lèi)型傳入。
小結(jié)
本文給你展示了 R 環(huán)境繪制動(dòng)態(tài)統(tǒng)計(jì)圖的方法讳癌,具體包含以下知識(shí)點(diǎn):
- 如何讀入
.RData
格式的數(shù)據(jù)文件穿稳; - 如何利用
ggplot
命令映射變量,選擇統(tǒng)計(jì)圖類(lèi)型(包括柱狀圖晌坤、散點(diǎn)圖和折線(xiàn)圖等)逢艘; - 如何使用
gganimate
的transition_time()
方法繪制基于時(shí)間數(shù)據(jù)的動(dòng)態(tài)圖; - 如何通過(guò)
labs
設(shè)置骤菠,動(dòng)態(tài)顯示時(shí)間它改,以便于和圖像的變化對(duì)應(yīng)。
為了展示樣例的最小化商乎,本文的動(dòng)態(tài)統(tǒng)計(jì)圖非常簡(jiǎn)單央拖,技術(shù)含量并不高。
拋磚引玉鹉戚。希望你舉一反三鲜戒,繪制出更有價(jià)值、內(nèi)容也更加豐富的動(dòng)態(tài)統(tǒng)計(jì)圖來(lái)抹凳。
如果你對(duì) ggplot2
繪圖包感興趣遏餐,想詳細(xì)了解其語(yǔ)法,可以讀作者 Hadley Wickham 自己寫(xiě)的書(shū)《ggplot2:數(shù)據(jù)分析與圖形藝術(shù)》赢底。
如果你想了解 gganimate 包的更多用法失都,可以閱讀官方文檔柏蘑,或者看這段作者的演講視頻。
希望這些資源粹庞,能對(duì)你今后可視化溝通咳焚、展示自己的數(shù)據(jù)分析結(jié)果,有所幫助信粮。
給你留個(gè)思考題:
本文中的數(shù)據(jù)黔攒,是從《如何用4行 R 語(yǔ)句,快速探索你的數(shù)據(jù)集强缘?》一文中的 nycflights13
數(shù)據(jù)集督惰,通過(guò)轉(zhuǎn)換(data manipulation)得來(lái)的。
你能不能自己利用 R 或者 Python 語(yǔ)句旅掂,完成這一轉(zhuǎn)化過(guò)程呢赏胚?
歡迎留言,把你的思考和解決過(guò)程分享給大家商虐。
小提示:
- 如果你用 R 觉阅,可以參考 dplyr 包的文檔;
- 如果你用 Python 秘车,可以參考《推薦Python數(shù)據(jù)框Pandas視頻教程》一文典勇。
喜歡請(qǐng)點(diǎn)贊。還可以微信關(guān)注和置頂我的公眾號(hào)“玉樹(shù)芝蘭”(nkwangshuyi)叮趴。
如果你對(duì)數(shù)據(jù)科學(xué)感興趣割笙,不妨閱讀我的系列教程索引貼《如何高效入門(mén)數(shù)據(jù)科學(xué)?》眯亦,里面還有更多的有趣問(wèn)題及解法伤溉。