How can I simplify repetitive if-elif statements in my grading system function?

◇◆丶佛笑我妖孽 提交于 2021-02-04 09:22:30

问题


The goal is to build a program to convert scores from a '0 to 1' system to an 'F to A' system:

  • If score >= 0.9 would print 'A'
  • If score >= 0.8 would print 'B'
  • 0.7, C
  • 0.6, D
  • And any value below that point, print F

This is the way to build it and it works on the program, but it's somewhat repetitive:

if scr >= 0.9:
    print('A')
elif scr >= 0.8:
    print('B')
elif scr >= 0.7:
    print('C')
elif scr >= 0.6:
    print('D')
else:
    print('F')

I would like to know if there is a way to build a function so that the compound statements wouldn't be as repetitive.

I'm a total beginner, but would something in the lines of :

def convertgrade(scr, numgrd, ltrgrd):
    if scr >= numgrd:
        return ltrgrd
    if scr < numgrd:
        return ltrgrd

be possible?

The intention here is that later we can call it by only passing the scr, numbergrade and letter grade as arguments:

convertgrade(scr, 0.9, 'A')
convertgrade(scr, 0.8, 'B')
convertgrade(scr, 0.7, 'C')
convertgrade(scr, 0.6, 'D')
convertgrade(scr, 0.6, 'F')

If it would be possible to pass fewer arguments, it would be even better.


回答1:


You can use the bisect module to do a numeric table lookup:

from bisect import bisect 

def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'):
     i = bisect(breakpoints, score)
     return grades[i]

>>> [grade(score) for score in [33, 99, 77, 70, 89, 90, 100]]
['F', 'A', 'C', 'C', 'B', 'A', 'A']



回答2:


You can do something along these lines:

# if used repeatedly, it's better to declare outside of function and reuse
# grades = list(zip('ABCD', (.9, .8, .7, .6)))

def grade(score):
    grades = zip('ABCD', (.9, .8, .7, .6))
    return next((grade for grade, limit in grades if score >= limit), 'F')

>>> grade(1)
'A'
>>> grade(0.85)
'B'
>>> grade(0.55)
'F'

This uses next with a default argument on a generator over the score-grade pairs created by zip. It is virtually the exact equivalent of your loop approach.




回答3:


You could assign each grade a threshold value:

grades = {"A": 0.9, "B": 0.8, "C": 0.7, "D": 0.6, "E": 0.5}

def convert_grade(scr):
    for ltrgrd, numgrd in grades.items():
        if scr >= numgrd:
            return ltrgrd
    return "F"



回答4:


In this specific case you don't need external modules or generators. Some basic math is enough (and faster)!

grades = ["A", "B", "C", "D", "F"]

def convert_score(score):
    return grades[-max(int(score * 10) - 5, 0) - 1]

# Examples:
print(convert_grade(0.61)) # "D"
print(convert_grade(0.37)) # "F"
print(convert_grade(0.94)) # "A"




回答5:


You can use np.select from numpy library for multiple conditions:

>> x = np.array([0.9,0.8,0.7,0.6,0.5])

>> conditions  = [ x >= 0.9,  x >= 0.8, x >= 0.7, x >= 0.6]
>> choices     = ['A','B','C','D']

>> np.select(conditions, choices, default='F')
>> array(['A', 'B', 'C', 'D', 'F'], dtype='<U1')



回答6:


I've got a simple idea to solve this :

def convert_grade(numgrd):
    number = min(9, int(numgrd * 10))
    number = number if number >= 6 else 4
    return chr(74 - number)

Now,

print(convert_grade(.95))  # --> A 
print(convert_grade(.9))  # --> A
print(convert_grade(.4))  # --> F
print(convert_grade(.2))  # --> F



回答7:


You could use numpy.searchsorted, which additionally gives you this nice option of processing multiple scores in a single call:

import numpy as np

grades = np.array(['F', 'D', 'C', 'B', 'A'])
thresholds = np.arange(0.6, 1, 0.1)

scores = np.array([0.75, 0.83, 0.34, 0.9])
grades[np.searchsorted(thresholds, scores)]  # output: ['C', 'B', 'F', 'A']



回答8:


You provided a simple case. However if your logic is getting more complicated, you may need a rules engine to handle the chaos.

You can try Sauron Rule engine or find some Python rules engines from PYPI.




回答9:


>>> grade = lambda score:'FFFFFFDCBAA'[int(score*100)//10]
>>> grade(0.8)
'B'



回答10:


You could also use a recursive approach:

grade_mapping = list(zip((0.9, 0.8, 0.7, 0.6, 0), 'ABCDF'))
def get_grade(score, index = 0):
    if score >= grade_mapping[index][0]:
        return(grade_mapping[index][1])
    else:
        return(get_grade(score, index = index + 1))

>>> print([get_grade(score) for score in [0, 0.59, 0.6, 0.69, 0.79, 0.89, 0.9, 1]])
['F', 'F', 'D', 'D', 'C', 'B', 'A', 'A']



回答11:


Here are some more succinct and less understandable approaches:

The first solution requires the use of the floor function from the math library.

from math import floor
def grade(mark):
    return ["D", "C", "B", "A"][min(floor(10 * mark - 6), 3)] if mark >= 0.6 else "F"

And if for some reason importing the math library is bothering you. You could use a work around for the floor function:

def grade(mark):
    return ["D", "C", "B", "A"][min(int(10 * mark - 6) // 1, 3)] if mark >= 0.6 else "F"

These are a bit complicated and I would advice against using them unless you understand what is going on. They are specific solutions that take advantage of the fact that the increments in grades are 0.1 meaning that using an increment other than 0.1 would probably not work using this technique. It also doesn't have an easy interface for mapping marks to grades. A more general solution such as the one by dawg using bisect is probably more appropriate or schwobaseggl's very clean solution. I'm not really sure why I'm posting this answer but it's just an attempt at solving the problem without any libraries (I'm not trying to say that using libraries is bad) in one line demonstrating the versatile nature of python.




回答12:


You can use a dict.

Code

def grade(score):
    """Return a letter grade."""
    grades = {100: "A", 90: "A", 80: "B", 70: "C", 60: "D"}
    return grades.get((score // 10) * 10, "F")

Demo

[grade(scr) for scr in [100, 33, 95, 61, 77, 90, 89]]

# ['A', 'F', 'A', 'D', 'C', 'A', 'B']

If scores are actually between 0 and 1, first multiply 100, then lookup the score.




回答13:


Hope following might help:if scr >= 0.9:print('A')elif 0.9 > scr >= 0.8:print('B')elif 0.8 > scr >= 0.7:Print('C')elif 0.7 scr >= 0.6:print('D')else:print('F')




回答14:


You could have a list of numbers, then a list of grades to go with it:

scores = (0.9, 0.8, 0.7, 0.6, 0.6)
lettergrades = ("A", "B", "C", "D", "F", "F")

Then, if you want to convert a specified score to a letter grade, you could do this:

item = 1 # Item 1 would be 0.8
scr = lettergrades[item]

Then your final score would be "B".



来源:https://stackoverflow.com/questions/61030617/how-can-i-simplify-repetitive-if-elif-statements-in-my-grading-system-function

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!