Pythonで選挙データを分析してみよう!⑦〜サポーターと選挙活動の効果測定編〜

このシリーズでは、2025/07/21に行われた参院選挙結果を分析するためのプログラムを、一つずつ紐解いて解説していきます。
選挙データ分析シリーズ、いよいよ最終回です。 これまで、複雑な開票結果を整形し、候補者のパフォーマンスを多角的に見てきました。今回は、その分析をさらに一歩進め、「サポーターの存在や街頭演説など得票率に関連するのか?」という問いに、データを使って答えることに挑戦します。

チームみらいには、サポーターが存在しています。
そのサポーターがどのくらい影響を及ぼしているのかを見たくて分析を行いました。

コードは、supporter_campaign_analysis.py。このスクリプトは、「チームみらい」のサポーター数街頭演説の回数といった選挙期間中の活動が、実際の得票にどれだけの影響を与えたのかを測定する、非常に高度な分析を行います。

分析の目的:「活動」と「結果」の因果関係を探る

選挙では、多くの人が様々な活動をします。しかし、「その活動に本当に意味はあったのか?」と問われると、感覚的にしか答えられないことも多いのではないでしょうか。

このスクリプトの目的は、

  • 市区町村ごとのサポーターの人数
  • 市区町村ごとの演説・街宣の実施回数

といった**「活動データ」と、これまでの分析で明らかになった「選挙結果データ」を突き合わせ、両者の間に統計的な関係があるのか、つまり「活動は票になったのか?」**を明らかにすることです。

Step 1: 異種データの結合 – 3つの世界を1つにする

この分析の出発点は、これまで扱ってきた「選挙結果」データに、新たに2つの外部データ「サポーターデータ」「演説・街宣データ」を合体させることです。

def main():
    # ... データ読み込み ...
    supporter_data = load_supporter_data()
    campaign_data = load_campaign_data()
    voting_results = load_voting_results()
    voting_info = load_voting_info()

    # 1. 投票結果データをベースにする
    merged_data = voting_results.copy()
    
    # 2. サポーターデータをマージ
    if supporter_data is not None:
        merged_data = merged_data.merge(
            supporter_data, 
            left_on='サポーター照合用', # 正規化した市区町村名
            right_on='市区町村', 
            how='left'
        )
        # サポーターがいない地域は 0 で埋める
        merged_data['サポーター数'] = merged_data['サポーター数'].fillna(0)

    # 3. 演説・街宣データをマージ
    if campaign_data is not None:
        merged_data = merged_data.merge(
            campaign_data, 
            left_on='街宣照合用', # 正規化した市区町村名
            right_on='市区町村', 
            how='left'
        )
        merged_data['演説街宣回数'] = merged_data['演説街宣回数'].fillna(0)

    # 4. 投票総数データをマージして、得票率を計算
    # ...

ここでの肝は、pd.merge を使って、異なる種類のデータを「市区町村名」をキーにして次々と結合していく点です。how='left' というオプションは、「投票結果データ」を基準として、サポーターや演説の情報があれば付け加える、という動きをします。情報がない地域(サポーターが0人、演説が0回)は fillna(0) でデータを補完し、分析可能な一枚岩のデータフレームを完成させます。

Step 2: 効果測定その① – 相関分析

データが一つにまとまったら、いよいよ効果測定です。その一つ目のアプローチが相関分析です。これは、2つの数値の関係性の強さを -1 から +1 の間の数値(相関係数)で表す統計手法です。

def calculate_correlations(merged_data):
    """
    相関関係を計算する関数
    """
    print("\n=== 相関分析結果 ===")
    
    # サポーター数と得票率の相関
    if 'サポーター数' in merged_data.columns and '得票率' in merged_data.columns:
        supporter_rate_corr = merged_data['サポーター数'].corr(merged_data['得票率'])
        print(f"サポーター数と得票率の相関係数: {supporter_rate_corr:.4f}")
    
    # 演説街宣回数と得票率の相関
    if '演説街宣回数' in merged_data.columns and '得票率' in merged_data.columns:
        campaign_rate_corr = merged_data['演説街宣回数'].corr(merged_data['得票率'])
        print(f"演説街宣回数と得票率の相関係数: {campaign_rate_corr:.4f}")

このスクリプトでは、pandasの .corr() メソッドを使って相関係数を一発で計算しています。例えば、「サポーター数と得票率の相関係数」がプラスで1に近いほど、「サポーターが多い地域ほど、得票率も高くなる傾向が強い」と言えます。

重要なのは、人口の多い地域で得票数が多くなるのは当たり前なので、得票「数」ではなく得票「率」との相関を見ている点です。これにより、地域の人口規模の影響を排した、より純粋な活動効果を測定しようとしています。

Step 3: 効果測定その② – グループ比較

相関係数は便利ですが、少し専門的で分かりにくいかもしれません。そこで、より直感的な二つ目のアプローチがグループ比較です。

def analyze_campaign_effect(merged_data):
    """
    演説・街宣の効果を分析する関数
    """
    print("\n=== 演説・街宣効果分析 ===")
    
    # 演説・街宣があった地域と、なかった地域にデータを分割
    campaign_yes = merged_data[merged_data['演説街宣回数'] > 0]
    campaign_no = merged_data[merged_data['演説街宣回数'] == 0]
    
    if len(campaign_yes) > 0 and len(campaign_no) > 0:
        # それぞれ
        yes_avg_rate = campaign_yes['得票率'].mean()
        no_avg_rate = campaign_no['得票率'].mean()
        
        print(f"演説・街宣あり - 平均得票率: {yes_avg_rate:.2f}%")
        print(f"演説・街宣なし - 平均得票率: {no_avg_rate:.2f}%")
        print(f"得票率差: {yes_avg_rate - no_avg_rate:+.2f}%")

やっていることは非常にシンプルです。「演説・街宣が1回以上あった地域」と「1回もなかった地域」の2つのグループを作り、それぞれのグループの平均得票率を比較しています。もし、演説があったグループの平均得票率の方が高ければ、それは演説活動に効果があった可能性を示唆します。サポーター数の分析でも同様に、サポーターが多い地域と少ない地域を比較しています。

まとめ

今回は、選挙結果という「結果」のデータと、サポーター数や演説回数といった「活動」のデータを組み合わせることで、選挙活動の効果を測定する手法を見てきました。

  • 異なる種類のデータも、共通のキー(市区町村名)があれば結合できる。
  • 相関分析は、2つの要素の関係性の強さを客観的な数値で示してくれる。
  • グループ比較は、「やった場合」と「やらなかった場合」の差を直感的に見せてくれる。

これまで4回にわたってデータ整形から分析まで見てきましたが、データ分析とは、単に数字を眺めることではなく、現実の世界で起きたことの「なぜ」を解き明かすための強力な武器であることが、感じていただけたのではないでしょうか。