如何从 bin 中分配整数值

How to assign integer value from bins

我正在尝试找到一种 pythonic 方法来根据变量所在的位置分配数值。即:

variable = 23
if variable < -100:
    return_value = -15
elif variable <= -5:
    return_value = -4
elif variable <= 5:
    return_value = 18
else:
    return_value = 88

我当然可以创建一个包含 buckets/values 的列表,并在找到正确值时遍历 return:

bucket_values = [(-100, -15), (-5, -4), (5, 18)]
default = 88
variable = 100
for lower_bound, value in bucket_values:
    if variable < lower_bound:
        return_value = value
        break
else:
    return_value = default

但随后我需要检查下限和上限以及相等性,即如果这是循环的第一次迭代,我必须检查是否为劣质 (<),然后我必须检查下一个循环是否为劣质 & 等于 (< =).

我正在寻找这样的东西 (Ruby):

buckets = [
[:<, -90, -57], 
[:<=, 5, -10], 
[:<=, 10, 3], 
[:>, 60, 40]] 

# Pass bucket to a method

我的问题是: 是否有一种 pythonic 方法可以使用变量边界和值来执行此操作?

我觉得这很 pythonic,但我不推荐它

>>> variable = 23
>>> return_value = -5 if variable<-100 else -4  if variable<=-4 else 18 if variable<= 5  else 88
>>> print(return_value)
88

请注意 88 是默认值。

编辑

您可以创建一个基于与上述 if... else 相同概念的函数。该函数将是这样的:

def pythonic(variable, bucket_values, default):
    for k,v in bucket_values:
        return_value = v if variable<k else "---"
        if return_value != "---":
            return return_value
    return default

你可以这样使用它:

>>> variable = 23
>>> bucket_values = [(-100, -15), (-5, -4), (5, 18)]
>>> print(pythonic(variable, bucket_values, 88))
88

>>> variable = 1
>>> print(pythonic(variable, bucket_values, 88))
18

如果我理解得很好,对于每个 "bucket" 你都有一个间隔。要检查值是否属于某个区间,您可以定义一个函数:

def check_value(value, interval):
    if value in range(interval[0], interval[1]+1):
        print('Value ', value)
        print('Interval ', interval)
    else:
        pass

现在只需遍历间隔列表以查找值所属的位置:

for interval in list_of_intervals:
    check_value(value, interval)

使用模块 operator 非常简单。这是一个例子:

>>> import operator
>>> bucket = (operator.ge, -100, operator.le, -5)
>>> def in_bucket(value, bucket): return bucket[0](value, bucket[1]) and bucket[2](value, bucket[3])
...
>>> in_bucket(-101, bucket)
False
>>> in_bucket(-100, bucket)
True
>>> in_bucket(-5, bucket)
True
>>> in_bucket(-4, bucket)
False

但是您可以通过定义更通用的结构来做得更好:

>>> conditions = ((operator.ge, -100), (operator.le, -5))
>>> def match_conditions(value, conditions): return all(c[0](value, c[1]) for c in conditions)
...
>>> match_conditions(-101, conditions)
False
>>> match_conditions(-100, conditions)
True
>>> match_conditions(-5, conditions)
True
>>> match_conditions(-4, conditions)
False

all 运算符 returns 为真当且仅当满足所有条件。 bucketconditions 之间的主要区别在于您可以添加与边界无关的条件,例如值必须是对:

>>> conditions = ((operator.ge, -100), (operator.le, -5), (lambda v, _: v%2==0, None))
>>> match_conditions(-7, conditions)
False
>>> match_conditions(-6, conditions)
True
>>> match_conditions(-5, conditions)    
False

现在你可以用字典来总结你的条件(你给的第一个例子):

>>> value_by_conditions = { 
... ((operator.lt, -100),): -15,
... ((operator.ge, -100), (operator.le, -5)): -4,
... ((operator.gt, -5), (operator.le, 5)): 18,
... ((operator.gt, 5),): 88,
... }
>>> next((v for cs, v in value_by_conditions.items() if match_conditions(23, cs)), None)
88
>>> next((v for cs, v in value_by_conditions.items() if match_conditions(-101, cs)), None)
-15
>>> next((v for cs, v in value_by_conditions.items() if match_conditions(-100, cs)), None)
-4

备注:

  1. 我使用了元组,因为列表不可散列(因此不能用作字典键);
  2. next((x for x in xs if <test>), None)xs 中第一个通过测试的元素。如果没有元素通过测试,它 returns 默认值 None ;
  3. 在旧版本的 Python (< 3.7) 中,您无法保证测试的顺序。如果您有重叠条件,这一点很重要。
  4. 这显然是次优的,因为你先测试 if value < 100 然后 if value >= 100,等等

这真的是pythonic吗?我不确定。看看 https://www.python.org/dev/peps/pep-0020/ 来提出自己的想法。