【问题标题】:How to convert a mapping of ranges into a dictionary如何将范围映射转换为字典
【发布时间】:2019-05-14 21:09:27
【问题描述】:

如何将其转换为字典?

if grade>=96.5:return 5.83
elif grade>=92.5:return 5.5
elif grade>=89.5:return 5.16
elif grade>=86.5:return 4.83
elif grade>=82.5:return 4.5
elif grade>=79.5:return 4.16
elif grade>=76.5:return 3.83
elif grade>=72.5:return 3.5
elif grade>=69.5:return 3.16
elif grade>=68.5:return 2.83
elif grade>=64.5:return 2.5
else:return 0

我知道如何制作基本的字典,但是我不确定它是否会像这样:

grade_checker = {
    grade>96.5:5.83
}

谢谢!

【问题讨论】:

  • 不知道为什么你会想要一个字典,一个函数仍然最适合这个。
  • 这不是有效的 python 语法,return 只能在函数内部使用。如果这是函数的一部分,则应包含所有代码。
  • 你用的是什么版本的 Python?

标签: python dictionary categorical-data


【解决方案1】:

无法将此逻辑转换为字典。字典是键值对,因此您无法仅通过查找来进行“大于”检查。

【讨论】:

  • 有一种方法,但它仍然会很丑。例如,您可以执行 d = {(0, 10): 0} 然后执行 next(v for k, v in d.items() if value in range(*k))
  • 在这种情况下,字典实际上只是变成了键值对的序列,在这种情况下,您可以将其存储为封装下限、上限和映射结果的对象序列。我实际上认为这不太安全,因为您需要证明边界不重叠 - 与原始 if, then, else 序列重叠边界是不可能的。
【解决方案2】:

简短的回答是您应该将其转换为字典。这最适合作为函数,而且您似乎只是缺少函数定义,因为我看到您在代码中使用return。字典由键值对构成,并且由于您的条件涉及>= 评估,因此不适合使用字典。请看下面的函数实现:

def grade_checker(grade):

    if grade>=96.5: return 5.83
    elif grade>=92.5: return 5.5
    elif grade>=89.5: return 5.16
    elif grade>=86.5: return 4.83
    elif grade>=82.5: return 4.5
    elif grade>=79.5: return 4.16
    elif grade>=76.5: return 3.83
    elif grade>=72.5: return 3.5
    elif grade>=69.5: return 3.16
    elif grade>=68.5: return 2.83
    elif grade>=64.5: return 2.5
    else: return 0

grade_checker(75)
grade_checker(62)
grade_checker(94)

返回:

3.5
0
5.5

【讨论】:

  • 我同意 dicts 不是正确的方法,但是像你这样编写函数似乎非常冗长和重复。 Grade-Value 对应该存储在一个序列中,并且函数应该遍历这些对,直到找到第一个匹配项。
  • 完全同意你的观点,只是想为 OP 提供一个解决方案,对他们的原始代码进行最少的更改(因为他们可能是初学者)。
【解决方案3】:

如果您确实需要使用字典,这将是一种方法;以字典键为条件检查值,以字典值作为返回值。

grade_checker = {
    96.5: 5.83,
    92.5: 5.5,
    89.5: 5.16,
    86.5: 4.83,
    82.5: 4.5,
    79.5: 4.16,
    76.5: 3.83,
    72.5: 3.5,
    69.5: 3.16,
    68.5: 2.83,
    64.5: 2.5
}

def check_grade(grade):
    for k in grade_checker:
        if grade >= k:
            return grade_checker[k]
    return 0

检查

>>> check_grade(45.5)
0
>>> check_grade(65.5)
2.5
>>> check_grade(95)
5.5

【讨论】:

  • 仅适用于尊重插入顺序的较新版本的 Python。对于其他版本,您可以只使用元组列表而不是字典,或者迭代反向排序的键,或者使用collections.OrderedDict
【解决方案4】:

如果您使用的是 3.6 之前的 Python,则可以使用 collections.OrderedDict(包括 Python 2.7),否则 dict 对象是原生插入排序的 (see here for more)!

这样,您可以简单地遍历您的 dict 并返回第一个范围匹配。

# python 3.6+
grade_table = {
    96.5: 5.83,
    ...
    64.5: 2.5,
}

# pre-3.6
from collections import OrderedDict
grade_table = OrderedDict((  # this is a tuple of tuples
    (96.5, 5.83),
    ...
    (64.5, 2.5),
))

def fn(student_grade):
    for grade, value in grade_table.iteritems():
        if student_grade >= grade:
            return value

    return 0  # default

请注意,如果您希望您的表格发生变化,那么测试您的 dict 是否按降序排列或始终接受可迭代的可迭代对象然后对它们进行排序可能是有意义的(我使用上面的元组元组,但任何相同的表单应该可以工作并且易于排序)否则将返回错误的结果。

【讨论】:

    【解决方案5】:

    另一种选择是使用range-key-dict

    from range_key_dict import RangeKeyDict
    
    range_key_dict = RangeKeyDict({
        (96.5, 100): 5.83,
        (92.5, 96.5): 5.5,
        (89.5, 92.5): 5.16,
        (86.5, 89.5): 4.83,
        (82.5, 86.5): 4.5,
        (79.5, 82.5): 4.16,
        (76.5, 79.5): 3.83,
        (72.5, 76.5): 3.5,
        (69.5, 72.5): 3.16,
        (68.5, 69.5): 2.83,
        (64.5, 68.5): 2.5,
        (0, 64.5): 0
    })
    
    assert range_key_dict[96.5] == 5.83
    assert range_key_dict[96.4] == 5.5
    assert range_key_dict[96.49] == 5.5
    

    您可以使用pip install range-key-dict 安装此 Python 包。

    您还需要检查源代码的复杂性,因为这不会像常规字典那样维护 O(1) 散列。

    只使用常规的 if 语句可能更容易和更有效。

    【讨论】:

      【解决方案6】:

      如果您想存储键值对并希望能够快速检索任意条目,则字典特别有用。正如其他答案所示,您只需要遍历元素序列并使用第一个匹配的值。所以最直接(也可能是最有效)的策略是使用序列数据类型。这是代码中的样子:

      pairs = (
          (96.5, 5.83),
          (92.5, 5.5),
          (89.5, 5.16),
          (86.5, 4.83),
          (82.5, 4.5),
          (79.5, 4.16),
          (76.5, 3.83),
          (72.5, 3.5),
          (69.5, 3.16),
          (68.5, 2.83),
          (64.5, 2.5),
      )
      
      def get_grade(g):
          for grade, value in pairs:
                  if g >= grade:
                      return value
          return 0
      

      字典很棒,但如果您不需要它们的功能,请使用更简单的东西。

      【讨论】:

        【解决方案7】:

        如果您可以使用第三方库,您可以通过pd.cut 使用 Pandas。如果您有大量输入等级要分类,这将特别有效。

        import pandas as pd
        
        grade_checker = {96.5: 5.83,
                         ...,
                         64.5: 2.5}
        
        keys, values = zip(*sorted(grade_checker.items()))
        keys += (float('inf'),)  # need to add upper boundary for pd.cut input
        
        grade = 65.5
        res = pd.cut([grade], keys, labels=values).astype(float)[0]  # 2.5
        

        查看相关:How to map numeric data into categories / bins in Pandas dataframe

        【讨论】:

          【解决方案8】:

          没有字典,你可以这样解决问题:

          import numpy as np
          GRADES = np.array(
              [[96.5 , 92.5 , 89.5 , 86.5 , 82.5 , 79.5 , 76.5 , 72.5 , 69.5 , 68.5 , 64.5 ],
               [ 5.83,  5.5 ,  5.16,  4.83,  4.5 ,  4.16,  3.83,  3.5 ,  3.16, 2.83,  2.5 ]])
          def get_grade(grade):
              try:
                  return GRADES[1][grade > [GRADES[0]][0]
              except:
                  return 0
          

          这比字典更可取,因为内置字典仅在 >= Python 3.6 中保证了有序性(即它们将按照插入键/值的顺序进行迭代)。能够在更多 Python 版本上运行代码比依赖特定版本详细信息更可取。

          【讨论】:

            【解决方案9】:

            可以使用字典来保存评分信息,但它并没有真正提供任何好处,因为您不能在这些范围内使用快速字典查找。相反,我建议使用 (points, grade) 对的排序列表,然后使用 bisect 对 O(logn) 中的匹配分数进行二进制搜索。

            >>> import bisect
            >>> grade_ranges = [(0, 0), (64.5, 2.5), (68.5, 2.83), (69.5, 3.16), 
            ...                 (72.5, 3.5), (76.5, 3.83), (79.5, 4.16), (82.5, 4.5), 
            ...                 (86.5, 4.83), (89.5, 5.16), (92.5, 5.5), (96.5, 5.83)]
            ...
            >>> points, grade = zip(*grade_ranges)
            >>> grade[bisect.bisect(points, 96.5)-1]
            5.83
            >>> grade[bisect.bisect(points, 73)-1]
            3.5
            >>> grade[bisect.bisect(points, 30)-1]
            0
            

            在这里将grade_ranges 解压缩到pointsscores 是可选的,但恕我直言,这样会更干净一些。如果您不解压缩,则必须将元组传递给bisect,例如bisect(grade_ranges, (55,))

            【讨论】:

              【解决方案10】:

              我不是数学家,但我认为插值可能,也许,适用于此?

                  from numpy import interp
              
                  ak = [k for k in gc.keys()]
                  av = [v for v in gc.values()]
              
                  # np.interp needs values from lowest to highest
                  ak.reverse()
                  av.reverse()
              
                  interp(79, ak, av)
                  >>> 4.105
              
                  interp(96, ak, av)
                  >>> 5.78875
              
                  interp(64, ak, av)
                  >>> 2.5
              

              您需要向上填充 100 并向下填充,因为它是插值,因此您感兴趣的数据点需要可采样范围内。

              【讨论】:

                猜你喜欢
                • 2017-03-10
                • 2018-11-17
                • 2014-09-12
                • 1970-01-01
                • 1970-01-01
                • 2019-10-13
                • 1970-01-01
                • 1970-01-01
                • 2019-11-24
                相关资源
                最近更新 更多