Step 1: Read each one of the following problems and its instructions, and examine the sample inputs and outputs carefully. Then, start coding each problem by creating a .py file or an iPython notebook. Make sure your code is debugged prior to logging on to Stepik.

Step 2: After you tested your code with ALL sample inputs and outputs, you may logon to Stepik to submit your code for final grading. In order to do that, navigate to Stepik Submission Portal on Canvas and click on the link to go to the Stepik module. You should copy and paste from your Python file to the Stepik code entry box. You can run (and rerun) your code to see how it does on the test cases (some of which you haven't seen).

Step 3: When you're satisfied that your code will pass all the tests, then submit your code. You
may resubmit your code until the assignment is due.


Problem 1 – Summary Statistics (50 points)
Write a program which calculates summary statistics for a set of input integers. Your program should get a single line of comma-separated integers from the user. Then, it should calculate the mean, median, mode, minimum, and maximum for the list of integers. You may not use the built-in functions numpy.mean(), numpy.median(), min(), or max()! How to calculate these values is described below:


1) Mean: the mean can be calculated by adding all the values in the list, and dividing by how many values there are in the list. The mean should be a float.

2) Median: the median can be calculated by sorting the values in the list, and taking the central value if there are an odd number of elements, or taking the average of the two central values if there is an even number of elements. The median should be a float.

3) Mode: for this assignment, we do NOT want you to calculate the most frequently occurring number! Instead, we want you to calculate a bucketed mode, and calculate which multiple of 10 has the most frequent occurrence (from that multiple of 10 up to but not including the next multiple). For example, if you have the numbers [26, 31, 34, 38, 40, 40] the mode is 30, because 0-9 has no values, 10-19 has no values, 20-29 has 1 value, 30-39 has 3 values, and 40-49 has 2 values. The mode is an integer, and should be a multiple of 10.

4) Minimum: the minimum is the smallest value in the list.

5) Maximum: the maximum is the largest value in the list.

Your output should be precise in how it prints values. All floats should have two decimal places, and all numerical values should be aligned in the output. You can use the “format()” function on a string to achieve this. For example, the code "{:10s}".format(my_str) will produce a string with at least 10 characters when formatting, and "{:8.3f}".format(my_float) will produce a string with at least 8 characters and will have three digits after the decimal. Experiment with this to understand how it works!

These summary statistics can be helpful in understanding the meaning or significance in a dataset. However, they don’t paint the whole picture. We will explore another way of visualizing the data in Problem 2.


Input
Output
119,120,107,121,138,173,101,113,1
34,141,117,110,130,111,128,118,12
0,92,111,89,163,134,104,115,112,1
25,176,107,155,100,138,137,110,12
4,122,86,135,69,119,145,145,100,1
31,110,130,114,147,136,112,114,12
5,122,103,124,93,132,103,130,73,1
15,120,126,126,119,94,151,105,130
,117,126,128,85,105,125,109,124,1
19,122,102,90,101,131,132,117,104
,143,129,119,120,137,123,140,163,
163,156,113,116,129,114,112,123,1
33,165,140,101,83,132,152,123,103
,131,107,138,96,129,119,129,143,1
62,129,123,126,102,151,130,111,13
6,130,113,127,128,117,112,113,107
,141,89,131,136,110,152,134,112,8
9,118,119,107,148,99,128,108,137,

98,125,90,119,96,108,114,107,126,

102,111,83,118,117,96,98,120,140,
96,74,97,90,100,121,128,117,118,1
36,101,137,125,104,110,126,112,10
4,85,125,138,124,108,112,126,139,
129,143,121,126

Mean 119.88
Median 120.00
Mode 110
Minimum 69
Maximum 176


Example 2:
Input
Output
55,106,78,62,120,74,148,66,67,60,
105,103,90,81,109,107,82,132,122,
114,127,120,70,74,108,114,70,92,1
12,105,81,69,87,85,83,68,100,84,8
2,72,131,88,90,93,90,76,101,91,96
,83,62,100,63,211,68,56,69,70,97,
73,104,90,55,78,84,52,113,110,121
,113,107,59,82,103,94,115,123,75,
105,87,78,91,79,74,100,72,84,89,7
6,83,93,90,74,181,120,61,72,144,8
7,122,151,79,111,71,75,91,155,84,
78,86,97,79,66,95,92,91,67,174,13
7,62,141,135,69,94,61,82,60,126,1
10,96,69,91,87,147,71,115,82,118,
63,124,115,97,64,79,154,79,80,101
,56,114,125,123,75,101,77,110,62,
103,87,74,92,84,114,116,102,91,57
,80,147,90,129,95,147,82,67,102,1
37,109,82,75,89,85,97,102,90,140,
167,65,92,97,59,100,88,83,80,142,
68,83,99,51
Mean 94.33
Median 90.00
Mode 80
Minimum 51
Maximum 211
Example 3:


Input
Output
110,180,148,146,160,144,150,136,1
86,148,218,226,140,150,148,128,15
0,162,146,102,124,152,134,130,196
,168,150,180,134,110,160,166,162,
110,192,244,124,214,144,186,152,1
66,138,132,134,144,156,162,186,14
6,130,180,204,164,182,138,142,212
,112,152,146,138,176,180,144,144,
226,162,130,142,170,166,130,206,1
34,164,154,164,116,150,156,166,18
0,128,114,212,116,120,156,146,202
,180,122,160,194,202,94,218,172,1
50,164,192,174,232,138,212,208,12
0,157,184,170,172,182,176,136,142
,157,152,206,136,154,198,152,150,
152,156,134,186,166,174,112,174,1
24,172,182,162,172,168,140,174,15
6,160,152,222,146,192,178,150,168
,228,124,172,160,156,186,196,132,
140,118,128,206,166,178,176,120,1
10,152,152,188,168,140,172,166,16
0,138,174,200,134,146,178,124,186
,190,206,186,138,160,150,152,157,
164,180,146,188,148,138,168,170,1
92,208

Mean 160.85
Median 158.50
Mode 150
Minimum 94
Maximum 244

Example 4:
Input
Output
143,103,34,77,140,19,87,48,144,50
,148,128,21,74,13,98,146,107,134,
115,78,92,121,31,146,29,147,89,6,
119,58,31,70,73,89,106,93,142,60,
32,122,74,82,23,51,137,132,35,93,
15,147,48,30,158,93,37,140,21,121
,155,60,41,56,96,45,93,111,94,17,
138,98,73,66,0,35,84,44,56,38,87,
156,117,93,112,9,137,88,40,115,10
1,157,34,99,130,12,28,78,150,112,
101,51,50,72,131,107,118,50,17,5,
123,105,3,128,153,57,89,85,79,125
,112,31,63,75,158,68,33,20,21,125
,27,148,110,134,51,53,12,77,12,29
,94,107,63,82,129,11,83,36,27,133
,29,42,37,98,23,47,102,79,7,49,38
,121,11,38,38,58,148,66,76,69,2,5
6,67,16,64,119,120,131,79,110,2,1
24,30,27,46,65,44,120,88,82,48,58
,101,59,125,11,147,149,23,46,130
Mean 77.47
Median 77.00
Mode 30
Minimum 0
Maximum 158

Problem 2 - Histogram

Write a program which prints a bucketed histogram for a set of values, coming from a commaseparated list of integers input by the user. Similar to how you calculated the mode in Problem 1, group all numbers into which multiple-of-ten they fall into (10 → 10, 23 → 20, 148 → 140). For each multiple of ten, first print the multiple using at least three spaces (you can use the formatting described in Problem 1), then a space, then some number of pound signs padded with spaces so that there are always 16 positions (this will be explained in the next paragraph), another space, and finally how many numbers there were in the dataset for that multiple of ten.

When calculating how many pound signs to print for a given bucket (multiple of ten), first you need to find what the maximum count is for all buckets. For example, if we have the numbers [26, 31, 34, 38, 40, 40], then the 0 bucket has zero values, the 10 bucket has zero values, the 20 bucket has one value, the 30 bucket has three values, and the 40 bucket has two values: the maximum count for this dataset is 3. For each bucket, find the ratio of that bucket’s count over the maximum value: the 0 bucket and the 10 bucket each have a ratio of 0/3 (0.0), the 20 bucket has a ratio of 1/3 (0.333), the 30 bucket has a ratio of 3/3 (1.0), and the 40 bucket has a ratio of 2/3 (0.666). Multiply each of these ratios by 16, round down to the nearest whole number, and that is how many pound signs to print for each bucket: the 0 and 10 bucket get zero pound signs (int(0.0 * 16) == 0), the 20 bucket gets five pound signs (int(0.333 * 16) == 5), the 30 bucket gets 16 pound signs (int(1.0 * 16) == 16), and the 40 bucket gets 10 pound signs (int(0.666 * 16) == 10). The final result should look like this:

0 0
10 0
20 ##### 1
30 ################ 3
40 ########## 2

This may seem complicated, but the result is a visualization which better shows the underlying story of the dataset! In fact, you are beginning to learn about different types of distributions: the numbers in Example 1 come from a Gaussian distribution, the numbers in Example 2 come from a Gamma distribution, the numbers in Example 3 come from a Poisson distribution, and the numbers in Example 4 come from a Uniform distribution.
Example 1:
Input
Output
119,120,107,121,138,173,101,113,1
34,141,117,110,130,111,128,118,12
0,92,111,89,163,134,104,115,112,1
25,176,107,155,100,138,137,110,12
4,122,86,135,69,119,145,145,100,1
31,110,130,114,147,136,112,114,12
5,122,103,124,93,132,103,130,73,1
15,120,126,126,119,94,151,105,130
,117,126,128,85,105,125,109,124,1
19,122,102,90,101,131,132,117,104
,143,129,119,120,137,123,140,163,
163,156,113,116,129,114,112,123,1
33,165,140,101,83,132,152,123,103
,131,107,138,96,129,119,129,143,1
62,129,123,126,102,151,130,111,13
6,130,113,127,128,117,112,113,107
,141,89,131,136,110,152,134,112,8
9,118,119,107,148,99,128,108,137,
98,125,90,119,96,108,114,107,126,
102,111,83,118,117,96,98,120,140,
96,74,97,90,100,121,128,117,118,1
36,101,137,125,104,110,126,112,10
4,85,125,138,124,108,112,126,139,
129,143,121,126
0 0
10 0
20 0
30 0
40 0
50 0
60 1
70 2
80 ## 8
90 #### 14
100 ########## 29
110 ################ 45
120 ################ 45
130 ########### 31
140 #### 12
150 ## 6
160 # 5
170 2

Example 2:
Input
Output
55,106,78,62,120,74,148,66,67,60,
105,103,90,81,109,107,82,132,122,
114,127,120,70,74,108,114,70,92,1
12,105,81,69,87,85,83,68,100,84,8
2,72,131,88,90,93,90,76,101,91,96
,83,62,100,63,211,68,56,69,70,97,
73,104,90,55,78,84,52,113,110,121
,113,107,59,82,103,94,115,123,75,
105,87,78,91,79,74,100,72,84,89,7
6,83,93,90,74,181,120,61,72,144,8
7,122,151,79,111,71,75,91,155,84,
78,86,97,79,66,95,92,91,67,174,13
7,62,141,135,69,94,61,82,60,126,1
10,96,69,91,87,147,71,115,82,118,
63,124,115,97,64,79,154,79,80,101
,56,114,125,123,75,101,77,110,62,
103,87,74,92,84,114,116,102,91,57
,80,147,90,129,95,147,82,67,102,1
37,109,82,75,89,85,97,102,90,140,
167,65,92,97,59,100,88,83,80,142,
68,83,99,51
0 0
10 0
20 0
30 0
40 0
50 #### 9
60 ########### 24
70 ############## 30
80 ################ 34
90 ############## 31
100 ########## 23
110 ####### 16
120 ###### 13
130 ## 5
140 ### 8
150 # 3
160 1
170 1
180 1
190 0
200 0
210 1
Example 3:
Input
Output
110,180,148,146,160,144,150,136,1
86,148,218,226,140,150,148,128,15
0,162,146,102,124,152,134,130,196
,168,150,180,134,110,160,166,162,
110,192,244,124,214,144,186,152,1
66,138,132,134,144,156,162,186,14
6,130,180,204,164,182,138,142,212
,112,152,146,138,176,180,144,144,
226,162,130,142,170,166,130,206,1
34,164,154,164,116,150,156,166,18
0,128,114,212,116,120,156,146,202
,180,122,160,194,202,94,218,172,1
50,164,192,174,232,138,212,208,12
0,157,184,170,172,182,176,136,142
,157,152,206,136,154,198,152,150,
152,156,134,186,166,174,112,174,1
24,172,182,162,172,168,140,174,15
6,160,152,222,146,192,178,150,168
,228,124,172,160,156,186,196,132,
140,118,128,206,166,178,176,120,1
10,152,152,188,168,140,172,166,16
0,138,174,200,134,146,178,124,186
,190,206,186,138,160,150,152,157,
164,180,146,188,148,138,168,170,1
92,208

0 0
10 0
20 0
30 0
40 0
50 0
60 0
70 0
80 0
90 1
100 1

110 ##### 10

120 ###### 12
130 ########### 22
140 ############ 24
150 ################ 30
160 ############### 29
170 ########## 20
180 ########## 20
190 #### 9
200 ##### 10
210 ### 6
220 ## 4
230 1
240 1

Example 4:
Input
Output
143,103,34,77,140,19,87,48,144,50
,148,128,21,74,13,98,146,107,134,
115,78,92,121,31,146,29,147,89,6,
119,58,31,70,73,89,106,93,142,60,
32,122,74,82,23,51,137,132,35,93,
15,147,48,30,158,93,37,140,21,121
,155,60,41,56,96,45,93,111,94,17,
138,98,73,66,0,35,84,44,56,38,87,
156,117,93,112,9,137,88,40,115,10
1,157,34,99,130,12,28,78,150,112,
101,51,50,72,131,107,118,50,17,5,
123,105,3,128,153,57,89,85,79,125
,112,31,63,75,158,68,33,20,21,125
,27,148,110,134,51,53,12,77,12,29
,94,107,63,82,129,11,83,36,27,133
,29,42,37,98,23,47,102,79,7,49,38
,121,11,38,38,58,148,66,76,69,2,5
6,67,16,64,119,120,131,79,110,2,1
24,30,27,46,65,44,120,88,82,48,58
,101,59,125,11,147,149,23,46,130
0 ####### 8
10 ########## 12
20 ############ 14
30 ################ 18
40 ########### 13
50 ############# 15
60 ######### 11
70 ############# 15
80 ########### 13
90 ########### 13
100 ######## 10
110 ########## 12
120 ############ 14
130 ######### 11
140 ############ 14
150 ######

Extra Credit: Problem 3 – Students and Courses (50 points)
Below is a framework of classes for Student, Students, Class, and Classes. First fill in the functionality for each method in each of those classes. After that, you’ll need to code up the functions for each function stub below.
Fill in the following framework of classes:

class Students:




def __init__(self, students=[]):



self.students = dict()



for student in self.students:



self.students[student.fetch_number()] = student



def add_student(self, student):



''' Add a student to the students dictionary.



Return True if successful, else return False. '''



# YOUR CODE HERE



def add_students(self, students):



''' Add list of students to the students dictionary. '''



# YOUR CODE HERE



def fetch_students(self):



''' Return the students dictionary. '''



return self.students



def fetch_student(self, studentID):



''' Return the student with this student number (ID). '''



# YOUR CODE HERE



def fetch_student_numbers(self):



return list(self.students.keys())



class Student:



def __init__(self, first_name = 'First_Name',



last_name = 'Last_Name',



student_number = '0000'):



self.first_name = first_name



self.last_name = last_name



self.student_number = student_number



self.past_courses = [] # course numbers (IDs)



self.current_courses = [] # course numbers (IDs)



def fetch_name(self):



''' Return the name of this student, (first_name, last_name). '''



# YOUR CODE HERE



def fetch_number(self):



''' Return this students student number (ID). '''



# YOUR CODE HERE



def add_past_courses(self, course_IDs = []):



''' Append this list of past course numbers (IDs)



to the list of this student's past courses. '''



# YOUR CODE HERE



def add_current_courses(self, course_IDs = []):



''' Append this list of current course numbers (IDs)



to the list of this student's current courses. '''



# YOUR CODE HERE



def add_course(self, courseID):



''' Append this course number (ID) to the list of current_courses. '''



# YOUR CODE HERE



def fetch_past_courses(self): return self.past_courses
def fetch_current_courses(self): return self.current_courses
def drop_current_course(self, courseID):



''' Drop this course number (ID) from the list of current_courses.



Return True if successful, else return False. '''



#YOUR CODE HERE



def drop_all_current_courses(self):



''' Empty the current_courses list. '''



self.current_courses = []



def current_to_past_courses(self):



''' Move all current course numbers (IDs) to the list of past_courses. '''



self.past_courses += self.current_courses



self.drop_all_current_courses()


class Courses:


def __init__(self, courses=[]):



self.courses = dict()



# This dictionary has course numbers (IDs) for its keys.



for course in courses:



self.courses[course.fetch_course_number()] = course



def add_course(self, course):



''' Add this course to the courses dictionary.



Return True if successful, else return False. '''



# YOUR CODE HERE



def fetch_number(self):



''' Return this students student number (ID). '''



# YOUR CODE HERE



def add_past_courses(self, course_IDs = []):



''' Append this list of past course numbers (IDs)



to the list of this student's past courses. '''



# YOUR CODE HERE



class Course:



def __init__(self, course_name = 'CS 000',



course_number = '0000',



semester = 'Fall',



year = 2019,



enrolled_students = []):



''' The enrolled_students argument is a list of student numbers (Ids) '''



self.course_name = course_name



self.course_number = course_number



self.semester = semester



self.year = year



# This dictionary has student numbers (IDs) as keys,



# and the student's grade as the value.



self.enrolled_students = {student : 'NG' for student in enrolled_students}



def fetch_course_name(self):



''' Return this course name. '''



# YOUR CODE HERE



def fetch_course_number(self):



''' Return this course number. '''



# YOUR CODE HERE



def fetch_enrolled_studentIDs(self):



''' Return a list of enrolled student numbers (IDs). '''



return list(self.enrolled_students.keys())



def fetch_enrolled_students(self):



''' Return the dictionary of enrolled students and their grades. '''



# YOUR CODE HERE



def fetch_when_offered(self):



''' Return (semester, year) of when this course was offered. '''



return (self.semester, self.year)


def enroll_student(self, studentID):
''' Enroll a student in the course.
Return True if successful, else return False. '''
# YOUR CODE HERE
def enroll_students(self, studentIDs):
# YOUR CODE HERE
def drop_student(self, studentID):
''' Drop this student from the course.
Return True if successful, else return False. '''
# YOUR CODE HERE
def submit_grade(self, studentID, grade):
''' Enter a grade for this student. '''
# YOUR CODE HERE
def fetch_grades(self):
''' Return the enrolled_students dictionary. '''
return self.enrolled_students
def fetch_grade(self, studentID):
''' Return this student’s grade. '''
# YOUR CODE HERE
In addition to completing the above classes, The functions you’ll implement are:
roll_courses():
''' A semester has finished, and the grades are all in. This function rolls all the current courses for each student from current_courses to past_courses. '''
compute_gpa(studentID):
''' Returns the GPA of the student with input student number(ID), rounded off to 2 decimal places. '''
compute_gpas(studentIDs):

''' Retuns a list of the gpas of all the students on the input list. '''

best_student():

''' Returns the name of the student with the highest GPA, in the format: FirstName LastName (with one space between them) '''
worst_student():
''' Returns the name of the student with the lowest GPA, in the format: FirstName LastName (with one space between them) '''
compute_mean_GPA():
''' Return the mean GPA of all students, rounded off to 2 decimal places. '''
compute_mean_course_GPA(courseID):
''' Returns the mean GPA for the grades assigned to students who took this course. '''


IMPORTANT: Instantiate these two classes as follows:
# Instantiate a Courses class and a Students class
all_courses = Courses()
all_students = Students()
You may refer to these globals from inside the classes, as you need them, and also from within the functions you’ll be implementing.

NOTE: Although there is only one Example, in this input are many tests you can use in debugging your code. They only output error messages if your code was incorrect in some way, so the messages will help you find bugs in your code. The other cases you’ll be graded on will be very similar, using your code to answer very similar questions, but for other students, classes, and grades.

Example 1:

Input
# Create some courses.
c001 = Course(course_name = 'CS 1', course_number='001', semester='Spring', year=2019)
c002 = Course(course_name = 'CS 2', course_number='002', semester='Spring', year=2019)
c003 = Course(course_name = 'CS 3', course_number='003', semester='Spring', year=2019)
c004 = Course(course_name = 'CS 4', course_number='004', semester='Spring', year=2019)
c005 = Course(course_name = 'CS 5', course_number='005', semester='Spring', year=2019)
# Add these classes to all_courses.
all_courses.add_courses([c001, c002, c003, c004, c005])
# Check that these classes were added correctly.
if all_courses.fetch_course_numbers() != ['001', '002', '003', '004', '005']:
print( 'Error adding courses')
# Create some students.
s001 = Student('Alan', 'Turing', '001')
s002 = Student('Grace', 'Hopper', '002')
s003 = Student('Ananda', 'Dev', '003')
s004 = Student('Lao', 'Tse', '004')
s005 = Student('Ada', 'Lovelace', '005')
s006 = Student('Claude', 'Shannon', '006')
s007 = Student('Radia', 'Perlman', '007')
# Add these students to all_students.
all_students.add_students([s001, s002, s003, s004, s005, s006, s007])
# Check that students were added correctly.
if all_students.fet