返回文章列表

Python 自動化圖表生成與統計報告

本文介紹如何使用 Python 結合 Matplotlib 與資料函式庫,自動產生統計圖表並生成網頁報告。文章涵蓋了從資料函式庫收集資料、使用 Agg 後端儲存圖表、計算統計指標,以及使用 Jinja2 範本引擎生成網頁的完整流程。同時也探討瞭如何規劃網站結構,以有效呈現不同時間尺度和探針的資料。

Web 開發 資料分析

Python 的 Matplotlib 函式庫提供強大的圖表繪製功能,但預設顯示在互動式視窗中,不利於自動化報告。本文介紹使用 Agg 後端直接將圖表儲存成檔案,方便整合到自動化系統。程式碼範例示範瞭如何使用 matplotlib.use('Agg') 設定後端,並使用 plt.savefig() 儲存圖表為 PNG 和 PDF 格式。結合資料函式庫操作,可以定期從資料函式庫讀取資料,自動生成統計圖表。文章也說明瞭如何使用 SQL 查詢從資料函式庫檢索主機和探針資料,並計算服務可用性等統計指標。最後,利用 Jinja2 範本引擎,將資料和圖表整合到網頁報告中,實作自動化報告生成。網站結構設計包含首頁、主機列表、主機詳情、探針詳情和時間尺度圖表等頁面,方便使用者瀏覽和分析資料。程式碼範例展示瞭如何使用 Jinja2 範本生成網頁,並動態嵌入圖表和統計資料。

統計資料收集與報告生成

將圖表儲存至檔案

到目前為止,我們已經探討了圖表生成的各種導向。你已經瞭解生成的圖表會在GUI中的互動式視窗中顯示。如果你只需要快速檢查結果,這是完全可以接受的,但這也意味著每次你想檢檢視表時,都需要執行完整的計算過程。你可以選擇從圖表顯示視窗中儲存圖表,但這是一個手動過程,不適合自動化報告系統。

matplotlib 使用影像後端處理程式來生成影像。對於大多數人來說,只需使用最流行的格式,如PNG、PDF、SVG、PS和EPS,matplotlib 提供了 Anti-Grain Geometry (Agg) 後端,它在後台使用C++ anti-grain影像渲染引擎。預設情況下,matplotlib 在匯入pyplot模組時使用其中一個GUI引擎(例如wxPython)。要改變這種行為,你必須首先指示它使用Agg後端,然後匯入pyplot。

使用 Agg 後端儲存影像至檔案

import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import numpy as np

fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
x = np.arange(100)
y = np.sin(2 * np.pi * x / 100)
ax.plot(y)
plt.savefig('sin-wave.png')
plt.savefig('sin-wave.pdf')

內容解密:

  1. 匯入必要的模組:首先匯入 matplotlibmatplotlib.pyplotnumpy。這裡特別需要注意的是,matplotlib.use('Agg') 的呼叫必須在匯入 matplotlib.pyplot 之前,以確保使用 Agg 後端。
  2. 建立圖形和軸:使用 plt.figure() 建立一個新的圖形,並使用 fig.add_subplot(1, 1, 1) 在這個圖形中新增一個軸。
  3. 生成資料:使用 np.arange(100) 生成一個包含0到99的陣列 x,並計算 y = np.sin(2 * np.pi * x / 100) 得到對應的正弦值。
  4. 繪製圖形:使用 ax.plot(y)y 對應的值繪製在軸上。
  5. 儲存圖形:最後,使用 plt.savefig() 將生成的圖形儲存為 PNG 和 PDF 格式的檔案。

圖表統計資料

我們花了很多時間討論各種統計資料分析方法。你知道如何檢查資料集中是否有趨勢,以及趨勢是正向還是負向。你還知道如何計算資料集的平均值,以及資料落在預定義邊界(標準差)內的可能性。現在讓我們看看如何應用這些知識。我們將構建一個簡單的應用程式,定期執行並生成狀態頁面。這些頁面是靜態頁面,將由 Apache 網頁伺服器提供服務。

從資料函式庫收集資料

第9章提供了我們用於監控系統的各種資料函式庫表格的詳細資訊,以及它們之間的關聯。因為我們在本章的例子中感興趣的是報告探針讀數,所以最感興趣的是 probereading 表格,它包含了從感測器獲得的原始資料。在處理之前,需要對這個表格的值進行過濾,所以我們需要知道這個讀數屬於哪個感測器——或者更準確地說,哪個探針。我們還需要按讀取這些探針的主機對探針讀數進行分組。換句話說,我們需要遍歷 host 表格中的所有條目;然後對於找到的每個主機,我們需要檢查哪些探針正在其上執行。一旦我們確定了整個主機到探針的組合,我們就需要取得隨時間變化的感測器讀數。

從資料函式庫檢索所有主機及相關探針

class SiteGenerator:
    def __init__(self, db_name):
        self.db_name = db_name
        self.conn = sqlite3.connect(self.db_name)
        self.hosts = []
        self._get_all_hosts()

    def _get_all_hosts(self):
        for h in self.conn.execute("SELECT * FROM host"):
            host_entry = list(h)
            query_str = """ SELECT hostprobe.id,
                                    probe.name,
                                    COALESCE(hostprobe.warning, probe.warning),
                                    COALESCE(hostprobe.error, probe.error)
                            FROM probe,
                                 hostprobe
                            WHERE probe.id = hostprobe.probe_id AND
                                  hostprobe.host_id = ?
                        """
            probes = self.conn.execute(query_str, (h[0],)).fetchall()
            host_entry.append(probes)
            self.hosts.append(host_entry)

內容解密:

  1. SiteGenerator 類別初始化:在初始化時,連線到指定的 SQLite 資料函式庫,並初始化一個空列表 hosts 用於儲存主機資訊。
  2. _get_all_hosts 方法:遍歷 host 表格中的所有主機,對於每個主機,查詢相關的探針資訊,包括探針ID、名稱、警告和錯誤閾值。
  3. 使用 COALESCE 函式:該函式傳回列表中的第一個非空值,用於處理主機特定閾值設定。如果主機特定閾值未設定(即為 NULL),則回退到預設值。

繪製時間尺度圖表

現在,我們已經擁有了進一步資料處理所需的所有資訊:主機和相關的主機探針。有很多不同的方法來表示我們收集的統計資訊。在本例中,我們將按照兩個引數之一對資訊進行排序:探針名稱和時間尺度。為了簡化實作,我們將使用預定義的可用時間尺度列表:1天、7天和30天。

網站結構規劃

為了開發範本和對應的程式碼,我發現先視覺化網站結構非常有幫助。圖11-7表示了我們的網站結構,以及示例HTML檔案名稱(ID待替換為實際值)和對應的Jinja2範本。

網站結構圖示

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title Python 自動化圖表生成與統計報告

package "統計分析流程" {
    package "資料收集" {
        component [樣本資料] as sample
        component [母體資料] as population
    }

    package "描述統計" {
        component [平均數/中位數] as central
        component [標準差/變異數] as dispersion
        component [分佈形狀] as shape
    }

    package "推論統計" {
        component [假設檢定] as hypothesis
        component [信賴區間] as confidence
        component [迴歸分析] as regression
    }
}

sample --> central : 計算
sample --> dispersion : 計算
central --> hypothesis : 檢驗
dispersion --> confidence : 估計
hypothesis --> regression : 建模

note right of hypothesis
  H0: 虛無假設
  H1: 對立假設
  α: 顯著水準
end note

@enduml

此圖示展示了網站的基本導航結構,從首頁到主機列表,再到各個主機和探針的詳情頁面,最終到達顯示不同時間尺度圖表的頁面。

圖示內容解密:

  1. 首頁:網站的首頁,通常包含導航連結或主要資訊的概覽。
  2. 主機列表:列出所有被監控的主機,供使用者選擇檢視詳情。
  3. 主機詳情:顯示特定主機上的探針及其狀態資訊。
  4. 探針詳情:提供特定探針的詳細資訊,包括其讀數和狀態。
  5. 時間尺度圖表:根據選定的時間尺度(例如1天、7天、30天),展示探針讀數的變化趨勢。

透過這種結構,使用者可以方便地導航至感興趣的探針詳情頁面,並檢視其在不同時間尺度下的表現。

網站統計資訊產生與網頁呈現

在建立網站統計資訊的過程中,我們首先需要產生主機列表頁面。這個頁面相對簡單,主要任務是將主機列表傳遞給範本引擎進行渲染。

主機列表頁面產生

主機列表頁面的產生主要依賴於 _generate_hosts_view 方法:

def _generate_hosts_view(self):
    t = self.tpl_env.get_template('index.template')
    f = open("%s/index.html" % self.location, 'w')
    f.write(t.render({'hosts': self.hosts}))
    f.close()

內容解密:

  1. self.tpl_env.get_template('index.template'):載入名為 index.template 的範本檔案,用於渲染主機列表頁面。
  2. open("%s/index.html" % self.location, 'w'):開啟(或建立)一個名為 index.html 的檔案於指定位置,準備寫入渲染後的內容。
  3. t.render({'hosts': self.hosts}):將 self.hosts 中的主機列表傳遞給範本引擎進行渲染,生成最終的 HTML 內容。
  4. f.write(...)f.close():將渲染後的內容寫入 index.html 檔案,並關閉檔案。

主機列表範本

範本檔案 index.template 的內容如下:

<h1>Hosts</h1>
<ul>
{% for host in hosts %}
<li><a href="host_{{ host[0] }}_details.html">{{ host[1] }}</a>
({{ host[2] }}:{{ host[3] }})</li>
{% endfor %}
</ul>

內容解密:

  1. {% for host in hosts %}:迴圈遍歷 hosts 列表中的每個主機。
  2. <a href="host_{{ host[0] }}_details.html">{{ host[1] }}</a>:為每個主機生成一個超連結,連結到對應的主機詳細資訊頁面,顯示主機名稱。
  3. ({{ host[2] }}:{{ host[3] }}):顯示主機的位址和監控代理的連線埠號。

主機詳細資訊頁面

主機詳細資訊頁面的產生涉及到兩個主要方法: _generate_host_toc_calculate_service_availability

_generate_host_toc 方法

def _generate_host_toc(self, host):
    probe_sa = {}
    for probe in host[4]:
        probe_sa[probe[1]] = {}
        for scale in TIMESCALES:
            probe_sa[probe[1]][scale] = self._calculate_service_availability(probe, scale)
    t = self.tpl_env.get_template('host.template')
    f = open("%s/host_%s_details.html" % (self.location, host[0]), 'w')
    f.write(t.render({ 'host': host, 'timescales': TIMESCALES, 'probe_sa': probe_sa }))
    f.close()

內容解密:

  1. probe_sa = {}:初始化一個字典,用於儲存每個探針在不同時間尺度下的服務可用性資料。
  2. 雙層迴圈:遍歷每個主機的探針和不同的時間尺度,呼叫 _calculate_service_availability 計算服務可用性。
  3. t.render({...}):將主機資訊、時間尺度和探針服務可用性資料傳遞給範本引擎進行渲染。

_calculate_service_availability 方法

def _calculate_service_availability(self, probe, scale):
    # ...
    sampling_rate = self.conn.execute("""SELECT probeinterval FROM probingschedule WHERE hostprobe_id=?""", (probe[0],)).fetchone()[0]
    records_to_read = int(24 * 60 * scale / sampling_rate)
    # ...

內容解密:

  1. sampling_rate:從資料函式庫中查詢指定探針的取樣率。
  2. records_to_read:根據取樣率和指定的時間尺度計算需要讀取的記錄數量。
  3. SQL 查詢:執行巢狀 SQL 查詢,計算超過閾值的記錄數量,從而得出服務可用性指標。

網站結構與資訊呈現

網站結構如圖 11-7 所示,主要包含以下幾個部分:

  • 首頁:列出所有可用的主機連結。
  • 主機詳細資訊頁面:顯示每個主機的服務可用性統計資料,並提供不同時間尺度和探針的圖表連結。
  • 時間尺度詳細資訊頁面:提供特定主機在不同時間尺度下的圖表連結。
  • 探針詳細資訊頁面:提供特定探針在不同時間尺度下的圖表連結。

透過上述結構和實作,能夠有效地呈現監控資料,並提供豐富的統計資訊和分析結果。

統計資料收集與報告生成

本章節將詳細介紹如何收集統計資料並生成報告。我們將探討相關的程式碼實作,包括主機詳情範本、圖表集合頁面以及效能圖表的繪製。

主機詳情範本

主機詳情範本負責顯示主機的可用性統計資料,並生成指向包含圖表的頁面的連結。以下為範本的程式碼範例:

<h1>主機詳情:{{ host[1] }}</h1>
<h2>按時間尺度分組的檢視</h2>
<p>您可以在此找到此主機上所有可用的探針在相同時間尺度上的檢視。</p>
<ul>
{% for scale in timescales %}
<li><a href="hsd_{{ host[0] }}_{{ scale }}.html">{{ scale }} 天檢視</a></li>
{% endfor %}
</ul>
<h2>按探針分組的檢視</h2>
<p>您可以在此找到相同探針的所有可用時間尺度檢視。</p>
<ul>
{% for probe in host[4] %}
<li><a href="hpd_{{ probe[0] }}.html">{{ probe[1] }}</a></li>
{% endfor %}
</ul>
<h2>主機統計資料</h2>
<h3>服務可用性詳情</h3>
{% for probe in probe_sa %}
<h4>"{{ probe }}" 檢查的可用性</h4>
<ul>
{% for scale in probe_sa[probe] %}
<li>在 {{ scale }} 天尺度上:
    <ul>
    <li>警告:{{ probe_sa[probe][scale][0]|round(3) }}%</li>
    <li>錯誤:{{ probe_sa[probe][scale][1]|round(3) }}%</li>
    </ul>
</li>
{% endfor %}
</ul>
{% endfor %}

內容解密:

  • 此範本使用 Jinja2 語法動態生成內容。
  • host[1]host[0] 分別代表主機的名稱和 ID。
  • timescales 是一個列表,包含不同的時間尺度值,用於生成對應的連結。
  • host[4] 是一個列表,包含主機上的探針資訊。
  • probe_sa 是一個字典,包含每個探針在不同時間尺度上的可用性統計資料。

圖表集合頁面

圖表集合頁面負責展示不同探針或時間尺度下的圖表。我們定義了兩個函式:_generate_host_probe_details_generate_host_scale_details,分別用於生成特定探針和特定時間尺度下的圖表集合頁面。

def _generate_host_probe_details(self, host_struct, probe_struct):
    t = self.tpl_env.get_template('host_probe_details.template')
    f = open("%s/hpd_%s.html" % (self.location, probe_struct[0]), 'w')
    images = []
    for scale in TIMESCALES:
        images.append([scale, "plot_%s_%s.png" % (probe_struct[0], scale)])
    f.write(t.render({'host': host_struct, 'probe': probe_struct, 'images': images}))
    f.close()

def _generate_host_scale_details(self, host_struct, scale):
    t = self.tpl_env.get_template('host_scale_details.template')
    f = open("%s/hsd_%s_%s.html" % (self.location, host_struct[0], scale), 'w')
    images = []
    for probe in host_struct[4]:
        images.append([probe[1], "plot_%s_%s.png" % (probe[0], scale)])
    f.write(t.render({'host': host_struct, 'scale': scale, 'images': images}))
    f.close()

內容解密:

  • _generate_host_probe_details 函式生成特定探針在不同時間尺度下的圖表集合頁面。
  • _generate_host_scale_details 函式生成特定時間尺度下不同探針的圖表集合頁面。
  • 兩個函式都使用範本引擎渲染範本,並將生成的 HTML 寫入檔案。
  • images 列表包含圖表的相關資訊,包括圖表的標題和檔案名稱。

效能圖表繪製

效能圖表繪製函式 _plot_time_graph 負責從資料函式庫中讀取資料並生成效能圖表。

def _plot_time_graph(self, hostprobe_id, time_window, sampling_rate, plot_title, plot_file_name, warn=None, err=None):
    records_to_read = int(time_window / sampling_rate)
    records = self.conn.execute("""SELECT timestamp, probe_value FROM probereading WHERE hostprobe_id=? LIMIT ?""", (hostprobe_id, records_to_read)).fetchall()
    time_array, val_array = zip(*records)
    mean = np.mean(val_array)
    std = np.std(val_array)
    warning_val = mean + 3 * std
    error_val = mean + 4 * std
    data_y = np.array(val_array)
    data_x = np.arange(len(data_y))
    data_time = [dateutil.parser.parse(s) for s in time_array]
    data_xtime = matplotlib.dates.date2num(data_time)
    a, b = np.polyfit(data_x, data_y, 1)
    matplotlib.rcParams['font.size'] = 10
    fig = plt.figure(figsize=(8,4))
    ax = fig.add_subplot(1, 1, 1)
    ax.set_title(plot_title + "\nMean: %.2f, Std Dev: %.2f, Warn Lvl: %.2f, Err Lvl: %.2f" % (mean, std, warning_val, error_val))
    ax.plot_date(data_xtime, data_y, 'b')
    ax.plot_date(data_xtime, data_x * a + b, color='black', linewidth=3, marker='None', linestyle='-', alpha=0.5)

內容解密:

  • 此函式從資料函式庫中讀取特定主機探針的資料,並計算相關的統計引數,如平均值和標準差。
  • 使用 NumPy 和 matplotlib 繪製效能圖表,並在圖表中顯示趨勢線。
  • 圖表的標題包含相關的統計資訊,如平均值、標準差、警告和錯誤閾值。