Spaces:
Sleeping
Sleeping
Upload 5 files
Browse files- .gitattributes +2 -0
- Voto_Logo_bpg-03.png +3 -0
- Voto_purple.png +0 -0
- match_checker2000.py +219 -0
- requirements.txt +5 -0
- tuda_logo.tif +3 -0
.gitattributes
CHANGED
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
+
tuda_logo.tif filter=lfs diff=lfs merge=lfs -text
|
37 |
+
Voto_Logo_bpg-03.png filter=lfs diff=lfs merge=lfs -text
|
Voto_Logo_bpg-03.png
ADDED
![]() |
Git LFS Details
|
Voto_purple.png
ADDED
![]() |
match_checker2000.py
ADDED
@@ -0,0 +1,219 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import json
|
3 |
+
import pandas as pd
|
4 |
+
import numpy as np
|
5 |
+
import matplotlib.pyplot as plt
|
6 |
+
|
7 |
+
footer="""<style>
|
8 |
+
a:link , a:visited{
|
9 |
+
color: blue;
|
10 |
+
background-color: transparent;
|
11 |
+
text-decoration: underline;
|
12 |
+
}
|
13 |
+
|
14 |
+
a:hover, a:active {
|
15 |
+
color: red;
|
16 |
+
background-color: transparent;
|
17 |
+
text-decoration: underline;
|
18 |
+
}
|
19 |
+
|
20 |
+
.footer {
|
21 |
+
position: fixed;
|
22 |
+
left: 0;
|
23 |
+
bottom: 0;
|
24 |
+
width: 100%;
|
25 |
+
background-color: cornflowerblue;
|
26 |
+
color: black;
|
27 |
+
text-align: center;
|
28 |
+
}
|
29 |
+
</style>
|
30 |
+
<div class="footer">
|
31 |
+
<p>This app was developed by <a href="https://www.voto.vote" target="_blank">VOTO</a>.
|
32 |
+
Developer: <a href="https://linkedin.com/in/thilo-dieing-bba5b2253" target="_blank">Thilo I. Dieing, M.Sc.</a></p>
|
33 |
+
</div>
|
34 |
+
"""
|
35 |
+
st.markdown(footer,unsafe_allow_html=True)
|
36 |
+
def add_logo():
|
37 |
+
st.markdown(
|
38 |
+
"""
|
39 |
+
<style>
|
40 |
+
|
41 |
+
[data-testid="stSidebarNav"]::before {
|
42 |
+
content: "Uni Mannheim ASR Team Project";
|
43 |
+
margin-left: 20px;
|
44 |
+
margin-top: 20px;
|
45 |
+
font-size: 20px;
|
46 |
+
position: relative;
|
47 |
+
top: 100px;
|
48 |
+
}
|
49 |
+
</style>
|
50 |
+
""",
|
51 |
+
unsafe_allow_html=True,
|
52 |
+
)
|
53 |
+
# functions
|
54 |
+
|
55 |
+
|
56 |
+
|
57 |
+
add_logo()
|
58 |
+
col1, col2 = st.columns(2)
|
59 |
+
with col1:
|
60 |
+
st.image("voto_purple.png", width=300)
|
61 |
+
with col2:
|
62 |
+
st.image("tuda_logo.tif", width=300)
|
63 |
+
|
64 |
+
# Title of the app
|
65 |
+
st.title("VOTO party insights")
|
66 |
+
|
67 |
+
# Allow the user to upload a file
|
68 |
+
uploaded_file = st.file_uploader("Upload the JSON file with your VOTO party answers", type=["json"])
|
69 |
+
|
70 |
+
# Check if a file is uploaded
|
71 |
+
if uploaded_file is not None:
|
72 |
+
try:
|
73 |
+
# Try to load the uploaded file as JSON
|
74 |
+
file_content = uploaded_file.read()
|
75 |
+
json_data = json.loads(file_content)
|
76 |
+
|
77 |
+
# If the file is valid JSON, show a success message
|
78 |
+
|
79 |
+
# Convert the JSON data to a DataFrame
|
80 |
+
if isinstance(json_data, list): # Expecting a list of dictionaries for a proper DataFrame
|
81 |
+
df = pd.DataFrame(json_data)
|
82 |
+
election=str(df["instance"].iloc[0])
|
83 |
+
statements = str(df["statement"].nunique())
|
84 |
+
statement_n = df["statement"].nunique()
|
85 |
+
parties = str(df["party_name"].nunique())
|
86 |
+
st.success("You have uploaded the party answers for the VOTO instance: "+election+" ("+statements+" statements; "+parties+" Parties)")
|
87 |
+
|
88 |
+
expander1 = st.expander("Statement Selection")
|
89 |
+
pivot_df = df.pivot(index='statement', columns='party_name', values='valuation')
|
90 |
+
# std
|
91 |
+
df['std_deviation'] = df.groupby('statement')['valuation'].transform('std')
|
92 |
+
# how many differnet postions
|
93 |
+
df['unique_postions'] = df.groupby('statement')['valuation'].transform('nunique')
|
94 |
+
# how many neutrals
|
95 |
+
df['n_neutral'] = df.groupby('statement')['valuation'].transform(lambda x: (x == 50).sum())
|
96 |
+
# standalone party postions
|
97 |
+
value_counts = df.groupby(['statement', 'valuation']).size().reset_index(name='Count')
|
98 |
+
unique_values = value_counts[value_counts['Count'] == 1]
|
99 |
+
single_counts = unique_values.groupby('statement').size().reset_index(name='SingleCount')
|
100 |
+
|
101 |
+
df = df.merge(single_counts, on='statement', how='left')
|
102 |
+
df['SingleCount'] = df['SingleCount'].fillna(0).astype(int)
|
103 |
+
#std without neutral
|
104 |
+
df_filtered = df[df['valuation'] != 50]
|
105 |
+
df_filtered['std_deviation2'] = df_filtered.groupby('statement')['valuation'].transform('std')
|
106 |
+
df2= df_filtered[["statement",'std_deviation2' ]].drop_duplicates()
|
107 |
+
df = df.merge(df2, on='statement', how='left', suffixes=('', '_new'))
|
108 |
+
|
109 |
+
sorted_single_counts = df[['statement', 'std_deviation']].drop_duplicates()
|
110 |
+
sorted_single_counts['AdjustedRank1'] = sorted_single_counts['std_deviation'].rank(method='dense', ascending=False).astype(int) - 1
|
111 |
+
df = df.merge(sorted_single_counts[['statement', 'AdjustedRank1']], on='statement', how='left', suffixes=('', '_new'))
|
112 |
+
|
113 |
+
sorted_single_counts = df[['statement', 'unique_postions']].drop_duplicates()
|
114 |
+
sorted_single_counts['AdjustedRank2'] = sorted_single_counts['unique_postions'].rank(method='dense', ascending=False).astype(int) - 1
|
115 |
+
df = df.merge(sorted_single_counts[['statement', 'AdjustedRank2']], on='statement', how='left', suffixes=('', '_new'))
|
116 |
+
|
117 |
+
sorted_single_counts = df[['statement', 'n_neutral']].drop_duplicates()
|
118 |
+
sorted_single_counts['AdjustedRank3'] = sorted_single_counts['n_neutral'].rank(method='dense').astype(int) - 1
|
119 |
+
df = df.merge(sorted_single_counts[['statement', 'AdjustedRank3']], on='statement', how='left', suffixes=('', '_new'))
|
120 |
+
|
121 |
+
sorted_single_counts = df[['statement', 'SingleCount']].drop_duplicates()
|
122 |
+
sorted_single_counts['AdjustedRank4'] = sorted_single_counts['SingleCount'].rank(method='dense', ascending=False).astype(int) - 1
|
123 |
+
df = df.merge(sorted_single_counts[['statement', 'AdjustedRank4']], on='statement', how='left', suffixes=('', '_new'))
|
124 |
+
|
125 |
+
sorted_single_counts = df[['statement', 'std_deviation2']].drop_duplicates()
|
126 |
+
sorted_single_counts['AdjustedRank5'] = sorted_single_counts['std_deviation2'].rank(method='dense', ascending=False).astype(int) - 1
|
127 |
+
df = df.merge(sorted_single_counts[['statement', 'AdjustedRank5']], on='statement', how='left', suffixes=('', '_new'))
|
128 |
+
|
129 |
+
df["statement_importance"]= (df["AdjustedRank1"]+df["AdjustedRank2"]+df["AdjustedRank3"]+df["AdjustedRank4"]+df["AdjustedRank5"])/5
|
130 |
+
df_pres= df[['statement',"statement_importance"]].drop_duplicates()
|
131 |
+
df_sorted1 = df_pres.sort_values(by='statement_importance')
|
132 |
+
df_sorted1.reset_index(drop=True, inplace=True)
|
133 |
+
|
134 |
+
df_sorted = df.sort_values(by='statement_importance')
|
135 |
+
df_sorted.reset_index(drop=True, inplace=True)
|
136 |
+
statn = expander1.slider("How many statements do you want in your final VOTO? You currently have **"+statements+ "** statements." , 0, int(statements),0)
|
137 |
+
if statn !=0:
|
138 |
+
expander1.write("**Based on that our metric recommends keeping the following " +str(statn) +" statements:**")
|
139 |
+
lstat=df_sorted1['statement'].tolist()
|
140 |
+
lstat1=lstat[:statn]
|
141 |
+
i=0
|
142 |
+
for l in lstat1:
|
143 |
+
i=i+1
|
144 |
+
expander1.markdown("<span style='color: green;'>"+str(i)+": "+l+"</span>", unsafe_allow_html=True)
|
145 |
+
expander1.write("**While the metric recommends dropping "+str(statement_n-statn) +" statements due to a lack of difference:**")
|
146 |
+
lstat=df_sorted1['statement'].tolist()
|
147 |
+
lstat1=lstat[statn:]
|
148 |
+
i=statn
|
149 |
+
for l in lstat1:
|
150 |
+
i=i+1
|
151 |
+
expander1.markdown("<span style='color: red;'>"+str(i)+": "+l+"</span>", unsafe_allow_html=True)
|
152 |
+
if expander1.button("Additional information on individual metric scores"):
|
153 |
+
expander1.dataframe(df_sorted)
|
154 |
+
|
155 |
+
expander2 = st.expander("Party Positions")
|
156 |
+
df = pd.DataFrame(json_data)
|
157 |
+
unique_party_names = df['party_name'].unique()
|
158 |
+
unique_party_names = [""] + list(unique_party_names) # Add an empty option
|
159 |
+
selected_party = expander2.selectbox("Select a Party", unique_party_names)
|
160 |
+
contains_25_or_75 = (25 in df['valuation'].values) or (75 in df['valuation'].values)
|
161 |
+
if contains_25_or_75:
|
162 |
+
y_ticks = [0, 25, 50, 75, 100] # Set y-ticks for scale 1 to 5
|
163 |
+
y_tick_labels = ['Strong Disagreement', 'Disagreement', 'Neutral', 'Agreement', 'Strong Agreement'] # Custom labels
|
164 |
+
else:
|
165 |
+
y_ticks = [0, 50, 100] # Set y-ticks for scale 1 to 3
|
166 |
+
y_tick_labels = ['Disagreement', 'Neutral', 'Agreement']
|
167 |
+
if selected_party: # Check if a party has been selected
|
168 |
+
df['short_text'] = df['statement'].apply(lambda x: ' '.join(x.split()[:11]))
|
169 |
+
filtered_df = df[df['party_name'] == selected_party]
|
170 |
+
fig, ax = plt.subplots()
|
171 |
+
ax.scatter(filtered_df['valuation'], filtered_df['short_text'], color='blue', s=100) # s is the size of points
|
172 |
+
ax.set_xlabel('Valuation')
|
173 |
+
ax.set_ylabel('Statements')
|
174 |
+
ax.set_title('Valuation by Statement')
|
175 |
+
ax.set_xticks(y_ticks) # Set y-ticks dynamically based on condition
|
176 |
+
ax.set_xticklabels(y_tick_labels)
|
177 |
+
plt.xticks(rotation=45)
|
178 |
+
ax.grid(True)
|
179 |
+
expander2.pyplot(fig)
|
180 |
+
expander3 = st.expander("Unique Party Positions")
|
181 |
+
df = pd.DataFrame(json_data)
|
182 |
+
unique_party_names = df['party_name'].unique()
|
183 |
+
unique_party_names = [""] + list(unique_party_names) # Add an empty option
|
184 |
+
selected_party = expander3.selectbox("Select a Party", unique_party_names, key="2")
|
185 |
+
contains_25_or_75 = (25 in df['valuation'].values) or (75 in df['valuation'].values)
|
186 |
+
if contains_25_or_75:
|
187 |
+
y_ticks = [0, 25, 50, 75, 100] # Set y-ticks for scale 1 to 5
|
188 |
+
y_tick_labels = ['Strong Disagreement', 'Disagreement', 'Neutral', 'Agreement', 'Strong Agreement'] # Custom labels
|
189 |
+
else:
|
190 |
+
y_ticks = [0, 50, 100] # Set y-ticks for scale 1 to 3
|
191 |
+
y_tick_labels = ['Disagreement', 'Neutral', 'Agreement']
|
192 |
+
if selected_party: # Check if a party has been selected
|
193 |
+
value_counts = df.groupby(['statement', 'valuation']).size().reset_index(name='Count')
|
194 |
+
unique_values = value_counts[value_counts['Count'] == 1]
|
195 |
+
unique_parties = df.merge(unique_values[['statement', 'valuation']], on=['statement', 'valuation'])
|
196 |
+
unique_parties_result = unique_parties[['statement', 'party_name', 'valuation']]
|
197 |
+
unique_parties_result['short_text'] = unique_parties_result['statement'].apply(lambda x: ' '.join(x.split()[:11]))
|
198 |
+
filtered_unique_parties_result = unique_parties_result[unique_parties_result['party_name'] == selected_party]
|
199 |
+
fig, ax = plt.subplots()
|
200 |
+
ax.scatter(filtered_unique_parties_result['valuation'], filtered_unique_parties_result['short_text'], color='blue', s=100) # s is the size of points
|
201 |
+
ax.set_xlabel('Valuation')
|
202 |
+
ax.set_ylabel('Statements')
|
203 |
+
ax.set_title('Valuation by Statement')
|
204 |
+
ax.set_xticks(y_ticks) # Set y-ticks dynamically based on condition
|
205 |
+
ax.set_xticklabels(y_tick_labels)
|
206 |
+
plt.xticks(rotation=45)
|
207 |
+
ax.grid(True)
|
208 |
+
expander3.pyplot(fig)
|
209 |
+
|
210 |
+
|
211 |
+
|
212 |
+
else:
|
213 |
+
st.warning("The JSON file structure is not suitable for DataFrame conversion. It should be a list of dictionaries.")
|
214 |
+
|
215 |
+
except json.JSONDecodeError:
|
216 |
+
# If the file is not a valid JSON, show an error message
|
217 |
+
st.error("This is not a valid JSON file. Please upload a valid JSON.")
|
218 |
+
else:
|
219 |
+
st.info("Please upload a JSON file to get started.")
|
requirements.txt
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
streamlit
|
2 |
+
jsonlib-python3
|
3 |
+
pandas
|
4 |
+
numpy
|
5 |
+
matplotlib
|
tuda_logo.tif
ADDED
|
Git LFS Details
|