ネット選挙が解禁されて早一年、どこぞの政党が勝つと日本が終わるというノストラダムスがネット上に大量発生して、キバヤシさんの突っ込みが追い付かない今日この頃いかがおすごしでしょうか?
さて、今回は比例代表選挙におけるドント式の計算をやってみようと思います。
##結果
http://needtec.sakura.ne.jp/analyze_election/page/dondt/shuin_47
投票数を入力して、「計算」ボタンを押すと、投票数に応じた議席数が取得できます。
政党支持率をそのまま突っ込むと、自民党が140議席取得して、維新の会すら全滅するという結果になります。
なお、2013年の参院選挙の時は、直近の東京都議会選挙の党別の得票率を入力すると、試算結果と実際の結果の差が1議席といい感じになりました。
##ソースコード
https://github.com/mima3/analyze_election
実際のドント式の計算ファイル
# 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の構成だったので、たぶん次の解散でもつかえるでしょう。
#!/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