一.下載數(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()
測試記錄:
我們將最高氣溫列表傳給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()
測試記錄:
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()
測試記錄:
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()
測試記錄:
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()
測試記錄:
通過著色,讓兩個數(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
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')
測試記錄:
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')
測試記錄:
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')
測試記錄:
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
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')
測試記錄:
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