pygal的簡(jiǎn)單使用

pygal的簡(jiǎn)單使用

例子來(lái)自此書(shū): 《Python編程從入門(mén)到實(shí)戰(zhàn)》【美】Eric Matthes

pygal是一個(gè)SVG圖表庫(kù)渠驼。SVG是一種矢量圖格式。全稱(chēng)Scalable Vector Graphics -- 可縮放矢量圖形疾瓮。

用瀏覽器打開(kāi)svg撮弧,可以方便的與之交互。

以下代碼均在Jupyter Notebook中運(yùn)行

模擬擲骰子

來(lái)看一個(gè)簡(jiǎn)單的例子易核。它模擬了擲骰子匈织。

import random

class Die:
    """
    一個(gè)骰子類(lèi)
    """
    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,統(tǒng)計(jì)每個(gè)數(shù)字出現(xiàn)的次數(shù)
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軸坐標(biāo)
hist.x_labels = [1, 2, 3, 4, 5, 6]
# x牡直、y軸的描述
hist.x_title = 'Result'
hist.y_title = 'Frequency of Result'
# 添加數(shù)據(jù)缀匕, 第一個(gè)參數(shù)是數(shù)據(jù)的標(biāo)題
hist.add('D6', frequencies)
# 保存到本地,格式必須是svg
hist.render_to_file('die_visual.svg')

使用瀏覽器打開(kāi)這個(gè)文件碰逸,鼠標(biāo)指向數(shù)據(jù)乡小,可以看到顯示了標(biāo)題“D6”谋梭, x軸的坐標(biāo)以及y軸坐標(biāo)鸳玩。

可以發(fā)現(xiàn)竟坛,六個(gè)數(shù)字出現(xiàn)的頻次是差不多的(理論上概率是1/6, 隨著實(shí)驗(yàn)次數(shù)的增加获印,趨勢(shì)越來(lái)越明顯)

同時(shí)擲兩個(gè)骰子

稍微改下代碼就行,再實(shí)例化一個(gè)骰子

die_1 = Die()
die_2 = Die()

result_list = []
for roll_num in range(5000):
    # 兩個(gè)骰子的點(diǎn)數(shù)和
    result = die_1.roll() + die_2.roll()
    result_list.append(result)

frequencies = []
# 能擲出的最大數(shù)
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'
# 添加數(shù)據(jù)
hist.add('two D6', frequencies)
# 格式必須是svg
hist.render_to_file('2_die_visual.svg')

從圖中可以看出褪猛,兩個(gè)骰子之和為7的次數(shù)最多赌结,和為2的次數(shù)最少。因?yàn)槟軘S出2的只有一種情況 -> (1, 1);而擲出7的情況有(1, 6) , (2, 5), (3, 4), (4, 3), (5, 2), (6, 1)共6種情況吠撮,其余數(shù)字的情況都沒(méi)有7的多摔癣,故擲得7得概率最大。

處理json數(shù)據(jù)--世界人口地圖

需要用到人口數(shù)據(jù)

點(diǎn)擊這里下載population.json纬向,該數(shù)據(jù)來(lái)源于okfn.org這個(gè)網(wǎng)站

打開(kāi)看下數(shù)據(jù)择浊,其實(shí)這是個(gè)很長(zhǎng)的列表,包含了許多國(guó)家從1960~2015年的人口數(shù)據(jù)逾条∽裂遥看第一數(shù)據(jù),如下师脂。后面的數(shù)據(jù)和第一個(gè)鍵都一樣担孔。

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

只有四個(gè)鍵,其中Country Code指的是國(guó)別碼吃警,這里是3位的糕篇。Value就是人口數(shù)了。

import json

filename = r'F:\Jupyter Notebook\matplotlib_pygal_csv_json\population.json'
with open(filename) as f:
    # json.load()可以將json文件轉(zhuǎn)為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']
        # 有些值是小數(shù),先轉(zhuǎn)為float再轉(zhuǎn)為int
        population = int(float(pop_dict['Value']))
        print(country_name + ': ' + population)

上面的程序打印了2015年各個(gè)國(guó)家的人口數(shù)安券,當(dāng)然要分析2014年的墩崩,代碼中數(shù)字改改就行。

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

需要注意的是侯勉,人口數(shù)據(jù)有些值是小數(shù)(不可思議)鹦筹。人口數(shù)據(jù)類(lèi)型是字符串str,如果直接轉(zhuǎn)int址貌,像'35435.12432'這樣的字符串是不能強(qiáng)轉(zhuǎn)位int的铐拐,必須先轉(zhuǎn)為float,再丟失精度轉(zhuǎn)為int练对。

獲取兩個(gè)字母的國(guó)別碼

我們的數(shù)據(jù)中遍蟋,國(guó)別碼是三位的,而pygal的地圖工具使用兩位國(guó)別碼锹淌。要使用pygal繪制世界地圖匿值。需要安裝依賴(lài)包。

pip install pygal_maps_world就可以了

國(guó)別碼位于i18n模塊

from pygal_maps_world.i18n import COUNTRIES這樣就導(dǎo)入了, COUNTRIES是一個(gè)字典赂摆,鍵是兩位國(guó)別碼挟憔,值是具體國(guó)家名钟些。

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

寫(xiě)一個(gè)函數(shù),根據(jù)具體國(guó)家名返回pygal提供的兩位國(guó)別碼

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

世界人口地圖繪制

先給出全部代碼绊谭,需要用到World類(lèi)

import json

from pygal_maps_world.i18n import COUNTRIES
from pygal_maps_world.maps import World
# 顏色相關(guān)
from pygal.style import RotateStyle
from pygal.style import LightColorizedStyle

def get_country_code(country_name):
    """
    根據(jù)國(guó)家名返回兩位國(guó)別碼
    """
    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']
        
        # 有些值是小數(shù)政恍,先轉(zhuǎn)為float再轉(zhuǎn)為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')
        

有幾個(gè)變量比較重要

  • cc_populations是一個(gè)dict,里面存放了兩位國(guó)別碼與人口的鍵值對(duì)达传。
  • cc_populations_1,cc_populations_2, cc_populations_3這是3個(gè)字典篙耗,把人口按照數(shù)量分階梯,人口一千萬(wàn)以下的存放在cc_populations_1中宪赶,一千萬(wàn)~十億級(jí)別的存放在cc_populations_2中宗弯,十億以上的存放在cc_populations_3中,這樣做的目的是使得顏色分層更加明顯搂妻,更方便找出各個(gè)階梯里人口最多的國(guó)家蒙保。由于分了三個(gè)層次,在添加數(shù)據(jù)的的時(shí)候也add三次欲主,把這三個(gè)字典分別傳進(jìn)去邓厕。
  • world = World(style=wm_style)這是一個(gè)地圖類(lèi),導(dǎo)入方法from pygal_maps_world.maps import World
  • wm_style = RotateStyle('#336699', base_style=LightColorizedStyle)這里修改了pygal默認(rèn)的主題顏色扁瓢,第一個(gè)參數(shù)是16進(jìn)制的RGB顏色详恼,前兩位代表R,中間兩位代表G引几,最后兩位代表B昧互。數(shù)字越大顏色越深。第二個(gè)參數(shù)設(shè)置基礎(chǔ)樣式為亮色主題她紫,pygal默認(rèn)使用較暗的顏色主題硅堆,通過(guò)此方法可以修改默認(rèn)樣式屿储。

中國(guó)大佬贿讹,No. 1

圖中可以看出,分的三個(gè)顏色層次為够掠。紫色系民褂,十億以上;藍(lán)色系疯潭,一千萬(wàn)到十億之間赊堪;綠色系,一千萬(wàn)一下竖哩。這三種顏色里面顏色最深的就對(duì)應(yīng)了三個(gè)階梯里人口最多的國(guó)家哭廉。

仔細(xì)觀察,圖中有些是空白的相叁。并不是這些地方全部沒(méi)人遵绰,而是我們的json數(shù)據(jù)中有些國(guó)家的名稱(chēng)與pygal中COUNTIES模塊的國(guó)家名不對(duì)應(yīng)導(dǎo)致辽幌。這算是一個(gè)遺憾,不過(guò)可以修改get_country_code函數(shù)椿访,使得遇到不對(duì)應(yīng)的國(guó)家名時(shí)乌企,不返回None。對(duì)于這些國(guó)家成玫,查閱COUNTRIES的代碼加酵,找出對(duì)應(yīng)的國(guó)別碼,返回之哭当,應(yīng)該就Ok了猪腕。比如下面這樣

# 傳入的參數(shù)country_name是json數(shù)據(jù)中的,可能與COUNTRIES里面的國(guó)家名不一致钦勘,按照上面的代碼直接就返回None码撰,導(dǎo)致繪圖時(shí)這個(gè)國(guó)家的人口數(shù)據(jù)空白
if country_name == 'Yemen, Rep':
    return 'ye'

不過(guò)我懶得試了233

使用Web API分析數(shù)據(jù)

以GitHub為例,我想查看最受歡迎的Python庫(kù)个盆。以stars排序脖岛。

訪問(wèn)這個(gè)網(wǎng)址就可查看。數(shù)據(jù)大概長(zhǎng)這樣

{
  "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,
        ...

  }, {...}
      ...]
}

第三個(gè)數(shù)據(jù)颊亮,items柴梆。里面是得到stars最多的top 30。name即庫(kù)名稱(chēng)终惑,owner下的login是庫(kù)的擁有者绍在,html_url是該庫(kù)的網(wǎng)址(注意owner下也有個(gè)html_url。不過(guò)那個(gè)是用戶(hù)的GitHub網(wǎng)址雹有,我們要定位到該用戶(hù)的具體這個(gè)庫(kù)偿渡,所以不要用owner下的html_url),stargazers_count至關(guān)重要霸奕,所得的stars數(shù)目溜宽。

順便說(shuō)下第一個(gè)鍵total_count,表示Python語(yǔ)言的倉(cāng)庫(kù)的總數(shù)质帅;第二個(gè)鍵适揉,incomplete_results,表示響應(yīng)的值不完全煤惩,一般來(lái)說(shuō)是false嫉嘀,表示響應(yīng)的數(shù)據(jù)完整。

import requests

url = 'https://api.github.com/search/repositories?q=language:python&sort=stars'
response = requests.get(url)
# 200為響應(yīng)成功
print(response.status_code, '響應(yīng)成功魄揉!')
response_dict = response.json()

total_repo = response_dict['total_count']
repo_list = response_dict['items']
print('總倉(cāng)庫(kù)數(shù): ', 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'])

其實(shí)像這樣已經(jīng)得到結(jié)果了

200 響應(yīng)成功剪侮!
總倉(cāng)庫(kù)數(shù):  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.
...

可視化一下當(dāng)然會(huì)更加直觀。

pygal可視化數(shù)據(jù)

代碼不是很難洛退,有一個(gè)plot_dict比較關(guān)鍵瓣俯,這是鼠標(biāo)放在條形圖上時(shí)红淡,會(huì)顯示出來(lái)的數(shù)據(jù),鍵基本上都是固定寫(xiě)法降铸,xlink里面時(shí)倉(cāng)庫(kù)地址在旱,只要點(diǎn)擊,瀏覽器就會(huì)新開(kāi)一個(gè)標(biāo)簽跳轉(zhuǎn)到該頁(yè)面了推掸!

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為響應(yīng)成功
print(response.status_code, '響應(yīng)成功桶蝎!')
response_dict = response.json()

total_repo = response_dict['total_count']
repo_list = response_dict['items']
print('總倉(cāng)庫(kù)數(shù): ', total_repo)
print('top', len(repo_list))

names, plot_dicts = [], []
for repo_dict in repo_list:
    names.append(repo_dict['name'])
    # 加上str強(qiáng)轉(zhuǎn),因?yàn)槲矣龅搅?NoneType' object is not subscriptable (: 說(shuō)明里面有個(gè)沒(méi)有此項(xiàng), 是NoneType
    plot_dict = {
        'value' : repo_dict['stargazers_count'],
        # 有些描述很長(zhǎng)很長(zhǎng)谅畅,選最前一部分
        'label' : str(repo_dict['description'])[:200]+'...',
        'xlink' : repo_dict['html_url']
    }
    plot_dicts.append(plot_dict)
    
# 改變默認(rèn)主題顏色登渣,偏藍(lán)色
my_style = LightenStyle('#333366', base_style=LightColorizedStyle)
# 配置
my_config = pygal.Config()
# x軸的文字旋轉(zhuǎn)45度
my_config.x_label_rotation = -45
# 隱藏左上角的圖例
my_config.show_legend = False
# 標(biāo)題字體大小
my_config.title_font_size = 30
# 副標(biāo)簽,包括x軸和y軸大部分
my_config.label_font_size = 20
# 主標(biāo)簽是y軸某數(shù)倍數(shù)毡泻,相當(dāng)于一個(gè)特殊的刻度胜茧,讓關(guān)鍵數(shù)據(jù)點(diǎn)更醒目
my_config.major_label_font_size = 24
# 限制字符為15個(gè),超出的以...顯示
my_config.truncate_label = 15
# 不顯示y參考虛線
my_config.show_y_guides = False
# 圖表寬度
my_config.width = 1000

# 第一個(gè)參數(shù)可以傳配置
chart = pygal.Bar(my_config, style=my_style)
chart.title = 'Most-Starred Python Projects on GitHub'
# x軸的數(shù)據(jù)
chart.x_labels = names
# 加入y軸的數(shù)據(jù)仇味,無(wú)需title設(shè)置為空呻顽,注意這里傳入的字典,
# 其中的鍵--value也就是y軸的坐標(biāo)值了
chart.add('', plot_dicts)
chart.render_to_file('most_stars_python_repo.svg')

看下圖丹墨,chrome瀏覽器里顯示效果廊遍。總感覺(jué)config里面有些設(shè)置沒(méi)有起到作用, x贩挣、y軸的標(biāo)簽還是那么小orz...不過(guò)plot_dict里面的三個(gè)數(shù)據(jù)都顯示出來(lái)了喉前,點(diǎn)擊即可跳轉(zhuǎn)。

好了王财,就折騰這么多吧卵迂,這個(gè)庫(kù)也不是特別大眾的...


by @sunhaiyu

2017.6.22

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市绒净,隨后出現(xiàn)的幾起案子见咒,更是在濱河造成了極大的恐慌,老刑警劉巖疯溺,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件论颅,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡囱嫩,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)漏设,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)墨闲,“玉大人,你說(shuō)我怎么就攤上這事郑口≡П蹋” “怎么了盾鳞?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)瞻离。 經(jīng)常有香客問(wèn)我腾仅,道長(zhǎng),這世上最難降的妖魔是什么套利? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任推励,我火速辦了婚禮,結(jié)果婚禮上肉迫,老公的妹妹穿的比我還像新娘验辞。我一直安慰自己,他們只是感情好喊衫,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布跌造。 她就那樣靜靜地躺著,像睡著了一般族购。 火紅的嫁衣襯著肌膚如雪壳贪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天寝杖,我揣著相機(jī)與錄音撑碴,去河邊找鬼。 笑死朝墩,一個(gè)胖子當(dāng)著我的面吹牛醉拓,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播收苏,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼亿卤,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了鹿霸?” 一聲冷哼從身側(cè)響起排吴,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎懦鼠,沒(méi)想到半個(gè)月后钻哩,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡肛冶,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年街氢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片睦袖。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡珊肃,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情伦乔,我是刑警寧澤厉亏,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站烈和,受9級(jí)特大地震影響爱只,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜招刹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一恬试、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蔗喂,春花似錦忘渔、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至乖阵,卻和暖如春宣赔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瞪浸。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工儒将, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人对蒲。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓钩蚊,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親蹈矮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子砰逻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

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