天天看點

Python學習for資料可視化項目(二)Python學習for資料可視化項目(二)

Python學習for資料可視化項目(二)

圖檔後續添加

文章目錄

  • Python學習for資料可視化項目(二)
    • 1 CSV檔案格式
      • 1.1 分析CSV檔案頭
      • 1.2 列印檔案頭及其位置
      • 1.3 提取并讀取資料
      • 1.4 繪制氣溫圖表
      • 1.5 子產品datetime
      • 1.6 在圖表中添加日期
      • 1.7 涵蓋更長的時間
      • 1.8 添加最低氣溫資料
      • 1.9 給圖表區域着色
      • 1.10 錯誤檢查
    • 2 制作世界人口地圖:JSON格式
      • 2.1 下載下傳世界人口資料
      • 2.2 提取相關資料
      • 2.3 将字元串轉換為數字值
      • 2.4 擷取兩個字母的國别碼
      • 2.5 制作世界地圖
      • 2.6 在世界地圖上呈現數字資料
      • 2.7 繪制完整的世界人口地圖
      • 2.8 根據人口數量将國家分組
      • 2.9 使用Pygal設定世界地圖的樣式

1 CSV檔案格式

在文本檔案中存儲資料最簡單的方式時将資料作為一系列**以逗号分隔的值(CSV)**寫入。這樣的檔案被成為CSV檔案。本部分将使用《Python程式設計:從入門到實踐》的配套資源,來對阿拉斯加西特卡地區的天氣資料進行分析,檔案名為sitka_weather_07-2014.csv。

1.1 分析CSV檔案頭

csv子產品包含在Python标準庫中,可用于分析CSV檔案中的資料行。

import csv
filename='sitka_weather_07-2014.csv'
#打開檔案,将結果檔案對象存儲在f
with open(filename) as f:
    #調用csv.reader()将檔案對象傳入,建立一個閱讀器對象
    reader=csv.reader(f)
    #csv中的next()函數傳回閱讀器對象中的下一行
    header_row=next(reader)
    print(header_row)
           

所打開檔案的第一行是與天氣相關的檔案頭:

檔案頭AKDT 表示阿拉斯加時間(Alaska Daylight Time) , 其位置表明每行的第一個值都是日期或時間。 檔案頭Max TemperatureF 指出每行的第二個值都是當天的最高華氏溫度。

注:檔案頭的格式并非總是一緻,空格和機關可能出現在奇怪的地方,這在原始檔案中很常見,不會帶來問題。

1.2 列印檔案頭及其位置

使用**enumerate()**來擷取每個元素的索引和值,以将清單中的每個檔案頭及其位置都列印出來:

import csv
filename='sitka_weather_07-2014.csv'

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

此時輸出如下:

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
           

從中看出,時間在第0列,最高氣溫在第1列。故而為了分析時間和最高氣溫,應當處理檔案中的每一行資料,提取出索引為0和1的值。

1.3 提取并讀取資料

建立一個空清單,用一個循環來将每一行的最高氣溫存進清單。

import csv
filename='sitka_weather_07-2014.csv'

with open(filename) as f:
    reader=csv.reader(f)
    header_row=next(reader)
    #print(header_row)
    #for index,columb_header in enumerate(header_row):
        #print(index,columb_header)
    highs=[]
    for row in reader:
        highs.append(row[1])

    print(highs)
           

注意:閱讀器對象從其停留的地方繼續往下讀取CSV檔案, 每次都自動傳回目前所處位置的下一行。

由于之前已經用next()讀取了檔案頭行, 程式中的循環将從第二行開始,而檔案中從這行開始包含的是實際資料。

由于是在清單中使用了字元串儲存天氣的資料,為了讓matplotlib可以讀取,應當将其轉換為數字。

上文程式中的循環部分改成:

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

此時資料為:

1.4 繪制氣溫圖表

使用matplotlib來建立一個顯示每日最高氣溫的折線圖。

import matplotlib.pyplot as plt
--snip--#接上節代碼
#設定分辨率和圖檔大小
fig=plt.figure(dpi=128,figsize=(10,6))
plt.plot(highs,c='red')

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()
           
Python學習for資料可視化項目(二)Python學習for資料可視化項目(二)

1.5 子產品datetime

下面在圖表中添加日期。在天氣資料檔案中,第一個日期在第二行。

使用**datetime子產品中的strptime()**方法來将一個如2014-7-1的字元串轉換為一個表示相應日期的對象。

該方法的第一個實參為包含日期的字元串,第二個實參為解讀字元串的式:’%Y-’ 将對應部分視為四位的年份; ‘%m-’ 将對應部分視為表示月份的數字; 而’%d’ 将對應部分視為月份中的一天(1~31)。其工作示例為:

>>> from datetime import datetime
>>> first_date=datetime.strptime('2014-7-1','%Y-%m-%d')
>>> print(first_date)
2014-07-01 00:00:00
>>> 
           

**datetime**子產品中設定日期和時間格式的實參:

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

1.6 在圖表中添加日期

這一小節中提取日期和最高氣溫,傳遞給**plot()**。

fig.autofmt_xdate() 來繪制斜的日期标簽,以避免彼此重疊。

import csv
#從子產品datetime導入datetime
from datetime import datetime
import matplotlib.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)

fig=plt.figure(dpi=128,figsize=(10,6))
plt.plot(dates,highs,c='red')

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()
           
Python學習for資料可視化項目(二)Python學習for資料可視化項目(二)

1.7 涵蓋更長的時間

将讀取的資料檔案替換成2014年一整年的檔案,再修改上節代碼中的标題即可。

此時的圖為:

Python學習for資料可視化項目(二)Python學習for資料可視化項目(二)

1.8 添加最低氣溫資料

在上文的程式中添加提取最低氣溫的代碼,然後将最低氣溫在圖中繪制出,并将顔色設為藍色。

#提取最低氣溫代碼修改為:
    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)
        --sinp--
     #繪制最低氣溫的代碼
     plt.plot(dates,lows,c='blue')
           
Python學習for資料可視化項目(二)Python學習for資料可視化項目(二)

1.9 給圖表區域着色

這裡通過着色來呈現每天的氣溫範圍。

使用**fill_between()**方法:接受一個x值的資料系列,接受兩個y值的資料系列,填充兩個y值資料系列之間的空間。

實參**alpha**:指定顔色的透明度,設為0表示完全透明,1表示完全不透明。

将上文繪制圖表部分的代碼修改如下

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.5)
           

此時圖表為:

Python學習for資料可視化項目(二)Python學習for資料可視化項目(二)

1.10 錯誤檢查

當檔案中的部分資料産生錯誤,如缺失、格式錯誤,可能會引發異常,需要妥善處理。

用上節代碼來處理加利福尼亞死亡谷的氣溫資料,檔案名death_valley_2014.csv。将會出現一個錯誤。

Traceback (most recent call last):
  File "/home/choupin/PycharmProjects/DataVisualization/DownloadData/highs_lows.py", line 19, in <module>
  #row[1]出錯說明時某一天的最高氣溫出錯
    high=int(row[1])
    #空字元無法轉成整型資料
ValueError: invalid literal for int() with base 10: ''

           

錯誤說明,程式無法處理某一天的最高氣溫,因為其無法将空字元串轉換成證書。

為此在原程式中添加處理異常的try-expect代碼塊。

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 date')
    else:
        dates.append(current_date)
        lows.append(low)
        highs.append(high)
           

這樣在讀取資料時如果發現資料缺失,那麼将抛出異常,并指出引發異常的日期,然後開始下一個循環,圖表依然可以繪制出,同時也能知道哪一天的資料出了問題。

Python學習for資料可視化項目(二)Python學習for資料可視化項目(二)

注:在處理其他資料錯誤的情況,還可以使用continue來跳過一些資料,或者使用remove()、del将已提取的資料删除。可根據情況而定

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

本節需要使用JSON格式的人口資料,并用json子產品來進行處理。

2.1 下載下傳世界人口資料

将檔案population_data.json複制到本章程式所在檔案夾,這個檔案包含全球大部分國家1960~2010年的人口資料。

注:github上有這個檔案,點此到達

注:Open Knowledge Foundation(http://data.okfn.org/ ) 提供了大量可以免費使用的資料集, 這些資料就來自其中一個資料集。

2.2 提取相關資料

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"
  },
  {
    "Country Name": "Arab World",
    "Country Code": "ARB",
    "Year": "1962",
    "Value": "101474075.8"
  },
  --snip--
           

可見其是一個以字典為元素的清單。

接下來提起2010年各個國家的人口資料。

#導入json子產品
import json
filename='population_data.json'

with open(filename) as f:
    pop_data=json.load(f)

for pop_dict in pop_data:
    #標明年份
    if pop_dict['Year']=='2010':
        country_name=pop_dict['Country Name']
        poplation=pop_dict['Value']
        print(country_name+':'+poplation)
           

輸出結果為:

Arab World:357868000
Caribbean small states:6880000
East Asia & Pacific (all income levels):2201536674
East Asia & Pacific (developing only):1961558757
Euro area:331766000
Europe & Central Asia (all income levels):890424544
Europe & Central Asia (developing only):405204000
European Union:502125000
--snip--
           

2.3 将字元串轉換為數字值

由于檔案中的鍵值對都是字元串,是以需要轉成數字值再用pygal來處理,在此轉成int型。

而由于有些資料帶有小數點,而不能直接将帶小數點的字元串轉成整型,是以先轉成浮點型,然後再轉成整型。

修改上節倒數兩行代碼為:

population=int(float(pop_dict['Value']))
print(country_name+':'+str(poplation))
           

2.4 擷取兩個字母的國别碼

Pygal中的地圖制作工具要求資料為特定的格式:用國别碼表示國家,以及用數字表示人口數量。處理地理政治資料時,經常需要用到幾個标準化國别碼集。

而檔案中的國别碼時三個字母的,是以需要轉換。

Pygal使用的國别碼原本存儲在子產品**il8n**中,然而該子產品已經被pygal-2.0.0移除,需要在子產品***pygal.map.world***中查找。字典COUNTRIES 包含的鍵和值分别為兩個字母的國别碼和國家名。要檢視這些國别碼,可從子產品i18n 中導入這個字典, 并列印其鍵和值:

from pygal.maps.world import COUNTRIES
for country_code in sorted(COUNTRIES.keys()):
    print(country_code,COUNTRIES[country_code])
           

輸出為:

ad Andorra
ae United Arab Emirates
af Afghanistan
al Albania
am Armenia
ao Angola
--snip--
           

編寫一個函數用于接受國家名稱,然後傳回國别碼,檔案名命名為country_codes.py。

from pygal.maps.world import COUNTRIES

def get_country_code(country_name):
    for code,name in COUNTRIES:
        if name==country_name:
            return code
    return None
           

改寫上文的程式,用上這個傳回國别碼的函數,将國别碼和對應國家人口列印出來。

import json
#導入傳回國别碼的函數
from country_codes import get_country_code

filename='population_data.json'

with open(filename) as f:
    pop_data=json.load(f)

for pop_dict in pop_data:
    if pop_dict['Year']=='2010':
        country_name=pop_dict['Country Name']
        poplation=int(float(pop_dict['Value']))
        #擷取國别碼
        code=get_country_code(country_name)
        if code:
            print(code+':'+str(poplation))
        else:
            #若未找到國别碼就傳回錯誤資訊,并指出國家名
            print('Error-'+country_name)
           

2.5 制作世界地圖

《Python程式設計:從入門到實踐》中寫到“pygal提供了圖表類型Worldmap”,實際上同上節一樣,這個子產品也已經從新版的pygal中移除了。需要到pygal.maps.world中找到,且子產品名從Worldmap變為World。是以代碼如下:

#導入需要的新子產品
import pygal.maps.world as m

wm=m.World()
wm.title='Noth,Central,and South America'

#使用該子產品的add方法來添加一個标簽和一個國家碼清單
#預設每次使用不同顔色
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('america.svg')
           
Python學習for資料可視化項目(二)Python學習for資料可視化項目(二)

2.6 在世界地圖上呈現數字資料

修改上小節程式,将傳入add方法的清單替換成字典,鍵值對是國别碼和對面國家的人口數量,即可在地圖上顯示國家的人口數量。

import pygal.maps.world as m

wm=m.World()
wm.title='Noth,Central,and South America'
#将清單改為字典
wm.add('North America',{'ca':34126000,'mx':309349000,'us':113423000})
#為友善先将這兩行注釋
#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('na_population.svg')
           
Python學習for資料可視化項目(二)Python學習for資料可視化項目(二)

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

先處理資料,将國别碼和人口數量轉換成字典,以友善pygal來讀取。

修改代碼如下:

import json
import pygal.maps.world as m
from country_codes import get_country_code

filename='population_data.json'

with open(filename) as f:
    pop_data=json.load(f)
#建立一個字典
cc_populations={}
for pop_dict in pop_data:
    if pop_dict['Year']=='2010':
        country_name=pop_dict['Country Name']
        poplation=int(float(pop_dict['Value']))
        code=get_country_code(country_name)
        if code:
            #以國别碼為鍵,人口為值存入
            cc_populations[code]=poplation

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

wm.render_to_file('world_population.svg')
           

此時的地圖圖表為:

Python學習for資料可視化項目(二)Python學習for資料可視化項目(二)

注:根據本文獲得的兩張svg圖檔,并未獲得“互動性功能”,即用浏覽器打開,滑鼠停留在國家上,并未出現人口資料,待後續解決。貌似是打開的浏覽器聯網問題,電腦聯網後竟然就有了。

2.8 根據人口數量将國家分組

根據人口數量将其分成三組

import json
import pygal.maps.world as m
from country_codes import get_country_code

filename='population_data.json'

with open(filename) as f:
    pop_data=json.load(f)
cc_populations={}
for pop_dict in pop_data:
    if pop_dict['Year']=='2010':
        country_name=pop_dict['Country Name']
        poplation=int(float(pop_dict['Value']))
        code=get_country_code(country_name)
        if code:
            cc_populations[code]=poplation

cc_pops1,cc_pops2,cc_pops3={},{},{}
for code,pop in cc_populations.items():
    if pop <10000000:
        cc_pops1[code]=pop
    elif pop<100000000:
        cc_pops2[code]=pop
    else:
        cc_pops3[code]=pop

wm=m.World()
wm.title='World Population in 2010,by Country'
wm.add('0-10m',cc_pops1)
wm.add('10m-1nb',cc_pops2)
wm.add('>1bn',cc_pops3)

wm.render_to_file('world_population1.svg')
           
Python學習for資料可視化項目(二)Python學習for資料可視化項目(二)

2.9 使用Pygal設定世界地圖的樣式

Pygal樣式存儲在子產品***style***中。這裡可以直接聲明該子產品,也可隻聲明子產品中将要用到的函數。

在此将使用***style***子產品中的***RotateStyle***指定讓pygal的圖表使用一種基色,讓三個分組的顔色差别更大。

然後使用***LightColorizedStyle***來加亮地圖的顔色。

import json
import pygal.maps.world as m
#聲明pygal.style子產品
import pygal.style as s
from country_codes import get_country_code

filename='population_data.json'

with open(filename) as f:
    pop_data=json.load(f)
cc_populations={}
for pop_dict in pop_data:
    if pop_dict['Year']=='2010':
        country_name=pop_dict['Country Name']
        poplation=int(float(pop_dict['Value']))
        code=get_country_code(country_name)
        if code:
            cc_populations[code]=poplation

cc_pops1,cc_pops2,cc_pops3={},{},{}
for code,pop in cc_populations.items():
    if pop <10000000:
        cc_pops1[code]=pop
    elif pop<100000000:
        cc_pops2[code]=pop
    else:
        cc_pops3[code]=pop

wm=m.World()
#第一個參數為16進制的RGB顔色,前兩位代表R,以此類推,數字越大顔色越深。
#第二個參數為設定基礎樣式為亮色主題,預設使用較暗的主題。
wm_style=s.RotateStyle('#336699',base_style=s.LightColorizedStyle)
#注意這裡用實參style傳遞
wm=m.World(style=wm_style)
wm.title='World Population in 2010,by Country'
wm.add('0-10m',cc_pops1)
wm.add('10m-1nb',cc_pops2)
wm.add('>1bn',cc_pops3)

wm.render_to_file('world_population2.svg')

           
Python學習for資料可視化項目(二)Python學習for資料可視化項目(二)