利用Python實現數據可視化 (入門)


什么是 matplotlib


matplotlib 是一個數學繪圖庫, 我們可以用它來制作一些簡單的圖表,例如折線圖,或散點圖。

繪制簡單的折線圖

import matplotlib.pyplot as plt

squares = [1, 4, 9, 16, 25]
plt.plot(squares)
plt.show()

模塊 pyplot 包含很多用於生成圖表的函數, 類似於 matlab

把列表作為參數傳入函數 pyplot.plot(),
這個函數會嘗試根據這些數字繪制出有意義的圖形。

函數 pyplot.show() 會打開圖像查看器,並顯示繪制的圖形。查看器可以實現放大縮小或者保存功能。

在畫出了一個簡單的圖標之后,我們可以對圖表進行簡單的改動,來增加圖形的可讀行,如修改標簽文字和線條粗細。

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軸上的刻度。

再完成了這些設置之后,就會發現圖形的可讀性有了一點提高。

但是,如果我們自習觀察圖表,我們會發現,圖中的數據並不是完全正確的,例如圖中4的平放式25.

這事由於繪圖工具plot的一些默認設置導致的問題。 如,plot的默認原點x=0,但是我們這個例子中要求的原點x=1

為了改變這種默認的設置,我們可以添加輸入值和輸出值

input_values = [1, 2, 3, 4, 5]
squares = [1, 4, 9, 16, 25]
plt.plot(input_values, squares, linewidth=5)

這樣一來我們就可以實現正確的繪制一個簡單的折線圖



繪制簡單的散點圖

有時我們需要繪制散點圖來處理離散的數據,所以我們就可以利用函數 scatter() 來繪制簡單的散點圖.

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 Number", 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()

在設置了各項參數之后,我們就可以得到一個較為完整的散點圖了。

不過,我們在數據處理時,大多數情況下都是要處理的是大量的數據,而不是只有一個離散的點,所以我們需要使用函數繪制一些列的點。

通過 列表 來給函數傳輸一系列的點

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=200)

--snip--

函數 scatter() 分別從 x_valuey_value 讀取一個值來繪制一個點 (x,y)。

手工輸入或手工計算列表中所有的值,在數據量很大的時候效率會非常的低。我們可以使用循環來代替我們完成計算。

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=1)

--snip--

# 設置每個坐標軸的取值范圍
plt.axis([0, 1100, 0, 1100000])

plt.show()

利用函數 range() 創建數字1~1000的列表,遍歷x的值並計算 x**2 ,並將其結果存儲到列表 y_values 中。

函數 axis() 指定每個坐標軸的取值范圍。向函數中傳入四個參數,x、y的最大值最小值。

這樣我們就可以讓循環來幫助我們計算大量的數據並繪圖了。

matplotlib 默認散點圖中的點為藍色點和黑色輪廓,但是在數據過多時我們會發現。黑色輪廓會粘連在一起。
不過我們可以通過改變一些參數來修改一些外觀。

plt.scatter(x_values, y_values, edgecolors='none', s=40)

這樣一來就會發現圖中將時藍色的實心點。

當然我們也可以修改數據點的顏色。

plt.scatter(x_values, y_values, c='red', edgecolors='none', s=40)

# 也可以使用RGB來設置自定義顏色
plt.scatter(x_values, y_values, c=(0, 0, 0.8), edgecolors='none', s=40)

為了使圖表的可讀性更強,也更漂亮,我們可以使用顏色映射(colormap)是一些列顏色,從其實顏色漸變到結束顏色。
在可視化中顏色映射可以突出數據的規律,較淺的顏色來顯示較小的值,並使用較深的顏色顯示較大的值。

我們只需在 scatter() 函數中的一個參數,就可將圖形改變為顏色映射。

plt.scatter(x_values, y_values, c=y_values, cmap=plt.cm.Blues,
    edgecolor='none', s=1)

為了避免我們在作圖之后忘記保存,我們可以在程序中直接添加自動保存功能。將 show() 函數替換為 savefig()

plt.savefig('squares_plot.png', bbox_inches='tight')


繪制隨機漫步圖

什么是 隨機漫步圖 ?

隨機漫步是一種路徑,這種路徑每次行走都是完全隨機的,沒有明確的方向,結果是有一些列隨機決策決定的。
我們可以把它當作螞蟻在暈頭轉向的情況下,每次都沿着隨機的方向行進所經過的路徑。

在生活中的很多領域我們都可以用到隨機漫步。
例如,漂浮在水滴上的划分因不斷受到水分子的擠壓而在水面上移動。
水滴中的分子運動是隨機的,因此划分在水面上的運動路徑猶如隨機漫步。

接下來我們將一步一步的實現隨機漫步。首先我們要先創建 RandomWalk 類。

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]

在該類中包含三個屬性,其中一個存儲隨機漫步的次數,在上面代碼中次數為5000個。
另外兩個是列表,分別存放隨機漫步經過的每個點的x和y坐標,
在上述代碼中規定所有的隨機漫步都始於(0,0)。


接下來使用函數 fill_walk() 來生成漫步包含的點,並決定每次漫步的方向以及漫步的距離。

from random import choice
    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)

在函數中設置一個循環,讓“螞蟻”不斷漫步,直到步數到達最大的設定。
使用 choice() 函數給 x_direction 選擇一個值,結果是向右走(1)或是向左走(-1),
同時用該函數為 y_direction 設置1~4中的一個值,來提供向方向的移動距離。

但如何確定“螞蟻”移動的方向是上下還是左右呢?

我們建立在x,y二維坐標軸上。

x_step為正,則向右。而為負,則向左。

y_step為正,則向上。而為負,則向下。

為了獲取漫步中下一個點的x值和y值,我們可以將 x/y_step + x/y_values 得到新的位置。

不過,這個 fill_walk() 函數過於冗長,我們可以對他進行重構。

    def get_step(self):
        """計算下一個隨機漫步落點"""
        direction = choice([1, -1])
        distance = choice([0, 1, 2, 3, 4])
        return direction * distance

    def fill_walk(self):
        """計算隨機漫步包含的所有點"""

        # 不斷漫步,直到列表到達指定的長度
        while len(self.x_values) < self.num_points:
            # 決定前進方向以及沿這個方向前進的距離
            x_step = self.get_step()

            y_step = self.get_step()

            # 拒絕原地踏步
            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)

為了繪制出隨機漫步的圖像,我們可以使用函數 scatter()

import matplotlib.pyplot as plt

from randomwalk import RandomWalk

# 創建一個RandomWalk實例,並將其包含的點都繪制出來
rw = RandomWalk()
rw.fill_walk()
plt.scatter(rw.x_values, rw.y_values, s=2)
plt.show()


同時我們也可以對隨機漫步的圖表的特性進行修改,我們的目的是突出每次漫步的重要特征,並讓分散注意力的元素不顯得那么顯眼。

對於顏色:

我們可以使用顏色映射來指出漫步中各點的先后順序,讓漫步的順序更加清晰。

point_numbers = list(range(rw.num_points))
    plt.scatter(rw.x_values, rw.y_values, c=point_numbers, cmap=plt.cm.Blues,
                edgecolors='none', s=10)

point_numbers 是一個0~5000的列表,用來記憶各個點出現的順序,當作參數傳入函數scatter()

對於坐標軸:

我們要隱藏坐標軸,因為坐標軸在有些隨機漫步的圖表中並不重要。
只需要兩條語句,設置 set_visible()False

plt.axes().get_xaxis().set_visible(False)
plt.axes().get_yaxis().set_visible(False)


使用 Pygal 來模擬拋骰子

什么是 Pygal ?

Pygal 是 Python 的可視化包,用來生成可縮放的矢量圖形文件。對於需要在尺寸不同的屏幕上顯示的圖表,使用 Pygal 繪制將很有用,
因為他們可以進行自動的縮放。

模擬骰子

對於骰子來說,我們可以創建一個Die類,來表示一個骰子。

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)

方法 roll() 使用函數 randint 隨機生成一個 1~面數之間的隨機數,模擬動作拋骰子。

使用 Pygal 創建圖表之前,我們先拋幾次骰子來獲得一些基礎數據。

from die import Die

# 創建一個D6(六面骰子)
die = Die()

# 拋幾次骰子,並將結果存儲在一個列表中
results = []
for roll_num in range(1000):
    result = die.roll()
    results.append(result)

print(results)

在上面的代碼中,我們拋了1000次骰子,作為接下來處理的基礎數據。

[4, 3, 6, 6, 5, 4, 4, 6, 2, 3, 3, 5, 6, 6, 3, 6, 3, 1, 1, 2,...]

接下來,對我們自己設計的數據來進行分析。為了分析我們設置的 Die 是否正確,我們對每一面出現的次數進行統計,
如果每一個面出現的次數相近,則表示我們創建的骰子類 Die 與我們生活中的骰子較為相似。

frequencies = []

for value in range(1, die.num_sides):
    frequency = results.count(value)
    frequencies.append(frequency)
    
print(frequencies)

列表 frequencies 用於存儲每種點數出現的次數,我們遍歷可能的點數,用函數 count() 計算每種點數在結果中出現的次數。

列表 freqencies : [163, 162, 177, 177, 140, 181]

目測結果每個面出現的次數偏差不大,為了更直觀的比較,我們將使用 Pygal 把數據做成可視化的形式。

import pygal

--snip--

hist = pygal.Bar()

hist.title = 'Results of rolling one D6 1009 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')

hist 存儲 pygal.Bar() 實例, 之后設置圖像的各種其他信息。
之后我們使用 add() 將一系列值添加到圖表中(向他傳遞要給添加的值指定的標簽,還有一個列表,其中包含將出現在圖表中的值)。


同時拋兩個骰子

同時拋兩個骰子,求兩個骰子的點數和。這樣的到的點數更多,結果分布情況也不同。
我們也通過簡單修改上面的代碼,實現同時拋兩個骰子,並對兩個骰子的數據結果可視化顯示,來研究分布結果。

from die import Die
import pygal

# 創建兩個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_reslut = die_1.num_sides + die_2.num_sides
for value in range(2, max_reslut + 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('die_visual.svg')


同時拋兩個面數不同的骰子

接下來我們創建一個6面骰子和10面骰子,看看同時拋這兩個骰子500000次的數據結果

from die import Die
import pygal

# 創建兩個D6(六面骰子)
die_1 = Die()
die_2 = Die(10)

# 拋幾次骰子,並將結果存儲在一個列表中
results = []
for roll_num in range(50000):
    result = die_1.roll() + die_2.roll()
    results.append(result)

# 分析結果
frequencies = []
max_reslut = die_1.num_sides + die_2.num_sides
for value in range(2, max_reslut + 1):
    frequency = results.count(value)
    frequencies.append(frequency)

hist = pygal.Bar()

hist.title = 'Results of rolling a D6 and a D10 dice 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('die_visual.svg')


接下來,我們可以對代碼進行重構,讓他更具有可用性。

我們可以先更改 x_label 的設置,讓設置更加自動化。

for num in range(2, max_result + 1):
    x_labels.append(str(num))
hist.x_labels = x_labels.copy()

然后把 die_visual.py 文件重構為函數。

def throw_two_dice(die_1_sides=6, die_2_sides=6):
    die_1 = Die(die_1_sides)
    die_2 = Die(die_2_sides)

# 拋幾次骰子,並將結果存儲在一個列表中
    results = []
    for roll_num in range(50000):
        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 a D' + str(die_1.num_sides) + ' and a D' + str(die_2.num_sides) + ' dice 50,000 times.'
    x_labels = []
    for num in range(2, max_result + 1):
        x_labels.append(str(num))
    hist.x_labels = x_labels.copy()
    hist.x_title = 'Result'
    hist.y_title = 'Frequency of Result'

    hist.add('D' + str(die_1.num_sides) + '+ D' + str(die_2.num_sides), frequencies)
    hist.render_to_file('die_visual.svg')


使用 Python 處理以 CSV 個數存儲的數據

什么是 CSV ?

CSV, Comma-Separated Values 是 逗號分隔值,其文件以純文本存儲表格數據(數字和文本)。
CSV文件由任意數目的記錄組成,記錄間以某種換行符分隔;
每條記錄由字段組成,字段間的分隔符是其它字符或字符串,最常見的是逗號或制表符。
CSV是一種通用的廣泛應用。最廣泛的應用是在程序之間轉移表格數據、相對簡單的文件格式,被用戶、商業和科學。

CSV 文件格式

import csv

filename = 'sitka_weather_07-2014.csv'
with open(filename) as f:
    reader = csv.reader(f)
    header_row = next(reader)
    print(header_row)

在模塊 csv 中存在一個閱讀器類 reader ,我們創建一個讀取 filename 的對象存儲在 reader 中。
reader 類中的方法 next() 可以返回文件的下一行,而第一次調用就代表返回文件的第一行。我們將返回的數據存儲在 header_row 中,
包含與天氣相關的文件頭,指出每行都包含哪些數據。

運行代碼可得到

['AKDT', 'Max TemperatureF', 'Mean TemperatureF', 'Min TemperatureF', ...]

reader 處理文件以逗號分割第一行數據,並將每項數據都作為一個元素存儲在列表中。

也可以更換另一種輸出方式

    for index, column_header in enumerate(header_row):
        print(index, column_header)

對列表調用 enumerate() 來獲取每個元素的索引及其值。

0 AKDT

1 Max TemperatureF

2 Mean TemperatureF

3 Min TemperatureF

...

接下來可以分別處理第0行的日期和第1行最高氣溫

首先讀取每天的最高氣溫:

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

閱讀器對象從其停留的地方繼續向下讀取 CSV 文件, 每次都是自動返回當前所處位置的下一行。
即從第二行開始讀。

得到數據:

['64', '71', '64', '59', '69', '62', '61', ...]

之后對這些最高溫度值繪制氣溫圖表

    # 根據數據繪制圖形
    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()

使用 plot() 函數繪畫一個簡單的折線圖,
但是對 xlabel() 函數,由於還沒有添加日期所以沒有給x軸添加坐標。

接下來我們從文件中讀取日期。從CSV文件中讀取數據時獲得的是一個字符串,
所以我們需要把字符串轉化為一個表示相應日期的對象。

dates = []
for row in reader:
    current_date = datetime.strptime(row[0], "%Y-%m-%d")
    dates.append(current_date)

plt.plot(dates, highs, c='red')

我們利用模塊 datetime 中的函數 strptime() 來將日期數據轉換為 datetime 類。
再調用 autofmt_xdate() 函數來繪制斜着的日期標簽,以免他們彼此重疊。

fig.autofmt_xdate()

繪制出圖形:

修改讀取文件,實現讀取更大范圍的數據。

再在圖表中添加最低氣溫數據,使圖表完整

lows = []
for row in reader:
    low = int(row[3])
    lows.append(low)

plt.plot(dates, lows, c='blue')

可以使用函數 fill_between() 來將最高氣溫和最低氣溫之間的區域塗色,使得氣溫范圍變得更加明顯。

plt.fill_between(dates, highs, lows, facecolor='blue', alpha=0.1)
# alpha 代表透明度,從0~1逐漸透明

有時候我們獲取的數據並不是完全正確的,可能原始數據中就會有一些錯誤。
如果按照上述程序來運行,若遇到存在錯誤的原始程序,則會導致程序崩潰,所以我們需要修改代碼,使代碼能夠應對這個問題。

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

每次我們從文件中提取信息,只要中間有一項確實,Python都會引發 ValueError 的異常
如果出現數據缺失,則會打印:

日期 + missing data

處理 JSON 文件存儲的數據

什么是 JSON 文件 ?

JSON (javascript Object Notation) 格式最初是為了 JavaScript 開發的,
但隨后成了一種常見格式,被包含 Python 在內的眾多語言中。

模塊 json 讓你能夠將簡單的Python數據結構轉儲到文件中,
並在程序再次運行時加載該文件中的數據,你還可以使用 json 在 Python 程序之間分享數據。更重要的是,
JSON 數據格式並非 Python 專用的,這讓你能夠以 JSON 格式存儲的數據與使用其他編程語言的人分享。

下載收盤價數據

我們可以先從網上下載文件,在程序中對下載后的本地文件進行處理。
也可以從在程序運行的過程中直接通過程序讀取網上的文檔並處理。

方法一:先從網上下載文件,再在程序中讀取本地文件

import json
filename = 'btc_close_2017.json'
with open(filename) as f:
    file = json.load(f)

print(file)

方法二:直接在程序中從網上下載文件

1.使用模塊 urllib 模塊中的函數 urlopen() 將 url 傳入到函數中,
Python 就會向網站發送請求,服務器響應后就把文件發送給 Python

from urllib.request import urlopen
import json

json_url = 'http://raw.githubusercontent.com/muxuezi/btc/master/btc_close_2017.json'
response = urlopen(json_url)
# 讀取數據
req = response.read()
# 將數據寫入文件
with open('btc_close_2017_urllib.json','wb') as f:
    f.write(req)
# 加載 json 格式
# 此時 req 和 f 可以互換
file_urllib = json.loads(req)
print(file_urllib)

2.使用模塊 requests 中的方法,可以讓上述過程變得簡單
函數 requests.get() 可以從網絡上下載文件到 Python 中的 req
req.text 為文件中的內容,而 req.json() 函數把內容轉化為 Python 能夠處理的格式。

import requests

json_url = 'http://raw.githubsercontent.com/muxuezi/btc/master/btc_close_2017.json'
req = requests.get(json_url)
# 將數據寫入文件
with open('btc_close_2017_request.json','w') as f:
    f.write(req.text)
file_requests = req.json()

提取相關數據

為了方便使用 Pygal 作圖,我們需要把文件中的數據提取到 Python 中,
並把數字字符串轉化為 int 的格式方便處理。

# 打印每一天的信息
for btc_dict in btc_data:
    date = btc_dict['date']
    month = int(btc_dict['month'])
    week = int(btc_dict['week'])
    weekday = btc_dict['weekday']
    close = int(float(btc_dict['close']))
    print("{} is month {} week {}, {}, the close prise is {} RMB".format(date, month, week, weekday, close))

這里需要注意的是,btc_dict['close'] 中的字符串為 '3928.6492' 形式的,
若直接轉化為 int 格式,則會出現 ValueError 異常。
所以我們需要先 float() 轉化為 float 類型,再 int()

得到的數據:

2017-01-01 is month 1 week 52, Sunday, the close prise is 6928 RMB

2017-01-02 is month 1 week 1, Monday, the close prise is 7070 RMB

......

繪制收盤價折線圖

在繪制折線圖之前,我們需要首先獲取 x 軸和 y 軸的信息,所以創建幾個列表來存儲數據。

# 創建5個列表,分別存儲日期和收盤價
dates, moenths, weeks, weekdays, close = [], [], [], [], []

# 每一天的信息
for btc_dict in btc_data:
    dates.append(btc_dict['date'])
    moenths.append(int(btc_dict['month']))
    weeks.append(int(btc_dict['week']))
    weekdays.append(btc_dict['weekday'])
    close.append(int(float(btc_dict['close'])))

我們使用 pygal 模塊中的 Line() 函數,之后傳入 x 軸和 y 軸參數,對圖像的其他設置進行調整。

import pygal
line_chart = pygal.Line(x_label_rotation=20, show_minor_x_labels=False)
line_chart.title = '收盤價 (¥)'
line_chart.x_labels = dates
# x軸坐標每間隔20天顯示一次
N = 20
line_chart.x_labels_major= dates[::N]
line_chart.add('收盤價', close)
line_chart.render_to_file('收盤價折線圖(¥).svg')

顯示的結果如下圖:

探索時間序列特征

進行時間序列分析,總是期望發現趨勢,周期性,和噪聲,從而能夠根據事實,預測未來,做出決策。為了尋找周期性,需要首先將非線性的趨勢消除。
對數變換,是常用的處理方法之一。

從收盤價的折線圖可以看出,2017年的總體趨勢是非線性的,而且增長幅度不斷增大,
似乎呈指數分布,但是我們還可以發現在每個嫉妒末似乎有一些相似的波動。盡管這些波動被增長的趨勢掩蓋了,
不過其中也許存在周期性。

使用 Python 標准庫中的 math 模塊,來對數據進行對數變換。

只對收盤價進行對數變換,而不改變日期叫做半對數變換。

close_log = [math.log10(num) for num in close]
line_chart.add('收盤價', close_log)
line_chart.render_to_file('收盤價對數折線圖(¥).svg')

用對數變換剔除非線性趨勢之后,整體上漲的趨勢更接近線性增長。並可以大致從圖中看出周期性。三月,六月,九月,都出現了明顯的波動。


使用 WebAPI

什么是 API ?

Web API 是網站的一部分,用於與使用非常具體的 URL 請求特定信息的程序交互。
這種請求稱為 API 調用。
請求的數據將以易於處理的格式(如JSON或CSV)返回。依賴於外部數據源的大多數應用程序都依賴於 API 調用,如
繼承社交媒體網站的應用程序。

我們將以 Github 網站為例,了解 API 的使用。

Github 的API可以讓我們能夠通過API調用來請求各種信息。在瀏覽器輸入:

https://api.github.com/search/repositories?q=language:python&sort=stars

/*
api.github.com/ 將請求發送到 Github 網站中響應 API 調用的部分;
search/repositories 讓 API 搜索 Github 上的所有代碼庫
? 代表我們要傳入一個實參
q 代表查詢,= 代表開始查詢
language:python 代表我們只想獲取語言為 python 的代碼庫
&sort=stars 代表排序的順序是按照星數排序
*/

得到的結果如下:

{
"total_count": 4812373,
"incomplete_results": false,
"items": [
  {
    "id": 83222441,
    "node_id": "MDEwOlJlcG9zaXRvcnk4MzIyMjQ0MQ==",
    "name": "system-design-primer",
    -snip--

可以看出,響應的結果是一個字典,包含了三個 key ,分別是 total_count 庫總數,incomplete_results 未完成結果,items 成員

處理 API 響應

接下來我們編寫程序,處理 API 響應。

import requests

# 執行API調用並存儲相應
url = 'http://api.github.com/search/repositories?q=language:python&sort=stars'
r = requests.get(url)
print("Status code:", r.status_code)

# 將API響應存儲在一個變量中
response_dict = r.json()

# 處理結果
print(response_dict.keys())

使用模塊 requests 來執行調用,調用函數 get() 將響應對象存儲在變量 r, 對象中存在一個屬性 status_code,他是一種狀態碼,
讓我們判斷請求是否成功。狀態碼200表示請求成功。

再調用 json() 函數將 API 返回的信息轉化為 Python 能夠處理的字典格式。
將字典存儲在 response_dict 中。輸出字典中的鍵得到:

Status code: 200

dict_keys(['total_count', 'incomplete_results', 'items'])

處理響應字典

得到 API 字典之后,就可以處理這個字典中的數據

# 探索有關倉庫的信息
repo_dicts = response_dict['items']
print("Repositories returned: ", len(repo_dicts))

# 研究第一個倉庫
repo_dict = repo_dicts[0]
print("\nKeys: ", len(repo_dict))
for key in sorted(repo_dict.keys()):
    print(key)

items 相關聯的值是一個列表,其中包含很多字典,每個字典都包含有關一個 Python 倉庫的信息。
而對於每一個倉庫字典,包含了這個庫的許多信息。我們可以通過打印一個倉庫字典中的鍵得到倉庫的一些信息。

運行結果:

Keys:  74
archive_url
archived
assignees_url
--snip--
url
watchers
watchers_count

Github 的 API 返回有關倉庫的大量信息,從返回結果看有 74 個鍵,
我們通過查看這些鍵就可以了解倉庫的大致信息。

輸出最受歡迎的倉庫

接下來借助代碼來查看這些信息。在循環中我們打印每個項目的名稱,所有者,星級,在 Github 上的 URL 及其描述。

for repo_dict in repo_dicts:
    print("\nSelected information about first repository:")
    print("Name: ", repo_dict['name'])
    print("Owner: ", repo_dict['owner']['login'])
    print("Star: ", repo_dict['stargazers_count'])
    print("Repository: ", repo_dict['html_url'])
    print("Created: ", repo_dict['created_at'])
    print("Updated: ", repo_dict['updated_at'])
    print("Description: ", repo_dict['description'])

得到的結果

Selected information about first repository:
Name:  awesome-python
Owner:  vinta
Star:  79188
Repository:  https://github.com/vinta/awesome-python
Created:  2014-06-27T21:00:06Z
Updated:  2020-02-13T06:49:04Z
Description:  A curated list of awesome Python frameworks, libraries, software and resources

Selected information about first repository:
Name:  public-apis
Owner:  public-apis
Star:  70563
Repository:  https://github.com/public-apis/public-apis
Created:  2016-03-20T23:49:42Z
Updated:  2020-02-13T07:01:48Z
Description:  A collective list of free APIs for use in software and web development.

--snip--

Selected information about first repository:
Name:  localstack
Owner:  localstack
Star:  23017
Repository:  https://github.com/localstack/localstack
Created:  2016-10-25T23:48:03Z
Updated:  2020-02-13T06:51:02Z
Description:  💻  A fully functional local AWS cloud stack. Develop and test your cloud & Serverless apps offline!

使用 Pygal 可視化倉庫

使用 Pygal 模塊中的函數 Bar() 構建柱狀圖

from pygal.style import LightColorizedStyle as LCS, LightenStyle as LS

# 探索有關倉庫的信息
repo_dicts = response_dict['items']

names, stars = [], []
for repo_dict in repo_dicts:
    names.append(repo_dict['name'])
    stars.append(repo_dict['stargazers_count'])

# 可視化
my_style = LS('#333366', base_style=LCS)
chart = pygal.Bar(style=my_style, x_label_rotation=45, show_legend=False)
chart.title = 'Most-Starred Python Project on Github'
chart.x_labels = names

chart.add('', stars)
chart.render_to_file('python_repos.svg')

顯示的柱狀圖:

接下來來改進這個圖表,進行多方面的定制。

# 可視化
my_style = LS('#333366', base_style=LCS)

# 創建 Config 對象
my_config = pygal.Config()
my_config.x_label_rotation = 45
my_config.show_legend = False
# 設置標題標簽的字體大小
my_config.title_font_size = 24
my_config.label_font_size = 14
my_config.major_label_font_size = 18
# 縮短較長的字符為15個
my_config.truncate_label = 15
# 隱藏水平線
my_config.show_y_guides = False
# 自定義寬度
my_config.width = 1000

chart = pygal.Bar(config=my_config, style=my_style)

得到的圖像:

為了讓圖表中顯示更多的信息,而不僅僅是代碼庫的名字和star數。我們可以創建一個列表存放我們想要添加的信息。

plot_dicts = []

plot_dict = {
        'value': repo_dict['stargazers_count'],
        'label': str(repo_dict['description']),
        'xlink': repo_dict['html_url']
    }
    plot_dicts.append(plot_dict)

--snip--

chart.add('', plot_dicts)

這樣我們就可以得到既包含星數還包含代碼庫的描述以及網址,並實現點擊表格就可以跳轉到該網址。


免責聲明!

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



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