Average of a nested dictionary - python

I have a nested dictionary for a grade-book program that looks like this(this is just an example, it could be any number of students or tests):
workDictionary = {'kevin': {'Test1': 97, 'Test2': 84, 'Test3': 89},
''Bob':{'Test1': 67, 'Test2': 74, 'Test3': 59},
'carol':{'Test1': 47, 'Test2': 94, 'Test3': 79},
'ted':{'Test1': 67, 'Test2': 64, 'Test3': 99}}
And I want to get the average of the innermost values, for instance:
finalGrade = {}
for k,v in workDictionary.iteritems():
finalGrade[k] = sum(v)/ float(len(v))
There are other factors however, i'm using pickling and an undefined amount of students and tests. This is the full program:
# Modules
import pickle
def dumpPickle(fileName):
pickle.dump(workDictionary, open(fileName,'wb'))
return
def loadUnPickle(fileName):
global workDictionary
workDictionary = pickle.load(open(fileName, 'rb'))
return(workDictionary)
workDictionary = {}
keys = workDictionary.keys()
values = workDictionary.values()
def myMenu():
mySelect = -1
while mySelect != 0:
print("\n1. Open Dictionary File\n"+
"2. Create/Write to a Dictionary\n"+
"3. Add a New Student\n"+
"4. Find a Student's Scores\n"+
"5. Add a New Student Score\n"+
"6. Display Dictionary Data\n"+
"0. Exit\n"
)
mySelect = int(input("Enter Menu Number: "))
if mySelect == 1:
fileName = input("Enter file name")
print("\nyour file is now open")
loadUnPickle(fileName)
elif mySelect == 2:
fileName = input("please create a new file.")
print("\nyour new file is now open")
elif mySelect == 3:
newStudent = input("Enter the new student's name")
firstTest = input("Enter the name of the first test")
testGrade = input("Enter the new student's first grade")
addDictionary = {newStudent:{firstTest:testGrade}}
workDictionary.update(addDictionary)
print("\n" + newStudent + str(workDictionary[newStudent]))
dumpPickle(fileName)
elif mySelect == 4:
print("\nEnter student name")
myName = input()
for name in workDictionary:
if name == myName:
print("\n",workDictionary.get(myName))
elif mySelect == 5:
print("\nEnter student name ")
myName = input()
print("\nEnter assignment to add or update")
myValue = input()
for name in workDictionary:
if name == myName:
newGrade = input("Enter new Grade")
workDictionary[name][myValue]= newGrade
dumpPickle(fileName)
print("\n" + name + str(workDictionary[name]))
elif mySelect == 6:
print(workDictionary)
return
# Main Loop
I want to add another menu selection that takes the average of a certain student and displays it.

This is what i wrote, but you can rewrite it so it would fir better in your program:
def student_avg(student):
summ = 0
grades_num = 0
for test, grade in student.items():
summ += grade
# unless you aren't sure that grade would be a int, in which case add exception
grades_num += 1
average = summ / grades_num
return average
average = student_avg(workDict["kevin"])

You can use a Dict Comprehension
from statistics import mean
avg_grades = {name: mean(tests.values()) for (name, tests) in workDictionary.items()}
The result stored in avg_grades will be:
{'Bob': 66.66666666666667,
'carol': 73.33333333333333,
'kevin': 90.0,
'ted': 76.66666666666667}

Related

Last paragraph of code is running when it does not need to

I am working on a username generator in Python, and everything is going well but one thing: the "IF" statement at the very end of the code is malfunctioning. The first part of the code works, but the last paragraph kicks in, even when I have typed in a supposedly valid choice.
The code:
[import random, tkinter
#variables that make up the name. the "1" symbolises the first part and the "2" symbolises the last part of the username
general1 = \["noob","baby","dude","soldier","discount"\]
general2 = \["kid","plant","painter","officer","conscience"\]
animal1 = \["frantic","fiesty","quick","quiet","loud"\]
animal2 = \["ferret","dog","hampster","cat","rabbit"\]
food1 = \["delicious","disgusting","stinky","fried","bitter"\]
food2 = \["pickle","chocolate","rice","water","lemonade"\]
name1 = \["dylan","eve","chris","simon","adele"\]
name2 = \["christopher","sharp","smith","james","baker"\]
#the main part
category = str(input("**USERNAME GENERATOR**\n\nWhat category do you want?\n1 for general\n2 for animals\n3 for food\n4 for human names\n\n"))
if category == "1":
output1 = random.choice((general1))
output2 = random.choice((general2))
endNumber = random.randint(0, 100)
print("\nYour random username is: ",output1,output2,endNumber)
if category == "2":
output1 = random.choice((animal1))
output2 = random.choice((animal2))
endNumber = random.randint(0, 100)
print("\nYour random username is: ",output1,output2,endNumber)
if category == "3":
output1 = random.choice((food1))
output2 = random.choice((food2))
endNumber = random.randint(0, 100)
print("\nYour random username is: ",output1,output2,endNumber)
if category == "4":
output1 = random.choice((name1))
output2 = random.choice((name2))
endNumber = random.randint(0, 100)
print("\nYour random username is: ",output1,output2,endNumber)
if category != ("1","2","3","4"):
print("\nPlease enter a valid option:")
category = str(input("What category do you want?\n1 for general\n2 for animals\n3 for food\n4 for human names\n\n"))][1]
if category != ("1","2","3","4"):
This only checks if category is equal to the tuple ("1","2","3","4"). You want to check if category is equal to any value of the tuple. Do that by changing that line to this:
if category not in ("1","2","3","4"):
After an if statement ends, the next line is executed.
In your case, the next line is the next if paragraph.
And after that the next one and so on.
e.g.:
if a:
do_action_a()
if b:
do_action_b()
if c:
do_action_c()
if not(a or b or c):
do_action_all_others()
Here you will always execute each paragraph independently of the previous one(s).
To avoid this, you can put each of the following statements in the
else of the previous if statement:
if a:
do_action_a()
else:
if b:
do_action_b()
else:
if c:
do_action_c()
else:
do_action_all_others()
However, there is an idiom for this: elif
so the pythonic is to use:
if a:
do_action_a()
elif b:
do_action_b()
elif c:
do_action_c()
else:
do_action_all_others()
Change the last conditional statement to
if category not in ('1', '2', '3', '4'):
print('\nPlease enter a valid option:')
category = str(input('. . .'))
My Refined Implementation of your code
import random
general_first = ['noob', 'baby', 'dude', 'soldier', 'discount']
general_last = ['kid', 'plant', 'painter', 'officer', 'conscience']
animal_first = ['frantic', 'fiesta', 'quick', 'quiet', 'loud']
animal_last = ['ferret', 'dog', 'hamster', 'cat', 'rabbit']
food_first = ['delicious', 'disgusting', 'stinky', 'fried', 'bitter']
food_last = ['pickle', 'chocolate', 'rice', 'water', 'lemonade']
name_first = ['dylan', 'eve', 'chris', 'simon', 'adele']
name_last = ['christopher', 'sharp', 'smith', 'james', 'baker']
def endNumber(): return str(random.randint(0, 100))
def firstName(values): return random.choice(values)
def lastName(values): return random.choice(values)
def generate_usernames(category):
if category == 1:
return firstName(general_first) + lastName(general_last) + endNumber()
elif category == 2:
return firstName(animal_first) + lastName(animal_last) + endNumber()
elif category == 3:
return firstName(food_first) + lastName(food_last) + endNumber()
elif category == 4:
return firstName(name_first) + lastName(name_last) + endNumber()
else:
return None
input_prompt = "What category do you want?" \
"\n'1' for general" \
"\n'2' for animals" \
"\n'3' for food" \
"\n'4' for human names" \
"\n\nCHOICE : "
invalid_prompt = "\nPlease enter a valid option"
print('\tUSERNAME GENERATOR')
while True:
input_category = int(input(input_prompt))
username = generate_usernames(input_category)
if username is not None:
print(f'Your Random Username is : {username}')
break
else:
print(invalid_prompt)

Getting UnboundLocalError while executing the program

I am writing a code for an application in Python. So far I have written this. I am getting an error at line 68 i.e. shopping_list = shopping_list.append(temp_df, ignore_index=True). The error message is given right after the code. I need to make a list where I am appending all the items added to the cart.
import sys
class Shopping:
def add_item(self):
items = int(input('Enter the number of items you want to send'))
for i in range(0, items):
print('Enter the details for the item')
type_pack = input('Enter the type(letter or parcel):')
weight = input('Enter the weight of the product: ')
destination = input('Enter the destination of the product: ')
zone = countries_zones.loc[countries_zones['Destination'] == destination.capitalize(), 'Zones'].iloc[0]
zone1 = int(zone[-1])
cost = 0
if type_pack.lower() == 'parcel':
if float(weight) < 3:
cost = parcel_by_sea_dataset[zone][parcel_by_sea_dataset.Weight == 'Over 2.5 kg up to 3kg'].iloc[0]
if cost == '-':
print("Sorry, no parcel services available for ", destination)
print('The cost of your stamp is', cost)
elif 3 <= float(weight) < 5:
cost = parcel_by_sea_dataset[zone][parcel_by_sea_dataset.Weight == 'Up to 5kg'].iloc[0]
if cost == '-':
print("Sorry, no parcel services available for ", destination)
print('The cost of your stamp is', cost)
elif 5 <= float(weight) < 10:
cost = parcel_by_sea_dataset[zone][parcel_by_sea_dataset.Weight == 'Up to 10kg'].iloc[0]
if cost == '-':
print("Sorry, no parcel services available for ", destination)
print('The cost of your stamp is', cost)
elif 10 <= float(weight) < 15:
cost = parcel_by_sea_dataset[zone][parcel_by_sea_dataset.Weight == 'Up to 15kg'].iloc[0]
if cost == '-':
print("Sorry, no parcel services available for ", destination)
print('The cost of your stamp is', cost)
elif 15 <= float(weight) < 20:
cost = parcel_by_sea_dataset[zone][parcel_by_sea_dataset.Weight == 'Up to 20kg'].iloc[0]
if cost == '-':
print("Sorry, no parcel services available for ", destination)
print('The cost of your stamp is', cost)
else:
print("please enter a number between 0-20")
print('Please chose the option below')
print('1. Add to cart')
print('2. Go back')
selection = input('Enter your choice: ')
if selection == '1':
temp_df = pd.DataFrame({'Item Type': [type_pack],
'Weight': [weight],
'Destination': [destination],
'Cost': [cost]})
shopping_list = shopping_list.append(temp_df, ignore_index=True)
print(shopping_list)
def delete_item(self):
pass
def amend_item(self):
pass
def print_list(self):
pass
def print_receipt(self):
pass
Traceback (most recent call last):
File "C:/Divyam Projects/oops1/stamp.py", line 114, in <module>
mainMenu()
File "C:/Divyam Projects/oops1/stamp.py", line 99, in mainMenu
shopping.add_item()
File "C:/Divyam Projects/oops1/stamp.py", line 68, in add_item
shopping_list = shopping_list.append(temp_df, ignore_index=True)
UnboundLocalError: local variable 'shopping_list' referenced before assignment
Thank you for your help in advance. :)
You are trying to use the shopping list and append something to it, but it was never created.
try this:
shopping_list = []
shopping_list.append(something,ignore_index=true)
you can declare the shopping_list above your class
The error states:
UnboundLocalError: local variable 'shopping_list' referenced before assignment
This means you are attempting to do something with a variable that doesn't exist yet. The solution is to declare it before you try to use it, probably directly below the line where you declare items in this function.
Another problem will arise: .append() has no return value, so shopping_list will become None if you assign the return value of .append() to it. In fact, .append() modifies the list in-place; there is no need to assign anything when appending.
Clarification:
# Incorrect
foo = []
foo.append('bar')
# foo is now None
# Correct
foo = []
foo.append('bar')
# foo is now ['bar']

Rewrite the code without using objects in python

When not using objects i am finding it difficult to store and parse through the data. Looking for ways where i can shorten the code using generator comprehension.
for a problem where input to the program code is as below
Courses
TRAN~Transfiguration~1~2011-2012~Minerva McGonagall
CHAR~Charms~1~2011-2012~Filius Flitwick
Students
SLY2301~Hannah Abbott
SLY2302~Euan Abercrombie
SLY2303~Stewart Ackerley
SLY2304~Bertram Aubrey
SLY2305~Avery
SLY2306~Malcolm Baddock
SLY2307~Marcus Belby
SLY2308~Katie Bell
SLY2309~Sirius Orion Black
Grades
TRAN~1~2011-2012~SLY2301~AB
TRAN~1~2011-2012~SLY2302~B
TRAN~1~2011-2012~SLY2303~B
TRAN~1~2011-2012~SLY2305~A
TRAN~1~2011-2012~SLY2306~BC
TRAN~1~2011-2012~SLY2308~A
TRAN~1~2011-2012~SLY2309~AB
CHAR~1~2011-2012~SLY2301~A
CHAR~1~2011-2012~SLY2302~BC
CHAR~1~2011-2012~SLY2303~B
CHAR~1~2011-2012~SLY2305~BC
CHAR~1~2011-2012~SLY2306~C
CHAR~1~2011-2012~SLY2307~B
CHAR~1~2011-2012~SLY2308~AB
EndOfInput
Expected Output
SLY2301~Hannah Abbott~9.5
SLY2302~Euan Abercrombie~7.5
SLY2303~Stewart Ackerley~8.0
SLY2304~Bertram Aubrey~0
SLY2305~Avery~8.5
SLY2306~Malcolm Baddock~6.5
SLY2307~Marcus Belby~8.0
SLY2308~Katie Bell~9.5
SLY2309~Sirius Orion Black~9.0
I managed to solve it using objects but is there any other way to write the code without using objects?
import sys
from Courses.Courses import Course
from Students.Students import Student
from Grades.Grades import Grade
courses = []
students = []
grades = []
gradeDict = {'A':10,'AB':9,'B':8,'BC':7,'C':6,'CD':5,'D':4}
courseCodeDict = {}
def readInput():
isSectionStart=True
while True:
# Reading data from console
input_var = raw_input()
if "EndOfInput" != input_var:
if input_var in "Courses Students Grades":
section=input_var
isSectionStart=True
else:
isSectionStart=False
if not isSectionStart:
extractDataFromRawData(input_var,section)
else:
break;
#printData(courses,students,grades)
calculateGradeAverage(grades,courses)
def calculateGradeAverage(grades,courses):
print("Calculating Average now...")
gradeRollNumberDict={}
courseGradeDict={}
gradesSet = {}
for course in courses:
courseCodeDict.update({course.course_code : 1})
for grade in grades:
if gradeRollNumberDict.get(grade.roll_number) == None:
grade.totalGradePoint = grade.grade
gradeRollNumberDict.update({grade.roll_number : grade.totalGradePoint})
else:
grade.totalGradePoint= grade.grade + gradeRollNumberDict.get(grade.roll_number)
gradeRollNumberDict.update({grade.roll_number : grade.totalGradePoint})
if courseGradeDict.get(grade.roll_number) == None:
grade.totalCourseTaken = courseCodeDict.get(grade.course_code)
courseGradeDict.update({grade.roll_number : courseCodeDict.get(grade.course_code)})
else:
grade.totalCourseTaken= courseCodeDict.get(grade.course_code) + courseGradeDict.get(grade.roll_number)
courseGradeDict.update({grade.roll_number : grade.totalCourseTaken})
for grade in grades:
grade.avgGrade = grade.totalGradePoint/grade.totalCourseTaken
grade.avgGrade = round(grade.avgGrade)
seenGrades = set()
uniqueGrades = []
grades.reverse()
for grade in grades:
if grade.roll_number not in seenGrades:
uniqueGrades.append(grade)
seenGrades.add(grade.roll_number)
#uniqueGrades.reverse()
for a in uniqueGrades:
print(a.roll_number)
#print(uniqueGrades)
grades=uniqueGrades
grades.sort(key=lambda grade:grade.roll_number)
for grade in grades:
print("RollNumber: {0} \t Total CourseTaken: {1} \t Total Grade Point: {2} \t Avg Grade: {3}".format(grade.roll_number,grade.totalCourseTaken,grade.totalGradePoint,grade.avgGrade))
def extractDataFromRawData(input_data,section):
if "Courses" == section:
courses.append(createCourseObject(input_data))
elif "Students" == section:
students.append(createStudentObject(input_data))
elif "Grades" == section:
grades.append(createGradeObject(input_data))
else:
print("Invalid input!!! Exiting the system...")
sys.exit()
def createCourseObject(input_data):
courseInputData = input_data.split("~")
course = Course(courseInputData[0],courseInputData[1],courseInputData[2],courseInputData[3],courseInputData[4])
return course
def createStudentObject(input_data):
studentInputData = input_data.split("~")
student = Student(studentInputData[0],studentInputData[1])
return student
def createGradeObject(input_data):
gradeInputData = input_data.split("~")
grade = Grade(gradeInputData[0],gradeInputData[1],gradeInputData[2],gradeInputData[3],gradeDict[gradeInputData[4]])
return grade
def printData(courses,students,grades):
printObject(courses,"Courses")
printObject(students,"Students")
printObject(grades,"Grades")
def printObject(list,object):
print("Printing %s"%object)
for data in list:
print(data)
if __name__ == '__main__':
readInput()
Code:
from collections import OrderedDict
from pprint import pprint as pp
SEPARATOR = "~"
GRADE_DICT = {
"A": 10,
"AB": 9,
"B": 8,
"BC": 7,
"C": 6,
"CD": 5,
"D": 4
}
def read_input_from_file(file_name="input.txt"):
course_list= list()
student_list = list()
grade_list = list()
section_map = {
"Courses": course_list,
"Students": student_list,
"Grades": grade_list,
}
with open(file_name) as f:
current_item = None
for line in f:
line = line.strip()
if line in section_map:
current_item = section_map[line]
elif line == "EndOfInput":
break
elif current_item is not None:
current_item.append(line)
else:
print("Ignoring line: {}".format(line))
return course_list, student_list, grade_list
def convert_names(name_list):
ret = OrderedDict()
for element in name_list:
id, name = element.split(SEPARATOR)
ret[id] = name
return ret
def convert_grades(grade_list):
ret = dict()
for element in grade_list:
course_id, student_id, grade_id = element.rsplit(SEPARATOR, 2)
ret.setdefault(student_id, dict())[course_id] = grade_id
return ret
def main():
course_list, student_list, grade_list = read_input_from_file()
student_dict = convert_names(student_list)
print("\n[SECTION 0]: Student IDs and names:\n")
pp(student_dict)
exam_stat_dict = convert_grades(grade_list)
print("\n[SECTION 1]: Grades organized by students and courses:\n")
pp(exam_stat_dict)
print("\n[SECTION 2]: Final Grades:\n")
for student_id in student_dict:
if student_id in exam_stat_dict:
grade_dict = exam_stat_dict[student_id]
grades_sum = sum([GRADE_DICT.get(item, 0) for item in grade_dict.values()])
print(SEPARATOR.join([student_id, student_dict[student_id], str(grades_sum/len(grade_dict))]))
else:
print(SEPARATOR.join([student_id, student_dict.get(student_id), "0.0"]))
if __name__ == "__main__":
main()
Output (I'm placing it before the Notes, since I'm going to refer to it from there):
(py35x64_test) c:\Work\Dev\StackOverflow\q45987148>python a.py
[SECTION 0]: Student IDs and names:
OrderedDict([('SLY2301', 'Hannah Abbott'),
('SLY2302', 'Euan Abercrombie'),
('SLY2303', 'Stewart Ackerley'),
('SLY2304', 'Bertram Aubrey'),
('SLY2305', 'Avery'),
('SLY2306', 'Malcolm Baddock'),
('SLY2307', 'Marcus Belby'),
('SLY2308', 'Katie Bell'),
('SLY2309', 'Sirius Orion Black')])
[SECTION 1]: Grades organized by students and courses:
{'SLY2301': {'CHAR~1~2011-2012': 'A', 'TRAN~1~2011-2012': 'AB'},
'SLY2302': {'CHAR~1~2011-2012': 'BC', 'TRAN~1~2011-2012': 'B'},
'SLY2303': {'CHAR~1~2011-2012': 'B', 'TRAN~1~2011-2012': 'B'},
'SLY2305': {'CHAR~1~2011-2012': 'BC', 'TRAN~1~2011-2012': 'A'},
'SLY2306': {'CHAR~1~2011-2012': 'C', 'TRAN~1~2011-2012': 'BC'},
'SLY2307': {'CHAR~1~2011-2012': 'B'},
'SLY2308': {'CHAR~1~2011-2012': 'AB', 'TRAN~1~2011-2012': 'A'},
'SLY2309': {'TRAN~1~2011-2012': 'AB'}}
[SECTION 2]: Final Grades:
SLY2301~Hannah Abbott~9.5
SLY2302~Euan Abercrombie~7.5
SLY2303~Stewart Ackerley~8.0
SLY2304~Bertram Aubrey~0.0
SLY2305~Avery~8.5
SLY2306~Malcolm Baddock~6.5
SLY2307~Marcus Belby~8.0
SLY2308~Katie Bell~9.5
SLY2309~Sirius Orion Black~9.0
Notes:
This is a "slightly" modified version of your code, that only uses stuff from Python standard library
Code explanation:
read_input_from_file (since it's only a helper function, I'm not going to insist much on it):
I saved the input (copy/paste) in a file (called it input.txt), and every time the program runs, it loads the data from there (the reason is obvious)
It (populates and) returns 3 lists (curses, students and grades from your code)
convert_names:
Converts every student name entry (as given in input) into a dictionary*: {id: name} (e.g. "SLY2301~Hannah Abbott" -> {"SLY2301": "Hannah Abbott"}) - the key will be id
*Since in a regular Python dictionary ([Python]: Mapping Types — dict) the keys are ordered by their hash (the hash function can change between Python versions), there's almost 100% chance that the dictionary elements won't be stored in the order they were inserted (as an example you could type in the Python console {1:2, 0:1} and you'll see that it will output {0: 1, 1: 2}), I'm using [Python]: class collections.OrderedDict([items]) which ensures the key order
The return value can be seen in program output (SECTION 0)
convert_grades:
This is where (most of) the magic takes place
Converts every grade entry (as given in input) in a dictionary: {student_id : {course_id: grade_id}} (the last 2 values are aggregated in an inner dictionary; e.g. "TRAN~1~2011-2012~SLY2301~AB" -> {"SLY2301": {"TRAN~1~2011-2012": "AB"}}). For that, I'm using [Python]: str.rsplit(sep=None, maxsplit=-1) with a maxsplit value of 2, as I don't care about the ~s in TRAN~1~2011-2012
If a student_id is present more than once (was to more than 1 course exam), I am just adding the course_id and grade_id in the inner dictionary (this is where [Python]: setdefault(key[, default]) comes into play)
The return value can be seen in program output (SECTION 1)
main:
The program main function. Here, I'm making use of the other functions and display the final data in a proper manner to the user (SECTION 2)
If there was a student that wasn't at any exam, like Bertram Aubrey (the id is not present in the exam statistics dictionary), I just print the id, name and 0.0
Otherwise, I calculate the arithmetic average from the grades in the inner dictionary (I am using [Python]: list Comprehensions to convert the grades into actual numbers, [Python]: sum(iterable[, start]) to sum the grades, then I divide the total by the number of inner dictionary keys) and display it, together with the id and name
The code runs with Python3 and Python2
#EDIT0:
Adding read_input function (read_input_from_file with minimum and trivial modifications) to read input from keyboard:
def read_input():
course_list= list()
student_list = list()
grade_list = list()
section_map = {
"Courses": course_list,
"Students": student_list,
"Grades": grade_list,
}
current_item = None
while(1):
line = input()
if line in section_map:
current_item = section_map[line]
elif line == "EndOfInput":
break
elif current_item is not None:
current_item.append(line)
else:
print("Ignoring line: {}".format(line))
return course_list, student_list, grade_list
Notes:
In order for this function to work with Python2, this code should be added at the beginning of the file:
import sys
if sys.version_info.major < 3:
input = raw_input
You can also use the input.txt file to test the code with large datasets (like provided in the question, without having to manually type all the data) like this:
python a.py < input.txt
#CristiFati using your code i managed to tweak to solve my problem as below.
from collections import OrderedDict
import sys
SEPARATOR = "~"
GRADE_DICT = {
"A": 10,
"AB": 9,
"B": 8,
"BC": 7,
"C": 6,
"CD": 5,
"D": 4
}
def read_input_from_file():
course_list= list()
student_list = list()
grade_list = list()
section_map = {
"Courses": course_list,
"Students": student_list,
"Grades": grade_list,
}
current_item = None
while True:
line = input()
#line = line.strip()
if line != "EndOfInput":
if line in section_map:
current_item = section_map[line]
elif current_item is not None:
current_item.append(line)
else:
print("Ignoring line: {}".format(line))
else:
break
return course_list, student_list, grade_list
def convert_names(name_list):
ret = OrderedDict()
for element in name_list:
id, name = element.split(SEPARATOR)
ret[id] = name
return ret
def convert_grades(grade_list):
ret = dict()
for element in grade_list:
course_id, student_id, grade_id = element.rsplit(SEPARATOR, 2)
ret.setdefault(student_id, dict())[course_id] = grade_id
return ret
def main():
course_list, student_list, grade_list = read_input_from_file()
student_list.sort()
student_dict = convert_names(student_list)
exam_stat_dict = convert_grades(grade_list)
for student_id in student_dict:
if student_id in exam_stat_dict:
grade_dict = exam_stat_dict[student_id]
grades_sum = sum([GRADE_DICT.get(item, 0) for item in grade_dict.values()])
print(SEPARATOR.join([student_id, student_dict[student_id], str(round(float(grades_sum/len(grade_dict)),2))]))
else:
print(SEPARATOR.join([student_id, student_dict.get(student_id), "0"]))
if __name__ == "__main__":
main()

Python: how do i sort the list inside the dictionary

I want to sort score_list by the values from class1.
I'm able to sort score_list alphabetically by simply sorting the keys, but I can't figure out how to sort the list numerically from largest to smallest.
Additionally, I want score_list to only store the last 3 inputs, and, if it is possible, I would like to find the average for each students last 3 scores.
enter code here:
score_list = []
class1 = {}
what_class = input ("what class would you like?")
if what_class == ("1") :
print("class1 results")
student_name =input("name?")
score = input("score?")
choose = input("do you want to entre another student? y,n")
while choose == "y":
student_name =input("name?")
score = input("score?")
score_list.append(score)
choose = input("do you want to entre another score? y,n")
class1.setdefault(student_name,score_list)
if choose == "n":
sorted_names = sorted(class1.keys())
print(class1)
class1_items = class1.items()
print (type(class1_items))
for i in class1_items:
print (i)
if someone can help me i would be very thankful it
thank you for the help
[]a better look on the objectives:1
enter code here
import heapq
from collections import deque
print("alphabetical(type:A),highest to lowest(type:B),Average(type:C)")
student_name = input("ENTER NAME")
result_type = input ("WHICH TYPE OF RESULTS WOULD YOU LIKE?")
which_class = input ("which class?")
if result_type == ("A") or result_type == ("a"):
print("alphabetical order(type:A)")
if result_type == ("B") or result_type == ("b"):
print("results in highest to lowest.")
score_list = deque(maxlen=3)
scores = input("Please enter your score.")
choose = input("do you want to entre another score? y,n")
while choose != "n":
scores = input("Please enter your score.")
choose = input("do you want to entre another score? y,n")
score_list.append(scores)
if choose != "y" :
print(score_list)
print((student_name)+"'s:"+"lowest to largest"+str(heapq.nsmallest(3, score_list)))
print((student_name)+"'s:"+"largest to lowest"+str(heapq.nlargest(3, score_list)))
if result_type == ("C") or result_type == ("c"):
print("results in average order.")
score_list = deque(maxlen=3)
scores = input("Please enter your score.")
choose = input("do you want to entre another score? y,n")
while choose != "n":
scores = input("Please enter the scores.")
choose = input("do you want to entre another score? y,n")
score_list.append(int(scores))
#print(len(score_list))
#print(sum(score_list))
total = (int(sum(score_list)))
length = (int(len(score_list)))
average= (total)/(length)
if choose != "y" :
print(student_name+"'s "+"average score:"+str(average))
#print("average: " +str(average))
this is how i have improved it i still cant figure out the alphabetically sort part.
the objectives to help you know what i am trying to do
There are tons of issues here, but I'll go over a few. You need to make sure you convert your input to an int(), otherwise you'll be doing math on strings and that's gonna cause a problem. Second, and most important, the dictionaries are not ordered, so you're never going to get the same last 3 entries.
You can use list comprehension to fetch the grades and sort them, which the first thing after else: does, because the input order is irrelevant. For the next two, you can't use the dictionary values, you must refer to the list that you're using to keep track of the input grades.
grade_list = []
class1 = {}
if input('what class would you like? ') == '1':
while True:
student_name = input('name? ')
score = int(input('score? ')) # must convert input to int
grade_list.append(score)
class1[student_name] = score
if input('do you want to enter another student? ') == 'y':
pass
else:
# fetch grades
grades = [v for k, v in class1.items()]
# print highest to lowest
print(sorted(grades, reverse=True))
# print last 3 grades
print(grade_list[-3:])
# print avg of last 3 grades
print(sum(grade_list[-3:]) / 3)
break
Output
what class would you like? 1
name? Abby
score? 11
do you want to enter another student? y
name? Bobby
score? 22
do you want to enter another student? y
name? Charles
score? 33
do you want to enter another student? y
name? Damon
score? 44
do you want to enter another student? n
[44, 33, 22, 11]
[22, 33, 44]
33.0
Update, you should be capable to just paste and run following code
but still, you needs to wrap the input loop by your self, which means, connect interfaces / function call
import json
class Quize():
def __init__(self):
self.db = {}
def createClass(self, class_name):
self.db[class_name] = {}
def createUser(self, class_name, user_name):
self.db[class_name][user_name] = []
def addScore(self, class_name, user_name, score):
self.db[class_name][user_name].append(score)
self.db[class_name][user_name] = self.db[class_name][user_name][-3:]
def printClass(self, class_name, order = None):
if not order:
print json.dumps(self.db[class_name], indent=4)
else:
to_sort = self.db[class_name]
print 'order', order
if order == 'by highest':
print sorted([ [max(to_sort[name]), name] for name in to_sort ], cmp = lambda x, y : y[0] - x[0])
if order == 'by lowest':
print sorted([ [min(to_sort[name]), name] for name in to_sort ], cmp = lambda x, y : x[0] - y[0])
if order == 'by average':
print sorted([ [sum(to_sort[name]) / len(to_sort[name]), name] for name in to_sort ],
cmp = lambda x, y : y[0] - x[0])
if order == 'by name':
print sorted([ [to_sort[name], name] for name in to_sort ], cmp = lambda x, y : x[1] > y[1])
def printUser(self, class_name, user_name):
print json.dumps(self.db[class_name][user_name], indent=4)
def dump(self):
print json.dumps(self.db, indent = 4)
ins = Quize()
ins.createClass('0302')
ins.createUser('0302', 'jack')
ins.createUser('0302', 'tom')
ins.addScore('0302', 'jack', 99)
ins.addScore('0302', 'jack', 92)
ins.addScore('0302', 'tom', 99)
ins.addScore('0302', 'tom', 92)
ins.addScore('0302', 'tom', 19)
ins.addScore('0302', 'tom', 29)
ins.addScore('0302', 'tom', 66)
ins.printClass('0302', 'by highest')
ins.printClass('0302', 'by lowest')
ins.printClass('0302', 'by average')
ins.printClass('0302', 'by name')
ins.printUser('0302', 'tom')
ins.printUser('0302', 'jack')
ins.printClass('0302')
ins.addScore('0302', 'jack', 11)
ins.addScore('0302', 'jack', 37)
ins.addScore('0302', 'tom', 80)
ins.addScore('0302', 'tom', 72)
ins.printClass('0302', 'by highest')
ins.printClass('0302', 'by lowest')
ins.printClass('0302', 'by average')
ins.printClass('0302', 'by name')
ins.printUser('0302', 'tom')
ins.printUser('0302', 'jack')
ins.printClass('0302')

How to repeat blocks of code in Python

I've created a code that allows a user to view the average score of the values that are in the file. In Example the Text File would look like the following:
Text File For Class 1: it is similar for each text file ; 2 and 3. just different names and values
Matt 2
Sid 4
Jhon 3
Harry 6
There are 3 classes altogether in which the user is prompted to choose which class they want to preview.
Code:
def main_menu():
print ("\n Main Menu ")
print ("1.Average Score of class = 'avg'")
main_menu()
option = input("option [avg]: ")
option_class = input("class: ")
one = "1.txt"
two = "2.txt"
three = "3.txt"
if option.lower() == 'avg' and option_class == '1':
with open(one) as f:
the_list = [int(l.strip().split()[-1]) for l in f]
b = sum(the_list)
length = len(the_list)
avg = float(b) / length if length else 0
print ("Average of Class is: ", avg)
if option.lower() == 'avg' and option_class == '2':
with open(two) as f:
the_list = [int(l.strip().split()[-1]) for l in f]
b = sum(the_list)
length = len(the_list)
avg = float(b) / length if length else 0
print ("Average of Class is: ", avg)
if option.lower() == 'avg' and option_class == '3':
with open(three) as f:
the_list = [int(l.strip().split()[-1]) for l in f]
b = sum(the_list)
length = len(the_list)
avg = float(b) / length if length else 0
print ("Average of Class is: ", avg)
Question
If i wanted to Keep Repeating the code above so that the user can keep using it until they want to exit. so, is it possible to put the code into a while loop and only stop the code if the user wants to, i.e the user is prompted if they want to choose another option and class.
NB: there will be other options such as alphabetical order however right now i only want to know how to do it for the average section.
Best thing you can do is to make a loop for user input and write a function for listing the file.
def main_menu():
print ("\n Main Menu ")
print ("1.Average Score of class = 'avg'")
main_menu()
option = ""
options = ["1", "2", "3"]
one = "1.txt"
two = "2.txt"
three = "3.txt"
def read_text_file(file): # standalone function for viewing files to reduce duplicate code
file += ".txt"
with open(file) as f:
the_list = [int(l.strip().split()[-1]) for l in f]
b = sum(the_list)
length = len(the_list)
avg = float(b) / length if length else 0
print ("Average of Class is: ", avg)
while True:
option = input("option [avg]: ").lower()
if option == "exit":
break # checks if user want to exit a program
else:
option_class = input("class: ")
if option == 'avg' and option_class in options:
read_text_file(option_class)
else:
print("nothing to show, asking again")
print("end of program")
As I mentioned in the comment section, you should leverage the power of functions here. By breaking down your components to manageable pieces, you actually afford yourself readability and flexibility. See code below, where I have two functions, one for averages and one for totals.
def get_class_average(class_number):
filename = "{0}.txt".format(class_number)
try:
with open(filename) as f:
the_list = [int(l.strip().split()[-1]) for l in f]
b = sum(the_list)
length = len(the_list)
avg = float(b) / length if length else 0
return avg
except:
print "No file with that name found."
def get_class_total(class_number):
filename = "{0}.txt".format(class_number)
try:
with open(filename) as f:
the_list = [int(l.strip().split()[-1]) for l in f]
b = sum(the_list)
return b
except:
print "No file with that name found."
def check_class_number(string_input):
try:
int(string_input)
return True
except ValueError:
return False
if __name__ == "__main__":
while True:
input_val = raw_input(
"Enter class number (enter 'exit' to quit program): ")
if input_val == 'exit':
break
if check_class_number(input_val): # If it's a valid class number.
method = raw_input("Enter method: ")
if method == 'avg':
avg = get_class_average(int(input_val))
print "The average of Class {0} is {1}".format(input_val, avg)
elif method == 'sum':
total = get_class_total(int(input_val))
print "The total of Class {0} is {1}".format(input_val, total)
else:
print "That is not a valid class number."
continue
Sample run:
The really fun part here is that you can even refactor get_class_average and get_class_total to be a single function that checks if the passed in method is avg or sum and returns the respective values from there (this is easily doable since you have practically the same lines of code for both functions, get_class_average just has an extra division involved).
Have fun.
Yes, you can just put your code within a while-loop and prompt the user for input:
def main_menu():
print ("\n Main Menu ")
print ("1.Average Score of class = 'avg'")
# End main_menu()
one = "1.txt"
two = "2.txt"
three = "3.txt"
keepGoing = True
while(keepGoing):
main_menu()
option = input("option [avg]: ")
option_class = input("class: ")
if option.lower() == 'avg' and option_class == '1':
with open(one) as f:
the_list = [int(l.strip().split()[-1]) for l in f]
b = sum(the_list)
length = len(the_list)
avg = float(b) / length if length else 0
print ("Average of Class is: ", avg)
if option.lower() == 'avg' and option_class == '2':
with open(two) as f:
the_list = [int(l.strip().split()[-1]) for l in f]
b = sum(the_list)
length = len(the_list)
avg = float(b) / length if length else 0
print ("Average of Class is: ", avg)
if option.lower() == 'avg' and option_class == '3':
with open(three) as f:
the_list = [int(l.strip().split()[-1]) for l in f]
b = sum(the_list)
length = len(the_list)
avg = float(b) / length if length else 0
print ("Average of Class is: ", avg)
# Prompt user for input on whether they want to continue or not:
while(True):
keepGoingStr = input("Would you like to continue? (Y/N)\n>>> ").lower()
if(keepGoingStr[0] == 'y'):
# Keep going
keepGoing = True
break
elif(keepGoingStr[0] == 'n')
# Stop
keepGoing = False
break
else:
print("Sorry, your input did not make sense.\nPlease enter either Y or N for yes or no.")
# end if
# end while - keep going input
# End While(keepGoing)
As mentioned in the comments, though, you should consider breaking up your code into functions.

Categories

Resources