Pythonで統計値を完全マスター【平均・中央値・最頻値・分散・標準偏差】

 

Pythonのstatisticsモジュールを使えば、基本的な統計値を簡単かつ正確に計算できます。この記事では、平均、中央値、最頻値、分散、標準偏差の計算方法を、実践的なサンプルコードと共に詳しく解説します。

statisticsモジュールの基本

基本的な統計関数

import statistics

# サンプルデータ
data = [10, 20, 20, 10, 10, 30, 20, 20, 20, 30, 30, 30]

# 基本統計値の計算
mean_val = statistics.mean(data)           # 平均
median_val = statistics.median(data)       # 中央値
mode_val = statistics.mode(data)           # 最頻値
variance_val = statistics.variance(data)   # 分散
stdev_val = statistics.stdev(data)         # 標準偏差

print(f"データ: {data}")
print(f"平均:     {mean_val:.2f}")
print(f"中央値:   {median_val}")
print(f"最頻値:   {mode_val}")
print(f"分散:     {variance_val:.2f}")
print(f"標準偏差: {stdev_val:.2f}")

平均値の計算

算術平均(mean)

import statistics

def detailed_mean_calculation(data):
    """平均値の詳細計算"""
    n = len(data)
    total = sum(data)
    mean_manual = total / n
    mean_builtin = statistics.mean(data)
    
    print(f"データ: {data}")
    print(f"要素数: {n}")
    print(f"合計: {total}")
    print(f"手動計算: {total}/{n} = {mean_manual:.3f}")
    print(f"statistics.mean(): {mean_builtin:.3f}")
    print(f"差: {abs(mean_manual - mean_builtin):.10f}")

# テストデータ
test_data = [85, 90, 78, 92, 88, 76, 95, 89]
detailed_mean_calculation(test_data)

加重平均

import statistics

def weighted_mean(values, weights):
    """加重平均の計算"""
    if len(values) != len(weights):
        raise ValueError("値と重みの数が一致しません")
    
    weighted_sum = sum(v * w for v, w in zip(values, weights))
    total_weight = sum(weights)
    
    return weighted_sum / total_weight

# 例:科目別成績の加重平均(単位数で重み付け)
subjects = {
    '数学': {'score': 85, 'credits': 3},
    '物理': {'score': 90, 'credits': 2},
    '化学': {'score': 78, 'credits': 2},
    '英語': {'score': 92, 'credits': 1}
}

scores = [subject['score'] for subject in subjects.values()]
credits = [subject['credits'] for subject in subjects.values()]

simple_avg = statistics.mean(scores)
weighted_avg = weighted_mean(scores, credits)

print("科目別成績:")
for subject, data in subjects.items():
    print(f"  {subject}: {data['score']}点 ({data['credits']}単位)")

print(f"\n単純平均: {simple_avg:.2f}点")
print(f"加重平均: {weighted_avg:.2f}点")

幾何平均と調和平均

import statistics

def geometric_mean(data):
    """幾何平均の計算(正の値のみ)"""
    if any(x <= 0 for x in data):
        raise ValueError("幾何平均は正の値のみで計算できます")
    
    product = 1
    for x in data:
        product *= x
    
    return product ** (1 / len(data))

# 成長率の平均を計算する例
growth_rates = [1.05, 1.03, 1.08, 1.02, 1.06]  # 各年の成長率

# Python 3.8以降では statistics.geometric_mean() が利用可能
if hasattr(statistics, 'geometric_mean'):
    geo_mean_builtin = statistics.geometric_mean(growth_rates)
else:
    geo_mean_builtin = "利用不可"

geo_mean_manual = geometric_mean(growth_rates)
harmonic_mean_val = statistics.harmonic_mean(growth_rates)

print(f"成長率データ: {growth_rates}")
print(f"算術平均: {statistics.mean(growth_rates):.4f}")
print(f"幾何平均: {geo_mean_manual:.4f}")
print(f"調和平均: {harmonic_mean_val:.4f}")

# 年平均成長率
annual_growth_rate = (geo_mean_manual - 1) * 100
print(f"年平均成長率: {annual_growth_rate:.2f}%")

中央値の計算

中央値の基本

import statistics

def detailed_median_calculation(data):
    """中央値の詳細計算"""
    sorted_data = sorted(data)
    n = len(sorted_data)
    
    print(f"元データ: {data}")
    print(f"ソート後: {sorted_data}")
    print(f"要素数: {n}")
    
    if n % 2 == 1:
        # 奇数個の場合
        median_index = n // 2
        median_manual = sorted_data[median_index]
        print(f"中央値位置: {median_index + 1}番目")
        print(f"中央値: {median_manual}")
    else:
        # 偶数個の場合
        mid1, mid2 = n // 2 - 1, n // 2
        median_manual = (sorted_data[mid1] + sorted_data[mid2]) / 2
        print(f"中央値位置: {mid1 + 1}番目と{mid2 + 1}番目の平均")
        print(f"中央値: ({sorted_data[mid1]} + {sorted_data[mid2]}) / 2 = {median_manual}")
    
    median_builtin = statistics.median(data)
    print(f"statistics.median(): {median_builtin}")

# 奇数個のデータ
odd_data = [23, 45, 56, 78, 32]
print("=== 奇数個のデータ ===")
detailed_median_calculation(odd_data)

print("\n=== 偶数個のデータ ===")
# 偶数個のデータ
even_data = [23, 45, 56, 78, 32, 67]
detailed_median_calculation(even_data)

median_low と median_high

import statistics

# 偶数個のデータで下位・上位中央値を計算
data = [1, 3, 5, 7]

median_low = statistics.median_low(data)    # 下位中央値
median_high = statistics.median_high(data)  # 上位中央値
median_normal = statistics.median(data)     # 通常の中央値

print(f"データ: {data}")
print(f"下位中央値: {median_low}")
print(f"上位中央値: {median_high}")
print(f"通常中央値: {median_normal}")

最頻値(Mode)の計算

単一最頻値と複数最頻値

import statistics
from collections import Counter

def analyze_mode(data):
    """最頻値の詳細分析"""
    counter = Counter(data)
    most_common = counter.most_common()
    
    print(f"データ: {data}")
    print(f"度数分布: {dict(counter)}")
    
    # 最高頻度
    max_count = most_common[0][1]
    modes = [value for value, count in most_common if count == max_count]
    
    print(f"最高頻度: {max_count}")
    print(f"最頻値: {modes}")
    
    try:
        mode_builtin = statistics.mode(data)
        print(f"statistics.mode(): {mode_builtin}")
    except statistics.StatisticsError as e:
        print(f"statistics.mode()エラー: {e}")
    
    # multimode(Python 3.8+)
    if hasattr(statistics, 'multimode'):
        multimode_result = statistics.multimode(data)
        print(f"statistics.multimode(): {multimode_result}")

# 単一最頻値
single_mode_data = [1, 2, 2, 3, 4, 2, 5]
print("=== 単一最頻値 ===")
analyze_mode(single_mode_data)

print("\n=== 複数最頻値 ===")
# 複数最頻値
multi_mode_data = [1, 1, 2, 2, 3, 4]
analyze_mode(multi_mode_data)

print("\n=== 最頻値なし(全て同じ頻度)===")
# 最頻値なし
no_mode_data = [1, 2, 3, 4, 5]
analyze_mode(no_mode_data)

分散と標準偏差

母分散と標本分散

import statistics
import math

def detailed_variance_calculation(data):
    """分散の詳細計算"""
    n = len(data)
    mean_val = statistics.mean(data)
    
    print(f"データ: {data}")
    print(f"平均: {mean_val:.3f}")
    print(f"要素数: {n}")
    
    # 偏差の計算
    deviations = [x - mean_val for x in data]
    squared_deviations = [d ** 2 for d in deviations]
    
    print(f"偏差: {[f'{d:.3f}' for d in deviations]}")
    print(f"偏差二乗: {[f'{d:.3f}' for d in squared_deviations]}")
    
    sum_squared_deviations = sum(squared_deviations)
    print(f"偏差二乗和: {sum_squared_deviations:.3f}")
    
    # 母分散(全データが母集団)
    population_var_manual = sum_squared_deviations / n
    population_var_builtin = statistics.pvariance(data)
    
    # 標本分散(サンプルデータ)
    sample_var_manual = sum_squared_deviations / (n - 1)
    sample_var_builtin = statistics.variance(data)
    
    print(f"\n母分散:")
    print(f"  手動計算: {sum_squared_deviations:.3f}/{n} = {population_var_manual:.3f}")
    print(f"  statistics.pvariance(): {population_var_builtin:.3f}")
    
    print(f"\n標本分散:")
    print(f"  手動計算: {sum_squared_deviations:.3f}/{n-1} = {sample_var_manual:.3f}")
    print(f"  statistics.variance(): {sample_var_builtin:.3f}")

# テストデータ
test_scores = [85, 87, 90, 92, 88, 86, 91, 89]
detailed_variance_calculation(test_scores)

標準偏差の計算

import statistics
import math

def detailed_stdev_calculation(data):
    """標準偏差の詳細計算"""
    
    # 分散を計算
    population_var = statistics.pvariance(data)
    sample_var = statistics.variance(data)
    
    # 標準偏差(分散の平方根)
    population_stdev_manual = math.sqrt(population_var)
    sample_stdev_manual = math.sqrt(sample_var)
    
    population_stdev_builtin = statistics.pstdev(data)
    sample_stdev_builtin = statistics.stdev(data)
    
    print(f"データ: {data}")
    print(f"平均: {statistics.mean(data):.3f}")
    
    print(f"\n母標準偏差:")
    print(f"  √{population_var:.3f} = {population_stdev_manual:.3f}")
    print(f"  statistics.pstdev(): {population_stdev_builtin:.3f}")
    
    print(f"\n標本標準偏差:")
    print(f"  √{sample_var:.3f} = {sample_stdev_manual:.3f}")
    print(f"  statistics.stdev(): {sample_stdev_builtin:.3f}")

# 実例:テストの点数
exam_scores = [72, 85, 91, 68, 87, 78, 94, 82, 76, 89]
detailed_stdev_calculation(exam_scores)

実践的な応用例

データの正規化(標準化)

import statistics

def standardize_data(data):
    """データの標準化(z-score)"""
    mean_val = statistics.mean(data)
    stdev_val = statistics.stdev(data)
    
    standardized = [(x - mean_val) / stdev_val for x in data]
    
    print(f"元データ: {data}")
    print(f"平均: {mean_val:.3f}, 標準偏差: {stdev_val:.3f}")
    print(f"標準化後: {[f'{z:.3f}' for z in standardized]}")
    
    # 標準化データの検証
    std_mean = statistics.mean(standardized)
    std_stdev = statistics.stdev(standardized)
    print(f"標準化後の平均: {std_mean:.6f} (≈0)")
    print(f"標準化後の標準偏差: {std_stdev:.6f} (≈1)")
    
    return standardized

# テスト点数の標準化
scores = [65, 70, 75, 80, 85, 90, 95]
z_scores = standardize_data(scores)

外れ値の検出

import statistics

def detect_outliers(data, threshold=2):
    """標準偏差を使った外れ値検出"""
    mean_val = statistics.mean(data)
    stdev_val = statistics.stdev(data)
    
    outliers = []
    normal_values = []
    
    for x in data:
        z_score = abs(x - mean_val) / stdev_val
        if z_score > threshold:
            outliers.append((x, z_score))
        else:
            normal_values.append(x)
    
    print(f"データ: {data}")
    print(f"平均: {mean_val:.2f}, 標準偏差: {stdev_val:.2f}")
    print(f"閾値: {threshold}σ")
    print(f"正常値: {normal_values}")
    print(f"外れ値: {[(val, f'{z:.2f}σ') for val, z in outliers]}")
    
    return outliers, normal_values

# 外れ値を含むデータ
data_with_outliers = [10, 12, 11, 13, 12, 11, 10, 50, 9, 11, 12]
outliers, normal = detect_outliers(data_with_outliers)

グレード分布の分析

import statistics
from collections import Counter

def grade_distribution_analysis(scores):
    """成績分布の統計分析"""
    
    # 基本統計値
    mean_score = statistics.mean(scores)
    median_score = statistics.median(scores)
    mode_scores = statistics.multimode(scores) if hasattr(statistics, 'multimode') else [statistics.mode(scores)]
    stdev_score = statistics.stdev(scores)
    
    print("=== 成績統計 ===")
    print(f"平均点: {mean_score:.1f}点")
    print(f"中央値: {median_score}点")
    print(f"最頻値: {mode_scores}点")
    print(f"標準偏差: {stdev_score:.1f}点")
    
    # グレード分類
    def score_to_grade(score):
        if score >= 90: return 'A'
        elif score >= 80: return 'B'
        elif score >= 70: return 'C'
        elif score >= 60: return 'D'
        else: return 'F'
    
    grades = [score_to_grade(score) for score in scores]
    grade_counter = Counter(grades)
    
    print(f"\n=== グレード分布 ===")
    for grade in ['A', 'B', 'C', 'D', 'F']:
        count = grade_counter[grade]
        percentage = count / len(scores) * 100
        print(f"{grade}グレード: {count}人 ({percentage:.1f}%)")
    
    # 偏差値の計算
    deviation_values = [50 + (score - mean_score) / stdev_score * 10 for score in scores]
    
    print(f"\n=== 偏差値範囲 ===")
    print(f"最高偏差値: {max(deviation_values):.1f}")
    print(f"最低偏差値: {min(deviation_values):.1f}")
    
    return {
        'mean': mean_score,
        'median': median_score,
        'mode': mode_scores,
        'stdev': stdev_score,
        'grades': grade_counter,
        'deviation_values': deviation_values
    }

# クラスの成績データ
class_scores = [95, 87, 92, 78, 85, 90, 76, 88, 93, 82, 79, 86, 91, 84, 77, 89, 94, 81, 83, 80]
analysis = grade_distribution_analysis(class_scores)

時系列データの移動統計

import statistics

def moving_statistics(data, window_size=3):
    """移動統計の計算"""
    
    moving_means = []
    moving_stdevs = []
    
    for i in range(len(data) - window_size + 1):
        window_data = data[i:i + window_size]
        moving_means.append(statistics.mean(window_data))
        moving_stdevs.append(statistics.stdev(window_data) if len(window_data) > 1 else 0)
    
    print(f"元データ: {data}")
    print(f"ウィンドウサイズ: {window_size}")
    print()
    
    print("期間 | データ範囲 | 移動平均 | 移動標準偏差")
    print("-" * 50)
    
    for i, (mean_val, stdev_val) in enumerate(zip(moving_means, moving_stdevs)):
        window_data = data[i:i + window_size]
        print(f"{i+1:4} | {window_data} | {mean_val:8.2f} | {stdev_val:11.2f}")
    
    return moving_means, moving_stdevs

# 売上データの例
monthly_sales = [120, 135, 142, 128, 156, 149, 163, 145, 138, 152, 167, 174]
moving_means, moving_stdevs = moving_statistics(monthly_sales, window_size=3)

エラーハンドリングと注意点

空データと異常値の処理

import statistics

def safe_statistics(data, name="データ"):
    """安全な統計計算"""
    print(f"=== {name} ===")
    
    if not data:
        print("エラー: データが空です")
        return None
    
    try:
        # 基本統計値の計算
        results = {}
        results['count'] = len(data)
        results['mean'] = statistics.mean(data)
        results['median'] = statistics.median(data)
        
        try:
            results['mode'] = statistics.mode(data)
        except statistics.StatisticsError:
            results['mode'] = "複数または存在しない"
        
        if len(data) > 1:
            results['variance'] = statistics.variance(data)
            results['stdev'] = statistics.stdev(data)
        else:
            results['variance'] = 0
            results['stdev'] = 0
        
        # 結果の表示
        for key, value in results.items():
            if isinstance(value, float):
                print(f"{key}: {value:.3f}")
            else:
                print(f"{key}: {value}")
        
        return results
        
    except Exception as e:
        print(f"計算エラー: {e}")
        return None

# 各種テストケース
test_cases = [
    ([], "空のデータ"),
    ([42], "単一データ"),
    ([1, 2, 3], "正常データ"),
    ([1, 1, 1], "同一値データ"),
    ([1, 2, 2, 3, 3], "複数最頻値データ")
]

for data, description in test_cases:
    result = safe_statistics(data, description)
    print()

数値精度の問題

import statistics
import decimal

def precision_comparison():
    """浮動小数点数vs高精度数での統計計算"""
    
    # 浮動小数点数
    float_data = [0.1, 0.2, 0.3, 0.4, 0.5]
    
    # Decimal型
    decimal_data = [decimal.Decimal('0.1'), decimal.Decimal('0.2'), 
                   decimal.Decimal('0.3'), decimal.Decimal('0.4'), 
                   decimal.Decimal('0.5')]
    
    print("=== 精度比較 ===")
    print(f"float平均: {statistics.mean(float_data)}")
    print(f"Decimal平均: {statistics.mean(decimal_data)}")
    
    # より極端な例
    extreme_float = [1e-15, 1e-15, 1e-15, 1.0]
    print(f"\n極端な例(float):")
    print(f"データ: {extreme_float}")
    print(f"合計: {sum(extreme_float)}")
    print(f"平均: {statistics.mean(extreme_float)}")

precision_comparison()

パフォーマンス最適化

大量データの効率的処理

import statistics
import time
import random

def benchmark_statistics():
    """統計計算のベンチマーク"""
    
    # 大量データの生成
    large_data = [random.gauss(100, 15) for _ in range(100000)]
    
    functions = [
        ('平均', statistics.mean),
        ('中央値', statistics.median),
        ('分散', statistics.variance),
        ('標準偏差', statistics.stdev)
    ]
    
    print(f"データサイズ: {len(large_data):,}要素")
    print("=" * 40)
    
    for name, func in functions:
        start_time = time.time()
        result = func(large_data)
        end_time = time.time()
        
        print(f"{name:8}: {result:8.3f} ({end_time - start_time:.4f}秒)")

benchmark_statistics()

ストリーミング統計

class StreamingStatistics:
    """ストリーミング統計計算クラス"""
    
    def __init__(self):
        self.count = 0
        self.sum = 0
        self.sum_squares = 0
        self.min_val = float('inf')
        self.max_val = float('-inf')
    
    def add_value(self, value):
        """新しい値を追加"""
        self.count += 1
        self.sum += value
        self.sum_squares += value ** 2
        self.min_val = min(self.min_val, value)
        self.max_val = max(self.max_val, value)
    
    def mean(self):
        """平均の計算"""
        return self.sum / self.count if self.count > 0 else 0
    
    def variance(self):
        """分散の計算"""
        if self.count <= 1:
            return 0
        mean_val = self.mean()
        return (self.sum_squares - self.count * mean_val ** 2) / (self.count - 1)
    
    def stdev(self):
        """標準偏差の計算"""
        return self.variance() ** 0.5
    
    def summary(self):
        """統計サマリーの表示"""
        print(f"要素数: {self.count}")
        print(f"最小値: {self.min_val}")
        print(f"最大値: {self.max_val}")
        print(f"平均: {self.mean():.3f}")
        print(f"分散: {self.variance():.3f}")
        print(f"標準偏差: {self.stdev():.3f}")

# ストリーミング統計の使用例
stream_stats = StreamingStatistics()

# データを順次追加
data_stream = [23, 45, 67, 12, 89, 34, 56, 78, 90, 21]

print("=== ストリーミング統計 ===")
for i, value in enumerate(data_stream, 1):
    stream_stats.add_value(value)
    print(f"\n{i}個目のデータ追加後:")
    if i <= 3:  # 最初の3つのみ詳細表示
        stream_stats.summary()
    else:
        print(f"平均: {stream_stats.mean():.3f}, 標準偏差: {stream_stats.stdev():.3f}")

print(f"\n=== 最終結果 ===")
stream_stats.summary()

# 検証:一括計算との比較
print(f"\n=== 検証 ===")
print(f"一括計算平均: {statistics.mean(data_stream):.3f}")
print(f"一括計算標準偏差: {statistics.stdev(data_stream):.3f}")

まとめ

Pythonの統計計算機能は以下の特徴を持ちます:

  • statisticsモジュールによる正確で簡単な計算
  • 母集団と標本の両方に対応
  • 加重平均、幾何平均、調和平均のサポート
  • 複数最頻値の処理
  • 高精度計算とストリーミング処理への対応

これらの統計関数は、データ分析、品質管理、学術研究、ビジネス分析など、様々な分野で基礎となる重要なツールです。データの性質を理解し、適切な統計値を選択して、意味のある洞察を得ることが重要です。

■プロンプトだけでオリジナルアプリを開発・公開してみた!!

■AI時代の第一歩!「AI駆動開発コース」はじめました!

テックジム東京本校で先行開始。

■テックジム東京本校

「武田塾」のプログラミング版といえば「テックジム」。
講義動画なし、教科書なし。「進捗管理とコーチング」で効率学習。
より早く、より安く、しかも対面型のプログラミングスクールです。

<短期講習>5日で5万円の「Pythonミニキャンプ」開催中。

<月1開催>放送作家による映像ディレクター養成講座

<オンライン無料>ゼロから始めるPython爆速講座