談談Python實戰資料視覺效果之pygal模組(實戰篇)

來源:互聯網
上載者:User

標籤:ref   建立   try   添加   from   .json   pytho   部分   初學者   

前沿

通過上一節談談Python實戰資料視覺效果之pygal模組(基礎篇)的學習,我們對pygal模組的使用有了初步的瞭解,本節將以實戰項目來加深pygal模組的使用。從網上可以下載JSON格式的人口資料,並使用json模組來處理它們,pygal模組提供了一個適合初學者使用的地圖建立工具,我們將使用它來對人口資料進行可視化,以探索全球人口的分布情況。針對JSON格式的人口資料檔案,可以通過談談Python實戰資料視覺效果之matplotlib模組(實戰篇)章節的配套資源來下載。對於本人在學習和編碼過程種遇到的問題,我都會逐一解決。

小插曲之執行效率

我在學習過程中,走各方論壇,發現一個有趣的文章。是針對Python執行效率問題的探究------加一行代碼讓python的運行速度提高100倍。什麼代碼這麼強大?我們測試看看,從1一直累加到1億。
(1)原始代碼:

import timedef foo(x, y):    tt = time.time()  # time.time()函數返回目前時間的時間戳記(1970 紀元年後經過的浮點秒數)    s = 0    for i in range(x, y):        s += i    print(‘Time used: {} sec‘.format(time.time() - tt))    return sprint(foo(1, 100000000))

什麼是時間戳記?時間戳記表示的是從 1970 年 1 月 1 日 00:00:00 開始按秒計算的位移量(time.gmtime(0))此模組中的函數無法處理 1970 紀元年以前的日期和時間或太遙遠的未來(處理極限取決於 C 函數庫,對於 32 位系統來說,是 2038 年)。
運行結果如下:

(2)加一行代碼,再看看結果:

from numba import jit  # 添加的代碼import time@jit  # 添加的代碼def foo(x, y):    tt = time.time()  # time.time()函數返回目前時間的時間戳記(1970 紀元年後經過的浮點秒數)    s = 0    for i in range(x, y):        s += i    print(‘Time used: {} sec‘.format(time.time() - tt))    return sprint(foo(1, 100000000))

運行結果如下:

總結:原始代碼測試出來的是23sec,加了一行代碼就編程0.25sec了,好像真的變快了將近100倍耶。具體實現原理貌似有點複雜,等以後知識面廣了再研究內部原理吧。

JSON格式資料

JSON(JavaScript Object Notation) 是一種輕量級的資料交換格式,易於人閱讀和編寫。例如:

[  {    "Country Name": "Arab World",    "Country Code": "ARB",    "Year": "1960",    "Value": "96388069"  },  {    "Country Name": "Arab World",    "Country Code": "ARB",    "Year": "1961",    "Value": "98882541.4"  },............

可以看出,這個檔案實際上就是一個很長的Python列表,其中每個元素都是一個包含四個鍵的字典:國家名、國別碼、年份以及表示人口數量的值。

提取JSON格式檔案的資料

在工程目錄下,建立一個world_population.py檔案,並將population_data.json格式檔案放到工程目錄下。然後編寫以下代碼嘗試提取經json模組轉化後的格式資料:

# 匯入json模組分析JSON格式檔案import jsonfilename = ‘population_data.json‘with open(filename) as f:    # 函數json.load()將資料(檔案對象)轉換為Python能處理的格式,    pop_data = json.load(f)  # pop_data是一個列表,每個元素都包含一個四個鍵的字典for pop_dict in pop_data:    # 只刷選出2010年份的國家人口數量    if pop_dict[‘Year‘] == ‘2010‘:        # 將每個國家的國家名、國家人口數儲存並列印輸出        country_name = pop_dict[‘Country Name‘]        population = int(float(pop_dict[‘Value‘]))          print(country_name + ":" + str(population))

運行結果如下:

需要注意的是,上面代碼擷取pop_dict[‘Value‘]的值是一個字串,而後面我們進行資料視覺效果時,人口數量必須使用數值才行,所以,我們先轉化為float類型,再轉化為int類型。為什麼不直接轉化為Int類型?這是因為當for迴圈遍曆到人口數值是包含小數點的字串(例如:‘1127437398.85751‘)時,Python不能直接轉化為整數,不然會出現類似下面的報錯:

為了消除這種錯誤,正確的做法是先將‘1127437398.85751‘字串轉化為float類型(1127437398.85751),再轉化為Int類型(1127437398)。

擷取兩個字母的國別碼

Pygal中的地圖製作工具要求資料為特定的格式:用國別碼錶示國家,以及用數字表示人口數量。最重要的問題是,population_data.json中包含的是三個字母的國別碼,但Pygal使用兩個字母的國別碼(儲存在i18n模組中,其實是在該模組的一個字典COUNTRIES裡,該字典包含的鍵和值分別為兩個字母的國別碼和國家名)來表示國家。所以我們要解決的問題就是根據國家名在i18n模組中的字典COUNTRIES裡擷取兩個字母的國別碼。這樣就可以在世界地圖上表示的國別碼和人口數量分別使用字典COUNTRIES裡的兩個字母的國別碼和population_data.json檔案裡的人口數量。好了,說瞭解決問題的流程,現在我們嘗試使用i18n模組來擷取字典COUNTRIES裡的鍵和值,先在工程目錄下建立一個country_codes.py檔案。
需要注意的是書本P327頁的16.2.4小節,匯入i18n模組的方法對於現在來說已經不適用了。如果匯入模組的代碼寫“from pygal.i18n import COUNTRIES”就會報以下的錯誤:

應該改為“from pygal_maps_world.i18n import COUNTRIES”才行。
代碼如下:

# 返回il8n模組中COUNTRIES字典中對應國家名的國別碼from pygal_maps_world.i18n import COUNTRIESdef get_country_code(country_name):    for code, name in COUNTRIES.items():  # 返回字典的所有索引值對        if name == country_name:  # 根據國家名返回兩個字母的國別碼            return code    return None  # 如果沒有找到則返回None

修改world_population.py檔案的代碼,代碼如下:

# 匯入json模組分析JSON格式檔案import jsonfrom country_codes import get_country_codefilename = ‘population_data.json‘with open(filename) as f:    # 函數json.load()將資料(檔案對象)轉換為Python能處理的格式,    pop_data = json.load(f)  # pop_data是一個列表,每個元素都包含一個四個鍵的字典for pop_dict in pop_data:    # 只刷選出2010年份的國家人口數量    if pop_dict[‘Year‘] == ‘2010‘:        # 將每個國家的國家名、國家人口數儲存並列印輸出        country_name = pop_dict[‘Country Name‘]        population = int(float(pop_dict[‘Value‘]))        code = get_country_code(country_name)  # 將population_data.json檔案擷取的國家名傳入函數,若存在則返回對應的國別碼        if code:  # 如果存在則輸出國家名對應的國別碼            print(code + ":" + str(population))        else:            print(‘ERROR - ‘+country_name)

運行結果如下:

從可以看出,其實有相當一部分國家沒有對應的國別碼,導致顯示錯誤訊息的原因有兩個。第一,並非所有人口數量都是國家,有些是地區和經濟類群。第二,有些統計資料使用了不同的完整國家名,所以識別不到。

製作世界地圖

有了兩個字母的國別碼後,我們可以進行以下步驟:
1.構造虛擬資料製作一個世界地圖顯示指定了國別碼的國家有哪些,還有呈現人口數量,來感受一下世界地圖的宏偉。
2.繪製一個2010年真實資料的完整的世界人口地圖圖表。
3.根據人口數量將國家分組
4.世界人口地圖圖表進行樣式最佳化處理。
(1)製作一個類比資料世界地圖
在此之前,P329頁16.2.5小節的代碼中調用函數建立世界地圖的方法對於現在也不適用了,如果代碼寫“wm = pygal.Worldmap() ”會報以下錯誤

將代碼改為“wm = pygal.maps.world.World()”就可以通過了。代碼如下:

import pygalwm = pygal.maps.world.World()  # 建立一個執行個體wm.title = ‘North,Central America‘# 利用add函數添加標籤和國家名還有人口數量,若參數2是一個列表(只有國家名,沒有指定人口數量),預設指定人口數量為1,那麼就是使用同一種顏色,並且顏色深淺一樣,除非人口數量不同。# 而參數2如果是一個字典,那麼說明指定國家名的同時還指定了人口數量,那麼雖然使用同一種類型顏色,但根據人口數量的多少決定顏色的深淺wm.add(‘North America‘, {‘ca‘: 10000, ‘mx‘: 20000, ‘us‘: 30000})wm.add(‘Central America‘, {‘bz‘: 40000, ‘cr‘: 50000, ‘gt‘: 60000, ‘hn‘: 70000, ‘ni‘: 80000, ‘pa‘: 90000, ‘sv‘: 100000})wm.render_to_file(‘americas.svg‘)

將americas.svg放入瀏覽器中顯示,運行結果如下:

上面的人口資料純屬虛構。從可以看出,將滑鼠移至國家上方便可顯示國家名和人口數,North America的三個國家分別使用同一種、但深淺不一的顏色來表示,其中人口數量越大,顏色越深。而Central America的多個國家也是同樣如此。
(2)繪製完整的世界人口地圖
要呈現其他國家的人口數量,需要將前面處理的資料(兩個字母的國別碼和對應國家的人口數量)轉換為Pygal要求的字典格式(即作為實參傳入add函數的第二個形參)。代碼如下:

import jsonimport pygalfrom country_codes import get_country_codefilename = ‘population_data.json‘with open(filename) as f:    # 函數json.load()將資料(檔案對象)轉換為Python能處理的格式,    pop_data = json.load(f)  # pop_data是一個列表,每個元素都包含一個四個鍵的字典cc_populations = {}for pop_dict in pop_data:    if pop_dict[‘Year‘] == ‘2010‘:        country_name = pop_dict[‘Country Name‘]        population = int(float(pop_dict[‘Value‘]))        code = get_country_code(country_name)        if code:            cc_populations[code] = populationwm = pygal.maps.world.World()wm.title = ‘World Population in 2010,by Country‘wm.add(‘2010‘,cc_populations)wm.render_to_file(‘world_population.svg‘)

運行結果如下:

根據上面可以總結出:若將整個世界所有國家的國別碼和人口數量都放進一個字典裡,再調用一個add函數添加進世界地圖,那麼就會把這個字典當成一個組,那麼在世界地圖顯示時,使用同一種、但深淺不一的紅顏色來表示,其中人口數量越大,顏色越深。再想想,這可不太行,因為整體來看,很難反映其人口數量的差別,為瞭解決這個問題,我們究其根源,主要是我們只使用了一個字典和只調用了一個add函數來表示整個世界的原因導致顏色單一,解決辦法是我們可以對其進行分組處理,人口數量多的為一組,人口數量中等的為一組,人口數量少的為一組,那麼就分為三組了。

根據人口數量將國家分組

針對上一節結論的分析,我們這一小節將採用分組的方式反映人口數量的差別。根據人口數量分成三組:少於1000萬的、介於1000萬和10億之間的以及超過10億的。
代碼如下:

import jsonimport pygalfrom country_codes import get_country_codefilename = ‘population_data.json‘with open(filename) as f:    # 函數json.load()將資料(檔案對象)轉換為Python能處理的格式,    pop_data = json.load(f)  # pop_data是一個列表,每個元素都包含一個四個鍵的字典cc_populations = {}for pop_dict in pop_data:    if pop_dict[‘Year‘] == ‘2010‘:        country_name = pop_dict[‘Country Name‘]        population = int(float(pop_dict[‘Value‘]))        code = get_country_code(country_name)        if code:            cc_populations[code] = population# 根據人口數量將所有的國家分成三組cc_pops_1, cc_pops_2, cc_pops_3 = {}, {}, {}for cc, pop in cc_populations.items():    if pop < 10000000:        cc_pops_1[cc] = pop    elif pop < 1000000000:        cc_pops_2[cc] = pop    else:        cc_pops_3[cc] = popwm = pygal.maps.world.World()  # 建立一個執行個體wm.title = ‘World Population in 2010,by Country‘wm.add(‘0-10m‘, cc_pops_1)wm.add(‘10m-1bn‘, cc_pops_2)wm.add(‘>1bn‘, cc_pops_3)wm.render_to_file(‘world_population.svg‘)

運行結果如下:

從可以看出,世界地圖使用三種不同的顏色,更直觀地看出人口數量的差別,在每組中,各個國家都按人口從少到多以淺到深的顏色。其中中國和印度是大於10億人的國家。

世界人口地圖圖表進行樣式最佳化處理

前面的案例,採用預設的顏色設定不怎麼好看,我們可以使用Pygal樣式設定指令來調整顏色。Pygal樣式儲存在模組style中,我們從這個模組中匯入了RotateStyle類,建立這個類的執行個體時,需要提供一個實參 —— 十六進位的 RGB 顏色。十六進位格式 的 RGB 顏色是一個以井號( # )打頭的字串,後面跟著 6 個字元,其中前兩個字元表示紅色分量,接下來的兩個表示綠色分量,最後兩個表示藍色分量。每個分量的取值範圍為 00 (沒有相應的顏色) ~FF (包含最多的相應顏色)。Pygal 通常預設使用較暗的顏色主題。使用 LightColorizedStyle 加亮了地圖的顏色。
代碼如下:

import jsonimport pygalfrom country_codes import get_country_codefrom pygal.style import LightColorizedStyle as LCS,RotateStyle as RS  # 匯入RotateStyle和 LightColorizedStyle,並取了別名,後面調用就採用別名,方便多了filename = ‘population_data.json‘with open(filename) as f:    # 函數json.load()將資料(檔案對象)轉換為Python能處理的格式,    pop_data = json.load(f)  # pop_data是一個列表,每個元素都包含一個四個鍵的字典cc_populations = {}for pop_dict in pop_data:    if pop_dict[‘Year‘] == ‘2010‘:        country_name = pop_dict[‘Country Name‘]        population = int(float(pop_dict[‘Value‘]))        code = get_country_code(country_name)        if code:            cc_populations[code] = population# 根據人口數量將所有的國家分成三組cc_pops_1, cc_pops_2, cc_pops_3 = {}, {}, {}for cc, pop in cc_populations.items():    if pop < 10000000:        cc_pops_1[cc] = pop    elif pop < 1000000000:        cc_pops_2[cc] = pop    else:        cc_pops_3[cc] = popwm_style = RS(‘#336699‘,base_style=LCS)  # 一個樣式對象,參數指定一個十六進位的RGB顏色wm = pygal.maps.world.World(style=wm_style)  # 建立一個執行個體,並傳入一個指定了顏色的樣式對象wm_stylewm.title = ‘World Population in 2010,by Country‘wm.add(‘0-10m‘, cc_pops_1)wm.add(‘10m-1bn‘, cc_pops_2)wm.add(‘>1bn‘, cc_pops_3)wm.render_to_file(‘world_population.svg‘)

運行結果如下:

談談Python實戰資料視覺效果之pygal模組(實戰篇)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.