せっかくだから俺は衆院選の比例代表の議席数を計算するぜ

  • 3
    Like
  • 0
    Comment
More than 1 year has passed since last update.

ネット選挙が解禁されて早一年、どこぞの政党が勝つと日本が終わるというノストラダムスがネット上に大量発生して、キバヤシさんの突っ込みが追い付かない今日この頃いかがおすごしでしょうか?

さて、今回は比例代表選挙におけるドント式の計算をやってみようと思います。

結果

http://needtec.sakura.ne.jp/analyze_election/page/dondt/shuin_47

投票数を入力して、「計算」ボタンを押すと、投票数に応じた議席数が取得できます。
政党支持率をそのまま突っ込むと、自民党が140議席取得して、維新の会すら全滅するという結果になります。

なお、2013年の参院選挙の時は、直近の東京都議会選挙の党別の得票率を入力すると、試算結果と実際の結果の差が1議席といい感じになりました。

ソースコード

https://github.com/mima3/analyze_election

実際のドント式の計算ファイル

dondt_util.py
# coding=utf-8
import os
import re
from collections import defaultdict
import math
import json
import copy
import operator

class political_party_info:
    """
    政党の情報を格納するクラス
    name : 政党名
    votes:得票数
    max    :立候補者数(いくら得票してもこれをこえた議席は取得できない)
    seats:取得議席数
    """
    def __init__(self, name, votes, max):
        self.name = name
        self.votes = votes
        self.max = max
        self.seats = 0

def select_political_party(votes):
    """
    政党名をキー、値をpolitical_party_infoとしたディクショナリ中でもっとも得票している党名を取得する
    """
    max = -1
    ret = None
    for k, v in votes.items():
        # 同数の場合、本来くじで決めるが今回は登録順とする
        if max < v.votes:
            ret = k
            max = v.votes
    return ret

def dondt(votes_data, max):
    """
    ドント方式による
    votes_data: 政党名をキー、値をpolitical_party_infoとしたディクショナリ
    max:総議席数
    votes_data[x].seatsに議席数が格納されます。
    """
    tmp_votes = copy.deepcopy(votes_data)
    for i in range(1, max+1):
        s = select_political_party(tmp_votes)
        if s is None:
            return None
        votes_data[s].seats += 1
        tmp_votes[s].votes = math.floor(votes_data[s].votes / (votes_data[s].seats + 1))
        if tmp_votes[s].max == votes_data[s].seats:
            #立候補した数超えたので以降この政党への投票は無効
            tmp_votes[s].votes = 0
    return votes_data

商を求める際の端数は切り捨てています。
もし同一の得票数になった場合は、本来はくじで決めるが、ここでは文字コード順にしています(ex みどりの党は自民党より優先されている)

ドント式の計算方法については下記を参照。
http://www.pref.tochigi.lg.jp/senkyo/sangisenkyo/qanda/qanda-9.html

比例区の情報

比例区のブロックの議席数や、各党の候補者数は本来であれば、総務省のページから作成するのが筋です。しかしながら、総務省はテキスト処理できないPDFでのみでしか公開しておりません。

比例ブロックの議席数はともかく、候補者のデータ入力はしんどいので、朝日新聞のホームページをWebスクレイピングして、CSVファイルを作成するようにしました。
以下のURLを対象としています。
http://www.asahi.com/senkyo/sousenkyo47/kouho/B01.html

http://www.asahi.com/senkyo/sousenkyo47/kouho/B11.html

2012年と同じようなHTMLの構成だったので、たぶん次の解散でもつかえるでしょう。

script/analyze_asahi_hirei.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
import urllib2
import lxml.html
import re
import os.path
import urlparse

def print_area(url):
    r = urllib2.urlopen(url, timeout=30)
    html = r.read()
    dom = lxml.html.fromstring(html)
    parties = dom.xpath('//div[@class="snkH2Box"]/h2')
    tables = dom.xpath('//table[@class="snkTbl01"]')
    block = dom.xpath('//div[@class="BreadCrumb"]/h1')[0].text_content().encode('utf-8')
    block = block[block.find(':')+len(':'):]
    for i in range(0, len(parties)):
        h2 = parties[i].text_content().encode('utf-8')
        partyName = h2.split('\n')[0]
        members = tables[i].xpath('tbody/tr')
        for m in members:
            name = m.xpath('td[@class="namae"]')[0].text_content().encode('utf-8')
            lstNum = m.xpath('td[@class="lstNum"]')[0].text_content().encode('utf-8')
            age = m.xpath('td[@class="age"]')[0].text_content().encode('utf-8')
            status = m.xpath('td[@class="status"]')[0].text_content().encode('utf-8')
            net = m.xpath('td[@class="net"]/ul')[0]
            twitterEl = net.xpath('li[@id="twitter"]/a')
            facebookEl = net.xpath('li[@id="facebook"]/a')
            hpEl = net.xpath('li[@id="HomePage1"]/a')
            areaEl = m.xpath('td[@class="w"]/a')
            area = ''
            twitter = ''
            facebook = ''
            hp = ''
            if twitterEl:
                twitter = twitterEl[0].attrib['href'].encode('utf-8')
            if facebookEl:
                facebook = facebookEl[0].attrib['href'].encode('utf-8')
            if hpEl:
                hp = hpEl[0].attrib['href'].encode('utf-8')
            if areaEl:
                area =areaEl[0].text_content().encode('utf-8')
            print ('%s,%s,%s,%s,%s,%s,%s,%s,%s,%s' % (block, partyName,lstNum, name, age, status, area, twitter, facebook, hp))

def main(argvs, argc):
    """
    このスクリプトでは、朝日新聞の情報から小選挙区の立候補者を取得します
    """
    for i in range(1, 12):
        url = ('http://www.asahi.com/senkyo/sousenkyo47/kouho/B%s.html' % str(i).zfill(2))
        print_area(url)

if __name__ == '__main__':
    argvs = sys.argv
    argc = len(argvs)
    sys.exit(main(argvs, argc))

朝日新聞からデータを取得したのち、全角を半角にしたり、「県」を付与した比例区の立候補者のデータ
https://github.com/mima3/analyze_election/blob/master/script/candidate_shuin_47_hirei.csv

朝日新聞のデータをみつつ作成した比例ブロックの情報CSV
https://github.com/mima3/analyze_election/blob/master/script/block_shuin_47_hirei.csv