forked from AlexTruongX/cs70-predict-final
-
Notifications
You must be signed in to change notification settings - Fork 0
/
predict_70_final.py
138 lines (118 loc) · 5.09 KB
/
predict_70_final.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import numpy as np
import scipy.stats as st
import argparse
"""
@author Alex Truong
Predicts final score while factoring in 50% two-way clobber policy for CS70
"""
def mt1_zscore(raw_score):
# Updated 7/17/22
mean = 134.76
std = 36.93
return (raw_score - mean)/std
def avg_std(score1, score2):
return (score1 + score2)/2
def grade(percentile):
if 0.95 <= percentile <= 100:
return 'A+'
elif 0.78 <= percentile <= 0.95:
return 'A'
elif 0.66 <= percentile <= 0.78:
return 'A-'
elif 0.43 <= percentile <= 0.66:
return 'B+'
elif 0.26 <= percentile <= 0.43:
return 'B'
elif 0.16 <= percentile <= 0.26:
return 'B-'
elif 0.11 <= percentile <= 0.16:
return 'C+'
elif 0.07 <= percentile <= 0.11:
return 'C'
elif 0.04 <= percentile <= 0.07:
return 'C-'
else:
return 'F'
def grade_to_z(grade):
grade = grade.strip().upper()
# Percentiles pulled from Berkeley Time (historically accurate)
grades_to_p = {'A+' : [0.95, 0.99], 'A' : [0.78, 0.95], 'A-' : [0.66, 0.78],
'B+' : [0.43, 0.66], 'B' : [0.26, 0.43], 'B-' : [0.16, 0.26],
'C+':[0.11, 0.16], 'C' : [0.07, 0.11], 'C-' : [0.04, 0.07],
'F' : [0.01, 0.04]}
if grade not in grades_to_p:
print(f"You are actually trolling. {grade} is not a possible grade.")
exit()
else:
percentile = grades_to_p[grade]
lower_z = round(st.norm.ppf(percentile[0]), 2) # norm.ppf is inverse of norm.cdf
upper_z = round(st.norm.ppf(percentile[1]), 2)
return [lower_z, upper_z]
def predict_final_std_all(mt1_raw):
grades = ['A+', 'A', 'A-', 'B+', 'B', 'B-', 'C+', 'C', 'C-', 'F']
mt1_std = mt1_zscore(mt1_raw)
print(f'Midterm 1 std: {round(mt1_std, 2)}')
print(f'Here are all the final std ranges you need to hit to reach a certain grade:')
for grade in grades:
grade_range = grade_to_z(grade)
lower = predict_final_std_exact(grade_range[0], mt1_raw, should_print=False, show_all=True)
upper = predict_final_std_exact(grade_range[1], mt1_raw, should_print=False, show_all=True)
low_str = type(lower) is str
up_str = type(upper) is str
if low_str and up_str:
print(f"{grade}: Probabilistically Impossible")
else:
print(f'{grade}: ({"-3.0 or lower" if low_str else lower},{"3.0 or higher" if up_str else upper})')
def predict_final_std_range(grade, mt1_raw):
grade_range = grade_to_z(grade)
mt1_std = mt1_zscore(mt1_raw)
lower = predict_final_std_exact(grade_range[0], mt1_raw, should_print=False)
upper = predict_final_std_exact(grade_range[1], mt1_raw, should_print=False)
print(f'Midterm 1 std: {round(mt1_std, 2)}')
print(f'To get an {grade}, you need to get a final std between ({lower},{upper}) based on past data.')
def predict_final_std_exact(desired_std, mt1_raw, should_print=True, show_all=False):
mt1_std = mt1_zscore(mt1_raw)
for final_std in np.arange(-3, 3, 0.001):
clobber = avg_std(mt1_std, final_std)
delta = 0.01
overall_std = round(avg_std(max(mt1_std, clobber)*.75, final_std), 3)
if abs(overall_std - desired_std) <= delta:
overall_p = st.norm.cdf(overall_std) # converts to percentile in normalized distribution
overall_grade = '\033[1m' + grade(overall_p)
if should_print:
print(f'Desired std for a grade of an {overall_grade}\033[0m: {desired_std}')
print(f'Midterm 1 std: ~{round(mt1_std, 2)}')
print(f'You need a final std of {round(final_std, 2)} to get an {overall_grade} \033[0min the class.')
return
else:
return round(final_std, 2)
if show_all:
return "impossible"
else:
print("Probabilistically Impossible")
exit()
def predict():
print('\u2500' * 10)
print("Option A: I want to end the class with <desired grade>, what final std range do I need to score within?")
print("Option B: I want to end the class with <exact desired std>, what exact final std do I need?")
print("Option C: I want a final std range for all possible grades if I scored <raw score> on Midterm 1")
print(' ' * 10)
choice = input('Which option sounds like you? A, B, or C? ').strip().upper()
if choice == 'A':
mt1_raw = input('Enter your midterm 1 raw score: ')
grade = input('Enter the grade you want to end the class with (e.g B+): ')
print()
predict_final_std_range(grade, float(mt1_raw))
elif choice == 'B':
mt1_raw = input('Enter your midterm 1 raw score: ')
overall_desired_std = input('Enter the standard deviation you want to end the class with: ')
print()
predict_final_std_exact(float(overall_desired_std), float(mt1_raw))
elif choice == 'C':
mt1_raw = input('Enter your midterm 1 raw score: ')
print()
predict_final_std_all(float(mt1_raw))
print('\u2500' * 10)
if __name__ == "__main__":
# calling the main function
predict()