关于python:如何从bins中分配整数值

How to assign integer value from bins

我试图找到一种方法,根据变量所在的位置来指定一个数值。即:

1
2
3
4
5
6
7
8
9
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的列表,并在找到正确的值时迭代并返回:

1
2
3
4
5
6
7
8
9
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

但是,我需要检查下限和上限以及相等性,即,如果它是循环的第一次迭代,我必须检查是否是下一个循环(<),然后我必须检查下一个循环是否是下一个循环(<=)。

我在找这样的东西(红宝石):

1
2
3
4
5
6
7
buckets = [
[:<, -90, -57],
[:<=, 5, -10],
[:<=, 10, 3],
[:>, 60, 40]]

# Pass bucket to a method

我的问题是:是否有一种方法可以通过变量边界和值来实现这一点?


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

1
2
3
4
5
6
7
8
9
10
11
12
>>> 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

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

1
2
3
4
5
6
7
8
9
10
11
>>> 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运算符返回真值。bucketconditions的关键区别在于,可以添加不涉及边界的条件,例如,值必须成对:

1
2
3
4
5
6
7
>>> 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

现在,您可以使用字典来总结您的条件(您给出的第一个示例):

1
2
3
4
5
6
7
8
9
10
11
12
>>> 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

笔记:

  • 我使用元组,因为列表不可哈希(因此不能用作dict键);
  • next((x for x in xs if ), None)xs中通过测试的第一个元素。如果没有元素通过测试,则返回默认值None
  • 在旧版本的python(<3.7)中,您不能保证测试的顺序。如果你有重叠的条件,这很重要。
  • 这显然是次优的,因为您测试的是value < 100,还是value >= 100等。
  • 这真的是Python吗?我不太确定。请浏览https://www.python.org/dev/peps/pep-0020/了解您的想法。


    如果我理解你的话,对于每一个"桶",你都有一个间隔。要检查值是否属于某个间隔,可以定义一个函数:

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

    现在,只需迭代一系列间隔来查找值所属的位置:

    1
    2
    for interval in list_of_intervals:
        check_value(value, interval)


    我觉得这是相当不错的Python,但我不推荐它

    1
    2
    3
    4
    >>> 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相同概念的函数。函数如下所示:

    1
    2
    3
    4
    5
    6
    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

    你可以这样使用它:

    1
    2
    3
    4
    5
    6
    7
    8
    >>> 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