Search code examples
pythonmatrixdelete-row

How to remove minimum value from row in a matrix-


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:

  1. If there is only one assignment (M = 1) the final grade is equal to the grade of that assignment.

  2. 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).

  3. 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.


Solution

  • 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]