m7n commited on
Commit
4d6df18
·
1 Parent(s): a3e05a5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +365 -4
app.py CHANGED
@@ -1,7 +1,368 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import gradio as gr
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
- def greet(name):
4
- return "Hello " + name + "!!"
5
 
6
- iface = gr.Interface(fn=greet, inputs="text", outputs="text")
7
- iface.launch()
 
1
+ # -*- coding: utf-8 -*-
2
+ """ranking_simulation_0.ipynb
3
+
4
+ Automatically generated by Colaboratory.
5
+
6
+ Original file is located at
7
+ https://colab.research.google.com/drive/1GzjewA8ePLJ1fdk76Qax-aRRsSsNp417
8
+ """
9
+
10
+ # Commented out IPython magic to ensure Python compatibility.
11
+ # %%capture
12
+ # !pip install gradio
13
+ # !pip install opinionated
14
+ #
15
+
16
+ import numpy as np
17
+ import pandas as pd
18
+ # import opinionated
19
+ import matplotlib.pyplot as plt
20
+ plt.style.use("opinionated_rc")
21
+
22
+ # from opinionated.core import download_googlefont
23
+ # download_googlefont('Quicksand', add_to_cache=True)
24
+ # plt.rc('font', family='Quicksand')
25
+
26
+
27
+ # import colormaps as cmaps
28
+
29
+ def simulate_applicant_judging(num_applicants, num_judges, ratings_per_applicant, alpha, beta,
30
+ judge_error=1, judgment_coarse_graining=False):
31
+ """
32
+ Simulates judging of applicants by a set of judges with coarse graining functionality and random tie-breaking in ranking.
33
+
34
+ :param num_applicants: Number of applicants to be judged.
35
+ :param num_judges: Number of judges available for judging.
36
+ :param ratings_per_applicant: Number of ratings each applicant should receive.
37
+ :param alpha: Alpha parameter for the Beta distribution.
38
+ :param beta: Beta parameter for the Beta distribution.
39
+ :param judge_error_std_dev: Standard deviation for the judge's margin of error.
40
+ :param judgment_coarse_graining: Number of buckets for coarse graining (2 to 100) or False to disable.
41
+ :return: A Pandas DataFrame with detailed results for each applicant.
42
+ """
43
+ # Generate the quality of applicants from a Beta distribution normalized to 0-100
44
+ applicant_qualities = np.random.beta(alpha, beta, num_applicants) * 100
45
+
46
+ # Function to apply coarse graining
47
+ def coarse_grain_evaluation(evaluation, grain_size):
48
+ return round(evaluation / (100 / grain_size)) * (100 / grain_size)
49
+
50
+ # Initialize evaluations dictionary
51
+ evaluations = {f"Applicant_{i+1}": [] for i in range(num_applicants)}
52
+ judge_workload = np.zeros(num_judges)
53
+
54
+ # Randomly assign judges to applicants
55
+ for _ in range(ratings_per_applicant):
56
+ for applicant in range(num_applicants):
57
+ probabilities = (max(judge_workload) - judge_workload + 1) / sum(max(judge_workload) - judge_workload + 1)
58
+ judge = np.random.choice(num_judges, p=probabilities)
59
+ judge_workload[judge] += 1
60
+ evaluation = np.random.uniform(applicant_qualities[applicant]-judge_error, applicant_qualities[applicant]+judge_error)
61
+
62
+ # Apply coarse graining if enabled
63
+ if judgment_coarse_graining:
64
+ evaluation = coarse_grain_evaluation(evaluation, judgment_coarse_graining)
65
+
66
+ evaluations[f"Applicant_{applicant+1}"].append(evaluation)
67
+
68
+ # Prepare data for DataFrame
69
+ data = []
70
+ for applicant, (quality, scores) in enumerate(zip(applicant_qualities, evaluations.values()), 1):
71
+ average_evaluation = np.mean(scores)
72
+ original_ranks = np.argsort(np.argsort(-np.array(scores))) + 1
73
+ data.append([f"Applicant_{applicant}", quality, average_evaluation, scores, list(original_ranks)])
74
+
75
+ # Create DataFrame
76
+ df = pd.DataFrame(data, columns=["Applicant", "Applicant Quality", "Average Evaluation", "Original Scores", "Rank of Original Scores"])
77
+
78
+ # Random tie-breaking function
79
+ def random_tie_breaking(df, column):
80
+ # Shuffle rows with the same value in the specified column
81
+ ranks = df[column].rank(method='first', ascending=False)
82
+ ties = df.duplicated(column, keep=False)
83
+ shuffled_ranks = ranks[ties].sample(frac=1).sort_index()
84
+ ranks[ties] = shuffled_ranks
85
+ return ranks
86
+
87
+ # Apply random tie-breaking to rankings
88
+ df['Rank of Evaluation'] = random_tie_breaking(df, 'Average Evaluation').astype(int)
89
+ df['Rank of Applicant Quality'] = random_tie_breaking(df, 'Applicant Quality').astype(int)
90
+
91
+ return df
92
+
93
+ # # Example usage with specified alpha and beta values
94
+ # df_results = simulate_applicant_judging(num_applicants=100, num_judges=10, ratings_per_applicant=5, alpha=4, beta=4, judgment_coarse_graining=10)
95
+ # df_results.head(30) # Displaying the top 30 rows for brevity
96
+
97
+
98
+
99
+
100
+ # df_results.sort_values(by='Rank of Evaluation').head(30)
101
+
102
+ import pandas as pd
103
+
104
+ def summarize_simulation_runs(num_runs, num_applicants, num_judges, ratings_per_applicant, top_n, alpha, beta,
105
+ judge_error=1, judgment_coarse_graining=False):
106
+ """
107
+ Runs the applicant judging simulation multiple times and summarizes how often each candidate by quality was in the top n.
108
+
109
+ :param num_runs: Number of times to run the simulation.
110
+ :param num_applicants: Number of applicants to be judged.
111
+ :param num_judges: Number of judges available for judging.
112
+ :param ratings_per_applicant: Number of ratings each applicant should receive.
113
+ :param top_n: Number of top positions to consider in the summary.
114
+ :param applicant_std_dev: Standard deviation for the quality of applicants.
115
+ :param judge_error_std_dev: Standard deviation for the judge's margin of error.
116
+ :param judgment_coarse_graining: Number of buckets for coarse graining or False to disable.
117
+ :return: A Pandas DataFrame summarizing the results.
118
+ """
119
+ # Initialize counts for each quality-ranked candidate in top n positions
120
+ top_n_counts = pd.DataFrame(0, index=range(1, num_applicants + 1), columns=[f'Top {i}' for i in range(1, top_n + 1)])
121
+
122
+ for _ in range(num_runs):
123
+ df_results = simulate_applicant_judging(num_applicants, num_judges, ratings_per_applicant,
124
+ alpha=alpha, beta=beta, judge_error=judge_error, judgment_coarse_graining=judgment_coarse_graining)
125
+ # Sort by Rank of Applicant Quality
126
+ sorted_by_quality = df_results.sort_values(by='Applicant Quality', ascending=False).reset_index()
127
+ # Sort by Rank of Evaluation
128
+ sorted_by_evaluation = df_results.sort_values(by='Rank of Evaluation').reset_index()
129
+
130
+ for i in range(top_n):
131
+ # Find which quality-ranked candidate is in this top evaluation position
132
+ quality_rank = sorted_by_quality[sorted_by_evaluation.loc[i, 'Applicant'] == sorted_by_quality['Applicant']].index[0] + 1
133
+ top_n_counts.loc[quality_rank, f'Top {i+1}'] += 1
134
+
135
+ return top_n_counts
136
+
137
+ # Example usage
138
+ # num_runs = 1000 # Number of simulation runs
139
+ # top_n_results = summarize_simulation_runs(num_runs=num_runs, num_applicants=100, num_judges=5, ratings_per_applicant=3,
140
+ # top_n=5,alpha=2, beta=1,judge_error=4, judgment_coarse_graining=False)
141
+ # top_n_results
142
+
143
+
144
+
145
+ import matplotlib.pyplot as plt
146
+ from matplotlib.colors import ListedColormap, LinearSegmentedColormap
147
+
148
+
149
+ def plot_top_n_results(top_n_results, num_runs):
150
+ """
151
+ Plots a stacked bar chart of the top-n results, filling out what's missing to num_runs in grey.
152
+
153
+ :param top_n_results: DataFrame containing the counts of each quality-ranked candidate in top n positions.
154
+ :param num_runs: Total number of simulation runs.
155
+ """
156
+
157
+ base_cmap = cmaps.green_green1_r.cut(0.10, 'right').discrete(top_n_results.shape[1])
158
+ newcolors = base_cmap(np.linspace(0, 1, top_n_results.shape[1]))
159
+
160
+ # Define a new color (pink) and add it to the colormap
161
+ pink = np.array([178/256, 171/256, 165/256, 1])
162
+ newcolors = np.vstack([newcolors, pink])
163
+ # Create a new ListedColormap
164
+ newcmp = ListedColormap(newcolors)
165
+
166
+ # Calculate the missing counts to fill up to num_runs
167
+ missing_counts = num_runs - top_n_results.sum(axis=1)
168
+
169
+ # Prepare data for stacked bar chart
170
+ data_to_plot = top_n_results.copy()
171
+ data_to_plot['Missing'] = missing_counts
172
+
173
+ # Create a figure and axis for plotting
174
+ fig, ax = plt.subplots(figsize=(12, 8))
175
+ # Plot stacked bar chart
176
+ data_to_plot.head(top_n_results.shape[1]).plot(kind='bar', stacked=True, colormap=newcmp, alpha=.9, ax=ax)
177
+ # Plot settings
178
+ ax.set_title('How often did the actually best get chosen?', loc='right') # Right-align title
179
+ ax.set_xlabel('Real Applicant Rank')
180
+ ax.set_ylabel('Selected in this many simulation runs')
181
+
182
+ # Conditionally add legend
183
+ if top_n_results.shape[1] <= 5:
184
+ labels = [label.replace("Top", "Rank") for label in top_n_results.columns] + ['Not chosen']
185
+
186
+ ax.legend(labels=labels, title='Rank in Evaluation', loc='lower center', bbox_to_anchor=(0.5, -0.2), ncol=top_n_results.shape[1]+1) # Legend below the chart
187
+ plt.tight_layout()
188
+ return fig
189
+
190
+ # plt.show()
191
+ # plot = plot_top_n_results(top_n_results, num_runs)
192
+
193
+ # num_applicants=100
194
+ # num_judges=10
195
+ # ratings_per_applicant=5
196
+ # top_n=5
197
+ # applicant_std_dev=20
198
+ # judge_error_std_dev=1
199
+ # judgment_coarse_graining=6
200
+
201
+ # top_n_counts = pd.DataFrame(0, index=range(1, num_applicants + 1), columns=[f'Top {i}' for i in range(1, top_n + 1)])
202
+
203
+ # df_results = simulate_applicant_judging(num_applicants, num_judges, ratings_per_applicant,
204
+ # applicant_std_dev, judge_error_std_dev, judgment_coarse_graining)
205
+ # display(df_results.sort_values(by='Rank of Evaluation').head(10))
206
+ # # Sort by Rank of Applicant Quality
207
+ # sorted_by_quality = df_results.sort_values(by='Applicant Quality', ascending=False).reset_index()
208
+ # # Sort by Rank of Evaluation
209
+ # sorted_by_evaluation = df_results.sort_values(by='Rank of Evaluation').reset_index()
210
+
211
+ # for i in range(top_n):
212
+ # # Find which quality-ranked candidate is in this top evaluation position
213
+ # quality_rank = sorted_by_quality[sorted_by_evaluation.loc[i, 'Applicant'] == sorted_by_quality['Applicant']].index[0] + 1
214
+ # top_n_counts.loc[quality_rank, f'Top {i+1}'] += 1
215
+
216
+ # display(top_n_counts.head(15))
217
+
218
+ # import numpy as np
219
+ # import matplotlib.pyplot as plt
220
+ # import seaborn as sns
221
+ # from scipy.stats import gaussian_kde
222
+
223
+ # def visualize_applicant_and_judge_distributions(alpha, beta, judge_error=1):
224
+ # """
225
+ # Visualizes the distribution of applicants' qualities and an example of a judge's evaluations for a random applicant
226
+ # using density estimates with normalized heights.
227
+
228
+ # :param applicant_std_dev: Standard deviation for the quality of applicants.
229
+ # :param judge_error_std_dev: Standard deviation for the judge's margin of error.
230
+ # """
231
+ # # Generate applicant qualities
232
+ # applicant_qualities = np.random.beta(alpha, beta, 5000) * 100
233
+
234
+ # # Choose a random applicant for judge's evaluation
235
+ # random_applicant_quality = np.random.choice(applicant_qualities)
236
+ # judge_evaluations = np.random.normal(random_applicant_quality, judge_error, 5000)
237
+
238
+ # # Calculate KDEs and find peak values
239
+ # kde_applicant = gaussian_kde(applicant_qualities)
240
+ # kde_judge = gaussian_kde(judge_evaluations)
241
+ # x = np.linspace(0, 100, 1000)
242
+ # kde_applicant_vals = kde_applicant(x)
243
+ # kde_judge_vals = kde_judge(x)
244
+ # peak_applicant = np.max(kde_applicant_vals)
245
+ # peak_judge = np.max(kde_judge_vals)
246
+ # scale_factor = peak_applicant / peak_judge
247
+
248
+ # # Plotting
249
+ # plt.figure(figsize=(12, 6))
250
+
251
+ # # Plot for distribution of all applicants
252
+ # sns.lineplot(x=x, y=kde_applicant_vals, color="blue", label='Applicant Qualities')
253
+ # plt.fill_between(x, kde_applicant_vals, color="blue", alpha=0.3)
254
+ # plt.title('Distribution of Applicant Qualities')
255
+ # plt.xlabel('Quality')
256
+ # plt.ylabel('Normalized Density')
257
+ # plt.legend()
258
+ # plt.xlim(0, 100)
259
+
260
+ # # # Plot for distribution of a single applicant's evaluations
261
+ # # sns.lineplot(x=x, y=kde_judge_vals * scale_factor, color='orange', label='Judge Evaluations')
262
+ # # plt.fill_between(x, kde_judge_vals * scale_factor, color="orange", alpha=0.3)
263
+ # # plt.title('Distribution of a Judge\'s Evaluations for a Chosen Applicant')
264
+ # # plt.xlabel('Evaluation Score')
265
+ # # plt.ylabel('Normalized Density')
266
+ # # plt.legend()
267
+ # # plt.xlim(0, 100)
268
+
269
+ # plt.tight_layout()
270
+ # plt.show()
271
+
272
+ # # Example usage
273
+ # visualize_applicant_and_judge_distributions(alpha=2, beta=1, judge_error=5)
274
+
275
  import gradio as gr
276
+ import matplotlib.pyplot as plt
277
+ from io import BytesIO
278
+
279
+ from scipy.stats import beta
280
+
281
+ # Function to plot beta distribution
282
+ def plot_beta_distribution(a, b, judgement_variability):
283
+ x = np.linspace(0, 1, 1000)
284
+ y = beta.pdf(x, a, b)
285
+
286
+ fig, ax = plt.subplots(figsize=(7, 3)) # Figure size
287
+ plt.fill_between(np.linspace(0, 100, 1000), y, color="#ee4d5a", alpha=0.8)
288
+ # plt.title('Distribution of Applicant Qualities')
289
+ plt.xlabel('True Applicants Quality-Distribution')
290
+ plt.xlim(0, 100)
291
+ ax.set_yticklabels([]) # Remove y-axis labels
292
+
293
+ # Drawing the line
294
+ line_length = 2 * judgement_variability
295
+ line_x = [50 - line_length/2, 50 + line_length/2]
296
+ plt.plot(line_x, [0, 0], color='black', linewidth=2)
297
+
298
+ # Labeling the line
299
+ plt.text(np.mean(line_x), 0.02, 'Judgement Variability', ha='center', va='bottom', color='black')
300
+
301
+ return fig
302
+
303
+ # Your existing function for running simulation and plotting
304
+ def run_simulation_and_plot(num_runs, num_applicants, num_judges, ratings_per_applicant, top_n, alpha, beta, judge_error, judgment_coarse_graining,judgment_coarse_graining_true_false):
305
+ if judgment_coarse_graining_true_false == False:
306
+ judgment_coarse_graining = False
307
+ top_n_results = summarize_simulation_runs(num_runs, num_applicants, num_judges, ratings_per_applicant, top_n, alpha, beta, judge_error, judgment_coarse_graining)
308
+ return plot_top_n_results(top_n_results, num_runs)
309
+
310
+
311
+
312
+ intro_html = """
313
+ <h1>On Rankings</h1>
314
+ <p>One of the central experiences of being an academic is the experience of being ranked. We are ranked when we apply for graduate school, or maybe already for a master's degree. We are ranked when we're up for faculty positions. We are ranked when we submit abstracts for conferences. And when we publish papers, we do so, of course, in journals that are ranked. The places where we work, the departments, are ranked, of course, as well. But although rankings apparently are catnip to academics, and probably everybody else as well, we do have some agreement. Most people probably share the intuition that there's something weird or iffy about rankings, and the suspicion that maybe often they are not as informative as there are some beings absolutely everywhere who suggest.</p>
315
+ """
316
+ comment_distribution_image = """<p>This is the disrtribution from which our applicants will be sampled:</p>"""
317
+
318
+ # Building the interface
319
+ with gr.Blocks(theme=gr.themes.Monochrome()) as demo:
320
+ with gr.Column():
321
+ gr.HTML(intro_html)
322
+ with gr.Row():
323
+ with gr.Column():
324
+ run_button = gr.Button("Run Simulations!")
325
+
326
+ # control applicant distribution
327
+ with gr.Group():
328
+ alpha_slider = gr.Slider(0.1, 5, step=0.1, value=1.4, label="Alpha (β Distribution)")
329
+ beta_slider = gr.Slider(0.1, 5, step=0.1,value=2.7, label="Beta (β Distribution)")
330
+ # simumlation-settings:
331
+ num_applicants = gr.Slider(10, 300, step=10, value=100, label="Number of Applicants")
332
+ num_judges = gr.Slider(1, 100, step=1, value=7, label="Number of Judges")
333
+ ratings_per_applicant = gr.Slider(1, 5, step=1, value=3, label="Ratings per Applicant", info='how many different ratings each application gets.')
334
+ top_n = gr.Slider(1, 40, step=1, value=5, label="Top N")
335
+
336
+ judge_error = gr.Slider(0, 10, step=1, value=2, label="Judge Error")
337
+ judgment_coarse_graining_true_false = gr.Checkbox(value= True, label="Coarse grain judgements.")
338
+ judgment_coarse_graining = gr.Slider(0, 30, step=1, value=7, label="Coarse Graining Factor")
339
+ num_runs = gr.Slider(10, 1000, step=10,value=100, label="Number of Runs")
340
+
341
+ # The button to run the simulation
342
+ # Sliders for alpha and beta parameters of the beta distribution
343
+
344
+ with gr.Column():
345
+ with gr.Group():
346
+ beta_plot = gr.Plot(label="Applicants quality distribution")
347
+ gr.HTML(comment_distribution_image)
348
+
349
+ # Your existing plot output
350
+ plot_output = gr.Plot(label="Simulation Results",show_label=True)
351
+ #https://discuss.huggingface.co/t/gradio-changing-background-colour-in-all-devices/42519
352
+
353
+ # Function call on button click
354
+ run_button.click(
355
+ run_simulation_and_plot,
356
+ inputs=[num_runs, num_applicants, num_judges, ratings_per_applicant, top_n, alpha_slider, beta_slider, judge_error, judgment_coarse_graining,judgment_coarse_graining_true_false],
357
+ outputs=[plot_output]
358
+ )
359
+
360
+ alpha_slider.change(plot_beta_distribution, inputs=[alpha_slider, beta_slider,judge_error], outputs=[beta_plot])
361
+ beta_slider.change(plot_beta_distribution, inputs=[alpha_slider, beta_slider,judge_error], outputs=[beta_plot])
362
+ judge_error.change(plot_beta_distribution, inputs=[alpha_slider, beta_slider,judge_error], outputs=[beta_plot])
363
+
364
+ demo.load(plot_beta_distribution, inputs=[alpha_slider, beta_slider,judge_error], outputs=[beta_plot])
365
 
366
+ if __name__ == "__main__":
367
+ demo.launch(debug=True)
368