python圖表之pygal入門篇

pygal的簡單使用

例子來自此書: 《Python編程從入門到實戰(zhàn)》【美】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另萤,統(tǒng)計每個數字出現的次數
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軸坐標俩由。

可以發(fā)現,六個數字出現的頻次是差不多的(理論上概率是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

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

<div class="image-package">

</div>

仔細觀察纫普,圖中有些是空白的。并不是這些地方全部沒人好渠,而是我們的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里面的三個數據都顯示出來了,點擊即可跳轉宰睡。

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


原文地址


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末拆内,一起剝皮案震驚了整個濱河市旋圆,隨后出現的幾起案子,更是在濱河造成了極大的恐慌麸恍,老刑警劉巖灵巧,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件搀矫,死亡現場離奇詭異,居然都是意外死亡刻肄,警方通過查閱死者的電腦和手機瓤球,發(fā)現死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來敏弃,“玉大人卦羡,你說我怎么就攤上這事÷蟮剑” “怎么了绿饵?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長瓶颠。 經常有香客問我拟赊,道長,這世上最難降的妖魔是什么粹淋? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任吸祟,我火速辦了婚禮,結果婚禮上廓啊,老公的妹妹穿的比我還像新娘欢搜。我一直安慰自己,他們只是感情好谴轮,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布炒瘟。 她就那樣靜靜地躺著,像睡著了一般第步。 火紅的嫁衣襯著肌膚如雪疮装。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天粘都,我揣著相機與錄音廓推,去河邊找鬼。 笑死翩隧,一個胖子當著我的面吹牛樊展,可吹牛的內容都是我干的。 我是一名探鬼主播堆生,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼专缠,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了淑仆?” 一聲冷哼從身側響起涝婉,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蔗怠,沒想到半個月后墩弯,有當地人在樹林里發(fā)現了一具尸體吩跋,經...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年渔工,在試婚紗的時候發(fā)現自己被綠了锌钮。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡涨缚,死狀恐怖轧粟,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情脓魏,我是刑警寧澤兰吟,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站茂翔,受9級特大地震影響混蔼,放射性物質發(fā)生泄漏。R本人自食惡果不足惜珊燎,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一惭嚣、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧悔政,春花似錦晚吞、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至芦瘾,卻和暖如春捌蚊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背近弟。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工缅糟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人祷愉。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓窗宦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親二鳄。 傳聞我的和親對象是個殘疾皇子迫摔,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內容