Python 模組設計的目標是提高程式碼的可重用性和可維護性。本文將探討如何透過函式封裝、條件式執行以及類別定義來最佳化模組結構。同時,我們也會深入研究如何利用 Python 進行統計分析,包括計算標準差、標準化分數和相關係數等。此外,本文還會介紹如何使用 Docstring 和 unittest 框架編寫單元測試,以確保程式碼的正確性和穩定性,並提升整體軟體品質。這些技術對於構建穩健、可擴充套件的 Python 應用程式至關重要,也是 Python 開發者必備的技能。

探討Python模組設計與類別定義

在前面的章節中,我們已經瞭解如何建立一個簡單的命令列模組。在本章中,我們將進一步探討如何改進模組設計,使其更具可重用性和擴充套件性。同時,我們也將介紹如何定義自己的類別,以更好地封裝資料和處理邏輯。

建立混合模組

為了使basic_stats.py模組更加完善,我們可以進行兩項重要的改進:

  1. 將所有處理邏輯封裝到一個函式定義中,例如analyze_cheese_deaths
  2. 新增一個if陳述式來判斷模組的使用情境。

以下是改進後的basic_stats.py範例:

"""Chapter 5 example 3."""
from ch_5_ex_1 import mean, mode, median
from ch_5_ex_1 import get_deaths, get_cheese

def analyze_cheese_deaths():
    year_deaths = list(get_deaths())
    years = list(year for year, death in year_deaths)
    deaths = list(death for year, death in year_deaths)
    print("Year Range", min(years), "to", max(years))
    print("Average Deaths {:.2f}".format(mean(deaths)))

    year_cheese = get_cheese()
    print("Average Cheese Consumption", mean([cheese for year, cheese in year_cheese]))

if __name__ == "__main__":
    analyze_cheese_deaths()

內容解密:

  • 將處理邏輯封裝到analyze_cheese_deaths函式中,使得程式碼更具可重用性。
  • 使用if __name__ == "__main__":來判斷是否為主模組,如果是,則執行analyze_cheese_deaths函式。
  • 這種設計使得模組既可以作為命令列工具執行,也可以被其他模組匯入使用。

定義自己的類別

在處理年度統計資料時,我們發現乳酪消耗量和W75死亡人數這兩個資料集具有相似的結構和操作。因此,我們可以定義一個名為AnnualStats的類別來封裝這些資料和操作。

以下是AnnualStats類別的定義:

from collections import Counter

class AnnualStats:
    def __init__(self, year_measure):
        self.year_measure = list(year_measure)
        self.data = list(v for yr, v in self.year_measure)
        self.counter = Counter(self.data)

    def __repr__(self):
        return repr(self.year_measure)

    def min_year(self):
        return min(yr for yr, v in self.year_measure)

    def max_year(self):
        return max(yr for yr, v in self.year_measure)

    def mean(self):
        return sum(self.data) / len(self.data)

    def median(self):
        mid, odd = divmod(len(self.data), 2)
        if odd:
            return sorted(self.data)[mid]
        else:
            pair = sorted(self.data)[mid-1:mid+1]
            return sum(pair) / 2

    def mode(self):
        value, count = self.counter.most_common(1)[0]
        return value

內容解密:

  • __init__方法用於初始化例項,接受一個包含年度統計資料的迭代器。
  • self.year_measure屬性儲存原始資料,self.data屬性儲存測量值,self.counter屬性用於計算眾數。
  • __repr__方法提供例項的字串表示。
  • min_yearmax_yearmeanmedianmode等方法提供了各種統計計算。
  • 這些方法都與例項的資料緊密相關,展現了物件導向程式設計的封裝特性。

深入統計分析:相關性與標準差的計算

在進行資料分析時,我們經常需要了解不同變數之間的相關性。這不僅可以幫助我們發現變數之間的因果關係,還可以用於預測或證明變數之間的獨立性。相關係數是衡量變數之間相關性的重要統計工具。

使用類別定義封裝資料處理

首先,我們定義了一個名為 AnnualStats 的類別,用於封裝與年度統計資料相關的處理。這個類別包含例項變數 self.dataself.counter,分別用於儲存資料值和根據這些資料值的 Counter 物件。

AnnualStats 類別的定義與使用

class AnnualStats:
    def __init__(self, year_measure):
        self.year_measure = year_measure
        self.data = [value for year, value in year_measure]
        self.counter = Counter(self.data)

    def mean(self):
        return sum(self.data) / len(self.data)

    def median(self):
        sorted_data = sorted(self.data)
        n = len(sorted_data)
        if n % 2 == 1:
            return sorted_data[n // 2]
        else:
            return (sorted_data[n // 2 - 1] + sorted_data[n // 2]) / 2

    def min_year(self):
        return min(year for year, value in self.year_measure)

    def max_year(self):
        return max(year for year, value in self.year_measure)

    def __repr__(self):
        return repr(self.year_measure)

使用 AnnualStats 類別

from ch_5_ex_1 import get_deaths, get_cheese

deaths = AnnualStats(get_deaths())
cheese = AnnualStats(get_cheese())

print("年份範圍", deaths.min_year(), deaths.max_year())
print("W75死亡平均值", deaths.mean())
print("乳酪消費中位數", cheese.median())
print("乳酪消費平均值", cheese.mean())
print(deaths)

內容解密:

  1. AnnualStats 類別的初始化__init__ 方法負責初始化例項變數,包括從 year_measure 中提取資料值到 self.data,以及建立一個根據 self.dataCounter 物件 self.counter
  2. 統計方法的實作:類別中定義了諸如 meanmedianmin_yearmax_year 等方法,用於計算資料的平均值、中位數、最小年份和最大年份。
  3. __repr__ 方法:傳回 self.year_measure 的字串表示,用於列印物件時的輸出。

計算標準差

標準差是衡量資料分散程度的重要指標。給定一組資料,其標準差可以告訴我們資料點與平均值之間的平均距離。

標準差的計算公式

標準差的計算公式為: [ \sigma_x = \sqrt{\frac{\sum_{i=1}^{n} (x_i - \mu_x)^2}{n}} ] 其中,(\sigma_x) 是變數 (x) 的標準差,(\mu_x) 是 (x) 的平均值,(n) 是資料點的數量。

在 AnnualStats 類別中新增 stddev 方法

import math

class AnnualStats:
    # ... 其他方法 ...

    def stddev(self):
        μ_x = self.mean()
        n = len(self.data)
        σ_x = math.sqrt(sum((x - μ_x) ** 2 for x in self.data) / n)
        return σ_x

內容解密:

  1. stddev 方法的實作:首先計算資料的平均值 μ_x,然後使用公式計算標準差 σ_x。這裡使用了生成器表示式來計算每個資料點與平均值之間的差的平方和。
  2. 使用 math.sqrt 函式:計算平方根以得到標準差。
  3. 標準差的意義:標準差小表示資料緊密聚集在平均值周圍;標準差大則表示資料分散較廣。

相關係數的計算與應用

相關係數是用於衡量兩個變數之間線性關係強度的重要統計量。我們可以使用 NumPy 或 SciPy 等函式庫來計算相關係數,也可以自行實作相關係數的計算。

相關係數的計算

相關係數的計算涉及多個步驟,包括計算兩個變數的平均值、標準差,以及它們的協方差。具體實作細節可以參考相關的統計學教材或 NumPy/SciPy 的檔案。

深入解析統計分析在資料處理中的應用

在資料分析領域,統計方法扮演著至關重要的角色。從計算平均值到標準差,再到標準化分數和相關係數,每一步都建立在嚴謹的數學基礎之上。本章將探討如何使用Python實作這些統計功能,並闡述其背後的數學原理。

標準差的計算與實作

標準差是用於衡量資料分散程度的重要指標。其數學表示式為: [ \sigma = \sqrt{\frac{\sum(x - \mu_x)^2}{n}} ] 其中,( \sigma ) 代表標準差,( x ) 是資料點,( \mu_x ) 是資料的平均值,( n ) 是資料點的數量。

Python實作

def stddev(self):
    n = sum(1 for x in self.data)  # 計算資料點數量
    μ_x = self.mean()  # 計算平均值
    return math.sqrt(sum((x - μ_x) ** 2 for x in self.data) / n)

內容解密:

  1. sum(1 for x in self.data) 計算資料集中的元素數量。
  2. self.mean() 呼叫類別方法計算資料的平均值。
  3. sum((x - μ_x) ** 2 for x in self.data) 計算每個資料點與平均值之差的平方和。
  4. 最終結果透過 math.sqrt 計算平方根,得到標準差。

標準化分數的計算

標準化分數(Z分數)用於衡量某個資料點相對於平均值的偏離程度,其公式為: [ S(x_i) = \frac{x_i - \mu_x}{\sigma_x} ] 其中,( S(x_i) ) 是標準化分數,( x_i ) 是某個資料點,( \mu_x ) 是平均值,( \sigma_x ) 是標準差。

Python實作

def stdscore(self):
    μ_x = self.mean()
    σ_x = self.stddev()
    return [(x - μ_x) / σ_x for x in self.data]

內容解密:

  1. 首先計算資料的平均值 μ_x 和標準差 σ_x
  2. 使用列表推導式對每個資料點 x 計算其標準化分數。
  3. 傳回包含所有標準化分數的列表。

相關係數的計算

相關係數用於衡量兩個資料序列之間的相關程度,其公式為: [ r = \frac{\sum(s(x_i) \times s(y_i))}{n} ] 其中,( s(x_i) ) 和 ( s(y_i) ) 分別是兩個序列的標準化分數,( n ) 是資料點的數量。

Python實作

def correlation1(d1, d2):
    n = len(d1.data)
    std_score_pairs = zip(d1.stdscore(), d2.stdscore())
    r = sum(x * y for x, y in std_score_pairs) / n
    return r

內容解密:

  1. zip(d1.stdscore(), d2.stdscore()) 將兩個序列的標準化分數配對。
  2. sum(x * y for x, y in std_score_pairs) 計算配對分數的乘積之和。
  3. 最終結果除以資料點數量 n,得到相關係數。

單元測試:確保軟體品質的利器

在軟體開發過程中,確保程式碼的正確性與穩定性是至關重要的。單元測試(Unit Testing)是一種有效的驗證方法,能夠幫助開發者及時發現並修復程式碼中的錯誤。Python 為單元測試提供了兩種主要方式:將範例程式碼嵌入模組、函式或類別的說明檔案中(Docstring),以及編寫獨立的 unittest.TestCase 類別。

使用 Docstring 進行單元測試

對於大多數開發者而言,在 Docstring 中加入測試範例是一種簡單且直觀的方法。這種方式不僅能夠提供測試範例,還能夠作為檔案使用,方便其他開發者理解程式碼的功能與用法。

如何撰寫 Docstring 測試範例

  1. 複製互動式 Python 的輸出結果:將正確的輸出結果複製到 Docstring 中,包含 >>> 提示符號,以便於識別測試範例。
  2. 預期輸出結果:確保包含預期的輸出結果,讓 doctest 模組能夠比對實際輸出與預期輸出。

以下是一個簡單的範例:

def mean(values):
    """計算序列的平均值(不適用於可迭代物件)
    
    >>> from ch_5_ex_1 import mean
    >>> mean([2, 4, 4, 4, 5, 5, 7, 9])
    5.0
    """
    return sum(values) / len(values)

執行 Docstring 測試

執行 doctest 的方式有多種,最簡單的方法是使用以下命令:

python3 -m doctest ch_5_ex_1.py

若需要更詳細的輸出,可以加上 -v 引數:

python3 -m doctest -v ch_5_ex_1.py

建立自測模組與獨立測試指令碼

除了直接執行 doctest,還可以將測試指令碼嵌入模組中,或是建立獨立的測試指令碼。

自測模組

利用 __name__ == "__main__" 的技巧,可以在模組中加入測試指令碼:

if __name__ == "__main__":
    import doctest
    doctest.testmod()

獨立測試指令碼

也可以建立獨立的測試指令碼,如 test.py

import doctest
import ch_5_ex_1
doctest.testmod(ch_5_ex_1)

執行該指令碼將輸出測試結果,例如:

TestResults(failed=0, attempted=2)

更複雜的測試場景

在某些情況下,需要特別注意 doctest 的輸出結果。例如,當處理字典(dict)或集合(set)時,由於元素的順序不固定,需要使用 sorted() 函式來確保順序的一致性。

使用 ELLIPSIS 忽略部分輸出

對於某些內部函式(如 id()repr()),其輸出結果可能會因執行環境而異。這時可以使用 # doctest: +ELLIPSIS 指令來忽略特定的輸出部分。

"""第5章,範例1
一些簡單的統計函式。
>>> from ch_5_ex_1 import mean, median
>>> data = [2, 4, 4, 4, 5, 5, 7, 9]
>>> data # doctest: +ELLIPSIS
[2, 4..., 9]
>>> mean(data)
5.0
>>> median(data)
4.5
"""

在類別定義中加入 Docstring 測試

除了模組與函式,類別定義中也可以加入 Docstring 測試。類別本身及其方法都可以包含 Docstring,從而提供更全面的測試與檔案說明。

class AnnualStats:
    """收集(年份,測量值)資料進行統計分析。
    
    >>> from ch_5_ex_4 import AnnualStats
    >>> test = AnnualStats([(2000, 2), (2001, 4), (2002, 4), (2003, 4), (2004, 5), (2005, 5), (2006, 7), (2007, 9)])
    >>> test.min_year()
    2000
    >>> test.mean()
    5.0
    >>> test.median()
    4.5
    """

詳細內容解說:

此段程式碼定義了一個名為 AnnualStats 的類別,用於收集並分析年度統計資料。透過在 Docstring 中加入測試範例,不僅提供了使用範例,也確保了類別方法的正確性。