Pythonで選挙データを分析してみよう!⑥〜ライバル候補との直接対決編〜
このシリーズでは、2025/07/21に行われた参院選挙結果を分析するためのプログラムを、一つずつ紐解いて解説していきます。
選挙データ分析シリーズ第6弾、今回はさらに一歩踏み込んで「比較分析」の世界に足を踏み入れます。
作成したコードは Github に公開しているので、興味があれば、見てみてください。
※コードは、予告なく、改変/削除されることがあります。
前回は一人の候補者(と政党)を多角的に分析しましたが、選挙は相対的な競争です。そこで今回は、特定の候補者同士を直接比較し、どの地域で、どのように競り合ったのかを可視化する detailed_comparison_analysis_csv.py スクリプトを解説していきます。
分析の目的:ライバルとの差を知る
今回の分析対象は、前回に引き続き「武藤 かず子」候補(チームみらい)。そして、比較対象として「古川 俊治」候補(自由民主党)と「江原 くみ子」候補(国民民主党)の二人を立てます。
このスクリプトのゴールは、
- 3者の総合的な得票力はどれくらい違うのか?
- 市区町村レベルで見たとき、誰が優勢だったのか?
- 「武藤」候補が善戦した地域、逆に差をつけられた地域はどこか?
といった、より戦略的な問いに答えるためのインサイトを得ることです。
Step 1: 比較対象のデータを抽出する
まずは、分析したい3名の候補者のデータを、整形済みのCSVからそれぞれ抜き出します。
def extract_candidate_electoral_data(candidates_df, voting_info_df, candidate_name):
"""
CSVデータから指定候補者の情報を抽出(総投票数付き)
"""
# 候補者名でデータをフィルタリング
candidate_data = candidates_df[candidates_df['候補者名'] == candidate_name].copy()
if candidate_data.empty:
return []
# 投票情報データとマージして、市区町村ごとの「投票者数計」を取得
merged_data = candidate_data.merge(
voting_info_df[['市区町村名', '投票者数計']],
on='市区町村名',
how='left'
)
# 投票者数に対する得票率を計算
merged_data['得票率'] = (merged_data['得票数'] / merged_data['投票者数計']) * 100
# ... データを整形して返す ...
return electoral_data
# --- main関数内での呼び出し ---
team_mirai_data = extract_candidate_electoral_data(candidates_df, voting_info_df, '武藤 かず子')
furukawa_data = extract_candidate_electoral_data(candidates_df, voting_info_df, '古川 俊治')
ehara_data = extract_candidate_electoral_data(candidates_df, voting_info_df, '江原 くみ子')前回の分析と同様に、merge を使って市区町村ごとの総投票数を紐づけ、投票者数に対する得票率を算出しています。ここでの得票率は、後の比較分析における最も重要な指標となります。
Step 2: 地域ごとの勝敗を判定する
3者のデータが揃ったら、いよいよ直接対決です。レポートを生成する関数の中で、非常に興味深い処理が行われています。
def generate_detailed_comparison_report(team_mirai_data, furukawa_data, ehara_data):
# ...
tm_df = pd.DataFrame(team_mirai_data)
fk_df = pd.DataFrame(furukawa_data)
eh_df = pd.DataFrame(ehara_data)
combined_data = []
# 武藤候補のデータ(市区町村)を基準にループ
for _, tm_row in tm_df.iterrows():
municipality = tm_row['municipality']
tm_votes = tm_row['votes']
# 同じ市区町村の、他の候補者の得票数を取得
fk_votes = fk_df[fk_df['municipality'] == municipality]['votes'].iloc[0]
eh_votes = eh_df[eh_df['municipality'] == municipality]['votes'].iloc[0]
# 3者の得票数をリストに格納
votes_list = [(tm_votes, 'チームみらい'), (fk_votes, '自民党'), (eh_votes, '国民民主')]
# 得票数が多い順に並び替え
votes_list.sort(key=lambda x: x[0], reverse=True)
# 武藤候補が何位だったかを計算
tm_rank = next((i+1 for i, (votes, party) in enumerate(votes_list) if party == 'チームみらい'), 4)
combined_data.append({
'municipality': municipality,
'tm_votes': tm_votes,
'tm_rank': tm_rank,
# ... 他のデータも格納 ...
})
combined_df = pd.DataFrame(combined_data)
# ...このコードのポイントは、市区町村ごとに3者の得票数をリストにまとめ、sortで順位付けしている点です。これにより、「この町では武藤さんが1位だった」「あそこの市では3位だった」という、地域ごとの詳細な勝敗を明らかにできます。
Step 3: 激戦区を見つけ出す
選挙戦において、圧勝した地域や大敗した地域だけでなく、「あと一歩で勝てたかもしれない」あるいは「僅差で勝った」という激戦区を特定することは非常に重要です。このスクリプトでは、そのための分析も行っています。
# ... combined_data を作成するループの中 ...
# 武藤候補の得票率と、他の2候補の得票率との差をそれぞれ計算し、
# より差が小さい方を「最小差」として記録
'min_diff': min(abs(tm_rate - fk_rate), abs(tm_rate - eh_rate))
# ...
# ... レポート生成の最後 ...
# 最小差でソートして、激戦だった地域をリストアップ
sorted_by_diff = combined_df.sort_values('min_diff', ascending=True)
report += "他候補との得票率差が最小だった地域(全地域降順):\n"
for i, (_, row) in enumerate(sorted_by_diff.iterrows(), 1):
report += f" {i:2d}. {row['municipality']}: 最小差{row['min_diff']:.2f}%\n"各市区町村で、武藤候補と他の2候補の得票率の差を計算し、その差が最も小さい min_diff を算出しています。そして、レポートの最後でこの min_diff が小さい順に並び替えることで、「どの地域が最も競り合っていたか」が一目瞭然になります。
まとめ
今回は、複数の候補者のデータを突き合わせることで、より戦略的な示唆を得るための比較分析の手法を見てきました。
- 分析の基本は、同じ土俵(今回は市区町村ごと)で数値を比較すること。
- 単純な得票数だけでなく、得票率や順位といった指標を用いることで、より本質的な強さ・弱さが見えてくる。
- 候補者間の「差」に着目することで、選挙戦の鍵となった「激戦区」を特定できる。
個別の分析から一歩進んだ比較分析を行うことで、データは単なる結果報告ではなく、次の戦略を立てるための羅針盤となり得ます。





