Using lists to calculate grades Python 3, help simplifying code - python

I need help/input on a homework assignment that I have. I already have a code that works but I was wondering if there was a simpler way to write the code to make it less lengthy.
Here are the directions for the assignment for reference:
There are ten names and five lists of test scores. The correspondence
between the names and the test scores are determined by positions. For example, the test scores for Cindy are 67, 92, 67, 43, 78. Drop the lowest of the five test scores for each student and average the rest and determine the letter grade for that student. Make sure your printout is the same as mine with the same column widths.
Here is my code:
names = ['April','Bill','Cindy','Dave','Emily', 'Frank','Gene','Hank','Irene','Jeff']
test1 = [34,21,67,45,88, 77,63,96,89,88]
test2 = [11,67,92,35,89, 25,78,94,81,63]
test3 = [94,33,67,34,67, 88,55,99,23,43]
test4 = [27,83,43,67,93, 45,67,77,86,90]
test5 = [43,76,78,45,65, 99,65,65,79,43]
total = [0,0,0,0,0,0,0,0,0,0]
min = [0,0,0,0,0,0,0,0,0,0]
percent = [0,0,0,0,0,0,0,0,0,0]
grade = ['F','F','F','F','F','F','F','F','F','F']
for i in range (10):
total[i] = test1[i]
min[i] = test1[i]
for i in range (10):
total[i] = total[i] + test2[i]
total[i] = total[i] + test3[i]
total[i] = total[i] + test4[i]
total[i] = total[i] + test5[i]
min[i] = min[i] if min[i] < test2[i] else test2[i]
min[i] = min[i] if min[i] < test3[i] else test3[i]
min[i] = min[i] if min[i] < test4[i] else test4[i]
min[i] = min[i] if min[i] < test5[i] else test5[i]
total[i] = total[i] - min[i]
for i in range (10):
percent[i] = total[i]/4.0
if percent[i] >= 90: grade[i] = 'A'
elif percent[i] >= 80: grade[i] = 'B'
elif percent[i] >= 70: grade[i] = 'C'
elif percent[i] >= 60: grade[i] = 'D'
elif percent[i] >= 50: grade[i] = 'F'
print (" %s\t%d %2.2f %s \n" %(names[i], total[i], percent[i], grade[i]))
The output should look like this:
April 198 49.50 F
Bill 259 64.75 D
Cindy 304 76.00 C
Dave 192 48.00 F
Emily 337 84.25 B
Frank 309 77.25 C
Gene 273 68.25 D
Hank 366 91.50 A
Irene 335 83.75 B
Jeff 284 71.00 C
Basically I just wanted to know if anyone had any ideas/methods for me to try in order to simplify the the code. I just get confused since the grades correspond vertically. It is good either way but I feel as if it could be modified. I am still somewhat a beginner, so I may not be familiar with what could be a simple fix :) Thanks!

This is an ideal use case for dictionaries rather than lists. Dictionaries allow you to assign values to keys. In this case, the keys would be the student names and the values their grades. E.g.,
{student_1 : [grade_1, grade_2 ... grade_n],
...
student_n : [grade_1, grade_2 ... grade_n]
}
In this way it would be much easier to associate grades with any given student.
Also, some very basic function definitions (e.g. average grade) would be very helpful here. These prove to be super useful to create a clean output using an f-string.
In summary: looking up dictionaries, functions and f-strings could teach you a lot of useful skills for further coding. I have included an example below, hope it is somewhat understandable.
names = ['April','Bill','Cindy','Dave','Emily',
'Frank','Gene','Hank','Irene','Jeff']
test1 = [34,21,67,45,88, 77,63,96,89,88]
test2 = [11,67,92,35,89, 25,78,94,81,63]
test3 = [94,33,67,34,67, 88,55,99,23,43]
test4 = [27,83,43,67,93, 45,67,77,86,90]
test5 = [43,76,78,45,65, 99,65,65,79,43]
#Assign every student to the dictionary
#So far their respective grades are an empty list
grades = dict()
for name in names:
grades[name] = []
#Group all tests in a single list, which is easier to iterate over
tests = [test1, test2, test3, test4, test5]
#Loop over all tests
#Every i-th student gets the i-th value of every test added to the dictionary
for test in tests:
for i in range(len(test)):
grades[names[i]].append(test[i])
#Remove the lowest grade and calculate the average afterwards
def avg_grade(grades):
grades.remove(min(grades))
return sum(grades) / len(grades)
#Associate a grade with a letter compared to a threshold
def letter(grade):
thresholds = [90, 80, 70, 60, 50]
letters = ['A', 'B', 'C', 'D', 'F']
for i in range(len(thresholds)):
if grade >= thresholds[i]:
return letters[i]
#Output as f-string
for name in names:
print(f'{name:6} {avg_grade(grades[name]):.2f} '
f'{letter(avg_grade(grades[name]))}')

Related

How to return grade as list

I have a list of items. I wanted to calculate the average scores and return grade values.
I wanted to Compare New_list value and return a list.Is there any simple way of doing it
lst = [[90,80,70,60], [10,81,50, 65]]
Lst_New=[]
New_list=list(map(lambda x:sum(x)/len(x), lst))
print(New_list)
if New_list[0]>90:
New_grade='A'
elif New_list[0]>=80 and New_list[0]<=90:
New_grade='B'
elif New_list[0]>=70 and New_list[0]<=80:
New_grade='C'
elif New_list[0]>=60 and New_list[0]<=70:
New_grade='D'
else:
New_grade='F'
if New_list[1]>90:
New_grade1='A'
elif New_list[1]>=80 and New_list[1]<=90:
New_grade1='B'
elif New_list[1]>=70 and New_list[1]<=80:
New_grade1='C'
elif New_list[1]>=60 and New_list[1]<=70:
New_grade1='D'
else:
New_grade1='F'
Lst_New.append(New_grade)
Lst_New.append(New_grade1)
print(Lst_New)
Everything you have tried so far is fine, you can just iterate New_List in a loop and include your if, elif block inside the loop, you don't need to define multiple variables, and use redundant codes:
for score in New_list:
if score > 90:
New_grade = 'A'
elif score >= 80 and score <= 90:
New_grade = 'B'
elif score >= 70 and score <= 80:
New_grade = 'C'
elif score >= 60 and score <= 70:
New_grade = 'D'
else:
New_grade = 'F'
Lst_New.append(New_grade)
You can go to the Python Control Flow Documentaion and learn about available control flows.
Improved version
Here is an improved version of your code, user3551354. It accomplishes exactly the same result (with regards to the contents of Lst_New), as your original code.
def grade_to_letter(grade):
if grade > 90: return 'A'
if grade > 80: return 'B'
if grade > 70: return 'C'
if grade > 60: return 'D'
return 'F'
lst = [[90,80,70,60], [10,81,50, 65]]
Lst_New=[]
for student in lst:
avg = sum(student) / len(student)
print(avg)
New_grade = grade_to_letter(avg)
Lst_New.append(New_grade)
print(Lst_New)
An even more improved version
I took the liberty to rewrite my first version of the program to follow some of the best industry standards, such as proper variable naming (following a convention using underscores), avoiding redundant code, and avoiding redundant variables by utilizing dictionaries. I also added comments, which is the golden industry standard when it comes to designing excellent code.
I hope this example (or revision of your program) helps you!
# Helper function: calculate a letter grade based on a given average score
def grade_to_letter(grade):
if grade > 90: return 'A'
if grade > 80: return 'B'
if grade > 70: return 'C'
if grade > 60: return 'D'
return 'F'
# Define the list of students,
# where each element of the list is a dictionary representing a single student
students = [
{
'scores': [ 90, 80, 70, 60 ],
'average_score': None,
'letter_grade': None
},
{
'scores': [ 10, 81, 50, 65 ],
'average_score': None,
'letter_grade': None
}
]
# Iterate over the students list
for student in students:
avg = sum(student['scores']) / len(student['scores'])
# Update the score and grade values in the dictionary (i.e., the student)
student['average_score'] = avg
student['letter_grade'] = grade_to_letter(avg)
# Now the `students` dictionary contains the updated averages and letter grades
print(students)
You could define a method which calculates the average of the grade in a list and then convert that average to the grade.
Here I'm mapping grades into a dictionary with a default value of 'F'.
from collections import defaultdict
def average_grade(grades):
dct_grades = {60: 'D', 70: 'C', 80: 'B', 90: 'A'}
dct_grades = defaultdict(lambda: 'F', dct_grades)
avg = sum(grades) / len(grades)
# print(avg)
return dct_grades[int(avg)//10*10]
Then you could just use a list comprehension to map the list containing the statistics:
lst = [[90,80,70,60], [10,81,50,65]]
[average_grade(grades) for grades in lst]
#=> ['C', 'F']
Please consider that dct_grades[100] #=> 'F', so maybe you need to add 100: 'A' to the dictionary.

Check if variable changes in for loop in Python

I have this code here and I'm looking for a way to check if min_score and max_score changes, and count how many times it changes. I can't seem to find a way to do it:
games = int(input())
score = list(input().split())
score = [int(x) for x in score]
for y in range(1, len(score) + 1):
min_score = (str(min(score[:y])) + " MIN")
max_score = (str(max(score[:y])) + " MAX")
print(min_score)
print(max_score)
This is a sample test case for reference:
9
10 5 20 20 4 5 2 25 1
First number is the size of the array, which in my code I never use because I make an array just from the string of numbers below ( in fact I don't even know why they give the size).
Basically I need to find how many times the max and min values change. I'm still a beginner in programming and I don't really know what to do..
you could just keep track of the lowest and highest number encountered and check if the current score is just below or above. A simple script could look like this:
scores = [10,5,20,20,4,5,2,25,1]
countChanges = 0
limitLow = float("inf")
limitHigh = -float("inf")
for s in scores:
if(s < limitLow):
countChanges += 1
limitLow = s
if(s > limitHigh):
countChanges += 1
limitHigh = s
print("current score: %3d limits: [%2d .. %2d] changes:%d" % (s, limitLow, limitHigh, countChanges))
spam = [10, 5, 20, 20, 4, 5, 2, 25, 1]
print(sum(n < min(spam[:idx]) for idx, n in enumerate(spam[1:], start=1)))
print(sum(n > max(spam[:idx]) for idx, n in enumerate(spam[1:], start=1)))
output
4
2
if you also want to account for initial value - add 1.
Looks like a hankerrank problem? They often give the length of the input to allow solutions without knowing about built-in methods like len().
Anyway,
You can initialise min and max to the first element of the array (if it exists, check the specification), or some suitably small and large values (again, check the possible values).
Then you can count the number of times min and max changes as you go.
Should be easy enough to adapt for the case that you just want to track any change, not min and max separately. It wasn't clear from your question.
scores = [10, 5, 20, 20, 4, 5, 2, 25, 1]
min_score = scores[0]
max_score = scores[0]
min_score_changes = 0
max_score_changes = 0
for score in scores:
if score < min_score:
min_score = score
min_score_changes += 1
if score > max_score:
max_score = score
max_score_changes += 1
I solved it like this after some thinking:
games = int(input())
score = list(input().split())
score = [int(x) for x in score]
min_score_list, max_score_list = [] , []
for y in range(1, len(score) + 1):
min_score = (min(score[:y]))
max_score = (max(score[:y]))
if min_score not in min_score_list:
min_score_list.append(min_score)
if max_score not in max_score_list:
max_score_list.append(max_score)
print((len(max_score_list) - 1), len(min_score_list) - 1)
I know it's not perfect code but at least I did myself :D

calculate avg from an input that is a string

I have a string that looks like the following
s = """nate : 9.5 ,8 ,5 ,8.7
moe : 7 ,6.8 , - ,1
sam : 6 ,4 , ,7.6
cat : 8 ,8.6 ,6 ,2"""
for the dash (- ) it should just be removed completely for ex: 6.8, - , 1 should become 6.8,1
for the blank parts, it should have 0 instead for ex 4, , 7.6 should become 4, 0 , 7.6
after that is all done I want to calculate the average of each person and display it as a dictionary
this what I have come up with and I couldn't manage to go any further
def avg(s):
s = s.replace(" - ,", ""). replace(", ,", ", 0 ,")
s= s.strip().splitlines()
students={}
for i in s:
s = i.strip().split(":")
students[s[0]]= s[1].strip().split(",")
Loop through the scores, skipping -, replacing empty strings with 0, and converting the rest to floats. Then calculate the average by dividing by the length, and store that in the students dictionary.
It's easier to replace individual list elements than replacing in the original lines, since your spacing is inconsistent.
def avg(s):
s= s.strip().splitlines()
students={}
for i in s:
s = i.split(":")
student = s[0].strip()
scores = [x.strip() for x in s[1].split(",")]
scores = [float(score) if score else 0 for score in scores if score != "-"]
students[student] = sum(scores) / len(scores) if scores else 0
# split s into lines and iterate over each line
for line in s.splitlines():
# init num_scores and total to zero for this student
num_scores = 0
total = 0.0
# get the name and the scores
name, scores = line.split(" : ")
# split scores on commas and iterate over each score
for score in scores.split(","):
# remove whitespace
score = score.strip()
# if we have something and it's not a dash
if score and score != "-":
num_scores += 1
total += float(score)
# print this student's name and average
print(f"{name}: average score {total/num_scores}")

Issues with KeyError: 0

Im having issued where my code keeps saying KeyError: 0, I have searched all over and couldn't find a way to fix this
student_scores = {'0':65.0, '1':54.7}
average_score = 66.0
i = 0
for v in student_scores:
if student_scores[i] >= average_score:
above_avg_check = "{} {}".format(student_names[i], student_scores[i])
above_avg.append(above_avg_check)
print(above_avg_check)
i += 1
I am not sure on what to do, i is both a counter and the key of student_scores, so I can use it in a while loop.
You're checking an integer index while your 0 index is actually a string.
This code compiles for me on repl, just convert your index values to strings before trying to find them
student_scores = {'0':65.0, '1':54.7}
average_score = 66.0
i = 0
for v in student_scores:
if student_scores[str(i)] >= average_score:
above_avg_check = "{} {}".format(student_names[str(i)], student_scores[str(i)])
above_avg.append(above_avg_check)
print(above_avg_check)
i += 1
You can use the .items() method and get the scores in a much more simple way. The you can format your string based on the student and their score within your for loop.
student_scores = {'0': 65.0, '1': 54.7}
average_score = 65.0
i = 0
above_avg = []
for student, score in student_scores.items():
if score >= average_score:
above_average_check = '{}: {}'.format(student, score)
above_avg.append(above_average_check)
print(above_average_check)
i += 1
And here is your output:
0: 65.0
This will get you the output that you are looking for as you iterate through each key and its corresponding value.

How to make a table with 3 data sets?

I've been going at this for a while but I'm not quite sure how to tackle it. I have three data sets- scores (A-F, counts the number of grades in each rank), count (the program prints a table of count by rank), and Percentage (dividing the count by the total in the data set to get the percentage in each rank).
The end result should look similar to this given 7 grades (integers).
(example user inputted data: [88, 76, 89, 78, 74, 87, 95])
>>> GRADE COUNT PERCENTAGE
>>> A 2 28.6%
>>> B 2 28.6%
>>> C 3 42.9%
>>> D 0 0%
>>> F 0 0%
Currently, this is my long, tedious, elementary code. I'm having a hard time with executing and making it work correctly and I probably am not making it work properly at all.
def grade_scores(scoreList):
for score in scoreList: #shows the scores in the score list, uses ddata from previous function
if score >= 91:
Ascore = 'A'
Acount +=1
sum(Ascore) / Acount len(scoreList) = Apercentage
elif score >= 81 and score <=90:
Bscore = 'B'
Bcount +=1
sum(Bscore) / Bcount len(scoreList) = Bpercentage
elif score >= 71 and score <=80:
Cscore = 'C'
Ccount +=1
sum(Cscore) / Ccount len(scoreList) = Cpercentage
elif score >= 61 and score <=70:
Dscore = 'D'
Dcount +=1
sum(Dscore) / Dcount len(scoreList) = Dpercentage
else:
Fscore = 'F'
Fcount +=1
sum(Dscore) / Dcount len(scoreList) = Dpercentage
for i in range(scoreList):
print ('Grade', '\t', 'Count', '\t', 'Percentage')
print(Ascore, end="\t", Acount, end="\t", Apercentage)
print(Bscore, end="\t", Bcount, end="\t", Bpercentage)
print(Cscore, end="\t", Ccount, end="\t", Cpercentage)
print(Dscore, end="\t", Dcount, end="\t", Dpercentage)
print(Fscore, end="\t", Fcount, end="\t", Fpercentage)
I would hope to make it more of a grid style instead but I don't think it is formatted correctly and does not seem to work properly ( I also get an error about "len" in the 6th line). I appreciate any input on this.
Not completely sure what you meant by the question, but first, the error message is most likely caused by a TypeError, because you are using the variable scoreList in the range() function (which takes in integers, and integers have no length).
Also you may want to consider to utilize lists to organize the data better.
For the grid style, you should use the .format() method to help you with printing the lines:
# Assuming you have 3 lists:
# grade = ['A','B','C','D','E']
# count = [2, 2, 3, 0, 0] Have another function capture user input
# and convert it into this list.
# percentage = [28.6, 28.6, 42.9, 0, 0] Same as above ^
print("SCORE COUNT PERCENTAGE")
for i in range(len(grade)):
print(" {} {} {} %".format(grade[i], count[i], percentage[i]))
For the elif conditionals, you would have to stick with what you already have. However, you may want to modify the variable names a bit, as a better way to do this is through lists:
for i in range(len(gradeInputList)):
if grade >= 90:
count[0] += 1 # Increase index 0 (Corresponds to value A) by 1
elif grade >= 80 and < 90:
count[1] += 1
# Omitted code here, you get the idea
# ...
else:
count[4] += 1
And at the end you could have another for loop that calculates the average:
for i in range(len(count)):
average[i] = count[i] / sum(count) * 100 # Calculates the percentage
Whew, hope this helped! :)
If you want to print something in grid format then use tabulate package. It is easy and convinient . Here is sample code :
from tabulate import tabulate
table = [["spam",42],["eggs",451],["bacon",0]]
headers = ["item", "qty"]
print tabulate(table, headers, tablefmt="grid")
This prints :
+--------+-------+
| item | qty |
+========+=======+
| spam | 42 |
+--------+-------+
| eggs | 451 |
+--------+-------+
| bacon | 0 |
+--------+-------+

Categories

Resources