【问题标题】:Finding where a value lands between two numbers in Python在 Python 中查找值在两个数字之间的位置
【发布时间】:2015-09-13 19:17:37
【问题描述】:

我有一个问题,我需要确定一个值在其他值之间的位置。这是一个非常长的问题......但它是一个令人费解的问题(至少对我而言)。

用以下数据可以看出问题的最简单表示:

我的值为 24.0。我需要确定该值在六个“范围”内的位置。范围是:10, 20, 30, 40, 50, 60。我需要计算范围内的值落在哪里。我可以看到它落在 20 到 30 之间。一个简单的 if 语句可以为我找到它。

我用于检查值是否在 20 到 30 之间的 if 语句是:

if value >=20 and value <=30:

很简单的东西。

我遇到的问题是当我尝试对输出进行排名时。

例如,假设每个范围值都有一个整数表示。 10 = 1、20 = 2、30 = 3、40 = 4、50 = 5、60 = 6、70 = 7。此外,假设该值小于两个值之间的中点,则为其分配较低值的排名输出。例如,我的 24 值在 20 到 30 之间,所以它应该被列为“2”。

这个例子本身就相当简单,但是使用真实世界的数据,我有如下范围和值:

  • 值 = -13,范围 = 5,35,30,25,-25,-30,-35
  • 值 = 50,范围 = 5,70,65,60,40,35,30
  • 值 = 6,范围 = 1,40,35,30,5,3,0

另一个问题 - 范围的顺序很重要。在上面,第一个范围数相当于排名 1,第二个相当于排名 2,依此类推,正如我在上面的几段中提到的。

范围值中的负数会造成麻烦,直到我决定使用百分位排名来消除所有负值。为此,我使用来自Map each list value to its corresponding percentile 的答案,如下所示:

y=[stats.percentileofscore(x, a, 'rank') for a in x]

其中 x 是范围和我正在检查的值。运行上面的 value=6 值会导致 y 为:

x = [1, 40, 35, 30, 5, 3, 0, 6]

y=[stats.percentileofscore(x, a, 'rank') for a in x]

查看“y”,我们将其视为:

[25.0, 100.0, 87.5, 75.0, 50.0, 37.5, 12.5, 62.5]

我现在需要做的是将最后一个值 (62.5) 与其他值进行比较,以根据以下排名图查看最终排名(排名 1 到 7):

1=25.0
2=100.0
3=87.5
4=75.0
5=50.0
6=37.5
7=12.5

如果该值介于两个值之间,则应为其分配较低的等级。在此示例中,62.5 值的最终排名值为 4,因为它位于 75.0(排名=4)和 50.0(排名=5)之间。

如果我将 'y' 分解并在多个 if/else 语句中使用这些值,则它适用于某些但不是全部(-13 示例 not 正常工作)。

我的问题是这样的:

如何在不构建庞大的 if/elif 结构的情况下以编程方式分析任何值/范围集以找到最终排名?这里有几个样本集。排名如下所示(范围中的第一个值 =1,第二个 = 2,依此类推)

  • 值 = -13,范围 = 5、35、30、25、-25、-30、-35 --> 等级 = 4
  • 值 = 50,范围 = 5、70、65、60、40、35、30 --> 排名 = 4
  • 值 = 6,范围 = 1、40、35、30、5、3,0 --> 等级 = 4
  • 值 = 24,范围 = 10、20、30、40、50、60、70 --> 排名 = 2
  • 值 = 2.26,范围 = 0.1、0.55、0.65、0.75、1.75、1.85、1.95 --> 排名 = 7
  • 值 = 31,范围 = 10、20、30、40、60、70、80 --> 排名 = 3

我可能在 python 中遗漏了一些非常容易做到这一点的东西......但是我已经在这堵墙上撞了几天没有进展。

感谢任何帮助/指针。

【问题讨论】:

  • 您是否查看过像 numpyscipypandas 这样的数值/科学库?这听起来像是他们会做的事情。
  • 是的。我经常使用这三个,但我没有找到任何我需要的东西。 scipy.stats 被用于这个的 percentileofscore 部分。
  • 或许也可以看看这个问题:stackoverflow.com/questions/6053974/…
  • 您可以将每个范围值作为键放在字典中。它们对应的值将是要分配的等级。这样,它就不会基于顺序,而是基于您分配的任何值。你也可以使用有序的字典来维持秩序。
  • 您的输入和期望的输出似乎与您所说的没有关系。你能澄清一下吗?

标签: python


【解决方案1】:

-13的第一个例子。

y = [5, 35, 30, 25, -25, -30, -35]
value_to_check = -13

max_rank = len(y) # Default value in case no range found (as per 2.26 value example)

for ii in xrange(len(y)-1,0,-1):
    if (y[ii] <= value_to_check <= y[ii-1]) or (y[ii] >= value_to_check >= y[ii-1]):
        max_rank = ii
        break

>>> max_rank
4

函数形式:

def get_rank(y, value_to_check):

    max_rank = len(y) # Default value in case no range found (as per 2.26 value example)

    for ii in xrange(len(y)-1,0,-1):
        if (y[ii] <= value_to_check <= y[ii-1]) or (y[ii] >= value_to_check >= y[ii-1]):
            max_rank = ii
            break

    return max_rank

当你打电话时:

>>> get_rank(y, value_to_check)
4

【讨论】:

  • 这为许多不同的选项提供了错误的答案。例如,Value = 6 with Ranges = 1, 40, 35, 30, 5, 3,0 - 应该给出 rank = 4 但这给出了 rank =1
  • 但是6不是在1到40之间,所以排名确实应该是1?
  • 确实如此……但它也在 30 到 5 之间(排名 = 4),所以我知道它必须在这里输出 4。这是我的困境! :)
  • 那么,您基本上希望它返回第一个匹配从范围末尾开始
  • @EricD.Brown:此方法现在可以正确获取您在问题中陈述的所有必需等级。
【解决方案2】:

这可以正确找到所有数据的答案:

def get_rank(l,n):
    mindiff = float('inf')
    minindex = -1
    for i in range(len(l) - 1):
        if l[i] <= n <= l[i + 1] or l[i + 1] <= n <= l[i]:
            diff = abs(l[i + 1] - l[i])
            if diff < mindiff:
                mindiff = diff
                minindex = i
    if minindex != -1:
        return minindex + 1         
    if n > max(l):
        return len(l)
    return 1

>>> test()
[5, 35, 30, 25, -25, -30, -35] -13 Desired: 4 Actual: 4
[5, 70, 65, 60, 40, 35, 30] 50 Desired: 4 Actual: 4
[1, 40, 35, 30, 5, 3, 0] 6 Desired: 4 Actual: 4
[10, 20, 30, 40, 50, 60, 70] 24 Desired: 2 Actual: 2
[0.1, 0.55, 0.65, 0.75, 1.75, 1.85, 1.95] 2.26 Desired: 7 Actual: 7
[10, 20, 30, 40, 60, 70, 80] 31 Desired: 3 Actual: 3

为了完整起见,这里是我的 test() 函数,但您只需要 get_rank 即可:

>>> def test():
        lists = [[[5, 35, 30, 25, -25, -30, -35],-13,4],[[5, 70, 65, 60, 40, 35, 30],50,4],[[1, 40, 35, 30, 5, 3,0],6,4],[[10, 20, 30, 40, 50, 60, 70],24,2],[[0.1, 0.55, 0.65, 0.75, 1.75, 1.85, 1.95],2.26,7],[[10, 20, 30, 40, 60, 70, 80],31,3]]
        for l,n,desired in lists:
            print l,n,'Desired:',desired,'Actual:',get_rank(l,n)

【讨论】:

  • 这看起来正在运行。我有许多值/范围要检查,但到目前为止看起来不错。对于 [0.025 0.25 0.23 0.2 -0.2 -0.23 -0.25] 和 0.15,它给你什么?它应该是 4,但例程给了我一个“1”。我正在测试的其他值看起来都是正确的。我看到了其他一些错误的“1”排名,但到目前为止,在大约 200 个样本中,95% 的输出是正确的
  • 该范围是否按任何特定顺序排列?
  • @Eric 我不确定为什么应该给出 4。根据我对问题的理解,应该返回 1。你能解释一下为什么应该给出 4 吗?跨度>
  • 好点:0.15 大于 0.025,所以它确实应该报告“1”。
  • 我很惊讶 COBOL 程序的原始开发人员仍然使用相同的组织,更不用说仍然希望记住程序的算法......
【解决方案3】:
def checker(term):
    return term if term >= 0 else abs(term)+1e10

l1, v1 = [5, 35, 30, 25, -25, -30, -35], -13 # Desired: 4
l2, v2 = [5, 70, 65, 60, 40, 35, 30], 50 # Desired: 4
l3, v3 = [1, 40, 35, 30, 5, 3, 0], 6 # Desired: 4
l4, v4 = [10, 20, 30, 40, 50, 60, 70], 24 # Desired: 2
l5, v5 = [0.1, 0.55, 0.65, 0.75, 1.75, 1.85, 1.95], 2.26 # Desired: 7
l6, v6 = [10, 20, 30, 40, 60, 70, 80], 31 # Desired: 3

结果:

>>> print(*(sorted(l_+[val], key=checker).index(val) for
... l_, val in zip((l1,l2,l3,l4,l5,l6),(v1,v2,v3,v4,v5,v6))), sep='\n')
4
4
4
2
7
3

【讨论】:

  • 我认为这只是利用指定测试中的一种模式,而不是解决上述问题。例如,[5, 70, 65, 60, 40, 35, 30] 和 31 你会得到什么?
  • 是的;这不适用于任意排序的lists,但我最初认为有某种逻辑排序顺序。我想我无论如何也猜不到令人惊讶的规范 COBOL 实现的算法。
猜你喜欢
  • 1970-01-01
  • 2015-02-05
  • 2017-01-13
  • 1970-01-01
  • 2015-02-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-08-22
相关资源
最近更新 更多