如何使用Python創建美觀而有見地的圖表

  • 2019 年 11 月 26 日
  • 筆記

作者 | Fabian Bosler

來源 | Medium

在今天的文章中,將研究使用Python繪製數據的三種不同方式。將通過利用《 2019年世界幸福報告》中的數據來做到這一點。用Gapminder和Wikipedia的信息豐富了《世界幸福報告》的數據,以便探索新的關係和可視化。

https://worldhappiness.report/ed/2019/

《世界幸福報告》試圖回答哪些因素影響全世界的幸福。

報告中的幸福定義為對「 Cantril階梯問題」的回答,要求被調查者以0到10的等級評估他們今天的生活,最糟糕的壽命為0,最可能的壽命為10。

在整篇文章中,將Life Ladder用作目標變量。每當談論人生階梯時,請思考幸福。

文章的結構

為了節省空間,有時會將多個圖表合併為一張圖像。但是請放心可以在此Repo或相應的Jupyter Notebook中找到所有基礎代碼。

https://github.com/FBosler/AdvancedPlotting

https://nbviewer.jupyter.org/github/FBosler/AdvancedPlotting/blob/master/The%20quick%2C%20the%20pretty%2C%20and%20the%20awesome.ipynb

目錄

  • Python繪圖歷史
  • 分佈的重要性
  • 加載數據和包導入
  • 快速:使用Pandas進行基本繪圖
  • 漂亮:與Seaborn的高級繪圖
  • 很棒:使用plotly創建很棒的交互式圖

Python繪圖歷史

大約兩年前,開始更認真地學習Python。驚嘆於Python本身或生態系統中眾多令人驚嘆的開源庫之一的簡單性和易用性。熟悉的命令,模式和概念越多,那麼所有事情就越有意義。

Matplotlib

使用Python進行繪圖的情況恰恰相反。最初用Matplotlib創建的幾乎每個圖表都看起來像是八十年代逃脫的罪行。更糟糕的是要創建這些可憎的東西,通常不得不在Stackoverflow上花費數小時。例如研究nitty-gritty命令以更改x-ticks的傾斜度或類似的愚蠢行為。甚至不要開始使用多張圖表。結果看起來令人印象深刻,並且以編程方式創建這些圖表是一種奇妙的感覺。例如,一次生成50個針對不同變量的圖表。但是,這只是很多工作,需要記住很多其他本來沒用的命令。

Seaborn

https://seaborn.pydata.org/

了解Seaborn是一種解脫。Seaborn提取了很多微調。毫無疑問,最終圖表的美學意義是一個巨大的飛躍。但是,它也是基於Matplotlib構建的。通常,對於非標準調整,仍然有必要深入了解機器級的matplotlib代碼。

Bokeh

https://docs.bokeh.org/en/latest/

簡要了解一下時間,以為Bokeh將成為首選解決方案。當從事地理空間可視化工作時遇到了Bokeh。但是,很快意識到,雖然Bokeh與眾不同,但它與matplotlib一樣複雜。

Plotly

https://plot.ly/python/

確實在一段時間前嘗試了plot.ly(從現在開始被稱為plotly)。再一次,致力於地理空間數據的可視化。那時,它似乎比前面提到的庫荒謬。需要一個帳戶,必須通過筆記本電腦登錄,然後才能使所有內容在線呈現。然後,將下載結果圖表。迅速放棄了。但是最近,看了一個有關YouTube的視頻,該視頻關於plotly express和plotly 4.0,最重要的是,擺脫了所有這些在線廢話。玩了一下,這篇文章就是它的結果。

Kepler.gl(地理空間數據榮譽獎)

https://kepler.gl/

Kepler.gl雖然絕對不是Python庫,但它是一種用於地理空間數據的基於Web的強大可視化工具。只需要CSV文件,即可使用Python輕鬆創建。試試看!

目前的工作流程

最終決定使用Pandas原生繪圖進行快速檢查,並使用Seaborn生成要在報表和演示文稿中使用的圖表(在視覺上很重要)。

分佈的重要性

在聖地亞哥學習期間,教過統計學(Stats 119)。統計119是統計的入門課程。該課程包括統計基礎知識,例如數據匯總(視覺和定量),賠率和概率的概念,回歸,抽樣以及最重要的分佈。這次是對數量和現象的理解幾乎全部轉變為基於分佈表示的時間(大部分時間是高斯)。

直到今天,驚訝地發現這兩個量的平均值是多少,標準差可以幫助您掌握一個現象。僅了解這兩個數字,就可以簡單地得出特定結果的可能性。人們立即知道大部分結果將在哪裡。它提供了一個參考框架,可以快速將軼事與有統計意義的事件區分開來,而無需進行過於複雜的計算。

總體而言,面對新數據時,第一步是嘗試形象化其分佈,以更好地理解數據。

加載數據和導入庫

請加載本文中將要使用的數據。對數據進行了一些預處理。在有意義的地方進行推斷。

# Load the data  data = pd.read_csv('https://raw.githubusercontent.com/FBosler/AdvancedPlotting/master/combined_set.csv')  # this assigns labels per year  data['Mean Log GDP per capita']  = data.groupby('Year')['Log GDP per capita'].transform(      pd.qcut,      q=5,      labels=(['Lowest','Low','Medium','High','Highest'])  )

數據集包含以下各列的值:

  • 年:計量年(從2007年到2018年)
  • 生命階梯:受訪者根據Cantril階梯以0到10的量度標準(今天最好的10分)衡量他們的生命價值
  • 對數人均 GDP :根據購買力平價(PPP)調整的人均 GDP,根據世界銀行於2018年11月14日發佈的世界發展指標(WDI)進行了調整,以2011年國際美元不變
  • 社會支持:對問題的回答:「如果遇到麻煩,您是否有親戚朋友可以在需要時幫助您?」
  • 出生時的健康預期壽命:出生時的預期壽命是根據世界衛生組織(WHO)全球衛生觀察站數據存儲庫中的數據構建的,其中提供了2005、2010、2015和2016年的數據。
  • 自由選擇生活:對問題的回答:「您對選擇自己的生活的自由感到滿意還是不滿意?」
  • 慷慨:與「人均GDP」相比,對「您在過去一個月向慈善機構捐款了嗎?」的回應
  • 對腐敗的看法:回答「腐敗是否在整個政府範圍內廣泛存在?」和「腐敗是否在企業內部廣泛分佈?」
  • 積極影響:包括前一天的幸福,笑聲和享受的平均頻率。
  • 負面影響:包括前一天擔憂,悲傷和憤怒的平均頻率。
  • 對國家政府的信心:不言自明
  • 民主素質:一個國家的民主程度
  • 交付質量:一個國家兌現其政策的效果如何
  • Gapminder預期壽命:Gapminder的預期壽命
  • Gapminder人口:一個國家的人口

導入庫

import plotly  import pandas as pd  import numpy as np  import seaborn as sns  import plotly.express as px  import matplotlib  %matplotlib inline  assert matplotlib.__version__ == "3.1.0","""  Please install matplotlib version 3.1.0 by running:  1) !pip uninstall matplotlib  2) !pip install matplotlib==3.1.0  """  

快速:使用Pandas進行基本繪圖

Pandas具有內置的繪圖功能,可以在Series或DataFrame上調用它。喜歡這些繪圖功能,因為它們簡潔,使用合理的默認值,並且可以快速了解發生的情況。

要創建繪圖,請.plot(kind=<TYPE OF PLOT>)像這樣調用數據:

np.exp(data[data['Year']==2018]['Log GDP per capita']).plot(      kind='hist'  )

運行上面的命令將產生以下圖表。

2018年:人均GDP的國家數量直方圖。毫不奇怪,大多數國家都是貧窮的!

使用Pandas進行繪圖時,有五個主要參數:

  • kind:Pandas必須知道要創建哪種圖,可以使用以下選項hist, bar, barh, scatter, area, kde, line, box, hexbin, pie。
  • figsize:允許覆蓋6英寸寬和4英寸高的默認輸出尺寸。figsize期望一個元組(例如,figsize=(12,8)經常使用的)
  • title:向圖表添加標題。在大多數情況下,用它來澄清圖表中顯示的內容,以便當回到圖表上時,可以快速確定發生了什麼。title需要一個字符串。
  • bins:允許覆蓋直方圖的bin寬度。bins需要一個列表或類似列表的值序列(例如bins=np.arange(2,8,0.25))
  • xlim/ylim:允許覆蓋軸的最大值和最小值的默認值。兩者,xlim並ylim期望一個元組(例如xlim=(0,5))

快速瀏覽各種可用的繪圖類型。

垂直條形圖

data[      data['Year'] == 2018  ].set_index('Country name')['Life Ladder'].nlargest(15).plot(      kind='bar',      figsize=(12,8)  )

2018年:15個最幸福的國家/地區名單由芬蘭領導

水平條形圖

np.exp(data[      data['Year'] == 2018  ].groupby('Continent')['Log GDP per capita']         .mean()).sort_values().plot(      kind='barh',      figsize=(12,8)  )

2011年按大陸平均人均國內生產總值(美元)明顯以澳大利亞和新西蘭為首

箱形圖

data['Life Ladder'].plot(      kind='box',      figsize=(12,8)  )

生命階梯分佈的箱形圖顯示,中位數約為5.5,範圍從低於3到最高8。

散點圖

data[['Healthy life expectancy at birth','Gapminder Life Expectancy']].plot(      kind='scatter',      x='Healthy life expectancy at birth',      y='Gapminder Life Expectancy',      figsize=(12,8)  )

《世界幸福報告》預期壽命與Gapminder預期壽命的散點圖顯示了兩者之間的高度相關性(預期)

六邊形圖

data[data['Year'] == 2018].plot(      kind='hexbin',      x='Healthy life expectancy at birth',      y='Generosity',      C='Life Ladder',      gridsize=20,      figsize=(12,8),      cmap="Blues", # defaults to greenish      sharex=False # required to get rid of a bug  )

2018年:赫克斯賓(Hexbin)圖,繪製了預期壽命與慷慨的圖表。垃圾箱的顏色表示各個垃圾箱中壽命梯的平均值。

餅形圖

data [data ['Year'] == 2018] .groupby(      ['Continent']  )['Gapminder人口']     .sum ()。plot(      kind ='pie',  figsize =(12,8),      cmap = 「 Blues_r」,#默認為orangish  )

2018年:餅圖顯示各大洲的總人口

堆積面積圖

data[data['Year'] == 2018].groupby(      ['Continent']  )['Gapminder Population'].sum().plot(      kind='pie',      figsize=(12,8),      cmap="Blues_r", # defaults to orangish  )

全球人口數量正在上升

折線圖

data [      data ['Country name'] =='Germany'  ] .set_index('Year')['Life Ladder']。plot(      kind ='line',      figsize =(12,8)  )

折線圖描繪了德國幸福的發展

關於Pandas繪圖的結論

用Pandas繪圖很方便。它易於訪問,而且速度很快。還有其他工具可以使圖表更具美學吸引力。前進到seaborn。

漂亮:與Seaborn的高級繪圖

Seaborn利用繪圖默認值。為了確保結果匹配,請運行以下命令。

sns.reset_defaults()  sns.set(      rc={'figure.figsize':(7,5)},      style="white" # nicer layout  )

繪製單變量分佈

直方圖和核密度分佈都是可視化特定變量的關鍵特徵的有效方法。看看如何在一個圖表中為單個變量或多個變量生成分佈。

左圖:2018年亞洲國家「救生梯」的直方圖和核密度估計;右圖:人均5桶GDP的「生命階梯」的內核密度估計-錢可以買到幸福

繪製雙變量分佈

每當想直觀地探索兩個或多個變量之間的關係時,通常都歸結為某種形式的散點圖和分佈評估。概念上相似的圖有三種變體。在每個這些圖中,中心圖(散點圖,雙變量KDE和hexbin)有助於理解兩個變量之間的聯合頻率分佈。此外,在中心圖的右邊界和上邊界,描繪了各個變量的邊際單變量分佈(作為KDE或直方圖)。

sns.jointplot(      x='Log GDP per capita',      y='Life Ladder',      data=data,      kind='scatter' # or 'kde' or 'hex'  )

中心圖上具有散點圖,雙變量kde和hexbin的Seaborn關節圖,中心圖左側和頂部均具有邊際分佈。

散點圖

散點圖是一種可視化兩個變量的聯合密度分佈的方法。可以通過添加色相來添加第三個變量,並通過添加size參數來添加第四個變量。

sns.scatterplot(      x='Log GDP per capita',      y='Life Ladder',      data=data[data['Year'] == 2018],      hue='Continent',      size='Gapminder Population'  )  # both, hue and size are optional  sns.despine() # prettier layout

記錄人均國內生產總值與生命梯的對比,顏色基於大陸和人口規模

小提琴圖

小提琴圖是箱形圖和籽粒密度估計值的組合。它起着箱形圖的作用。它顯示了跨類別變量的定量數據分佈,以便可以比較那些分佈。

sns.set(      rc={'figure.figsize':(18,6)},      style="white"  )  sns.violinplot(      x='Continent',      y='Life Ladder',      hue='Mean Log GDP per capita',      data=data  )  sns.despine()

在小提琴上繪製大陸與生命梯的圖,使用人均GDP GDP來對數據進行分組。看來人均GDP越高,幸福感就越強

配對圖

Seaborn對圖在一個大網格中繪製了兩個變量散點圖的所有組合。通常感覺這有點信息過載,但是它可以幫助發現模式。

sns.set(      style="white",      palette="muted",      color_codes=True  )  sns.pairplot(      data[data.Year == 2018][[          'Life Ladder','Log GDP per capita',          'Social support','Healthy life expectancy at birth',          'Freedom to make life choices','Generosity',          'Perceptions of corruption', 'Positive affect',          'Negative affect','Confidence in national government',          'Mean Log GDP per capita'      ]].dropna(),      hue='Mean Log GDP per capita'  )

Seaborn散點圖網格,其中所有選定變量相對於網格下部和上部的每個其他變量均分散分佈,對角線包含kde圖。

FacetGrid

Seaborn的FacetGrid是使用Seaborn的最令人信服的論據之一,因為它使創建多圖變得輕而易舉。通過對圖,已經看到了FacetGrid的示例。FacetGrid允許創建按變量分段的多個圖表。例如,行可以是一個變量(人均GDP類別),列可以是另一個變量(大陸)。

它確實需要比個人更需要的自定義(即,使用matplotlib),但這仍然很引人注目。

FacetGrid —線圖

g = sns.FacetGrid(      data.groupby(['Mean Log GDP per capita','Year','Continent'])['Life Ladder'].mean().reset_index(),      row='Mean Log GDP per capita',      col='Continent',      margin_titles=True  )  g = (g.map(plt.plot, 'Year','Life Ladder'))

Y軸為生命梯,X軸為年。網格的列是大陸,網格的行是人均平均GDP的不同水平。對於北美人均原木平均GDP 較低的國家和歐洲人均原木GDP 中等或較高的國家,總體情況似乎有所改善

FacetGrid —直方圖

g = sns.FacetGrid(data, col="Continent", col_wrap=3,height=4)  g = (g.map(plt.hist, "Life Ladder",bins=np.arange(2,9,0.5)))

FacetGrid,按洲顯示LifeLadder的直方圖

FacetGrid — 帶注釋的KDE圖

也可以向網格中的每個圖表添加構面特定的符號。在下面的示例中,將平均值和標準偏差相加,並在該平均值處繪製一條垂直線(下面的代碼)。

基於大陸的生命梯子內核密度估計,並帶有平均值和標準差

def vertical_mean_line(x, **kwargs):      plt.axvline(x.mean(), linestyle ="--",                  color = kwargs.get("color", "r"))      txkw = dict(size=15, color = kwargs.get("color", "r"))        label_x_pos_adjustment = 0.08 # this needs customization based on your data      label_y_pos_adjustment = 5 # this needs customization based on your data      if x.mean() < 6: # this needs customization based on your data          tx = "mean: {:.2f}n(std: {:.2f})".format(x.mean(),x.std())          plt.text(x.mean() + label_x_pos_adjustment, label_y_pos_adjustment, tx, **txkw)      else:          tx = "mean: {:.2f}n  (std: {:.2f})".format(x.mean(),x.std())          plt.text(x.mean() -1.4, label_y_pos_adjustment, tx, **txkw)    _ = data.groupby(['Continent','Year'])['Life Ladder'].mean().reset_index()    g = sns.FacetGrid(_, col="Continent", height=4, aspect=0.9, col_wrap=3, margin_titles=True)  g.map(sns.kdeplot, "Life Ladder", shade=True, color='royalblue')  g.map(vertical_mean_line, "Life Ladder")

FacetGrid —熱圖

最喜歡的繪圖類型之一是熱圖FacetGrid,即網格每個面上的熱圖。這種類型的繪圖對於在一個繪圖中可視化四個維度和一個度量很有用。該代碼有點麻煩,但可以根據需要快速進行調整。值得注意的是,這種圖表需要相對大量的數據或適當的細分,因為它不能很好地處理缺失值。

在外排顯示的是一年範圍,在外排顯示的是人均GDP,在內排顯示的是感知的腐敗程度,內排則為各洲。我們看到,幸福感朝着右上角增加(即人均GDP高和感知腐敗低)。時間的影響並不確定,某些大洲(歐洲和北美)似乎比其他大洲(非洲)更快樂。

def draw_heatmap(data,inner_row, inner_col, outer_row, outer_col, values, vmin,vmax):      sns.set(font_scale=1)      fg = sns.FacetGrid(          data,          row=outer_row,          col=outer_col,          margin_titles=True      )        position = left, bottom, width, height = 1.4, .2, .1, .6      cbar_ax = fg.fig.add_axes(position)        fg.map_dataframe(          draw_heatmap_facet,          x_col=inner_col,          y_col=inner_row,          values=values,          cbar_ax=cbar_ax,          vmin=vmin,          vmax=vmax      )        fg.fig.subplots_adjust(right=1.3)      plt.show()    def draw_heatmap_facet(*args, **kwargs):      data = kwargs.pop('data')      x_col = kwargs.pop('x_col')      y_col = kwargs.pop('y_col')      values = kwargs.pop('values')      d = data.pivot(index=y_col, columns=x_col, values=values)      annot = round(d,4).values      cmap = sns.color_palette("Blues",30) + sns.color_palette("Blues",30)[0::2]      #cmap = sns.color_palette("Blues",30)      sns.heatmap(          d,          **kwargs,          annot=annot,          center=0,          cmap=cmap,          linewidth=.5      )    # Data preparation  _ = data.copy()  _['Year'] = pd.cut(_['Year'],bins=[2006,2008,2012,2018])    _['GDP per Capita'] = _.groupby(['Continent','Year'])['Log GDP per capita'].transform(      pd.qcut,      q=3,      labels=(['Low','Medium','High'])  ).fillna('Low')    _['Corruption'] = _.groupby(['Continent','GDP per Capita'])['Perceptions of corruption'].transform(      pd.qcut,      q=3,      labels=(['Low','Medium','High'])  )    _ = _[_['Continent'] != 'Oceania'].groupby(['Year','Continent','GDP per Capita','Corruption'])['Life Ladder'].mean().reset_index()  _['Life Ladder'] = _['Life Ladder'].fillna(-10)    draw_heatmap(      data=_,      outer_row='Corruption',      outer_col='GDP per Capita',      inner_row='Year',      inner_col='Continent',      values='Life Ladder',      vmin=3,      vmax=8,  )

很棒:使用plotly創建很棒的交互式圖

沒有更多的Matplotlib!Plotly具有三個重要功能:

  • 懸停:將鼠標懸停在圖表上時,將彈出注釋
  • 互動性:無需任何其他設置即可使圖表互動(即穿越時空)
  • 漂亮的地理空間圖: Plotly具有一些內置的基本地圖繪製功能,可以使用mapbox集成來生成驚人的圖表。

散點圖

通過運行調用繪圖fig = x.<PLOT TYPE>(PARAMS),然後fig.show()像這樣:

fig = px.scatter(      data_frame=data[data['Year'] == 2018],      x="Log GDP per capita",      y="Life Ladder",      size="Gapminder Population",      color="Continent",      hover_name="Country name",      size_max=60  )  fig.show()

散布散點圖,繪製人均國內生產總值對生命梯的圖,其中顏色表示大陸和標記人口的大小

散點圖-漫步時光

fig = px.scatter(      data_frame=data,      x="Log GDP per capita",      y="Life Ladder",      animation_frame="Year",      animation_group="Country name",      size="Gapminder Population",      color="Continent",      hover_name="Country name",      facet_col="Continent",      size_max=45,      category_orders={'Year':list(range(2007,2019))}  )  fig.show()

可視化多年來繪製的數據如何變化

平行類別-一種可視化類別的有趣方式

def q_bin_in_3(col):      return pd.qcut(          col,          q=3,          labels=['Low','Medium','High']      )  _ = data.copy()  _['Social support'] = _.groupby('Year')['Social support'].transform(q_bin_in_3)  _['Life Expectancy'] = _.groupby('Year')['Healthy life expectancy at birth'].transform(q_bin_in_3)  _['Generosity'] = _.groupby('Year')['Generosity'].transform(q_bin_in_3)  _['Perceptions of corruption'] = _.groupby('Year')['Perceptions of corruption'].transform(q_bin_in_3)  _ = _.groupby(['Social support','Life Expectancy','Generosity','Perceptions of corruption'])['Life Ladder'].mean().reset_index()  fig = px.parallel_categories(_, color="Life Ladder", color_continuous_scale=px.colors.sequential.Inferno)  fig.show()

似乎並非所有對生活期望很高的國家都感到高興!

條形圖—交互式過濾器的示例

fig = px.bar(      data,      x="Continent",      y="Gapminder Population",      color="Mean Log GDP per capita",      barmode="stack",      facet_col="Year",      category_orders={"Year": range(2007,2019)},      hover_name='Country name',      hover_data=[          "Mean Log GDP per capita",          "Gapminder Population",          "Life Ladder"      ]  )  fig.show()

過濾條形圖很容易。毫不奇怪,韓國是亞洲富裕國家之一。

整體情節-幸福如何隨着時間而變化

fig = px.choropleth(      data,      locations="ISO3",      color="Life Ladder",      hover_name="Country name",      animation_frame="Year")  fig.show()

多年以來幸福如何演變的地圖可視化。敘利亞和阿富汗處於生命階梯範圍的盡頭(毫不奇怪)