So our assinment is to create a function that take the input grades~(An N ×M matrix containing grades on the 7-step-scale given to N students on M different assignments.), and compute gradesFinal(A vector of length n containing the final grade for each of the N students.).
For each student, the final grade must be computed in the following way:
If there is only one assignment (M = 1) the final grade is equal to the grade of that assignment.
If there are two or more assignments (M > 1) the lowest grade is discarded. The final grade is computed as the mean of M − 1 highest grades rounded to the nearest grade on the scale (using the function roundGrade).
Irrespective of the above, if a student has received the grade −3 in one or more assignments, the final grade must always be −3.
I have inserted my roundGrade function below, but when I try to run a matrix I recieve. "TypeError: unsupported operand type(s) for /: 'NoneType' and 'int'
". This means I probably misunderstood something completely.
import numpy as np
def computeFinalGrades(grades):
if len(grades)==1:
gradesFinal = grades
elif len(grades)>=2:
trimmed = grades.remove(min(grades))
finalgrade = np.mean(trimmed)
gradesFinal = roundGrade(finalgrade)
elif grades[grades==-3]:
gradesFinal= -3
return gradesFinal
def roundGrade(grades):
grade_groups = [-3,0,2,4,7,10,12]
gradesRounded = [min(grade_groups,key=lambda x:abs(grade-x)) for grade in grades]
return gradesRounded
I appreciate your help, and feel welcome to show me a smarter method.
There are roughly 5 areas which need to be changed in the original code: Indentation, list.remove
, if
/elif
order, the way the presence of a value is checked for in a list and passing of lists in the roundGrade()
function.
1. Indentation
Firstly, your indentation is incorrect. The python interpreter takes into account the indentation so that's important. After each if statement, you should indent the code block for that particular condition. Besides that, you should also make sure your if and elifs are on the same indented level. So it should look something like this.
import numpy as np
def computeFinalGrades(grades):
if len(grades)==1:
gradesFinal = grades
elif len(grades)>=2:
trimmed = grades.remove(min(grades))
finalgrade = np.mean(trimmed)
gradesFinal = roundGrade(finalgrade)
elif grades[grades==-3]:
gradesFinal= -3
return gradesFinal
def roundGrade(grades):
grade_groups = [-3,0,2,4,7,10,12]
gradesRounded = [min(grade_groups,key=lambda x:abs(grade-x)) for grade in grades]
return gradesRounded
2. list.remove returns None
Secondly, under the assumption that you are passing in a list to computeFinalGrades, grades.remove in trimmed = grades.remove(min(grades))
modifies the grades list inplace and returns None
. Thus when u call the function roundGrade on it, you get the NoneType Error.
3. Order of if/elif blocks
Thirdly, the flow of the if blocks is wrong. Consider the scenario where you pass in the array [1,2,3,4,5,-3] to computeFinalGrades([1,2,3,4,5,-3])
, then the if statement if len(grades)==1
will return True and evaluation will cease there. Instead, you should move grades[grades==-3]
to the top as the first statement to be evaluated as once this statement is true, -3 should always be returned.
4. Checking for a value in a list
Fourth, grades[grades==-3]
first evaluates if the variable grades refers to -3 which in turn returns false because grades is always a list and not the integer -3. At this point, you have grades[False]
which is treated equivalently as grades[0]
thus u always get the first result. Instead, the pythonic way is to use the in operator. I.E. elif -3 in grades:
5. roundGrade tries to iterate on non-iterables twice
Fifth, gradesFinal = roundGrade(finalgrade)
does not work because finalgrade = np.mean(trimmed)
which is a single number. However u call roundGrade on this single number and then u iterate on this single digit for grade in grades
inside the function roundGrade
. The same error occurs when you try to min(grade_groups,key=lambda x:abs(grade-x))
. This is because you are calling min on a single value. If you wanted to min this pairing, a better way would be gradesRounded = [(grade,lambda x:abs(mean_grade-x)) for grade in grade_groups]
.Thus you should be aware of when you are passing in a list or variable.
I have edited roundGrade to take in a single value then iterate over your grade groupings to create a list of tuples of the absolute difference in value, followed by the grade in the grade groupings. Then i sort it in ascending order and simply return the second value in the first tuple which is your grade in the grade table. Be aware though that this has the drawback of always returning the lower grade in the grade_groups if the mean_grade is exactly between two neighboring values in grade_groups.
Putting it all together, one way of doing this is:
import numpy as np
def computeFinalGrades(grades):
if -3 in grades:
print grades
gradesFinal= -3
elif len(grades)>=2:
grades.remove(min(grades))
finalgrade = np.mean(grades)
gradesFinal = roundGrade(finalgrade)
elif len(grades)==1:
gradesFinal = grades
return gradesFinal
def roundGrade(mean_grade):
grade_groups = [-3,0,2,4,7,10,12]
gradesRounded = [(abs(grade-mean_grade),grade)for grade in grade_groups]
gradesRounded.sort()
return gradesRounded[0][1]