-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcentral_tendency.py
314 lines (215 loc) · 8.09 KB
/
central_tendency.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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
from collections import Counter
from typing import Union
from ast import literal_eval
import doctest
# Displays three common measures of central tendency as
# options for the user to select. to the user that will
COMMON_MEASUREMENTS = """\n\nSelect An Option:
1. Mean
2. Median
3. Mode
"""
def ask_user_yes_no(yes_no_question) -> bool:
"""Simplifies if/else to determine the correct answers from the user input.
Args:
yes_no_question: A string that asks user a yes or no question.
Returns:
True if the user's answer is in CHOICE_YES,
and False otherwise.
Prints a message to the user if their input are not similar
to the ones in CHOICE_YES and CHOICE_NO.
"""
CHOICE_YES = ("yes", 'y')
CHOICE_NO = ("no", 'n')
while True:
user_choice = input(yes_no_question).lower()
if user_choice in CHOICE_YES:
return True
elif user_choice in CHOICE_NO:
return False
else:
print("\nInvalid Input. Try again.")
def get_number_dataset() -> list:
"""Asks the user to input the dataset that is needed to
calculate mean, median, and mode.
Returns:
A list of integer or float numbers, depending on the data
type present in the dataset.
Prints out a message if the user tries to give non-numerical
values or providing less than 2 values.
"""
while True:
user_number_input = input("\nEnter Number Dataset: ").replace(
",", " ").split()
try:
number_dataset = [literal_eval(num) for num in user_number_input]
except ValueError:
print("\nNumbers Only For Mean and Median!")
print(f"\nYour Input: {''.join(user_number_input).strip('')}")
continue
if len(number_dataset) < 2:
print("\nPlease enter at least 2 values.")
else:
return number_dataset
def get_mode_dataset() -> Union[str, list[str]]:
"""Asks the user to input the values into the dataset
in order to get the frequency of those values.
Returns:
Strings or a list of strings containing the values in a data set.
Prints out a message if the dataset contains less than 2 values.
"""
while True:
user_mode_input = input(
"\nEnter Mode Dataset: ").replace(",", " ").split()
if len(user_mode_input) == 1:
for char in user_mode_input:
if len(char) > 1:
return char
if len(user_mode_input) < 2:
print("\nPlease enter at least 2 values.")
else:
return user_mode_input
def select_choice() -> int:
"""Asks the user to select an option that is used to calculate
the mean, median, or mode.
Returns:
An integer of the user's selection.
Prints out a list of math operations.
Prints out a message if the user inputs a value other than integer or
selects an option that is beyond the specified range of options.
"""
print(COMMON_MEASUREMENTS)
while True:
user_choice = input("Select an option | 1 | 2 | 3 |: ")
try:
user_choice = int(user_choice)
except ValueError:
print(f"\nInvalid Input: {user_choice!r}\n")
continue
if user_choice > 3:
print("\nOption selected must be from 1 to 5 only!\n")
else:
return user_choice
def calculate_mean(numbers_list: Union[list[int, float]]) -> float:
"""Calculates mean.
Args:
numbers_list: A list of integer or float numbers.
Returns:
A float number due to division.
Examples:
>>> calculate_mean([1, 2, 3, 4, 5])
3.0
>>> calculate_mean([2.4, 6.5, 3.1, 9.2, 13.5])
6.94
>>> calculate_mean([535, 120, 76, 32, 45])
161.6
"""
return sum(numbers_list) / len(numbers_list)
def calculate_median(numbers_list: Union[list[int, float]]) -> Union[int, float]:
"""Calculates median.
Args:
numbers_list: A list of integer or float numbers.
Returns:
An integer or a float number. The result of the calculation
is determined by the given dataset.
Examples:
>>> calculate_median([105, 321, 95, 64, 31])
95
>>> calculate_median([1.0, 2.0, 3, 4.0, 5])
3
>>> calculate_median([3.2, 12.5, 20, 21.2, 32, 51])
20.6
"""
num_length = len(numbers_list)
numbers_list.sort()
if not num_length % 2:
calc_median_1 = numbers_list[num_length//2]
calc_median_2 = numbers_list[num_length//2 - 1]
median = (calc_median_1 + calc_median_2)/2
else:
median = numbers_list[num_length//2]
return median
def find_mode(dataset: Union[str, list[str]]) -> Union[str, list[str]]:
"""Finds the frequency of a particular value.
Args:
dataset: A string or a list of strings.
Returns:
A string if a trivial case occurs or a list
containing a string of element/s.
A message telling the user that no mode was found
if the user gave a list of elements
that occur only once.
Examples:
>>> find_mode(['830', '476', '5664', 'Smash Mouth'])
'No Mode Found!'
>>> find_mode(['Shrek', 'Shrek', 'is', 'life'])
'Shrek'
>>> find_mode(['3', '3', '3'])
'3'
>>> find_mode(['nike', 'nike', 'adidas', '88', '25', '88'])
['nike', '88']
>>> find_mode('aabbccc')
'c'
>>> find_mode('abbbccc')
['b', 'c']
"""
counter = Counter(dataset)
if len(set(dataset)) == 1:
return set(dataset).pop()
elif len(counts := set(counter.values())) == 1:
return "No Mode Found!"
mode = [key for key, count in counter.items() if count == max(counts)]
return mode if len(mode) > 1 else mode.pop()
def check_choices(option: int):
"""Determines the option selected and perform a measure
of central tendency based on the selected choice.
Args:
option: An integer to determine which type of calculation
based on the user's selection.
Formats the result of mean calculation using locale aware separator.
"""
if option == 1 or option == 2:
dataset = get_number_dataset()
elif option == 3:
dataset = get_mode_dataset()
if option == 1:
mode_measurement = "Mean"
data = f"{calculate_mean(dataset):n}"
elif option == 2:
mode_measurement = "Median"
data = calculate_median(dataset)
elif option == 3:
mode_measurement = "Mode"
data = find_mode(dataset)
return print_result(mode_measurement, data)
def print_result(mode: str, result: Union[str, list[str]]) -> None:
"""Prints the result of the data set.
Args:
mode: A string that displays the selected option.
result: A string or a list of strings containing the result
of the selected option.
"""
print(f"\n{mode}: {result}")
def should_calculate_again() -> bool:
"""Asks the user if they want to calculate again.
Returns:
True if the user wants to perform another
calculation, False otherwise.
"""
return ask_user_yes_no("\n\nWould you like to perform "
"another calculation? (Y/N): ")
def main() -> None:
"""A function that initializes the calling of the
other functions to start the program.
Restarts program if should_calculate_again() returns True, else prints
a message telling the user that the program has exited.
"""
while True:
user_option = select_choice()
check_choices(user_option)
if not should_calculate_again():
break
print("\n\n-----Program Exited-----\n\n")
if __name__ == "__main__":
doctest.testmod()
main()