Python系列17-數(shù)據(jù)可視化之下載數(shù)據(jù)

一.下載數(shù)據(jù)簡介

在本博客中椭坚,你將從網(wǎng)上下載數(shù)據(jù),并對這些數(shù)據(jù)進(jìn)行可視化。網(wǎng)上的數(shù)據(jù)多得難以置信,且大多未經(jīng)過仔細(xì)檢查慢睡。如果能夠?qū)@些數(shù)據(jù)進(jìn)行分析锯蛀,你就能發(fā)現(xiàn)別人沒有發(fā)現(xiàn)的規(guī)律和關(guān)聯(lián)撑蒜。

我們將訪問并可視化以兩種常見格式存儲的數(shù)據(jù):CSV 和JSON辆亏。我們將使用Python模塊csv 來處理以CSV(逗號分隔的值)格式存儲的天氣數(shù)據(jù),找出兩個不同地區(qū)在一段時間內(nèi)的最高溫度和最低溫度骨稿。然后笨鸡,我們將使用matplotlib根據(jù)下載的數(shù)據(jù)創(chuàng)建一個圖表,展示兩個不同地區(qū)的氣溫變化:阿拉斯加錫特卡和加利福尼亞死亡谷坦冠。在本博客的后面形耗,我們將使用模塊json 來訪問以JSON格式存儲的人口數(shù)據(jù),并使用Pygal繪制一幅按國別劃分的人口地圖辙浑。

閱讀本博客后激涤,你將能夠處理各種類型和格式的數(shù)據(jù)集,并對如何創(chuàng)建復(fù)雜的圖表有更深入的認(rèn)識判呕。要處理各種真實世界的數(shù)據(jù)集倦踢,必須能夠訪問并可視化各種類型和格式的在線數(shù)據(jù)。

1.1 CSV文件格式

要在文本文件中存儲數(shù)據(jù)侠草,最簡單的方式是將數(shù)據(jù)作為一系列以逗號分隔的值 (CSV)寫入文件辱挥。這樣的文件稱為CSV文件。例如边涕,下面是一行CSV格式的天氣數(shù)據(jù):

2014-1-5,61,44,26,18,7,-1,56,30,9,30.34,30.27,30.15,,,,10,4,,0.00,0,,195

這是阿拉斯加錫特卡2014年1月5日的天氣數(shù)據(jù)晤碘,其中包含當(dāng)天的最高氣溫和最低氣溫褂微,還有眾多其他數(shù)據(jù)。CSV文件對人來說閱讀起來比較麻煩园爷,但程序可輕松地提取并處理其中的值宠蚂,這有助于加快數(shù)據(jù)分析過程。

我們將首先處理少量錫特卡的CSV格式的天氣數(shù)據(jù)腮介,這些數(shù)據(jù)可在本書的配套資源(https://www.nostarch.com/pythoncrashcourse/ )中找到肥矢。請將文件sitka_weather_07-2014.csv復(fù)制到
存儲本章程序的文件夾中(下載本書的配套資源后端衰,你就有了這個項目所需的所有文件)叠洗。

1.1.1 分析CSV文件頭

csv 模塊包含在Python標(biāo)準(zhǔn)庫中,可用于分析CSV文件中的數(shù)據(jù)行旅东,讓我們能夠快速提取感興趣的值灭抑。下面先來查看這個文件的第一行,其中包含一系列有關(guān)數(shù)據(jù)的描述

代碼:
highs_lows.py

import csv

filename = 'sitka_weather_07-2014.csv'

with open(filename) as f:
    reader = csv.reader(f)
    header_row = next(reader)
    print(header_row)

測試記錄:

E:\python\learn_python1\venv\Scripts\python.exe E:/python/learn_python1/數(shù)據(jù)可視化/highs_lows.py
['AKDT', 'Max TemperatureF', 'Mean TemperatureF', 'Min TemperatureF', 'Max Dew PointF', 'MeanDew PointF', 'Min DewpointF', 'Max Humidity', ' Mean Humidity', ' Min Humidity', ' Max Sea Level PressureIn', ' Mean Sea Level PressureIn', ' Min Sea Level PressureIn', ' Max VisibilityMiles', ' Mean VisibilityMiles', ' Min VisibilityMiles', ' Max Wind SpeedMPH', ' Mean Wind SpeedMPH', ' Max Gust SpeedMPH', 'PrecipitationIn', ' CloudCover', ' Events', ' WindDirDegrees']

Process finished with exit code 0

reader處理文件中以逗號分隔的第一行數(shù)據(jù)抵代,并將每項數(shù)據(jù)都作為一個元素存儲在列表中腾节。文件頭AKDT 表示阿拉斯加時間(Alaska Daylight Time),其位置表明每行的第一個值都
是日期或時間荤牍。文件頭Max TemperatureF 指出每行的第二個值都是當(dāng)天的最高華氏溫度案腺。可通過閱讀其他的文件頭來確定文件包含的信息類型康吵。

注意  文件頭的格式并非總是一致的劈榨,空格和單位可能出現(xiàn)在奇怪的地方。這在原始數(shù)據(jù)文件中很常見晦嵌,但不會帶來任何問題同辣。

1.1.2 打印文件頭及其位置

為讓文件頭數(shù)據(jù)更容易理解,將列表中的每個文件頭及其位置打印出來

代碼:
highs_lows.py

import csv

filename = 'sitka_weather_07-2014.csv'

with open(filename) as f:
    reader = csv.reader(f)
    header_row = next(reader)
    
    for index, column_header in enumerate(header_row):
        print(index, column_header)

測試記錄:

E:\python\learn_python1\venv\Scripts\python.exe E:/python/learn_python1/數(shù)據(jù)可視化/highs_lows.py
0 AKDT
1 Max TemperatureF
2 Mean TemperatureF
3 Min TemperatureF
4 Max Dew PointF
5 MeanDew PointF
6 Min DewpointF
7 Max Humidity
8  Mean Humidity
9  Min Humidity
10  Max Sea Level PressureIn
11  Mean Sea Level PressureIn
12  Min Sea Level PressureIn
13  Max VisibilityMiles
14  Mean VisibilityMiles
15  Min VisibilityMiles
16  Max Wind SpeedMPH
17  Mean Wind SpeedMPH
18  Max Gust SpeedMPH
19 PrecipitationIn
20  CloudCover
21  Events
22  WindDirDegrees

Process finished with exit code 0

1.1.3 提取并讀取數(shù)據(jù)

知道需要哪些列中的數(shù)據(jù)后惭载,我們來讀取一些數(shù)據(jù)旱函。首先讀取每天的最高氣溫

代碼:
highs_lows.py

import csv

# 從文件中獲取最高氣溫
filename = 'sitka_weather_07-2014.csv'

with open(filename) as f:
    reader = csv.reader(f)
    header_row = next(reader)

    highs = []
    for row in reader:
        highs.append(row[1])

    print(highs)

測試記錄:

E:\python\learn_python1\venv\Scripts\python.exe E:/python/learn_python1/數(shù)據(jù)可視化/highs_lows.py
['64', '71', '64', '59', '69', '62', '61', '55', '57', '61', '57', '59', '57', '61', '64', '61', '59', '63', '60', '57', '69', '63', '62', '59', '57', '57', '61', '59', '61', '61', '66']

Process finished with exit code 0

我們創(chuàng)建了一個名為highs 的空列表,再遍歷文件中余下的各行描滔。閱讀器對象從其停留的地方繼續(xù)往下讀取CSV文件棒妨,每次都自動返回當(dāng)前所處位置的下一行。由于我們已經(jīng)讀取了文件頭行含长,這個循環(huán)將從第二行開始——從這行開始包含的是實際數(shù)據(jù)券腔。每次執(zhí)行該循環(huán)時,我們都將索引1處(第2列)的數(shù)據(jù)附加到highs 末尾.

我們提取了每天的最高氣溫茎芋,并將它們作為字符串整潔地存儲在一個列表中颅眶。
下面使用int() 將這些字符串轉(zhuǎn)換為數(shù)字,讓matplotlib能夠讀取它們:

-- snip
    for row in reader:
        high = int(row[1])
        highs.append(high)
-- snip

1.1.4 繪制氣溫圖表

為可視化這些氣溫數(shù)據(jù)田弥,我們首先使用matplotlib創(chuàng)建一個顯示每日最高氣溫的簡單圖形

代碼:
highs_lows.py

import csv
from matplotlib import pyplot as plt

# 從文件中獲取最高氣溫
filename = 'sitka_weather_07-2014.csv'

with open(filename) as f:
    reader = csv.reader(f)
    header_row = next(reader)

    highs = []
    for row in reader:
        high = int(row[1])
        highs.append(high)

    # print(highs)

# 根據(jù)數(shù)據(jù)繪制圖形
fig = plt.figure(dpi=128, figsize=(10, 6))
plt.plot(highs, c='red')

# 設(shè)置圖形的格式
plt.title("Daily high temperatures, July 2014", fontsize=24)
plt.xlabel('', fontsize=16)
plt.ylabel("Temperature (F)", fontsize=16)
plt.tick_params(axis='both', which='major', labelsize=16)
plt.show()

測試記錄:

image.png

我們將最高氣溫列表傳給plot() 涛酗,并傳遞c='red' 以便將數(shù)據(jù)點繪制為紅色(紅色顯示最高氣溫,藍(lán)色顯示最低氣溫)。接下來商叹,我們設(shè)置了一些其他的格式燕刻,如字體大小和標(biāo)簽。鑒于我們還沒有添加日期剖笙,因此沒有給x 軸添加標(biāo)簽卵洗,但plt.xlabel() 確實修改了字體大小,讓默認(rèn)標(biāo)簽更容易看清弥咪。上圖:一個簡單的折線圖过蹂,顯示了阿拉斯加錫特卡2014年7月每天的最高氣溫。

1.1.5 模塊datetime

下面在圖表中添加日期聚至,使其更有用酷勺。在天氣數(shù)據(jù)文件中,第一個日期在第二行

2014-7-1,64,56,50,53,51,48,96,83,58,30.19,--snip--

讀取該數(shù)據(jù)時扳躬,獲得的是一個字符串脆诉,因為我們需要想辦法將字符串'2014-7-1' 轉(zhuǎn)換為一個表示相應(yīng)日期的對象。為創(chuàng)建一個表示2014年7月1日的對象贷币,可使用模塊datetime 中的方法strptime() 击胜。我們在終端會話中看看strptime() 的工作原理

C:\>python
Python 3.6.3 (v3.6.3:2c5fed8, Oct  3 2017, 18:11:49) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> from datetime import datetime
>>> first_date = datetime.strptime('2014-7-1', '%Y-%m-%d')
>>> print(first_date)
2014-07-01 00:00:00
>>>

我們首先導(dǎo)入了模塊datetime 中的datetime 類,然后調(diào)用方法strptime() 役纹,并將包含所需日期的字符串作為第一個實參偶摔。第二個實參告訴Python如何設(shè)置日期的格式。在這個示例中字管,'%Y-' 讓Python將字符串中第一個連字符前面的部分視為四位的年份啰挪;'%m-' 讓Python將第二個連字符前面的部分視為表示月份的數(shù)字;而'%d' 讓Python將字符串的最后一部分視為月份中的一天(1~31)嘲叔。

方法strptime() 可接受各種實參亡呵,并根據(jù)它們來決定如何解讀日期。表16-1列出了其中一些這樣的實參硫戈。

實參 含義
%A 星期的名稱锰什,如Monday
%B 月份名,如January
%m 用數(shù)字表示的月份(01~12)
%d 用數(shù)字表示月份中的一天(01~31)
%Y 四位的年份丁逝,如2015
%y 兩位的年份汁胆,如15
%H 24小時制的小時數(shù)(00~23)
%I 12小時制的小時數(shù)(01~12)
%p am或pm
%M 分鐘數(shù)(00~59)
%S 秒數(shù)(00~61)

1.1.6 在圖表中添加日期

知道如何處理CSV文件中的日期后,就可對氣溫圖形進(jìn)行改進(jìn)了霜幼,即提取日期和最高氣溫嫩码,并將它們傳遞給plot()

代碼:
highs_lows.py

import csv
from datetime import datetime
from matplotlib import pyplot as plt

# 從文件中獲取最高氣溫
filename = 'sitka_weather_07-2014.csv'

with open(filename) as f:
    reader = csv.reader(f)
    header_row = next(reader)

    dates, highs = [], []
    for row in reader:
        current_date = datetime.strptime(row[0], "%Y-%m-%d")
        dates.append(current_date)

        high = int(row[1])
        highs.append(high)

    # print(highs)

# 根據(jù)數(shù)據(jù)繪制圖形
fig = plt.figure(dpi=128, figsize=(10, 6))
plt.plot(dates ,highs, c='red')

# 設(shè)置圖形的格式
plt.title("Daily high temperatures, July 2014", fontsize=24)
plt.xlabel('', fontsize=16)
fig.autofmt_xdate()
plt.ylabel("Temperature (F)", fontsize=16)
plt.tick_params(axis='both', which='major', labelsize=16)
plt.show()

測試記錄:

image.png

1.1.7 涵蓋更長的時間

設(shè)置好圖表后,我們來添加更多的數(shù)據(jù)罪既,以成一幅更復(fù)雜的錫特卡天氣圖铸题。請將文件sitka_weather_2014.csv復(fù)制到存儲本章程序的文件夾中铡恕,該文件包含Weather Underground提供的整年的錫特卡天氣數(shù)據(jù)。

現(xiàn)在可以創(chuàng)建覆蓋整年的天氣圖了:
代碼:
highs_lows.py

import csv
from datetime import datetime
from matplotlib import pyplot as plt

# 從文件中獲取最高氣溫
filename = 'sitka_weather_2014.csv'

with open(filename) as f:
    reader = csv.reader(f)
    header_row = next(reader)

    dates, highs = [], []
    for row in reader:
        current_date = datetime.strptime(row[0], "%Y-%m-%d")
        dates.append(current_date)

        high = int(row[1])
        highs.append(high)

    # print(highs)

# 根據(jù)數(shù)據(jù)繪制圖形
fig = plt.figure(dpi=128, figsize=(10, 6))
plt.plot(dates ,highs, c='red')

# 設(shè)置圖形的格式
plt.title("Daily high temperatures - 2014", fontsize=24)
plt.xlabel('', fontsize=16)
fig.autofmt_xdate()
plt.ylabel("Temperature (F)", fontsize=16)
plt.tick_params(axis='both', which='major', labelsize=16)
plt.show()

測試記錄:

image.png

1.1.8 再繪制一個數(shù)據(jù)系列

上圖改進(jìn)后的圖表顯示了大量意義深遠(yuǎn)的數(shù)據(jù)丢间,但我們可以在其中再添加最低氣溫數(shù)據(jù)探熔,使其更有用。為此烘挫,需要從數(shù)據(jù)文件中提取最低氣溫诀艰,并將它們添加到圖表中,如下所示:
代碼:
highs_lows.py

import csv
from datetime import datetime
from matplotlib import pyplot as plt

# 從文件中獲取最高氣溫
filename = 'sitka_weather_2014.csv'

with open(filename) as f:
    reader = csv.reader(f)
    header_row = next(reader)

    dates, highs, lows = [], [], []
    for row in reader:
        current_date = datetime.strptime(row[0], "%Y-%m-%d")
        dates.append(current_date)

        high = int(row[1])
        highs.append(high)

        low = int(row[3])
        lows.append(low)

    # print(highs)

# 根據(jù)數(shù)據(jù)繪制圖形
fig = plt.figure(dpi=128, figsize=(10, 6))
plt.plot(dates ,highs, c='red')
plt.plot(dates ,lows , c='blue')

# 設(shè)置圖形的格式
plt.title("Daily high and low temperatures - 2014", fontsize=24)
plt.xlabel('', fontsize=16)
fig.autofmt_xdate()
plt.ylabel("Temperature (F)", fontsize=16)
plt.tick_params(axis='both', which='major', labelsize=16)
plt.show()

測試記錄:

image.png

1.1.9 給圖表區(qū)域著色

添加兩個數(shù)據(jù)系列后饮六,我們就可以了解每天的氣溫范圍了其垄。下面來給這個圖表做最后的修飾,通過著色來呈現(xiàn)每天的氣溫范圍喜滨。為此捉捅,我們將使用方法fill_between() ,它接受一個 x 值系列和兩個 y 值系列虽风,并填充兩個 y 值系列之間的空間

代碼:
highs_lows.py

import csv
from datetime import datetime
from matplotlib import pyplot as plt

# 從文件中獲取最高氣溫
filename = 'sitka_weather_2014.csv'

with open(filename) as f:
    reader = csv.reader(f)
    header_row = next(reader)

    dates, highs, lows = [], [], []
    for row in reader:
        current_date = datetime.strptime(row[0], "%Y-%m-%d")
        dates.append(current_date)

        high = int(row[1])
        highs.append(high)

        low = int(row[3])
        lows.append(low)

    # print(highs)

# 根據(jù)數(shù)據(jù)繪制圖形
fig = plt.figure(dpi=128, figsize=(10, 6))
plt.plot(dates ,highs, c='red', alpha=0.5)
plt.plot(dates ,lows , c='blue', alpha=0.5)
plt.fill_between(dates, highs, lows, facecolor='blue', alpha=0.1)

# 設(shè)置圖形的格式
plt.title("Daily high and low temperatures - 2014", fontsize=24)
plt.xlabel('', fontsize=16)
fig.autofmt_xdate()
plt.ylabel("Temperature (F)", fontsize=16)
plt.tick_params(axis='both', which='major', labelsize=16)
plt.show()

測試記錄:

image.png

通過著色,讓兩個數(shù)據(jù)集之間的區(qū)域顯而易見寄月。

1.1.10 錯誤檢查

我們應(yīng)該能夠使用有關(guān)任何地方的天氣數(shù)據(jù)來運行highs_lows.py中的代碼辜膝,但有些氣象站會偶爾出現(xiàn)故障,未能收集部分或全部其應(yīng)該收集的數(shù)據(jù)漾肮。缺失數(shù)據(jù)可能會引發(fā)異常厂抖,如果不妥善地處理,還可能導(dǎo)致程序崩潰克懊。

例如忱辅,我們來看看生成加利福尼亞死亡谷的氣溫圖時出現(xiàn)的情況。將文件death_valley_2014.csv復(fù)制到本章程序所在的文件夾谭溉,再修改highs_lows.py墙懂,使其生成死亡谷的氣溫圖
highs_lows.py

--snip--
# 從文件中獲取日期、最高氣溫和最低氣溫
filename = 'death_valley_2014.csv'
with open(filename) as f:
--snip--

運行這個程序時扮念,出現(xiàn)了一個錯誤损搬,如下述輸出的最后一行所示:

Traceback (most recent call last):
File "highs_lows.py", line 17, in <module>
high = int(row[1])
ValueError: invalid literal for int() with base 10: ''

該traceback指出,Python無法處理其中一天的最高氣溫柜与,因為它無法將空字符串(' ' )轉(zhuǎn)換為整數(shù)巧勤。只要看一下death_valley_2014.csv,就能發(fā)現(xiàn)其中的問題

2014-2-16,,,,,,,,,,,,,,,,,,,0.00,,,-1

其中好像沒有記錄2014年2月16日的數(shù)據(jù)弄匕,表示最高溫度的字符串為空颅悉。為解決這種問題,我們在從CSV文件中讀取值時執(zhí)行錯誤檢查代碼迁匠,對分析數(shù)據(jù)集時可能出現(xiàn)的異常進(jìn)行處理

highs_lows.py

import csv
from datetime import datetime
from matplotlib import pyplot as plt

# 從文件中獲取最高氣溫
filename = 'death_valley_2014.csv'

with open(filename) as f:
    reader = csv.reader(f)
    header_row = next(reader)

    dates, highs, lows = [], [], []
    for row in reader:
        try:
            current_date = datetime.strptime(row[0], "%Y-%m-%d")
            high = int(row[1])
            low = int(row[3])
        except ValueError:
            print(current_date, 'missing data')
        else:
            dates.append(current_date)
            highs.append(high)
            lows.append(low)

    # print(highs)

# 根據(jù)數(shù)據(jù)繪制圖形
fig = plt.figure(dpi=128, figsize=(10, 6))
plt.plot(dates ,highs, c='red', alpha=0.5)
plt.plot(dates ,lows , c='blue', alpha=0.5)
plt.fill_between(dates, highs, lows, facecolor='blue', alpha=0.1)

# 設(shè)置圖形的格式
plt.title("Daily high and low temperatures - 2014\nDeath Valley, CA", fontsize=24)
plt.xlabel('', fontsize=16)
fig.autofmt_xdate()
plt.ylabel("Temperature (F)", fontsize=16)
plt.tick_params(axis='both', which='major', labelsize=16)
plt.show()

對于每一行剩瓶,我們都嘗試從中提取日期秕脓、最高氣溫和最低氣溫。只要缺失其中一項數(shù)據(jù)儒搭,Python就會引發(fā)ValueError 異常吠架,而我們可這樣處理:打印一條錯誤消息,指出缺失數(shù)據(jù)的日期搂鲫。打印錯誤消息后傍药,循環(huán)將接著處理下一行。如果獲取特定日期的所有數(shù)據(jù)時沒有發(fā)生錯誤魂仍,將運行else 代碼塊拐辽,并將數(shù)據(jù)附加到相應(yīng)列表的末尾。鑒于我們繪圖時使用的是有關(guān)另一個地方的信息擦酌,我們修改了標(biāo)題俱诸,在圖表中指出了這個地方。

測試記錄:
如果你現(xiàn)在運行highs_lows.py 赊舶,將發(fā)現(xiàn)缺失數(shù)據(jù)的日期只有一個

E:\python\learn_python1\venv\Scripts\python.exe E:/python/learn_python1/數(shù)據(jù)可視化/highs_lows.py
2014-02-16 00:00:00 missing data
image.png

1.2 制作世界人口地圖:JSON格式

在本節(jié)中睁搭,你將下載JSON格式的人口數(shù)據(jù),并使用json 模塊來處理它們笼平。Pygal提供了一個適合初學(xué)者使用的地圖創(chuàng)建工具园骆,你將使用它來對人口數(shù)據(jù)進(jìn)行可視化,以探索全球人口的分布情況寓调。

1.2.1 下載世界人口數(shù)據(jù)

將文件population_data.json復(fù)制到本章程序所在的文件夾中锌唾,這個文件包含全球大部分國家1960~2010年的人口數(shù)據(jù)。Open Knowledge Foundation(http://data.okfn.org/ )提供了大量可以免費使用的數(shù)據(jù)集夺英,這些數(shù)據(jù)就來自其中一個數(shù)據(jù)集晌涕。

1.2.2 提取相關(guān)的數(shù)據(jù)

我們來研究一下population_data.json,看看如何著手處理這個文件中的數(shù)據(jù):
population_data.json

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

這個文件實際上就是一個很長的Python列表痛悯,其中每個元素都是一個包含四個鍵的字典:國家名余黎、國別碼、年份以及表示人口數(shù)量的值灸蟆。我們只關(guān)心每個國家2010年的人口數(shù)量驯耻,因此我們首先編寫一個打印這些信息的程序.
代碼:
world_population.py

import json

# 將數(shù)據(jù)加載到一個列表中
filename = 'population_data.json'
with open(filename) as f:
    pop_data = json.load(f)

# 打印每個國家2010年的人口數(shù)量
for pop_dict in pop_data:
    if pop_dict['Year'] == '2010':
        country_name = pop_dict['Country Name']
        population = pop_dict['Value']
        print(country_name + ": " + population)

測試記錄:

E:\python\learn_python1\venv\Scripts\python.exe E:/python/learn_python1/數(shù)據(jù)可視化/world_population.py
Arab World: 357868000
Caribbean small states: 6880000
East Asia & Pacific (all income levels): 2201536674
East Asia & Pacific (developing only): 1961558757
Euro area: 331766000
--snip--
Uzbekistan: 28228000
Vanuatu: 240000
Venezuela, RB: 28834000
Vietnam: 86928000
Virgin Islands (U.S.): 110000
West Bank and Gaza: 4152000
Yemen, Rep.: 24053000
Zambia: 12927000
Zimbabwe: 12571000

Process finished with exit code 0

1.2.3 將字符串轉(zhuǎn)換為數(shù)字值

population_data.json中的每個鍵和值都是字符串。為處理這些人口數(shù)據(jù)炒考,我們需要將表示人口數(shù)量的字符串轉(zhuǎn)換為數(shù)字值可缚,為此我們使用函數(shù)int()
代碼:
world_population.py

import json

# 將數(shù)據(jù)加載到一個列表中
filename = 'population_data.json'
with open(filename) as f:
    pop_data = json.load(f)

# 打印每個國家2010年的人口數(shù)量
for pop_dict in pop_data:
    if pop_dict['Year'] == '2010':
        country_name = pop_dict['Country Name']
        population = int(pop_dict['Value'])
        print(country_name + ": " + str(population))

測試記錄:
對于有些值,這種轉(zhuǎn)換會導(dǎo)致錯誤:

E:\python\learn_python1\venv\Scripts\python.exe E:/python/learn_python1/數(shù)據(jù)可視化/world_population.py
Arab World: 357868000
Traceback (most recent call last):
  File "E:/python/learn_python1/數(shù)據(jù)可視化/world_population.py", line 12, in <module>
    population = int(pop_dict['Value'])
ValueError: invalid literal for int() with base 10: '1127437398.85751'
Caribbean small states: 6880000
East Asia & Pacific (all income levels): 2201536674
East Asia & Pacific (developing only): 1961558757
Euro area: 331766000
--snip--

原始數(shù)據(jù)的格式常常不統(tǒng)一斋枢,因此經(jīng)常會出現(xiàn)錯誤帘靡。導(dǎo)致上述錯誤的原因是,Python不能直接將包含小數(shù)點的字符串'1127437398.85751' 轉(zhuǎn)換為整數(shù)(這個小數(shù)值可能是人口數(shù)據(jù)缺失時通過插值得到的)瓤帚。為消除這種錯誤描姚,我們先將字符串轉(zhuǎn)換為浮點數(shù)涩赢,再將浮點數(shù)轉(zhuǎn)換為整數(shù)。
代碼:

import json

# 將數(shù)據(jù)加載到一個列表中
filename = 'population_data.json'
with open(filename) as f:
    pop_data = json.load(f)

# 打印每個國家2010年的人口數(shù)量
for pop_dict in pop_data:
    if pop_dict['Year'] == '2010':
        country_name = pop_dict['Country Name']
        population = int(float(pop_dict['Value']))
        print(country_name + ": " + str(population))

每個字符串都成功地轉(zhuǎn)換成了浮點數(shù)轩勘,再轉(zhuǎn)換為整數(shù)筒扒。以數(shù)字格式存儲人口數(shù)量值后,就可以使用它們來制作世界人口地圖了绊寻。

1.2.4 獲取兩個字母的國別碼

制作地圖前花墩,還需要解決數(shù)據(jù)存在的最后一個問題。Pygal中的地圖制作工具要求數(shù)據(jù)為特定的格式:用國別碼表示國家澄步,以及用數(shù)字表示人口數(shù)量冰蘑。處理地理政治數(shù)據(jù)時,經(jīng)常需要用到幾個標(biāo)準(zhǔn)化國別碼集村缸。population_data.json中包含的是三個字母的國別碼祠肥,但Pygal使用兩個字母的國別碼。我們需要想辦法根據(jù)國家名獲取兩個字母的國別碼梯皿。

Pygal使用的國別碼存儲在模塊i18n (internationalization的縮寫)中仇箱。字典COUNTRIES 包含的鍵和值分別為兩個字母的國別碼和國家名。要查看這些國別碼索烹,可從模塊i18n 中導(dǎo)入這個字典工碾,并打印其鍵和值:

pygal.i18n 已經(jīng)不存在了,現(xiàn)在已經(jīng)更改成了 pygal_maps_world 百姓,需要單獨通過pip下載

C:\>pip install pygal_maps_world
Collecting pygal_maps_world
  Downloading pygal_maps_world-1.0.2.tar.gz (270 kB)
     |████████████████████████████████| 270 kB 598 kB/s
Requirement already satisfied: pygal>=1.9.9 in c:\users\administrator\appdata\local\programs\python\python36\lib\site-packages (from pygal_maps_world) (2.4.0)
Using legacy 'setup.py install' for pygal-maps-world, since package 'wheel' is not installed.
Installing collected packages: pygal-maps-world
    Running setup.py install for pygal-maps-world ... done
Successfully installed pygal-maps-world-1.0.2

代碼:

from pygal_maps_world.i18n import COUNTRIES

for country_code in sorted(COUNTRIES.keys()):
    print(country_code, COUNTRIES[country_code])

測試記錄:

E:\python\learn_python1\venv\Scripts\python.exe E:/python/learn_python1/數(shù)據(jù)可視化/countries.py
ad Andorra
ae United Arab Emirates
af Afghanistan
al Albania
--snip--
yt Mayotte
za South Africa
zm Zambia
zw Zimbabwe

Process finished with exit code 0

為獲取國別碼,我們將編寫一個函數(shù)况木,它在COUNTRIES 中查找并返回國別碼垒拢。我們將這個函數(shù)放在一個名為country_codes 的模塊中,以便能夠在可視化程序中導(dǎo)入它
代碼:

from pygal_maps_world.i18n import COUNTRIES

def get_country_code(country_name):
    """ 根據(jù)指定的國家火惊,返回Pygal使用的兩個字母的國別碼 """
    for code, name in COUNTRIES.items():
        if name == country_name:
            return code

print(get_country_code('Andorra'))
print(get_country_code('United Arab Emirates'))
print(get_country_code('Afghanistan'))

測試記錄:

E:\python\learn_python1\venv\Scripts\python.exe E:/python/learn_python1/數(shù)據(jù)可視化/countries.py
ad
ae
af

Process finished with exit code 0

接下來求类,在world_population.py中導(dǎo)入get_country_code
代碼:
world_population.py

import json

from country_codes import get_country_code

# 將數(shù)據(jù)加載到一個列表中
filename = 'population_data.json'
with open(filename) as f:
    pop_data = json.load(f)

# 打印每個國家2010年的人口數(shù)量
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:
            print(country_name + ": " + str(population))
        else:
            print('ERROR - ' + country_name)

測試記錄:

E:\python\learn_python1\venv\Scripts\python.exe E:/python/learn_python1/數(shù)據(jù)可視化/world_population.py
ERROR - Arab World
ERROR - Caribbean small states
ERROR - East Asia & Pacific (all income levels)
ERROR - East Asia & Pacific (developing only)
ERROR - Euro area
ERROR - Europe & Central Asia (all income levels)
--snip--
Zambia: 12927000
Zimbabwe: 12571000

Process finished with exit code 0

1.2.5 制作世界地圖

有了國別碼后,制作世界地圖易如反掌屹耐。Pygal提供了圖表類型Worldmap 尸疆,可幫助你制作呈現(xiàn)各國數(shù)據(jù)的世界地圖。為演示如何使用Worldmap 惶岭,我們來創(chuàng)建一個突出北美寿弱、中美和南美的簡單地圖

代碼:
americas.py

import pygal_maps_world.maps

wm = pygal_maps_world.maps.World()
wm.title = 'North, Central, and South America'

wm.add('North America', ['ca', 'mx', 'us'])
wm.add('Central America', ['bz', 'cr', 'gt', 'hn', 'ni', 'pa', 'sv'])
wm.add('South America', ['ar', 'bo', 'br', 'cl', 'co', 'ec', 'gf',
                         'gy', 'pe', 'py', 'sr', 'uy', 've'])

wm.render_to_file('americas.svg')

測試記錄:

image.png

1.2.6 在世界地圖上呈現(xiàn)數(shù)字?jǐn)?shù)據(jù)

為練習(xí)在地圖上呈現(xiàn)數(shù)字?jǐn)?shù)據(jù),我們來創(chuàng)建一幅地圖按灶,顯示三個北美國家的人口數(shù)量

代碼:
na_populations.py

import pygal_maps_world.maps

wm = pygal_maps_world.maps.World()
wm.title = 'Populations of Countries in North America'
wm.add('North America',{ 'ca': 34126000, 'us':309349000, 'mx':113423000})

wm.render_to_file('na_populations.svg')

測試記錄:

image.png

1.2.7 繪制完整的世界人口地圖

要呈現(xiàn)其他國家的人口數(shù)量症革,需要將前面處理的數(shù)據(jù)轉(zhuǎn)換為Pygal要求的字典格式:鍵為兩個字母的國別碼,值為人口數(shù)量鸯旁。為此噪矛,在world_population.py中添加如下代碼
代碼:
world_population.py

import json
import pygal_maps_world.maps
from country_codes import get_country_code

# 將數(shù)據(jù)加載到一個列表中
filename = 'population_data.json'
with open(filename) as f:
    pop_data = json.load(f)

# 創(chuàng)建一個包含人口數(shù)量的字典
cc_pupulations = {}
for pop_dict in pop_data:
    if pop_dict['Year'] == '2010':
        country = pop_dict['Country Name']
        population = int(float(pop_dict['Value']))
        code = get_country_code(country)
        if code:
            cc_pupulations[code] = population

wm = pygal_maps_world.maps.World()
wm.title = 'World Population in 2010, by Country'
wm.add('2010', cc_pupulations)

wm.render_to_file('world_population.svg')

測試記錄:

image.png

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

印度和中國的人口比其他國家多得多量蕊,但在當(dāng)前的地圖中,它們的顏色與其他國家差別較小艇挨。中國和印度的人口都超過了10億残炮,接下來人口最多的國家是美國,但只有大約3億缩滨。下面不將所有國家都作為一個編組势就,而是根據(jù)人口數(shù)量分成三組——少于1000萬的、介于1000萬和10億之間的以及超過10億的

代碼:
world_population.py

import json
import pygal_maps_world.maps
from country_codes import get_country_code

# 將數(shù)據(jù)加載到一個列表中
filename = 'population_data.json'
with open(filename) as f:
    pop_data = json.load(f)

# 創(chuàng)建一個包含人口數(shù)量的字典
cc_pupulations = {}
for pop_dict in pop_data:
    if pop_dict['Year'] == '2010':
        country = pop_dict['Country Name']
        population = int(float(pop_dict['Value']))
        code = get_country_code(country)
        if code:
            cc_pupulations[code] = population

# 根據(jù)人口數(shù)量將所有的國家分為三組
cc_pops_1, cc_pops_2, cc_pops_3 = {}, {}, {}
for cc, pop in cc_pupulations.items():
    if pop < 10000000:
        cc_pops_1[cc] = pop
    elif pop < 1000000000:
        cc_pops_2[cc] = pop
    else:
        cc_pops_3[cc] = pop

# 看看每組分別包含多少個國家
print(len(cc_pops_1), len(cc_pops_2), len(cc_pops_3))

wm = pygal_maps_world.maps.World()
wm.title = 'World Population in 2010, by Country'
wm.add('0-10m', cc_pops_1)
wm.add('10m-1bm', cc_pops_2)
wm.add('>1bn', cc_pops_3)


wm.render_to_file('world_population.svg')

測試記錄:

E:\python\learn_python1\venv\Scripts\python.exe E:/python/learn_python1/數(shù)據(jù)可視化/world_population.py
85 69 2

Process finished with exit code 0

image.png

1.2.9 使用Pygal設(shè)置世界地圖的樣式

在這個地圖中楷怒,根據(jù)人口將國家分組雖然很有效蛋勺,但默認(rèn)的顏色設(shè)置很難看。例如鸠删,在這里抱完,Pygal選擇了鮮艷的粉色和綠色基色。下面使用Pygal樣式設(shè)置指令來調(diào)整顏色刃泡。

我們也讓Pygal使用一種基色巧娱,但將指定該基色,并讓三個分組的顏色差別更大.

代碼:
world_population.py

import json
import pygal_maps_world.maps
from pygal.style import RotateStyle
from country_codes import get_country_code

# 將數(shù)據(jù)加載到一個列表中
filename = 'population_data.json'
with open(filename) as f:
    pop_data = json.load(f)

# 創(chuàng)建一個包含人口數(shù)量的字典
cc_pupulations = {}
for pop_dict in pop_data:
    if pop_dict['Year'] == '2010':
        country = pop_dict['Country Name']
        population = int(float(pop_dict['Value']))
        code = get_country_code(country)
        if code:
            cc_pupulations[code] = population

# 根據(jù)人口數(shù)量將所有的國家分為三組
cc_pops_1, cc_pops_2, cc_pops_3 = {}, {}, {}
for cc, pop in cc_pupulations.items():
    if pop < 10000000:
        cc_pops_1[cc] = pop
    elif pop < 1000000000:
        cc_pops_2[cc] = pop
    else:
        cc_pops_3[cc] = pop

# 看看每組分別包含多少個國家
print(len(cc_pops_1), len(cc_pops_2), len(cc_pops_3))

wm_style = RotateStyle('#336699')
wm = pygal_maps_world.maps.World(style=wm_style)
wm.title = 'World Population in 2010, by Country'
wm.add('0-10m', cc_pops_1)
wm.add('10m-1bm', cc_pops_2)
wm.add('>1bn', cc_pops_3)


wm.render_to_file('world_population.svg')

測試記錄:

image.png

1.2.10 加亮顏色主題

Pygal通常默認(rèn)使用較暗的顏色主題烘贴。為方便印刷禁添,我使用LightColorizedStyle 加亮了地圖的顏色。這個類修改整個圖表的主題桨踪,包括背景色老翘、標(biāo)簽以及各個國家的顏色。要使用這個樣式锻离,先導(dǎo)入它.

from pygal.style import LightColorizedStyle

然后就可獨立地使用LightColorizedStyle 了铺峭,例如:

wm_style = LightColorizedStyle

然而使用這個類時,你不能直接控制使用的顏色汽纠,Pygal將選擇默認(rèn)的基色卫键。要設(shè)置顏色,可使用RotateStyle 虱朵,并將LightColorizedStyle 作為基本樣式莉炉。為此,導(dǎo)入LightColorizedStyle 和RotateStyle

from pygal.style import LightColorizedStyle, RotateStyle

再使用RotateStyle 創(chuàng)建一種樣式碴犬,并傳入另一個實參base_style:

wm_style = RotateStyle('#336699', base_style=LightColorizedStyle)

這設(shè)置了較亮的主題絮宁,同時根據(jù)通過實參傳遞的顏色給各個國家著色。使用這種樣式時翅敌,生成的圖表與本書的屏幕截圖更一致羞福。
嘗試為不同的可視化選擇合適的樣式設(shè)置指令時担扑,在import 語句中指定別名會有所幫助:

from pygal.style import LightColorizedStyle as LCS, RotateStyle as RS

這樣养涮,樣式定義將更短:

wm_style = RS('#336699', base_style=LCS)

通過使用幾個樣式設(shè)置指令侥涵,就能很好地控制圖表和地圖的外觀裂问。

參考

1.Python編程:從入門到實踐
2.https://github.com/ehmatthes/pcc/tree/master/chapter_16

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市张峰,隨后出現(xiàn)的幾起案子泪蔫,更是在濱河造成了極大的恐慌,老刑警劉巖喘批,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件撩荣,死亡現(xiàn)場離奇詭異,居然都是意外死亡饶深,警方通過查閱死者的電腦和手機(jī)餐曹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來敌厘,“玉大人台猴,你說我怎么就攤上這事【懔剑” “怎么了饱狂?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長宪彩。 經(jīng)常有香客問我休讳,道長,這世上最難降的妖魔是什么尿孔? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任俊柔,我火速辦了婚禮,結(jié)果婚禮上活合,老公的妹妹穿的比我還像新娘婆咸。我一直安慰自己,他們只是感情好芜辕,可當(dāng)我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著块差,像睡著了一般侵续。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上憨闰,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天状蜗,我揣著相機(jī)與錄音,去河邊找鬼鹉动。 笑死轧坎,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的泽示。 我是一名探鬼主播缸血,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蜜氨,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了捎泻?” 一聲冷哼從身側(cè)響起飒炎,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎笆豁,沒想到半個月后郎汪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡闯狱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年煞赢,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片哄孤。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡照筑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出录豺,到底是詐尸還是另有隱情朦肘,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布双饥,位于F島的核電站媒抠,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏咏花。R本人自食惡果不足惜趴生,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望昏翰。 院中可真熱鬧苍匆,春花似錦、人聲如沸棚菊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽统求。三九已至检碗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間码邻,已是汗流浹背折剃。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留像屋,地道東北人怕犁。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親奏甫。 傳聞我的和親對象是個殘疾皇子戈轿,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容