【问题标题】:How can I find the specific interval (via list) within which a given value occurs, in order to return the corresponding value from a second list?如何找到给定值出现的特定间隔(通过列表),以便从第二个列表中返回相应的值?
【发布时间】:2017-06-05 00:16:22
【问题描述】:

我正在编写一个 python 代码来充当成绩簿。我已经计算了每个学生获得的总分,现在卡在我的代码的最后一步 - 为学生分配一个字母等级。我的代码结构是定义了评估单个学生表现的函数,这样一个“主函数”就可以通过每个学生对应的索引号重复调用这些函数。因此,我能够生成一个我试图压缩和简化的工作代码。我在posts like these 中检查了类似的问题,但我无法根据我的问题调整这些方法(我认为是因为我的 for 循环搞砸了索引)。

工作代码:

import numpy as np

failing_score = 100
perfect_score = 200
letter_grades = ['A', 'A-', 'B+', 'B', 'B-', 'C+', 'C', 'C-', 'D+', 'D', 'D-', 'F']
grade_borders = np.linspace(perfect_score, failing_score, len(letter_grades))

print(grade_borders)
>> [ 200.          190.90909091  181.81818182  172.72727273  163.63636364
  154.54545455  145.45454545  136.36363636  127.27272727  118.18181818
  109.09090909  100.        ]

def get_letter_grade(student_grade, grade_borders=grade_borders):
    """
    perfect score = 200, failing score ≤ 100

    190.9 - 200:    A
    181.8 - 190.9:  A-
    172.7 - 181.8:  B+
    etc
    """
    if student_grade >= grade_borders[1]:
        letter = 'A'
    elif grade_borders[2] <= student_grade < grade_borders[1]:
        letter = 'A-'
    elif grade_borders[3] <= student_grade < grade_borders[2]:
        letter = 'B+'
    elif grade_borders[4] <= student_grade < grade_borders[3]:
        letter = 'B'
    elif grade_borders[5] <= student_grade < grade_borders[4]:
        letter = 'B-'
    elif grade_borders[6] <= student_grade < grade_borders[5]:
        letter = 'C+'
    elif grade_borders[7] <= student_grade < grade_borders[6]:
        letter = 'C'
    elif grade_borders[8] <= student_grade < grade_borders[7]:
        letter = 'C-'
    elif grade_borders[9] <= student_grade < grade_borders[8]:
        letter = 'D+'
    elif grade_borders[10] <= student_grade < grade_borders[9]:
        letter = 'D'
    elif grade_borders[11] <= student_grade < grade_borders[10]:
        letter = 'D-'
    elif student_grade < grade_borders[11]:
        letter = 'F'
    return letter

print(get_letter_grade(182)) 
>> A-

我不喜欢使用这么多 if/elif 循环来指定每个索引和每个字母等级。所以我尝试通过使用 for 循环来修改我的方法;边界处的值(A 和 F)分别处理,而中间的等级可以通过与 index 函数相同的代码来处理。

尝试失败:

def assign_letter_grade(student_score, grade_borders=grade_borders, letter_grades=letter_grades):
    """
    This is my unsuccessful attempt at replicating the function above.
    """
    res = 0 # not including this line ==> UnboundLocalError: local variable 'res' referenced before assignment
    for idx in range(len(grade_borders)):
        if idx == 0:
            if student_score >= grade_borders[1]:
                res = letter_grades[idx]
                print("--", res)
        elif idx > 0 and idx < len(letter_grades)-1:
            if (student_score >= grade_borders[idx+1]) and (student_score < grade_borders[idx]) is True:
                res = letter_grades[idx]
                print("--", res)
        elif idx == len(letter_grades)-1:
            if student_score < grade_borders[len(grade_borders)-1]:
                res = letter_grades[idx]
                print("--", res)
    return res

print(assign_letter_grade(182)) 

问题: 我在上面的代码中有什么错误?这是一种愚蠢的方法吗(为什么/为什么不呢?)我考虑过另一种替代方法,它使用直方图来查找与学生成绩相对应的非零区间,但我不确定效率和实施。

【问题讨论】:

    标签: python-3.x loops numpy count histogram


    【解决方案1】:

    对于区间搜索,可以使用numpy的searchsorted。但是,如果您从最小到最大对值进行排序,事情会更加顺利。

    letter_grades = np.array(letter_grades[::-1])
    grade_borders = grade_borders[::-1]
    
    def assign_letter_grade(student_score, grade_borders=grade_borders, letter_grades=letter_grades):
        return letter_grades[np.searchsorted(grade_borders, student_score)]
    

    这个函数的几个例子:

    assign_letter_grade(95)
    Out: 'F'
    
    assign_letter_grade(105)
    Out: 'D-'
    
    assign_letter_grade(182)
    Out: 'A-'
    

    Python 的 bisect 模块也可以做到这一点。

    import bisect
    
    bisect.bisect(grade_borders, 182)
    Out: 10
    
    letter_grades[bisect.bisect(grade_borders, 182)]
    Out: 'A-'
    

    numpy 函数的优点是它是矢量化的,因此您可以传递分数数组:

    assign_letter_grade([95, 110, 135, 157, 189, 198])
    Out: 
    array(['F', 'D', 'C-', 'B-', 'A-', 'A'], 
          dtype='<U2')
    

    当然,同样可以在循环中使用 bisect 来完成,但 numpy 的版本可能会更快。

    【讨论】:

      【解决方案2】:

      我能够调整直方图方法,但带有弃用警告。

      def hlet(student_score, grade_borders=grade_borders):
          bins = grade_borders[::-1]
          interval, _, _ = plt.hist(student_score, bins=bins)
          interval = interval[::-1]
          letter_idx = np.where(interval>0)[0]
          return letter_grades[letter_idx]
      
      print(hlet(182))
      
      >> VisibleDeprecationWarning: converting an array with ndim > 0 to an index will result in an error in the future
        return letter_grades[letter_idx]
      A-
      

      虽然这种方法有效,但我仍然很想知道其他方法如何解决这个问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多