談?wù)凱ython實(shí)戰(zhàn)數(shù)據(jù)可視化之pygal模塊(實(shí)戰(zhàn)篇)

前沿

通過上一節(jié)談?wù)凱ython實(shí)戰(zhàn)數(shù)據(jù)可視化之pygal模塊(基礎(chǔ)篇)的學(xué)習(xí),我們對pygal模塊的使用有了初步的了解,本節(jié)將以實(shí)戰(zhàn)項(xiàng)目來加深pygal模塊的使用。從網(wǎng)上可以下載JSON格式的人口數(shù)據(jù),并使用json模塊來處理它們,pygal模塊提供了一個適合初學(xué)者使用的地圖創(chuàng)建工具,我們將使用它來對人口數(shù)據(jù)進(jìn)行可視化,以探索全球人口的分布情況。針對JSON格式的人口數(shù)據(jù)文件,可以通過談?wù)凱ython實(shí)戰(zhàn)數(shù)據(jù)可視化之matplotlib模塊(實(shí)戰(zhàn)篇)章節(jié)的配套資源來下載。對于本人在學(xué)習(xí)和編碼過程種遇到的問題,我都會逐一解決。

創(chuàng)新互聯(lián)建站提供高防物理服務(wù)器租用、云服務(wù)器、香港服務(wù)器、成都二樞服務(wù)器租用托管

小插曲之執(zhí)行效率

我在學(xué)習(xí)過程中,走各方論壇,發(fā)現(xiàn)一個有趣的帖子。是針對Python執(zhí)行效率問題的探究------加一行代碼讓python的運(yùn)行速度提高100倍。什么代碼這么強(qiáng)大?我們測試看看,從1一直累加到1億。
(1)原始代碼:

import time

def foo(x, y):
    tt = time.time()  # time.time()函數(shù)返回當(dāng)前時間的時間戳(1970 紀(jì)元年后經(jīng)過的浮點(diǎn)秒數(shù))
    s = 0
    for i in range(x, y):
        s += i
    print('Time used: {} sec'.format(time.time() - tt))
    return s

print(foo(1, 100000000))

什么是時間戳?時間戳表示的是從 1970 年 1 月 1 日 00:00:00 開始按秒計(jì)算的偏移量(time.gmtime(0))此模塊中的函數(shù)無法處理 1970 紀(jì)元年以前的日期和時間或太遙遠(yuǎn)的未來(處理極限取決于 C 函數(shù)庫,對于 32 位系統(tǒng)來說,是 2038 年)。
運(yùn)行結(jié)果如下:
談?wù)凱ython實(shí)戰(zhàn)數(shù)據(jù)可視化之pygal模塊(實(shí)戰(zhàn)篇)
(2)加一行代碼,再看看結(jié)果:

from numba import jit  # 添加的代碼
import time

@jit  # 添加的代碼
def foo(x, y):
    tt = time.time()  # time.time()函數(shù)返回當(dāng)前時間的時間戳(1970 紀(jì)元年后經(jīng)過的浮點(diǎn)秒數(shù))
    s = 0
    for i in range(x, y):
        s += i
    print('Time used: {} sec'.format(time.time() - tt))
    return s

print(foo(1, 100000000))

運(yùn)行結(jié)果如下:
談?wù)凱ython實(shí)戰(zhàn)數(shù)據(jù)可視化之pygal模塊(實(shí)戰(zhàn)篇)
總結(jié):原始代碼測試出來的是23sec,加了一行代碼就編程0.25sec了,好像真的變快了將近100倍耶。具體實(shí)現(xiàn)原理貌似有點(diǎn)復(fù)雜,等以后知識面廣了再研究內(nèi)部原理吧。

JSON格式數(shù)據(jù)

JSON(JavaScript Object Notation) 是一種輕量級的數(shù)據(jù)交換格式,易于人閱讀和編寫。例如:

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

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

提取JSON格式文件的數(shù)據(jù)

在工程目錄下,創(chuàng)建一個world_population.py文件,并將population_data.json格式文件放到工程目錄下。然后編寫以下代碼嘗試提取經(jīng)json模塊轉(zhuǎn)化后的格式數(shù)據(jù):

# 導(dǎo)入json模塊分析JSON格式文件
import json

filename = 'population_data.json'
with open(filename) as f:
    # 函數(shù)json.load()將數(shù)據(jù)(文件對象)轉(zhuǎn)換為Python能處理的格式,
    pop_data = json.load(f)  # pop_data是一個列表,每個元素都包含一個四個鍵的字典

for pop_dict in pop_data:
    # 只刷選出2010年份的國家人口數(shù)量
    if pop_dict['Year'] == '2010':
        # 將每個國家的國家名、國家人口數(shù)保存并打印輸出
        country_name = pop_dict['Country Name']
        population = int(float(pop_dict['Value']))  
        print(country_name + ":" + str(population))

運(yùn)行結(jié)果如下:
談?wù)凱ython實(shí)戰(zhàn)數(shù)據(jù)可視化之pygal模塊(實(shí)戰(zhàn)篇)
需要注意的是,上面代碼獲取pop_dict['Value']的值是一個字符串,而后面我們進(jìn)行數(shù)據(jù)可視化時,人口數(shù)量必須使用數(shù)值才行,所以,我們先轉(zhuǎn)化為float類型,再轉(zhuǎn)化為int類型。為什么不直接轉(zhuǎn)化為Int類型?這是因?yàn)楫?dāng)for循環(huán)遍歷到人口數(shù)值是包含小數(shù)點(diǎn)的字符串(例如:'1127437398.85751')時,Python不能直接轉(zhuǎn)化為整數(shù),不然會出現(xiàn)類似下面的報(bào)錯:
談?wù)凱ython實(shí)戰(zhàn)數(shù)據(jù)可視化之pygal模塊(實(shí)戰(zhàn)篇)
為了消除這種錯誤,正確的做法是先將'1127437398.85751'字符串轉(zhuǎn)化為float類型(1127437398.85751),再轉(zhuǎn)化為Int類型(1127437398)。

獲取兩個字母的國別碼

Pygal中的地圖制作工具要求數(shù)據(jù)為特定的格式:用國別碼表示國家,以及用數(shù)字表示人口數(shù)量。最重要的問題是,population_data.json中包含的是三個字母的國別碼,但Pygal使用兩個字母的國別碼(存儲在i18n模塊中,其實(shí)是在該模塊的一個字典COUNTRIES里,該字典包含的鍵和值分別為兩個字母的國別碼和國家名)來表示國家。所以我們要解決的問題就是根據(jù)國家名在i18n模塊中的字典COUNTRIES里獲取兩個字母的國別碼。這樣就可以在世界地圖上表示的國別碼和人口數(shù)量分別使用字典COUNTRIES里的兩個字母的國別碼和population_data.json文件里的人口數(shù)量。好了,說了解決問題的流程,現(xiàn)在我們嘗試使用i18n模塊來獲取字典COUNTRIES里的鍵和值,先在工程目錄下創(chuàng)建一個country_codes.py文件。
需要注意的是書本P327頁的16.2.4小節(jié),導(dǎo)入i18n模塊的方法對于現(xiàn)在來說已經(jīng)不適用了。如果導(dǎo)入模塊的代碼寫“from pygal.i18n import COUNTRIES”就會報(bào)以下的錯誤:
談?wù)凱ython實(shí)戰(zhàn)數(shù)據(jù)可視化之pygal模塊(實(shí)戰(zhàn)篇)
應(yīng)該改為“from pygal_maps_world.i18n import COUNTRIES”才行。
代碼如下:

# 返回il8n模塊中COUNTRIES字典中對應(yīng)國家名的國別碼
from pygal_maps_world.i18n import COUNTRIES

def get_country_code(country_name):
    for code, name in COUNTRIES.items():  # 返回字典的所有鍵值對
        if name == country_name:  # 根據(jù)國家名返回兩個字母的國別碼
            return code
    return None  # 如果沒有找到則返回None

修改world_population.py文件的代碼,代碼如下:

# 導(dǎo)入json模塊分析JSON格式文件
import json
from country_codes import get_country_code

filename = 'population_data.json'
with open(filename) as f:
    # 函數(shù)json.load()將數(shù)據(jù)(文件對象)轉(zhuǎn)換為Python能處理的格式,
    pop_data = json.load(f)  # pop_data是一個列表,每個元素都包含一個四個鍵的字典

for pop_dict in pop_data:
    # 只刷選出2010年份的國家人口數(shù)量
    if pop_dict['Year'] == '2010':
        # 將每個國家的國家名、國家人口數(shù)保存并打印輸出
        country_name = pop_dict['Country Name']
        population = int(float(pop_dict['Value']))
        code = get_country_code(country_name)  # 將population_data.json文件獲取的國家名傳入函數(shù),若存在則返回對應(yīng)的國別碼
        if code:  # 如果存在則輸出國家名對應(yīng)的國別碼
            print(code + ":" + str(population))
        else:
            print('ERROR - '+country_name)

運(yùn)行結(jié)果如下:
談?wù)凱ython實(shí)戰(zhàn)數(shù)據(jù)可視化之pygal模塊(實(shí)戰(zhàn)篇)
從上圖可以看出,其實(shí)有相當(dāng)一部分國家沒有對應(yīng)的國別碼,導(dǎo)致顯示錯誤消息的原因有兩個。第一,并非所有人口數(shù)量都是國家,有些是地區(qū)和經(jīng)濟(jì)類群。第二,有些統(tǒng)計(jì)數(shù)據(jù)使用了不同的完整國家名,所以識別不到。

制作世界地圖

有了兩個字母的國別碼后,我們可以進(jìn)行以下步驟:
1.構(gòu)造虛擬數(shù)據(jù)制作一個世界地圖顯示指定了國別碼的國家有哪些,還有呈現(xiàn)人口數(shù)量,來感受一下世界地圖的宏偉。
2.繪制一個2010年真實(shí)數(shù)據(jù)的完整的世界人口地圖圖表。
3.根據(jù)人口數(shù)量將國家分組
4.世界人口地圖圖表進(jìn)行樣式優(yōu)化處理。
(1)制作一個模擬數(shù)據(jù)世界地圖
在此之前,P329頁16.2.5小節(jié)的代碼中調(diào)用函數(shù)創(chuàng)建世界地圖的方法對于現(xiàn)在也不適用了,如果代碼寫“wm = pygal.Worldmap() ”會報(bào)以下錯誤
談?wù)凱ython實(shí)戰(zhàn)數(shù)據(jù)可視化之pygal模塊(實(shí)戰(zhàn)篇)
將代碼改為“wm = pygal.maps.world.World()”就可以通過了。代碼如下:

import pygal

wm = pygal.maps.world.World()  # 創(chuàng)建一個實(shí)例
wm.title = 'North,Central America'

# 利用add函數(shù)添加標(biāo)簽和國家名還有人口數(shù)量,若參數(shù)2是一個列表(只有國家名,沒有指定人口數(shù)量),默認(rèn)指定人口數(shù)量為1,那么就是使用同一種顏色,并且顏色深淺一樣,除非人口數(shù)量不同。
# 而參數(shù)2如果是一個字典,那么說明指定國家名的同時還指定了人口數(shù)量,那么雖然使用同一種類型顏色,但根據(jù)人口數(shù)量的多少決定顏色的深淺
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放入瀏覽器中顯示,運(yùn)行結(jié)果如下:
談?wù)凱ython實(shí)戰(zhàn)數(shù)據(jù)可視化之pygal模塊(實(shí)戰(zhàn)篇)
上面的人口數(shù)據(jù)純屬虛構(gòu)。從上圖可以看出,將鼠標(biāo)移至國家上方便可顯示國家名和人口數(shù),North America的三個國家分別使用同一種、但深淺不一的顏色來表示,其中人口數(shù)量越大,顏色越深。而Central America的多個國家也是同樣如此。
(2)繪制完整的世界人口地圖
要呈現(xiàn)其他國家的人口數(shù)量,需要將前面處理的數(shù)據(jù)(兩個字母的國別碼和對應(yīng)國家的人口數(shù)量)轉(zhuǎn)換為Pygal要求的字典格式(即作為實(shí)參傳入add函數(shù)的第二個形參)。代碼如下:

import json
import pygal
from country_codes import get_country_code

filename = 'population_data.json'
with open(filename) as f:
    # 函數(shù)json.load()將數(shù)據(jù)(文件對象)轉(zhuǎn)換為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

wm = pygal.maps.world.World()
wm.title = 'World Population in 2010,by Country'
wm.add('2010',cc_populations)

wm.render_to_file('world_population.svg')

運(yùn)行結(jié)果如下:
談?wù)凱ython實(shí)戰(zhàn)數(shù)據(jù)可視化之pygal模塊(實(shí)戰(zhàn)篇)
根據(jù)上面可以總結(jié)出:若將整個世界所有國家的國別碼和人口數(shù)量都放進(jìn)一個字典里,再調(diào)用一個add函數(shù)添加進(jìn)世界地圖,那么就會把這個字典當(dāng)成一個組,那么在世界地圖顯示時,使用同一種、但深淺不一的紅顏色來表示,其中人口數(shù)量越大,顏色越深。再想想,這可不太行,因?yàn)檎w來看,很難反映其人口數(shù)量的差別,為了解決這個問題,我們究其根源,主要是我們只使用了一個字典和只調(diào)用了一個add函數(shù)來表示整個世界的原因?qū)е骂伾珕我?,解決辦法是我們可以對其進(jìn)行分組處理,人口數(shù)量多的為一組,人口數(shù)量中等的為一組,人口數(shù)量少的為一組,那么就分為三組了。

根據(jù)人口數(shù)量將國家分組

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

import json
import pygal
from country_codes import get_country_code

filename = 'population_data.json'
with open(filename) as f:
    # 函數(shù)json.load()將數(shù)據(jù)(文件對象)轉(zhuǎn)換為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

# 根據(jù)人口數(shù)量將所有的國家分成三組
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] = pop

wm = pygal.maps.world.World()  # 創(chuàng)建一個實(shí)例
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')

運(yùn)行結(jié)果如下:
談?wù)凱ython實(shí)戰(zhàn)數(shù)據(jù)可視化之pygal模塊(實(shí)戰(zhàn)篇)
從上圖可以看出,世界地圖使用三種不同的顏色,更直觀地看出人口數(shù)量的差別,在每組中,各個國家都按人口從少到多以淺到深的顏色。其中中國和印度是大于10億人的國家。

世界人口地圖圖表進(jìn)行樣式優(yōu)化處理

前面的案例,采用默認(rèn)的顏色設(shè)置不怎么好看,我們可以使用Pygal樣式設(shè)置指令來調(diào)整顏色。Pygal樣式存儲在模塊style中,我們從這個模塊中導(dǎo)入了RotateStyle類,創(chuàng)建這個類的實(shí)例時,需要提供一個實(shí)參 —— 十六進(jìn)制的 RGB 顏色。十六進(jìn)制格式 的 RGB 顏色是一個以井號( # )打頭的字符串,后面跟著 6 個字符,其中前兩個字符表示紅色分量,接下來的兩個表示綠色分量,最后兩個表示藍(lán)色分量。每個分量的取值范圍為 00 (沒有相應(yīng)的顏色) ~FF (包含最多的相應(yīng)顏色)。Pygal 通常默認(rèn)使用較暗的顏色主題。使用 LightColorizedStyle 加亮了地圖的顏色。
代碼如下:

import json
import pygal
from country_codes import get_country_code
from pygal.style import LightColorizedStyle as LCS,RotateStyle as RS  # 導(dǎo)入RotateStyle和 LightColorizedStyle,并取了別名,后面調(diào)用就采用別名,方便多了

filename = 'population_data.json'
with open(filename) as f:
    # 函數(shù)json.load()將數(shù)據(jù)(文件對象)轉(zhuǎn)換為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

# 根據(jù)人口數(shù)量將所有的國家分成三組
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] = pop

wm_style = RS('#336699',base_style=LCS)  # 一個樣式對象,參數(shù)指定一個十六進(jìn)制的RGB顏色
wm = pygal.maps.world.World(style=wm_style)  # 創(chuàng)建一個實(shí)例,并傳入一個指定了顏色的樣式對象wm_style
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')

運(yùn)行結(jié)果如下:
談?wù)凱ython實(shí)戰(zhàn)數(shù)據(jù)可視化之pygal模塊(實(shí)戰(zhàn)篇)

名稱欄目:談?wù)凱ython實(shí)戰(zhàn)數(shù)據(jù)可視化之pygal模塊(實(shí)戰(zhàn)篇)
網(wǎng)頁鏈接:http://bm7419.com/article26/jccpcg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供全網(wǎng)營銷推廣、營銷型網(wǎng)站建設(shè)網(wǎng)站維護(hù)、網(wǎng)站策劃企業(yè)建站、搜索引擎優(yōu)化

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都網(wǎng)站建設(shè)