Last active
November 4, 2021 15:57
-
-
Save leonidk/a7a34ab2243363560a1067a313f12122 to your computer and use it in GitHub Desktop.
just some voting stuff
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"import numpy as np\n", | |
"import matplotlib.pyplot as plt\n", | |
"import pandas as pd\n", | |
"\n", | |
"from collections import defaultdict" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"df = pd.read_excel(\"Apparel Voting Form 2021 (Responses).xlsx\")\n", | |
"#df = pd.read_excel(\"RoboOrg Class T-Shirt Design Vote 2019 (Responses).xlsx\")\n", | |
"idx_col = 2 # 2 in '20 is shirt. 4 is color, 9 is products. In 19, 2 is all we need\n", | |
"# in '21, 2 is shirt, 5 is color, 6 is clothing, 7 is accessories, 8 is decorations\n", | |
"if False: # combine multiple options, treat as one. \n", | |
" valid_opt = [2,5,6,7,8] # [2,4,9]\n", | |
" mega_row = []\n", | |
" for row in df.iloc[:,valid_opt].itertuples():\n", | |
" new_row = [_ for _ in row if type(_)==str]\n", | |
" mega_row.append(', '.join(new_row) if len(new_row) > 0 else np.nan)\n", | |
" df['mega'] = mega_row\n", | |
" idx_col = list(df.columns).index('mega')\n", | |
"\n", | |
"target_col = list(df.columns)[idx_col]\n", | |
"df = df.dropna(subset=[target_col])\n", | |
"df.columns,target_col,df.shape" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# commas as delimiters, so replace patterns with internal commas\n", | |
"subs = {\n", | |
" 'Eat, Sleep, Robotics, Repeat': 'Eat Sleep Robotics Repeat'\n", | |
"}\n", | |
"options = set()\n", | |
"for o in df[target_col].unique():\n", | |
" for k,v in subs.items():\n", | |
" o = o.replace(k,v)\n", | |
" for name in o.split(', '):\n", | |
" options.add(name)\n", | |
"options = sorted(list(options))\n", | |
"vectors = defaultdict(list)\n", | |
"for row in df.itertuples():\n", | |
" o = row[idx_col+1]\n", | |
" for k,v in subs.items():\n", | |
" o = o.replace(k,v)\n", | |
" for design in options:\n", | |
" vectors[design].append(design in o)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"for k,v in vectors.items():\n", | |
" df[k] = v" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# dataframe of booleans for each option\n", | |
"X = df[options]" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"def get_results(X,seconds_limit=15):\n", | |
" all_res = {}\n", | |
" \n", | |
" Xn = X/np.array(X).sum(1,keepdims=True)\n", | |
" # fractional winner\n", | |
" res = sorted([(v,k) for k,v in Xn.sum(0).items()],reverse=True)\n", | |
" all_res['Frac'] = {i+1:'{1} ({0:.0f})'.format(*v) for i,v in enumerate(res)}\n", | |
" \n", | |
" # normal winner\n", | |
" res = sorted([(v,k) for k,v in X.sum(0).items()],reverse=True)\n", | |
" all_res['Count'] = {i+1:'{1} ({0:.0f})'.format(*v) for i,v in enumerate(res)}\n", | |
" \n", | |
" # remove old votes\n", | |
" res = {}\n", | |
" Xc = X.copy()\n", | |
" while Xc.shape[0] > 0:\n", | |
" pop = sorted([(v,k) for k,v in Xc.sum(0).items()],reverse=True)[0][1]\n", | |
" st = '{1} ({0:.0f})'.format(Xc[pop].sum(),pop)\n", | |
" #print(st)\n", | |
" res[len(res)+1] = st\n", | |
" Xc = Xc[~Xc[pop]]\n", | |
" all_res['Greedy'] = res\n", | |
" \n", | |
" # seq pav. remove but give existing votes partial credit\n", | |
" voted = np.zeros_like(X.iloc[:,0],dtype=bool)\n", | |
" res = {}\n", | |
" selected_r = []\n", | |
" i = 1\n", | |
" while voted.sum() != X.shape[0]:\n", | |
" Xr = X.copy()\n", | |
" Xr[voted] = 1/i\n", | |
" pop = sorted([(v,k) for k,v in Xr.sum(0).items() if k not in selected_r],reverse=True)[0][1]\n", | |
" st = '{1} ({0:.0f})'.format(Xr[pop].sum(),pop)\n", | |
" #print(st)\n", | |
" res[len(res)+1] = st\n", | |
" voted |= Xr[pop].astype(bool)\n", | |
" selected_r.append(pop)\n", | |
" i+=1\n", | |
" all_res['Thiele'] = res\n", | |
" \n", | |
" # seq pav but propery credit!?\n", | |
" voted = np.zeros_like(X.iloc[:,0],dtype=int)\n", | |
" selected_r = []\n", | |
" i = 1\n", | |
" res = {}\n", | |
" while (voted > 0).sum() != X.shape[0]:\n", | |
" Xr = X.copy()\n", | |
" if (voted >0).sum() > 0:\n", | |
" Xr[voted > 0] = np.array(Xr[voted > 0])/(1+voted[voted > 0])[:,None] \n", | |
" pop = sorted([(v,k) for k,v in Xr.sum(0).items() if k not in selected_r],reverse=True)[0][1]\n", | |
" st = '{1} ({0:.0f})'.format(Xr[pop].sum(),pop)\n", | |
" #print(st)\n", | |
" res[len(res)+1] = st\n", | |
" voted += (Xr[pop]).astype(bool).astype(int)\n", | |
" selected_r.append(pop)\n", | |
" i+=1\n", | |
" all_res['Thiele_C'] = res\n", | |
" \n", | |
" # real pav. takes exponentially long, so might be slow\n", | |
" import itertools\n", | |
" import time\n", | |
" start_t = time.time()\n", | |
" resA = {}\n", | |
" early = False\n", | |
" col_i = np.arange(X.shape[1])\n", | |
" prev = []\n", | |
" prev_n = 0\n", | |
" for n in col_i+1:\n", | |
" res = []\n", | |
" for combo in itertools.combinations(X.columns,n):\n", | |
" s = (X[list(combo)].sum(1) > 0).sum()\n", | |
" res.append((s,combo))\n", | |
" if time.time() - start_t > seconds_limit:\n", | |
" early = True\n", | |
" break\n", | |
"\n", | |
" res = sorted(res,reverse=True)\n", | |
" new = ([_ for _ in sorted(res[0][1]) if _ not in prev] + [''])[0]\n", | |
" st = '{1} ({0:.0f})'.format(res[0][0]-prev_n,new)\n", | |
" resA[len(resA)+1] = st\n", | |
"\n", | |
" #print(st)\n", | |
" prev_n = res[0][0]\n", | |
" prev.append(new)\n", | |
" if prev_n == X.shape[0]:\n", | |
" break\n", | |
" if early:\n", | |
" break\n", | |
" all_res['PAV'] = resA\n", | |
" \n", | |
" return pd.DataFrame(all_res).T" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"shirt_results = get_results(X)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"pd.set_option('display.max_rows', 500)\n", | |
"shirt_results.T" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"shirt_results." | |
] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Python 3", | |
"language": "python", | |
"name": "python3" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython3", | |
"version": "3.8.8" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 2 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment