申明: 本站飛宇網 https://feiyetopro.blogspot.com/。自網路收集整理之書籍、文章、影音僅供預覽交流學習研究,其[書籍、文章、影音]情節內容, 評論屬其個人行為, 與本網站無關。版權歸原作者和出版社所有,請在下載 24 小時內刪除,不得用作商業用途;如果您喜歡其作品,請支持訂閱購買[正版]。謝謝!
專案2 資料視覺化
第 15 章 生成資料
資料視覺化 指的是通過視覺化表示來探索資料,它與資料採擷 緊密相關,而資料採擷指的是使用代碼來探索資料集的規律和關聯。資料集可以是用一行代碼就能表示的小型數位清單,也可以是數以吉位元組的資料。
漂亮地呈現資料關乎的並非僅僅是漂亮的圖片。以引人注目的簡潔方式呈現資料,讓觀看者能夠明白其含義,發現資料集中原本未意識到的規律和意義。
所幸即便沒有超級電腦,也能夠視覺化複雜的資料。鑒於Python的高效性,使用它在筆記型電腦上就能快速地探索由數百萬個資料點組成的資料集。資料點並非必須是數位,利用本書前半部分介紹的基本知識,也可以對非數位資料進行分析。
在基因研究、天氣研究、政治經濟分析等眾多領域,大家都使用Python來完成資料密集型工作。資料科學家使用Python編寫了一系列令人印象深刻的視覺化和分析工具,其中很多也可供你使用。最流行的工具之一是matplotlib,它是一個數學繪圖庫,我們將使用它來製作簡單的圖表,如折線圖和散點圖。然後,我們將基於隨機漫步概念生成一個更有趣的資料集——根據一系列隨機決策生成的圖表。
我們還將使用Pygal包,它專注于生成適合在數位設備上顯示的圖表。通過使用Pygal,可在使用者與圖表交互時突出元素以及調整其大小,還可輕鬆地調整整個圖表的尺寸,使其適合在微型智慧手錶或巨型顯示器上顯示。我們將使用Pygal以各種方式探索擲骰子的結果。
15.1 安裝matplotlib
首先,需要安裝matplotlib,我們將使用它來製作開始的幾個圖表。如果你還未使用過pip,請參閱12.2.1節。
15.1.1 在Linux系統中安裝matplotlib
如果你使用的是系統自帶的Python版本,可使用系統的包管理器來安裝matplotlib,為此只需執行一行命令:
$
sudo apt-get install python3-matplotlib
如果你使用的是Python 2.7,請執行如下命令:
$
sudo apt-get install python-matplotlib
如果你安裝了較新的Python版本,就必須安裝matplotlib依賴的一些庫:
$
sudo apt-get install python3.5-dev python3.5-tk tk-dev
$
sudo apt-get install libfreetype6-dev g++
再使用pip來安裝matplotlib:
$ pip
install --user matplotlib
15.1.2 在OS X系統中安裝matplotlib
Apple的標準Python安裝自帶了matplotlib。要檢查系統是否安裝了matplotlib,可打開一個終端會話並嘗試導入matplotlib。如果系統沒有自帶matplotlib,且你的Python是使用Homebrew安裝的,則可以像下面這樣安裝matplotlib:
$ pip
install --user matplotlib
注意 安裝包時可能需要使用pip3 ,而不是pip 。另外,如果這個命令不管用,你可能需要刪除標誌--user 。
15.1.3 在Windows系統中安裝matplotlib
在Windows系統中,首先需要安裝Visual Studio。為此,請訪問https://dev.windows.com/ ,按一下Downloads,再查找Visual Studio
Community——一組免費的Windows開發工具。請下載並運行該安裝程式。
接下來,需要下載matplotlib安裝程式。為此,請訪問https://pypi.python.org/pypi/matplotlib/ ,並查找與你使用的Python版本匹配的wheel檔(副檔名為.whl的文件)。例如,如果你使用的是32位的Python 3.5,則需要下載matplotlib-1.4.3-cp35-none-win32.whl。
注意 如果找不到與你安裝的Python版本匹配的檔,請去http://www.lfd.uci.edu/-gohlke/pythonlibs/#matplotlib 看看,這個網站發佈安裝程式的時間通常比matplotlib官網早些。
將這個.whl檔複製到你的專案檔案夾,打開一個命令視窗,並切換到該專案檔案夾,再使用pip來安裝matplotlib:
>
cd python_work
python_work>
python -m pip install --user matplotlib-1.4.3-cp35-none-win32.whl
15.1.4 測試matplotlib
安裝必要的包後,對安裝進行測試。為此,首先使用命令python 或python3 啟動一個終端會話,再嘗試導入matplotlib:
$
python3
>>>
import matplotlib
>>>
如果沒有出現任何錯誤消息,就說明你的系統安裝了matplotlib,可以接著閱讀下一節。
注意 如果你在安裝過程中遇到了麻煩,請參閱附錄C。如果依然無濟於事,請向他人尋求幫助。對於你遇到的問題,只要向經驗豐富的Python程式師提供少量的資訊,他們很可能很快就能幫你解決。
15.1.5 matplotlib畫廊
要查看使用matplotlib可製作的各種圖表,請訪問http://matplotlib.org/ 的示例畫廊。按一下畫廊中的圖表,就可查看用於生成圖表的代碼。
15.2 繪製簡單的折線圖
下面來使用matplotlib繪製一個簡單的折線圖,再對其進行定制,以實現資訊更豐富的資料視覺化。我們將使用平方數序列1、4、9、16和25來繪製這個圖表。
只需向matplotlib提供如下數位,matplotlib就能完成其他的工作:
mpl_squares.py
import
matplotlib.pyplot as plt
squares
= [1, 4, 9, 16, 25]
plt.plot(squares)
plt.show()
我們首先導入了模組pyplot ,並給它指定了別名plt ,以免反復輸入pyplot 。線上示例大都這樣做,因此這裡也這樣做。模組pyplot 包含很多用於生成圖表的函數。
我們創建了一個清單,在其中存儲了前述平方數,再將這個清單傳遞給函數plot() ,這個函數嘗試根據這些數位繪製出有意義的圖形。plt.show() 打開matplotlib檢視器,並顯示繪製的圖形,如圖15-1所示。檢視器讓你能夠縮放和導航圖形,另外,按一下磁片圖示可將圖形保存起來。
15.2.1 修改標籤文字和線條粗細
圖15-1所示的圖形表明數位是越來越大的,但標籤文字太小,線條太細。所幸matplotlib讓你能夠調整視覺化的各個方面。
下面通過一些定制來改善這個圖形的可讀性,如下所示:
mpl_squares.py
import matplotlib.pyplot as plt
squares = [1, 4, 9, 16, 25]
❶ plt.plot(squares,
linewidth=5)
# 設置圖表標題,並給坐標軸加上標籤
❷ plt.title("Square
Numbers", fontsize=24)
❷ plt.xlabel("Value",
fontsize=14)
plt.ylabel("Square of Value",
fontsize=14)
# 設置刻度標記的大小
❹ plt.tick_params(axis='both',
labelsize=14)
plt.show()
參數linewidth (見❶)決定了plot() 繪製的線條的粗細。函數title() (見❷)給圖表指定標題。在上述代碼中,出現了多次的參數fontsize 指定了圖表中文字的大小。
函數xlabel() 和ylabel() 讓你能夠為每條軸設置標題(見❸);而函數tick_params() 設置刻度的樣式(見❹),其中指定的實參將影響x 軸和y 軸上的刻度(axes='both' ),並將刻度標記的字型大小設置為14(labelsize=14 )。
最終的圖表閱讀起來容易得多了,如圖15-2所示:標籤文字更大,線條也更粗。
15.2.2 校正圖形
圖形更容易閱讀後,我們發現沒有正確地繪製資料:折線圖的終點指出4.0的平方為25!下面來修復這個問題。
當你向plot() 提供一系列數字時,它假設第一個資料點對應的 x 座標值為0,但我們的第一個點對應的 x 值為1。為改變這種默認行為,我們可以給plot() 同時提供輸入值和輸出值:
mpl_squares.py
import
matplotlib.pyplot as plt
input_values
= [1, 2, 3, 4, 5]
squares
= [1, 4, 9, 16, 25]
plt.plot(input_values,
squares, linewidth=5)
# 設置圖表標題並給坐標軸加上標籤
--snip--
現在plot() 將正確地繪製資料,因為我們同時提供了輸入值和輸出值,它無需對輸出值的生成方式作出假設。最終的圖形是正確的,如圖15-3所示。
使用plot() 時可指定各種實參,還可使用眾多函數對圖形進行定制。本章後面處理更有趣的資料集時,將繼續探索這些定制函數。
15.2.3 使用scatter() 繪製散點圖並設置其樣式
有時候,需要繪製散點圖並設置各個數據點的樣式。例如,你可能想以一種顏色顯示較小的值,而用另一種顏色顯示較大的值。繪製大型資料集時,你還可以對每個點都設置同樣的樣式,再使用不同的樣式選項重新繪製某些點,以突出它們。
要繪製單個點,可使用函數scatter() ,並向它傳遞一對 x 和 y 座標,它將在指定位置繪製一個點:
scatter_squares.py
import
matplotlib.pyplot as plt
plt.scatter(2,
4)
plt.show()
下面來設置輸出的樣式,使其更有趣:添加標題,給軸加上標籤,並確保所有文本都大到能夠看清:
import matplotlib.pyplot as plt
❶ plt.scatter(2,
4, s=200)
# 設置圖表標題並給坐標軸加上標籤
plt.title("Square Numbers",
fontsize=24)
plt.xlabel("Value", fontsize=14)
plt.ylabel("Square of Value",
fontsize=14)
# 設置刻度標記的大小
plt.tick_params(axis='both', which='major',
labelsize=14)
plt.show()
在❶處,我們調用了scatter() ,並使用實參s 設置了繪製圖形時使用的點的尺寸。如果此時運行scatter_squares.py,將在圖表中央看到一個點,如圖15-4所示。
15.2.4 使用scatter() 繪製一系列點
要繪製一系列的點,可向scatter() 傳遞兩個分別包含x 值和y 值的列表,如下所示:
scatter_squares.py
import
matplotlib.pyplot as plt
x_values
= [1, 2, 3, 4, 5]
y_values
= [1, 4, 9, 16, 25]
plt.scatter(x_values,
y_values, s=100)
# 設置圖表標題並給坐標軸指定標籤
--snip--
清單x_values 包含要計算其平方值的數字,而清單y_values 包含前述每個數字的平方值。將這些列表傳遞給scatter() 時,matplotlib依次從每個列表中讀取一個值來繪製一個點。要繪製的點的座標分別為 (1, 1)、(2, 4)、(3, 9)、(4, 16)和(5, 25),最終的結果如圖15-5所示。
15.2.5 自動計算資料
手工計算列表要包含的值可能效率低下,需要繪製的點很多時尤其如此。可以不必手工計算包含點座標的列表,而讓Python迴圈來替我們完成這種計算。下面是繪製1000個點的代碼:
scatter_squares.py
import matplotlib.pyplot as plt
❶ x_values
= list(range(1, 1001))
y_values = [x**2 for x in x_values]
❷ plt.scatter(x_values,
y_values, s=40)
# 設置圖表標題並給坐標軸加上標籤
--snip--
# 設置每個坐標軸的取值範圍
❸ plt.axis([0,
1100, 0, 1100000])
plt.show()
我們首先創建了一個包含 x 值的清單,其中包含數字1~1000(見❶)。接下來是一個生成 y 值的列表解析,它遍歷 x 值(for x in x_values ),計算其平方值(x**2 ),並將結果存儲到清單y_values 中。然後,將輸入列表和輸出列表傳遞給scatter() (見❷)。
由於這個資料集較大,我們將點設置得較小,並使用函數axis() 指定了每個坐標軸的取值範圍(見❸)。函數axis() 要求提供四個值:x 和 y 坐標軸的最小值和最大值。在這裡,我們將 x 坐標軸的取值範圍設置為0~1100,並將 y 坐標軸的取值範圍設置為0~1 100 000。結果如圖15-6所示。
15.2.6 刪除資料點的輪廓
matplotlib允許你給散點圖中的各個點指定顏色。默認為藍色點和黑色輪廓,在散點圖包含的資料點不多時效果很好。但繪製很多點時,黑色輪廓可能會粘連在一起。要刪除資料點的輪廓,可在調用scatter() 時傳遞實參edgecolor='none' :
plt.scatter(x_values,
y_values, edgecolor='none', s=40)
將相應調用修改為上述代碼後,如果再運行scatter_squares.py,在圖表中看到的將是藍色實心點。
15.2.7 自訂顏色
要修改資料點的顏色,可向scatter() 傳遞參數c ,並將其設置為要使用的顏色的名稱,如下所示:
plt.scatter(x_values,
y_values, c='red', edgecolor='none', s=40)
你還可以使用RGB顏色模式自訂顏色。要指定自訂顏色,可傳遞參數c ,並將其設置為一個元組,其中包含三個0~1之間的小數值,它們分別表示紅色、綠色和藍色分量。例如,下面的代碼行創建一個由淡藍色點組成的散點圖:
plt.scatter(x_values,
y_values, c=(0, 0, 0.8), edgecolor='none', s=40)
值越接近0,指定的顏色越深,值越接近1,指定的顏色越淺。
15.2.8 使用顏色映射
顏色映射 (colormap)是一系列顏色,它們從起始顏色漸變到結束顏色。在視覺化中,顏色映射用於突出資料的規律,例如,你可能用較淺的顏色來顯示較小的值,並使用較深的顏色來顯示較大的值。
模組pyplot 內置了一組顏色映射。要使用這些顏色映射,你需要告訴pyplot 該如何設置資料集中每個點的顏色。下面演示了如何根據每個點的 y 值來設置其顏色:
scatter_squares.py
import
matplotlib.pyplot as plt
x_values
= list(range(1001))
y_values
= [x**2 for x in x_values]
plt.scatter(x_values,
y_values, c=y_values, cmap=plt.cm.Blues,
edgecolor='none', s=40)
# 設置圖表標題並給坐標軸加上標籤
--snip--
我們將參數c 設置成了一個 y 值列表,並使用參數cmap 告訴pyplot 使用哪個顏色映射。這些代碼將 y 值較小的點顯示為淺藍色,並將 y 值較大的點顯示為深藍色,生成的圖形如圖15-7所示。
注意 要瞭解pyplot 中所有的顏色映射,請訪問http://matplotlib.org/ ,按一下Examples,向下滾動到Color Examples,再按一下colormaps_reference。
15.2.9 自動保存圖表
要讓程式自動將圖表保存到檔中,可將對plt.show() 的調用替換為對plt.savefig() 的調用:
plt.savefig('squares_plot.png',
bbox_inches='tight')
第一個實參指定要以什麼樣的檔案名保存圖表,這個檔將存儲到scatter_squares.py所在的目錄中;第二個實參指定將圖表多餘的空白區域裁剪掉。如果要保留圖表周圍多餘的空白區域,可省略這個實參。
動手試一試
15-1 立方 :數字的三次方被稱為其立方。請繪製一個圖形,顯示前5個整數的立方值,再繪製一個圖形,顯示前5000個整數的立方值。
15-2 彩色立方 :給你前面繪製的立方圖指定顏色映射。
15.3 隨機漫步
在本節中,我們將使用Python來生成隨機漫步資料,再使用matplotlib以引人矚目的方式將這些資料呈現出來。隨機漫步 是這樣行走得到的路徑:每次行走都完全是隨機的,沒有明確的方向,結果是由一系列隨機決策決定的。你可以這樣認為,隨機漫步就是螞蟻在暈頭轉向的情況下,每次都沿隨機的方向前行所經過的路徑。
在自然界、物理學、生物學、化學和經濟領域,隨機漫步都有其實際用途。例如,漂浮在水滴上的花粉因不斷受到水分子的擠壓而在水面上移動。水滴中的分子運動是隨機的,因此花粉在水面上的運動路徑猶如隨機漫步。我們稍後將編寫的代碼模擬了現實世界的很多情形。
15.3.1 創建RandomWalk() 類
為模擬隨機漫步,我們將創建一個名為RandomWalk 的類,它隨機地選擇前進方向。這個類需要三個屬性,其中一個是存儲隨機漫步次數的變數,其他兩個是清單,分別存儲隨機漫步經過的每個點的 x 和 y 座標。
RandomWalk 類只包含兩個方法:__init__() 和fill_walk() ,其中後者計算隨機漫步經過的所有點。下面先來看看__init__() ,如下所示:
random_walk.py
❶ from
random import choice
class RandomWalk():
"""一個生成隨機漫步資料的類"""
❷ def __init__(self, num_points=5000):
"""初始化隨機漫步的屬性"""
self.num_points = num_points
# 所有隨機漫步都始於(0, 0)
❸ self.x_values = [0]
self.y_values = [0]
為做出隨機決策,我們將所有可能的選擇都存儲在一個清單中,並在每次做決策時都使用choice() 來決定使用哪種選擇(見❶)。接下來,我們將隨機漫步包含的默認點數設置為5000,這大到足以生成有趣的模式,同時又足夠小,可確保能夠快速地模擬隨機漫步(見❷)。然後,在❸處,我們創建了兩個用於存儲x 和y 值的列表,並讓每次漫步都從點(0, 0)出發。
15.3.2 選擇方向
我們將使用fill_walk() 來生成漫步包含的點,並決定每次漫步的方向,如下所示。請將這個方法添加到random_walk.py中:
random_walk.py
def fill_walk(self):
"""計算隨機漫步包含的所有點"""
# 不斷漫步,直到列表達到指定的長度
❶ while len(self.x_values) <
self.num_points:
# 決定前進方向以及沿這個方向前進的距離
❷ x_direction = choice([1, -1])
x_distance = choice([0, 1, 2, 3,
4])
❸ x_step = x_direction * x_distance
y_direction = choice([1, -1])
y_distance = choice([0, 1, 2, 3, 4])
❹ y_step = y_direction * y_distance
# 拒絕原地踏步
❺ if x_step == 0 and y_step == 0:
continue
# 計算下一個點的x和y值
❻ next_x = self.x_values[-1] +
x_step
next_y = self.y_values[-1] +
y_step
self.x_values.append(next_x)
self.y_values.append(next_y)
在❶處,我們建立了一個迴圈,這個迴圈不斷運行,直到漫步包含所需數量的點。這個方法的主要部分告訴Python如何類比四種漫步決定:向右走還是向左走?沿指定的方向走多遠?向上走還是向下走?沿選定的方向走多遠?
我們使用choice([1, -1]) 給x_direction 選擇一個值,結果要麼是表示向右走的1,要麼是表示向左走的-1(見❷)。接下來,choice([0,
1, 2, 3, 4]) 隨機地選擇一個0~4之間的整數,告訴Python 沿指定的方向走多遠(x_distance )。(通過包含0,我們不僅能夠沿兩個軸移動,還能夠沿y 軸移動。)
在❸和❹處,我們將移動方向乘以移動距離,以確定沿 x 和 y 軸移動的距離。如果x_step 為正,將向右移動,為負將向左移動,而為零將垂直移動;如果y_step 為正,就意味著向上移動,為負意味著向下移動,而為零意味著水準移動。如果x_step 和y_step 都為零,則意味著原地踏步,我們拒絕這樣的情況,接著執行下一次迴圈(見❺)。
為獲取漫步中下一個點的 x 值,我們將x_step 與x_values 中的最後一個值相加(見❻),對於 y 值也做相同的處理。獲得下一個點的 x 值和 y 值後,我們將它們分別附加到列表x_values 和y_values 的末尾。
15.3.3 繪製隨機漫步圖
下面的代碼將隨機漫步的所有點都繪製出來:
rw_visual.py
import matplotlib.pyplot as plt
from random_walk import RandomWalk
# 創建一個RandomWalk實例,並將其包含的點都繪製出來
❶ rw =
RandomWalk()
rw.fill_walk()
❷ plt.scatter(rw.x_values,
rw.y_values, s=15)
plt.show()
我們首先導入了模組pyplot 和RandomWalk 類,然後創建了一個RandomWalk 實例,並將其存儲到rw 中(見❶),再調用fill_walk() 。在❷處,我們將隨機漫步包含的 x 和 y 值傳遞給scatter() ,並選擇了合適的點尺寸。圖15-8顯示了包含5000個點的隨機漫步圖(本節的示意圖未包含matplotlib檢視器部分,但你運行rw_visual.py時,依然會看到)。
15.3.4 模擬多次隨機漫步
每次隨機漫步都不同,因此探索可能生成的各種模式很有趣。要在不多次運行程式的情況下使用前面的代碼模擬多次隨機漫步,一種辦法是將這些代碼放在一個while 迴圈中,如下所示:
rw_visual.py
import matplotlib.pyplot as plt
from random_walk import RandomWalk
# 只要程式處於活動狀態,就不斷地類比隨機漫步
while True:
# 創建一個RandomWalk實例,並將其包含的點都繪製出來
rw
= RandomWalk()
rw.fill_walk()
plt.scatter(rw.x_values, rw.y_values,
s=15)
plt.show()
❶ keep_running = input("Make another
walk? (y/n): ")
if keep_running == 'n':
break
這些代碼模擬一次隨機漫步,在matplotlib檢視器中顯示結果,再在不關閉檢視器的情況下暫停。如果你關閉檢視器,程式將詢問你是否要再模擬一次隨機漫步。如果你輸入y ,可模擬多次隨機漫步:這些隨機漫步都在起點附近進行,大多沿特定方向偏離起點,漫步點分佈不均勻等。要結束程式,請輸入n 。
注意 如果你使用的是Python 2.7,別忘了將❶處的input() 替換為raw_input() 。
15.3.5 設置隨機漫步圖的樣式
在本節中,我們將定制圖表,以突出每次漫步的重要特徵,並讓分散注意力的元素不那麼顯眼。為此,我們確定要突出的元素,如漫步的起點、終點和經過的路徑。接下來確定要使其不那麼顯眼的元素,如刻度標記和標籤。最終的結果是簡單的視覺化表示,清楚地指出了每次漫步經過的路徑。
15.3.6 給點著色
我們將使用顏色映射來指出漫步中各點的先後順序,並刪除每個點的黑色輪廓,讓它們的顏色更明顯。為根據漫步中各點的先後順序進行著色,我們傳遞參數c ,並將其設置為一個列表,其中包含各點的先後順序。由於這些點是按順序繪製的,因此給參數c 指定的清單只需包含數位1~5000,如下所示:
rw_visual.py
--snip--
while True:
# 創建一個RandomWalk實例,並將其包含的點都繪製出來
rw = RandomWalk()
rw.fill_walk()
❶ point_numbers = list(range(rw.num_points))
plt.scatter(rw.x_values, rw.y_values,
c=point_numbers, cmap=plt.cm.Blues,
edgecolor='none', s=15)
plt.show()
keep_running = input("Make another
walk? (y/n): ")
--snip--
在❶處,我們使用了range() 生成了一個數字清單,其中包含的數字個數與漫步包含的點數相同。接下來,我們將這個清單存儲在point_numbers 中,以便後面使用它來設置每個漫步點的顏色。我們將參數c 設置為point_numbers ,指定使用顏色映射Blues ,並傳遞實參edgecolor=none 以刪除每個點周圍的輪廓。最終的隨機漫步圖從淺藍色漸變為深藍色,如圖15-9所示。
15.3.7 重新繪製起點和終點
除了給隨機漫步的各個點著色,以指出它們的先後順序外,如果還能呈現隨機漫步的起點和終點就更好了。為此,可在繪製隨機漫步圖後重新繪製起點和終點。我們讓起點和終點變得更大,並顯示為不同的顏色,以突出它們,如下所示:
rw_visual.py
--snip--
while
True:
--snip--
plt.scatter(rw.x_values, rw.y_values,
c=point_numbers, cmap=plt.cm.Blues,
edgecolor='none', s=15)
# 突出起點和終點
plt.scatter(0, 0, c='green',
edgecolors='none', s=100)
plt.scatter(rw.x_values[-1], rw.y_values[-1], c='red',
edgecolors='none',
s=100)
plt.show()
--snip--
為突出起點,我們使用綠色繪製點(0, 0),並使其比其他點大(s=100 )。為突出終點,我們在漫步包含的最後一個 x 和 y 值處繪製一個點,將其顏色設置為紅色,並將尺寸設置為100。請務必將這些代碼放在調用plt.show() 的代碼前面,確保在其他點的上面繪製起點和終點。
如果你現在運行這些代碼,將能準確地知道每次隨機漫步的起點和終點(如果起點和終點不明顯,請調整它們的顏色和大小,直到明顯為止)。
15.3.8 隱藏坐標軸
下面來隱藏這個圖表中的坐標軸,以免我們注意的是坐標軸而不是隨機漫步路徑。要隱藏坐標軸,可使用如下代碼:
rw_visual.py
--snip--
while True:
--snip--
plt.scatter(rw.x_values[-1],
rw.y_values[-1], c='red', edgecolors='none',
s=100)
# 隱藏坐標軸
❶ plt.axes().get_xaxis().set_visible(False)
plt.axes().get_yaxis().set_visible(False)
plt.show()
--snip--
為修改坐標軸,使用了函數plt.axes() (見❶)來將每條坐標軸的可見性都設置為False 。隨著你越來越多地進行資料視覺化,經常會看到這種串接方法的方式。
如果你現在運行rw_visual.py,將看到一系列圖形,但看不到坐標軸。
15.3.9 增加點數
下面來增加點數,以提供更多的資料。為此,我們在創建RandomWalk 實例時增大num_points 的值,並在繪圖時調整每個點的大小,如下所示:
rw_visual.py
--snip--
while
True:
#創建一個RandomWalk實例,並將其包含的點都繪製出來
rw = RandomWalk(50000)
rw.fill_walk()
# 繪製點並將圖形顯示出來
point_numbers = list(range(rw.num_points))
plt.scatter(rw.x_values, rw.y_values, c=point_numbers,
cmap=plt.cm.Blues,
edgecolor='none', s=1)
--snip--
這個示例模擬了一次包含50 000個點的隨機漫步(以類比現實情況),並將每個點的大小都設置為1。最終的隨機漫步圖更纖細,猶如雲朵,如圖15-10所示。正如你看到的,我們使用簡單的散點圖製作出了一件藝術品!
請嘗試修改上述代碼,看看將漫步包含的點數增加到多少後,程式的運行速度變得極其緩慢或繪製出的圖形變得很難看。
15.3.10 調整尺寸以適合螢幕
圖表適合螢幕大小時,更能有效地將資料中的規律呈現出來。為讓繪圖視窗更適合螢幕大小,可像下面這樣調整matplotlib輸出的尺寸:
rw_visual.py
--snip--
while
True:
# 創建一個RandomWalk實例,並將其包含的點都繪製出來
rw = RandomWalk()
rw.fill_walk()
# 設置繪圖視窗的尺寸
plt.figure(figsize=(10, 6))
--snip--
函數figure() 用於指定圖表的寬度、高度、解析度和背景色。你需要給形參figsize 指定一個元組,向matplotlib指出繪圖視窗的尺寸,單位為英寸。
Python假定螢幕解析度為80圖元/英寸,如果上述代碼指定的圖表尺寸不合適,可根據需要調整其中的數位。如果你知道自己的系統的解析度,可使用形參dpi 向figure() 傳遞該解析度,以有效地利用可用的螢幕空間,如下所示:
plt.figure(dpi=128,
figsize=(10, 6))
動手試一試
15-3 分子運動 :修改rw_visual.py,將其中的plt.scatter() 替換為plt.plot() 。為類比花粉在水滴表面的運動路徑,向plt.plot() 傳遞rw.x_values 和rw.y_values ,並指定實參值linewidth 。使用5000個點而不是50 000個點。
15-4 改進的隨機漫步 :在類RandomWalk 中,x_step 和y_step 是根據相同的條件生成的:從列表[1, -1] 中隨機地選擇方向,並從列表[0, 1, 2, 3, 4] 中隨機地選擇距離。請修改這些列表中的值,看看對隨機漫步路徑有何影響。嘗試使用更長的距離選擇列表,如0~8;或者將-1從 x 或 y 方向列表中刪除。
15-5 重構 :方法fill_walk() 很長。請新建一個名為get_step() 的方法,用於確定每次漫步的距離和方向,並計算這次漫步將如何移動。然後,在fill_walk() 中調用get_step() 兩次:
x_step
= get_step()
y_step
= get_step()
通過這樣的重構,可縮小fill_walk() 的規模,讓這個方法閱讀和理解起來更容易。
15.4 使用Pygal模擬擲骰子
在本節中,我們將使用Python視覺化包Pygal來生成可縮放的向量圖形檔。對於需要在尺寸不同的螢幕上顯示的圖表,這很有用,因為它們將自動縮放,以適合觀看者的螢幕。如果你打算以線上方式使用圖表,請考慮使用Pygal來生成它們,這樣它們在任何設備上顯示時都會很美觀。
在這個項目中,我們將對擲骰子的結果進行分析。擲6面的常規骰子時,可能出現的結果為1~6點,且出現每種結果的可能性相同。然而,如果同時擲兩個骰子,某些點數出現的可能性將比其他點數大。為確定哪些點數出現的可能性最大,我們將生成一個表示擲骰子結果的資料集,並根據結果繪製出一個圖形。
在數學領域,常常利用擲骰子來解釋各種資料分析,但它在賭場和其他博弈場景中也得到了實際應用,在遊戲《大富翁》以及眾多角色扮演遊戲中亦如此。
15.4.1 安裝Pygal
請使用pip 來安裝Pygal(如果還未使用過pip ,請參閱12.2.1節)。
在Linux和OS X系統中,應執行的命令類似於下面這樣:
pip
install --user pygal
在Windows系統中,命令類似於下面這樣:
python
-m pip install --user pygal
注意 你可能需要使用命令pip3 而不是pip ,如果這還是不管用,你可能需要刪除標誌--user 。
15.4.2 Pygal畫廊
要瞭解使用Pygal可創建什麼樣的圖表,請查看圖表類型畫廊:訪問http://www.pygal.org/ ,按一下Documentation,再按一下Chart types。每個示例都包含原始程式碼,讓你知道這些圖表是如何生成的。
15.4.3 創建Die 類
下面的類模擬擲一個骰子:
die.py
from random import randint
class Die():
"""表示一個骰子的類"""
❶ def __init__(self, num_sides=6):
"""骰子默認為6面"""
self.num_sides = num_sides
def roll(self):
""""返回一個位於1和骰子面數之間的隨機值"""
❷ return randint(1, self.num_sides)
方法__init__() 接受一個可選參數。創建這個類的實例時,如果沒有指定任何實參,面數默認為6;如果指定了實參,這個值將用於設置骰子的面數(見❶)。骰子是根據面數命名的,6面的骰子名為D6,8面的骰子名為D8,以此類推。
方法roll() 使用函數randint() 來返回一個1和麵數之間的亂數(見❷)。這個函數可能返回起始值1、終止值num_sides 或這兩個值之間的任何整數。
15.4.4 擲骰子
使用這個類來創建圖表前,先來擲D6骰子,將結果列印出來,並檢查結果是否合理:
die_visual.py
from die import Die
# 創建一個D6
❶ die =
Die()
# 擲幾次骰子,並將結果存儲在一個清單中
results = []
❷ for
roll_num in range(100):
result = die.roll()
results.append(result)
print(results)
在❶處,我們創建了一個Die 實例,其面數為預設值6。在❷處,我們擲骰子100次,並將每次的結果都存儲在清單results 中。下面是一個示例結果集:
[4,
6, 5, 6, 1, 5, 6, 3, 5, 3, 5, 3, 2, 2, 1, 3, 1, 5, 3, 6, 3, 6, 5, 4,
1, 1, 4, 2, 3, 6, 4, 2, 6, 4, 1, 3, 2, 5, 6,
3, 6, 2, 1, 1, 3, 4, 1, 4,
3, 5, 1, 4, 5, 5, 2, 3, 3, 1, 2, 3, 5, 6, 2,
5, 6, 1, 3, 2, 1, 1, 1, 6,
5, 5, 2, 2, 6, 4, 1, 4, 5, 1, 1, 1, 4, 5, 3,
3, 1, 3, 5, 4, 5, 6, 5, 4,
1, 5, 1, 2]
通過快速掃描這些結果可知,Die 類看起來沒有問題。我們見到了值1和6,這表明返回了最大和最小的可能值;我們沒有見到0或7,這表明結果都在正確的範圍內。我們還看到了1~6的所有數字,這表明所有可能的結果都出現了。
15.4.5 分析結果
為分析擲一個D6骰子的結果,我們計算每個點數出現的次數:
die_visual.py
--snip--
# 擲幾次骰子,並將結果存儲在一個清單中
results = []
❶ for
roll_num in range(1000):
result = die.roll()
results.append(result)
# 分析結果
frequencies = []
❷ for value
in range(1, die.num_sides+1):
❸ frequency = results.count(value)
❹ frequencies.append(frequency)
print(frequencies)
由於我們將使用Pygal來進行分析,而不是將結果列印出來,因此可以將模擬擲骰子的次數增加到1000(見❶)。為分析結果,我們創建了空清單frequencies ,用於存儲每種點數出現的次數。在❷處,我們遍歷可能的點數(這裡為1~6),計算每種點數在results 中出現了多少次(見❸),並將這個值附加到列表frequencies 的末尾(見❹)。接下來,我們在視覺化之前將這個列表列印出來:
[155,
167, 168, 170, 159, 181]
結果看起來是合理的:我們看到了6個值——擲D6骰子時可能出現的每個點數對應一個;我們還發現,沒有任何點數出現的頻率比其他點數高很多。下面來視覺化這些結果。
15.4.6 繪製長條圖
有了頻率清單後,我們就可以繪製一個表示結果的長條圖。長條圖 是一種橫條圖,指出了各種結果出現的頻率。創建這種長條圖的代碼如下:
die_visual.py
import pygal
--snip--
# 分析結果
frequencies = []
for value in range(1, die.num_sides+1):
frequency = results.count(value)
frequencies.append(frequency)
# 對結果進行視覺化
❶ hist =
pygal.Bar()
hist.title = "Results of rolling one D6
1000 times."
❷ hist.x_labels
= ['1', '2', '3', '4', '5', '6']
hist.x_title = "Result"
hist.y_title = "Frequency of
Result"
❸ hist.add('D6',
frequencies)
hist.render_to_file('die_visual.svg')
為創建橫條圖,我們創建了一個pygal.Bar() 實例,並將其存儲在hist 中(見❶)。接下來,我們設置hist 的屬性title (用於標示長條圖的字串),將擲D6骰子的可能結果用作 x 軸的標籤(見❷),並給每個軸都添加了標題。在❸處,我們使用add() 將一系列值添加到圖表中(向它傳遞要給添加的值指定的標籤,還有一個列表,其中包含將出現在圖表中的值)。最後,我們將這個圖表渲染為一個SVG檔,這種檔的副檔名必須為.svg。
要查看生成的長條圖,最簡單的方式是使用Web流覽器。為此,在任何Web流覽器中新建一個標籤頁,再在其中打開檔die_visual.svg(它位於die_visual.py所在的資料夾中)。你將看到一個類似於圖15-11所示的圖表(為方便印刷,我稍微修改了這個圖表;預設情況下,Pygal生成的圖表的背景比你在圖15-11中看到的要暗)。
注意,Pygal讓這個圖表具有交互性:如果你將滑鼠指向該圖表中的任何條形,將看到與之相關聯的資料。在同一個圖表中繪製多個資料集時,這項功能顯得特別有用。
15.4.7 同時擲兩個骰子
同時擲兩個骰子時,得到的點數更多,結果分佈情況也不同。下面來修改前面的代碼,創建兩個D6骰子,以模擬同時擲兩個骰子的情況。每次擲兩個骰子時,我們都將兩個骰子的點數相加,並將結果存儲在results 中。請複製die_visual.py並將其保存為dice_visual.py,再做如下修改:
dice_visual.py
import pygal
from die import Die
# 創建兩個D6骰子
die_1 = Die()
die_2 = Die()
# 擲骰子多次,並將結果存儲到一個清單中
results = []
for roll_num in range(1000):
❶ result = die_1.roll() + die_2.roll()
results.append(result)
# 分析結果
frequencies = []
❷
max_result = die_1.num_sides + die_2.num_sides
❸ for value
in range(2, max_result+1):
frequency = results.count(value)
frequencies.append(frequency)
# 視覺化結果
hist = pygal.Bar()
❹ hist.title
= "Results of rolling two D6 dice 1000 times."
hist.x_labels = ['2', '3', '4', '5', '6',
'7', '8', '9', '10', '11', '12']
hist.x_title = "Result"
hist.y_title = "Frequency of Result"
hist.add('D6 + D6', frequencies)
hist.render_to_file('dice_visual.svg')
創建兩個Die 實例後,我們擲骰子多次,並計算每次的總點數(見❶)。可能出現的最大點數12為兩個骰子的最大可能點數之和,我們將這個值存儲在了max_result 中(見❷)。可能出現的最小總點數2為兩個骰子的最小可能點數之和。分析結果時,我們計算2到max_result 的各種點數出現的次數(見❸)。我們原本可以使用range(2,
13) ,但這只適用於兩個D6骰子。模擬現實世界的情形時,最好編寫可輕鬆地模擬各種情形的代碼。前面的代碼讓我們能夠模擬擲任何兩個骰子的情形,而不管這些骰子有多少面。
創建圖表時,我們修改了標題、x 軸標籤和資料數列(見❹)。(如果列表x_labels 比這裡所示的長得多,那麼編寫一個迴圈來自動生成它將更合適。)
運行這些代碼後,在流覽器中刷新顯示圖表的標籤頁,你將看到如圖15-12所示的圖表。
這個圖表顯示了擲兩個D6骰子時得到的大致結果。正如你看到的,總點數為2或12的可能性最小,而總點數為7的可能性最大,這是因為在6種情況下得到的總點數都為7。這6種情況如下:1和6、2和5、3和4、4和3、5和2、6和1。
15.4.8 同時擲兩個面數不同的骰子
下面來創建一個6面骰子和一個10面骰子,看看同時擲這兩個骰子50 000次的結果如何:
different_dice.py
from die import Die
import pygal
# 創建一個D6和一個D10
die_1 = Die()
❶ die_2 =
Die(10)
# 擲骰子多次,並將結果存儲在一個清單中
results = []
for roll_num in range(50000):
result = die_1.roll() + die_2.roll()
results.append(result)
# 分析結果
--snip--
# 視覺化結果
hist = pygal.Bar()
❷ hist.title
= "Results of rolling a D6 and a D10 50,000 times."
hist.x_labels = ['2', '3', '4', '5', '6', '7',
'8', '9', '10', '11', '12',
'13', '14', '15', '16']
hist.x_title = "Result"
hist.y_title = "Frequency of
Result"
hist.add('D6 + D10', frequencies)
hist.render_to_file('dice_visual.svg')
為創建D10骰子,我們在創建第二個Die 實例時傳遞了實參10 (見❶)。我們還修改了第一個迴圈,以模擬擲骰子50 000次而不是1000次。可能出現的最小總點數依然是2,但現在可能出現的最大總點數為16,因此我們相應地調整了標題、x 軸標籤和資料數列標籤(見❷)。
圖15-13顯示了最終的圖表。可能性最大的點數不是一個,而是5個,這是因為導致出現最小點數和最大點數的組合都只有一種(1和1以及6和10),但面數較小的骰子限制了得到中間點數的組合數:得到總點數7、8、9、10和11的組合數都是6種。因此,這些總點數是最常見的結果,它們出現的可能性相同。
通過使用Pygal來模擬擲骰子的結果,能夠非常自由地探索這種現象。只需幾分鐘,就可以擲各種骰子很多次。
動手試一試
15-6 自動生成標籤 :請修改die.py和dice_visual.py,將用來設置hist.x_labels 值的列表替換為一個自動生成這種列表的迴圈。如果你熟悉列表解析,可嘗試將die_visual.py和dice_visual.py中的其他for 迴圈也替換為列表解析。
15-7 兩個D8骰子: 請模擬同時擲兩個8面骰子1000次的結果。逐漸增加擲骰子的次數,直到系統不堪重負為止。
15-8 同時擲三個骰子 :如果你同時擲三個D6骰子,可能得到的最小點數為3,而最大點數為18。請通過視覺化展示同時擲三個D6骰子的結果。
15-9 將點數相乘 :同時擲兩個骰子時,通常將它們的點數相加。請通過視覺化展示將兩個骰子的點數相乘的結果。
15-10 練習使用本章介紹的兩個庫 :嘗試使用matplotlib通過視覺化來類比擲骰子的情況,並嘗試使用Pygal通過視覺化來類比隨機漫步的情況。
15.5 小結
在本章中,你學習了:如何生成資料集以及如何對其進行視覺化;如何使用matplotlib創建簡單的圖表,以及如何使用散點圖來探索隨機漫步過程;如何使用Pygal來創建長條圖,以及如何使用長條圖來探索同時擲兩個面數不同的骰子的結果。
使用代碼生成資料集是一種有趣而強大的方式,可用於類比和探索現實世界的各種情形。完成後面的資料視覺化專案時,請注意可使用代碼模擬哪些情形。請研究新聞媒體中的視覺化,看看其中是否有圖表是以你在這些專案中學到的類似方式生成的。
在第16章中,我們將從網上下載資料,並繼續使用matplotlib和Pygal來探索這些資料。














0 留言:
發佈留言