pygal的簡單使用


pygal的簡單使用

例子來自此書: 《Python編程從入門到實戰》【美】Eric Matthes

pygal是一個SVG圖表庫。SVG是一種矢量圖格式。全稱Scalable Vector Graphics -- 可縮放矢量圖形。

用瀏覽器打開svg,可以方便的與之交互。

以下代碼均在Jupyter Notebook中運行

模擬擲骰子

來看一個簡單的例子。它模擬了擲骰子。

import random class Die: """ 一個骰子類 """ def __init__(self, num_sides=6): self.num_sides = num_sides def roll(self): return random.randint(1, self.num_sides) 

模擬擲骰子並可視化

import pygal die = Die() result_list = [] # 擲1000次 for roll_num in range(1000): result = die.roll() result_list.append(result) frequencies = [] # 范圍1~6,統計每個數字出現的次數 for value in range(1, die.num_sides + 1): frequency = result_list.count(value) frequencies.append(frequency) # 條形圖 hist = pygal.Bar() hist.title = 'Results of rolling one D6 1000 times' # x軸坐標 hist.x_labels = [1, 2, 3, 4, 5, 6] # x、y軸的描述 hist.x_title = 'Result' hist.y_title = 'Frequency of Result' # 添加數據, 第一個參數是數據的標題 hist.add('D6', frequencies) # 保存到本地,格式必須是svg hist.render_to_file('die_visual.svg') 

使用瀏覽器打開這個文件,鼠標指向數據,可以看到顯示了標題“D6”, x軸的坐標以及y軸坐標。

可以發現,六個數字出現的頻次是差不多的(理論上概率是1/6, 隨着實驗次數的增加,趨勢越來越明顯)

 
 

同時擲兩個骰子

稍微改下代碼就行,再實例化一個骰子

die_1 = Die()
die_2 = Die()

result_list = []
for roll_num in range(5000): # 兩個骰子的點數和 result = die_1.roll() + die_2.roll() result_list.append(result) frequencies = [] # 能擲出的最大數 max_result = die_1.num_sides + die_2.num_sides for value in range(2, max_result + 1): frequency = result_list.count(value) frequencies.append(frequency) # 可視化 hist = pygal.Bar() hist.title = 'Results of rolling two D6 dice 5000 times' hist.x_labels = [x for x in range(2, max_result + 1)] hist.x_title = 'Result' hist.y_title = 'Frequency of Result' # 添加數據 hist.add('two D6', frequencies) # 格式必須是svg hist.render_to_file('2_die_visual.svg') 

從圖中可以看出,兩個骰子之和為7的次數最多,和為2的次數最少。因為能擲出2的只有一種情況 -> (1, 1);而擲出7的情況有(1, 6) , (2, 5), (3, 4), (4, 3), (5, 2), (6, 1)共6種情況,其余數字的情況都沒有7的多,故擲得7得概率最大。

 
 

處理json數據--世界人口地圖

需要用到人口數據

點擊這里下載population.json,該數據來源於okfn.org這個網站

打開看下數據,其實這是個很長的列表,包含了許多國家從1960~2015年的人口數據。看第一數據,如下。后面的數據和第一個鍵都一樣。

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

只有四個鍵,其中Country Code指的是國別碼,這里是3位的。Value就是人口數了。

import json filename = r'F:\Jupyter Notebook\matplotlib_pygal_csv_json\population.json' with open(filename) as f: # json.load()可以將json文件轉為Python能處理的形式,這里位列表,列表里是字典 pop_data = json.load(f) cc_populations = {} for pop_dict in pop_data: if pop_dict['Year'] == '2015': country_name = pop_dict['Country Name'] # 有些值是小數,先轉為float再轉為int population = int(float(pop_dict['Value'])) print(country_name + ': ' + population) 

上面的程序打印了2015年各個國家的人口數,當然要分析2014年的,代碼中數字改改就行。

Arab World: 392168030 Caribbean small states: 7116360 Central Europe and the Baltics: 103256779 Early-demographic dividend: 3122757473.68203 East Asia & Pacific: 2279146555 ... 

需要注意的是,人口數據有些值是小數(不可思議)。人口數據類型是字符串str,如果直接轉int,像'35435.12432'這樣的字符串是不能強轉位int的,必須先轉為float,再丟失精度轉為int。

獲取兩個字母的國別碼

我們的數據中,國別碼是三位的,而pygal的地圖工具使用兩位國別碼。要使用pygal繪制世界地圖。需要安裝依賴包。

pip install pygal_maps_world就可以了

國別碼位於i18n模塊

from pygal_maps_world.i18n import COUNTRIES這樣就導入了, COUNTRIES是一個字典,鍵是兩位國別碼,值是具體國家名。

key -> value
af Afghanistan
af Afghanistan
al Albania
al Albania
dz Algeria
dz Algeria
ad Andorra
ad Andorra
ao Angola

寫一個函數,根據具體國家名返回pygal提供的兩位國別碼

def get_country_code(country_name): """ 根據國家名返回兩位國別碼 """ for code, name in COUNTRIES.items(): if name == country_name: return code return None 

世界人口地圖繪制

先給出全部代碼,需要用到World

import json from pygal_maps_world.i18n import COUNTRIES from pygal_maps_world.maps import World # 顏色相關 from pygal.style import RotateStyle from pygal.style import LightColorizedStyle def get_country_code(country_name): """ 根據國家名返回兩位國別碼 """ for code, name in COUNTRIES.items(): if name == country_name: return code return None filename = r'F:\Jupyter Notebook\matplotlib_pygal_csv_json\population.json' with open(filename) as f: pop_data = json.load(f) cc_populations = {} for pop_dict in pop_data: if pop_dict['Year'] == '2015': country_name = pop_dict['Country Name'] # 有些值是小數,先轉為float再轉為int population = int(float(pop_dict['Value'])) code = get_country_code(country_name) if code: cc_populations[code] = population # 為了使顏色分層更加明顯 cc_populations_1,cc_populations_2, cc_populations_3 = {}, {}, {} for cc, population in cc_populations.items(): if population < 10000000: cc_populations_1[cc] = population elif population < 1000000000: cc_populations_2[cc] = population else: cc_populations_3[cc] = population wm_style = RotateStyle('#336699', base_style=LightColorizedStyle) world = World(style=wm_style) world.title = 'World Populations in 2015, By Country' world.add('0-10m', cc_populations_1) world.add('10m-1bn', cc_populations_2) world.add('>1bn', cc_populations_3) world.render_to_file('world_population_2015.svg') 

有幾個變量比較重要

  • cc_populations是一個dict,里面存放了兩位國別碼與人口的鍵值對。
  • cc_populations_1,cc_populations_2, cc_populations_3這是3個字典,把人口按照數量分階梯,人口一千萬以下的存放在cc_populations_1中,一千萬~十億級別的存放在cc_populations_2中,十億以上的存放在cc_populations_3中,這樣做的目的是使得顏色分層更加明顯,更方便找出各個階梯里人口最多的國家。由於分了三個層次,在添加數據的的時候也add三次,把這三個字典分別傳進去。
  • world = World(style=wm_style)這是一個地圖類,導入方法from pygal_maps_world.maps import World
  • wm_style = RotateStyle('#336699', base_style=LightColorizedStyle)這里修改了pygal默認的主題顏色,第一個參數是16進制的RGB顏色,前兩位代表R,中間兩位代表G,最后兩位代表B。數字越大顏色越深。第二個參數設置基礎樣式為亮色主題,pygal默認使用較暗的顏色主題,通過此方法可以修改默認樣式。

中國大佬,No. 1

圖中可以看出,分的三個顏色層次為。紫色系,十億以上;藍色系,一千萬到十億之間;綠色系,一千萬一下。這三種顏色里面顏色最深的就對應了三個階梯里人口最多的國家。

 
 

仔細觀察,圖中有些是空白的。並不是這些地方全部沒人,而是我們的json數據中有些國家的名稱與pygal中COUNTIES模塊的國家名不對應導致。這算是一個遺憾,不過可以修改get_country_code函數,使得遇到不對應的國家名時,不返回None。對於這些國家,查閱COUNTRIES的代碼,找出對應的國別碼,返回之,應該就Ok了。比如下面這樣

# 傳入的參數country_name是json數據中的,可能與COUNTRIES里面的國家名不一致,按照上面的代碼直接就返回None,導致繪圖時這個國家的人口數據空白 if country_name == 'Yemen, Rep': return 'ye' 

不過我懶得試了233

使用Web API分析數據

以GitHub為例,我想查看最受歡迎的Python庫。以stars排序。

訪問這個網址就可查看。數據大概長這樣

{
  "total_count": 1767997, "incomplete_results": false, "items": [{ { "id": 21289110, "name": "awesome-python", "full_name": "vinta/awesome-python", "owner": { "login": "vinta", ... }, ... "html_url": "https://github.com/vinta/awesome-python", ... "stargazers_count": 35234, ... }, {...} ...] } 

第三個數據,items。里面是得到stars最多的top 30。name即庫名稱,owner下的login是庫的擁有者,html_url是該庫的網址(注意owner下也有個html_url。不過那個是用戶的GitHub網址,我們要定位到該用戶的具體這個庫,所以不要用owner下的html_url),stargazers_count至關重要,所得的stars數目。

順便說下第一個鍵total_count,表示Python語言的倉庫的總數;第二個鍵,incomplete_results,表示響應的值不完全,一般來說是false,表示響應的數據完整。

import requests url = 'https://api.github.com/search/repositories?q=language:python&sort=stars' response = requests.get(url) # 200為響應成功 print(response.status_code, '響應成功!') response_dict = response.json() total_repo = response_dict['total_count'] repo_list = response_dict['items'] print('總倉庫數: ', total_repo) print('top', len(repo_list)) for repo_dict in repo_list: print('\nName: ', repo_dict['name']) print('Owner: ', repo_dict['owner']['login']) print('Stars: ', repo_dict['stargazers_count']) print('Repo: ', repo_dict['html_url']) print('Description: ', repo_dict['description']) 

其實像這樣已經得到結果了

200 響應成功! 總倉庫數: 1768021 top 30 Name: awesome-python Owner: vinta Stars: 35236 Repo: https://github.com/vinta/awesome-python Description: A curated list of awesome Python frameworks, libraries, software and resources Name: httpie Owner: jakubroztocil Stars: 30149 Repo: https://github.com/jakubroztocil/httpie Description: Modern command line HTTP client – user-friendly curl alternative with intuitive UI, JSON support, syntax highlighting, wget-like downloads, extensions, etc. https://httpie.org Name: thefuck Owner: nvbn Stars: 28535 Repo: https://github.com/nvbn/thefuck Description: Magnificent app which corrects your previous console command. ... 

可視化一下當然會更加直觀。

pygal可視化數據

代碼不是很難,有一個plot_dict比較關鍵,這是鼠標放在條形圖上時,會顯示出來的數據,鍵基本上都是固定寫法,xlink里面時倉庫地址,只要點擊,瀏覽器就會新開一個標簽跳轉到該頁面了!

import requests import pygal from pygal.style import LightColorizedStyle, LightenStyle url = 'https://api.github.com/search/repositories?q=language:python&sort=stars' response = requests.get(url) # 200為響應成功 print(response.status_code, '響應成功!') response_dict = response.json() total_repo = response_dict['total_count'] repo_list = response_dict['items'] print('總倉庫數: ', total_repo) print('top', len(repo_list)) names, plot_dicts = [], [] for repo_dict in repo_list: names.append(repo_dict['name']) # 加上str強轉,因為我遇到了'NoneType' object is not subscriptable (: 說明里面有個沒有此項, 是NoneType plot_dict = { 'value' : repo_dict['stargazers_count'], # 有些描述很長很長,選最前一部分 'label' : str(repo_dict['description'])[:200]+'...', 'xlink' : repo_dict['html_url'] } plot_dicts.append(plot_dict) # 改變默認主題顏色,偏藍色 my_style = LightenStyle('#333366', base_style=LightColorizedStyle) # 配置 my_config = pygal.Config() # x軸的文字旋轉45度 my_config.x_label_rotation = -45 # 隱藏左上角的圖例 my_config.show_legend = False # 標題字體大小 my_config.title_font_size = 30 # 副標簽,包括x軸和y軸大部分 my_config.label_font_size = 20 # 主標簽是y軸某數倍數,相當於一個特殊的刻度,讓關鍵數據點更醒目 my_config.major_label_font_size = 24 # 限制字符為15個,超出的以...顯示 my_config.truncate_label = 15 # 不顯示y參考虛線 my_config.show_y_guides = False # 圖表寬度 my_config.width = 1000 # 第一個參數可以傳配置 chart = pygal.Bar(my_config, style=my_style) chart.title = 'Most-Starred Python Projects on GitHub' # x軸的數據 chart.x_labels = names # 加入y軸的數據,無需title設置為空,注意這里傳入的字典, # 其中的鍵--value也就是y軸的坐標值了 chart.add('', plot_dicts) chart.render_to_file('most_stars_python_repo.svg') 

看下圖,chrome瀏覽器里顯示效果。總感覺config里面有些設置沒有起到作用, x、y軸的標簽還是那么小orz...不過plot_dict里面的三個數據都顯示出來了,點擊即可跳轉。

 
 

好了,就折騰這么多吧,這個庫也不是特別大眾的



作者:sunhaiyu
鏈接:https://www.jianshu.com/p/6875f8050499
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM