<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
	<id>https://wikimirror.piraten.tools/wiki/index.php?action=history&amp;feed=atom&amp;title=Benutzer%3AEntropy%2FBewertungswahl%2FEUAV</id>
	<title>Benutzer:Entropy/Bewertungswahl/EUAV - Versionsgeschichte</title>
	<link rel="self" type="application/atom+xml" href="https://wikimirror.piraten.tools/wiki/index.php?action=history&amp;feed=atom&amp;title=Benutzer%3AEntropy%2FBewertungswahl%2FEUAV"/>
	<link rel="alternate" type="text/html" href="https://wikimirror.piraten.tools/wiki/index.php?title=Benutzer:Entropy/Bewertungswahl/EUAV&amp;action=history"/>
	<updated>2026-04-06T12:58:22Z</updated>
	<subtitle>Versionsgeschichte dieser Seite in Piratenwiki Mirror</subtitle>
	<generator>MediaWiki 1.35.14</generator>
	<entry>
		<id>https://wikimirror.piraten.tools/wiki/index.php?title=Benutzer:Entropy/Bewertungswahl/EUAV&amp;diff=55914126&amp;oldid=prev</id>
		<title>imported&gt;Entropy: bugfix for ties</title>
		<link rel="alternate" type="text/html" href="https://wikimirror.piraten.tools/wiki/index.php?title=Benutzer:Entropy/Bewertungswahl/EUAV&amp;diff=55914126&amp;oldid=prev"/>
		<updated>2014-01-03T23:26:38Z</updated>

		<summary type="html">&lt;p&gt;bugfix for ties&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Neue Seite&lt;/b&gt;&lt;/p&gt;&lt;div&gt;Dies ist der Quellcode zur Implementation der Wahlverfahren a) und b) der [[Bundesparteitag_2014.1/WahlordungAV|Wahlordnung zur Europawahl-Listenaufstellung 2014]].&lt;br /&gt;
&lt;br /&gt;
Als Eingabe-Datei wird das von Michael Ebner vorgebene Format für die Stimmen genutzt (&amp;quot;ja/enthaltung/nein&amp;quot; pro Zeile, wobei ja/nein jeweils aus zehn mit ; getrennten Rängen bestehen. Bein Rängen und Enthaltung sind die Kandidatennummern jeweils durch , getrennt).&lt;br /&gt;
&lt;br /&gt;
Das Programm erzeugt zusätzlich eine Datei &amp;quot;input.csv&amp;quot;, bei der die Stimmen in ein einfache CSV-Tabelle konvertiert sind. Damit kann jeder die Auswertung auch in einem anderen Programm nachvollziehen (z.B. Tabellenkalkulation). Die erste Zeile enthält die Kandidatennummern. Enthaltungen sind durch 0 ersetzt.&lt;br /&gt;
&lt;br /&gt;
Auswertung eines &amp;quot;Range voting&amp;quot; Wahlgangs von a)&lt;br /&gt;
 python range.py ballots.txt&lt;br /&gt;
Auswertung von b) &amp;quot;Reweighted range voting&amp;quot;:&lt;br /&gt;
 python range.py -p ballots.txt&lt;br /&gt;
Optionale Parameter jeweils&lt;br /&gt;
* Liste der Namen der Kandidaten (Kandidat 1=erste Zeile usw)&lt;br /&gt;
 -n names.txt&lt;br /&gt;
* begrenzte Liste: es werden nur die ersten N Plätze ermittelt&lt;br /&gt;
 -l N&lt;br /&gt;
* zu ignorierende Kandidaten (die ausgeschieden oder bereits gewählt sind&amp;quot;: Kandidatennummer (gezählt ab 1) durch Kommata getrennt (ohne Leerzeichen!)&lt;br /&gt;
 -i c1,c2,c3&lt;br /&gt;
* Zufallszahlen für das Losen bei Gleichstand (sehr unwahrscheinlich, zur Sicherheit 5 Stück ermitteln). Diese sollten unabhängig und vor der Wahl z.B. durch Münzwurf ermittelt und deren Liste von 0 oder 1 wird zeilenweise in der Datei gespeichert werden. Sollten keine ausreichenden Zufallszahlen vorhanden sein, fragt das Programm automatisch beim Losen nach einer Eingabe.&lt;br /&gt;
 -r random.txt&lt;br /&gt;
* Hilfe&lt;br /&gt;
 -h&lt;br /&gt;
&lt;br /&gt;
Und hier der Python (&amp;gt;=2.7) Source code (erfordert numpy). Das Wahlverfahren selbst findet sich in am Ende (hinter &amp;quot;Elect the winners&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
 #!/usr/bin/env python&lt;br /&gt;
 # -*- coding: iso-8859-15 -*-&lt;br /&gt;
 # version 1.1&lt;br /&gt;
 # (reweighted) range voting&lt;br /&gt;
 # copyleft 2014 by entropy@heterarchy.net&lt;br /&gt;
 # license: http://www.gnu.org/licenses/gpl.html&lt;br /&gt;
 # requires Python &amp;gt;=2.7, numpy&amp;gt;=1.6&lt;br /&gt;
 &lt;br /&gt;
 from __future__ import division, print_function&lt;br /&gt;
 import sys, os, argparse, itertools&lt;br /&gt;
 import numpy as np&lt;br /&gt;
 &lt;br /&gt;
 parser = argparse.ArgumentParser('range voting')&lt;br /&gt;
 parser.add_argument(&amp;quot;input&amp;quot;,help=&amp;quot;input file&amp;quot;)&lt;br /&gt;
 parser.add_argument(&amp;quot;-i&amp;quot;, &amp;quot;--ignore&amp;quot;, metavar='IGNORE', default='', help=&amp;quot;candidate numbers to ignore (comma-separated)&amp;quot;)&lt;br /&gt;
 parser.add_argument(&amp;quot;-l&amp;quot;, &amp;quot;--length&amp;quot;, metavar='LENGTH', type=int,default=0, help=&amp;quot;maximum list length&amp;quot;)&lt;br /&gt;
 parser.add_argument(&amp;quot;-n&amp;quot;, &amp;quot;--names&amp;quot;, metavar='NAMES',help='file with candidate names')&lt;br /&gt;
 parser.add_argument(&amp;quot;-p&amp;quot;, &amp;quot;--proportional&amp;quot;, action=&amp;quot;store_true&amp;quot;, default=False, help=&amp;quot;proportional variant (reweighted range voting)&amp;quot;)&lt;br /&gt;
 parser.add_argument(&amp;quot;-r&amp;quot;, &amp;quot;--random&amp;quot;, metavar='COINS',help='file with a list of 0/1 coin tosses (linewise)')&lt;br /&gt;
 args = parser.parse_args()&lt;br /&gt;
 &lt;br /&gt;
 # read predetermined random numbers (0/1) for ties (optional)&lt;br /&gt;
 random = []&lt;br /&gt;
 if args.random:&lt;br /&gt;
     for coin in open(args.random,'rt').readlines():&lt;br /&gt;
         random.append((int(coin)&amp;gt;0)*1)&lt;br /&gt;
 # read candidate names (optional)&lt;br /&gt;
 names = []&lt;br /&gt;
 if args.names:&lt;br /&gt;
     for cand in open(args.names,'rt').readlines():&lt;br /&gt;
         cand=cand.rstrip()&lt;br /&gt;
         if '#' in cand: cand=cand[cand.index('#')+2:]&lt;br /&gt;
         names.append(cand)&lt;br /&gt;
 # read ballots into a matrix (nballots,ncands) of scores (0..9)&lt;br /&gt;
 # input line per ballot: ;9...1/0/0;9*empty candidate numbers separated by comma&lt;br /&gt;
 # abstention and -1 are counted as 0 score&lt;br /&gt;
 ncands, votes = 0, []&lt;br /&gt;
 for ib,ballot in enumerate(open(args.input,'rt').readlines()):&lt;br /&gt;
     ballot = ballot.rstrip()&lt;br /&gt;
     if '#' in ballot: ballot=ballot[:ballot.index(' #')-1]&lt;br /&gt;
     approv=ballot.split('/')&lt;br /&gt;
     if not len(approv[0])+len(approv[2]): continue # complete abstention&lt;br /&gt;
     vote, bad = [], False&lt;br /&gt;
     for ia,ranks in enumerate(approv): # yes,abstention,no&lt;br /&gt;
         ranks = ranks.split(';')&lt;br /&gt;
         if (ia==1 and len(ranks)!=1) or (ia!=1 and len(ranks)!=10):&lt;br /&gt;
             print('invalid number of ranks in ballot', ib+1)&lt;br /&gt;
             bad = True&lt;br /&gt;
             break&lt;br /&gt;
         for ir,cands in enumerate(ranks):&lt;br /&gt;
             if (not ia and not ir) or (ia==2 and ir): # only -1..9 allowed&lt;br /&gt;
                 bad = len(cands)&lt;br /&gt;
                 if bad: &lt;br /&gt;
                     print('score outside of range in ballot', ib+1)&lt;br /&gt;
                     break&lt;br /&gt;
                 continue&lt;br /&gt;
             if cands:&lt;br /&gt;
                 cands = np.array(eval('['+cands+']'),'i')-1 # candidate numbers start at 0&lt;br /&gt;
                 ncands = max(ncands,max(cands)+1) # find max candidate number&lt;br /&gt;
             else: cands = np.array([],'i')&lt;br /&gt;
             if ia==2: vote[-1] = np.concatenate((vote[-1],cands)) # append -1 to abstentions = 0&lt;br /&gt;
             else: vote.append(cands)&lt;br /&gt;
         if bad: break&lt;br /&gt;
     if not bad: votes.append(vote) # ignore invalid ballots&lt;br /&gt;
 ballots = np.zeros((len(votes),ncands),'i')&lt;br /&gt;
 allcands= set(range(ncands))&lt;br /&gt;
 for ib,vote in enumerate(votes):&lt;br /&gt;
     cands = itertools.chain.from_iterable(vote)&lt;br /&gt;
     rem = allcands.difference(cands)&lt;br /&gt;
     if rem: print('missing candidates',list(rem),'in ballot', ib+1)&lt;br /&gt;
     for score in range(10): # reverse order: 9,8,...,0&lt;br /&gt;
         ballots[ib,vote[9-score]] = score&lt;br /&gt;
 &lt;br /&gt;
 nballots = len(ballots)&lt;br /&gt;
 print(nballots, 'ballots read')&lt;br /&gt;
 assert not names or len(names)==ncands, &amp;quot;mismatch of candidate names&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
 candlut = -1*np.ones(ncands,'i') # candidate lookup table&lt;br /&gt;
 selection = list(range(ncands))&lt;br /&gt;
 ignore = eval('['+args.ignore+']')&lt;br /&gt;
 selection = [cand for cand in selection if not cand in ignore]&lt;br /&gt;
 ncands = len(selection)&lt;br /&gt;
 print(ncands, 'candidates')&lt;br /&gt;
 if not args.length: args.length = ncands&lt;br /&gt;
 candlut[selection] = list(range(ncands))&lt;br /&gt;
 ballots = ballots[:,selection] # remove candidates&lt;br /&gt;
 &lt;br /&gt;
 # save converted input as csv, first line candidate numbers&lt;br /&gt;
 np.savetxt(&amp;quot;input.csv&amp;quot;, np.vstack((np.array(selection,'i')+1,ballots)), delimiter=&amp;quot;,&amp;quot;, fmt='%i')&lt;br /&gt;
 &lt;br /&gt;
 def getnumber(n): # read a number between 0 and n&lt;br /&gt;
      while True:&lt;br /&gt;
         x = input('Please enter a number between 0 and %i:' % n)&lt;br /&gt;
         if not x: continue&lt;br /&gt;
         try: x=int(x)&lt;br /&gt;
         except ValueError: continue&lt;br /&gt;
         if x&amp;lt;=n and x&amp;gt;=0: return x&lt;br /&gt;
 &lt;br /&gt;
 # elect the winners&lt;br /&gt;
 done, elected = np.zeros((1,nballots)), np.ones(ncands)&lt;br /&gt;
 for place in range(1,ncands+1):&lt;br /&gt;
     if args.proportional:&lt;br /&gt;
         weights = 9/(9+2*done) # major fractions (Webster, Sainte-Lague) method&lt;br /&gt;
         total = np.sum(weights.T * ballots,axis=0)&lt;br /&gt;
     else:&lt;br /&gt;
         total = np.sum(ballots,axis=0)&lt;br /&gt;
     total *= elected # reverse sign for already elected candidates&lt;br /&gt;
     best = total==max(total)&lt;br /&gt;
     winners = np.nonzero(best)[0]&lt;br /&gt;
     if not args.proportional and len(winners)&amp;gt;1: &lt;br /&gt;
         nyes = np.sum(ballots&amp;gt;0,axis=0) # count no of &amp;gt;0 votes&lt;br /&gt;
         nyes[~best] = -1 # only ties&lt;br /&gt;
         winners = np.nonzero(nyes==max(nyes))[0]&lt;br /&gt;
     if len(winners)&amp;gt;1: # tie&lt;br /&gt;
         if len(winners)&amp;gt;2 or not random: winner = getnumber(len(winners)-1)&lt;br /&gt;
         else: winner = random.pop()&lt;br /&gt;
         winner = winners[winner]&lt;br /&gt;
     else: winner = winners[0]&lt;br /&gt;
     icand = selection[winner]&lt;br /&gt;
     cand = names[icand] if names else icand+1&lt;br /&gt;
     tie = '(tie)' if len(winners)&amp;gt;1 else &amp;quot;&amp;quot;&lt;br /&gt;
     print(&amp;quot;%i. %s = %.2f %s&amp;quot; % (place, cand, total[winner],tie))&lt;br /&gt;
     if args.proportional: done += ballots[:,winner]&lt;br /&gt;
     elected[winner] = -1 # negative weight&lt;br /&gt;
     if args.length and place==args.length: break&lt;/div&gt;</summary>
		<author><name>imported&gt;Entropy</name></author>
	</entry>
</feed>