从成对列表中获取最小值和最大值

时间:2019-10-25 08:46:58

标签: python list readability

我需要各个坐标的极值(最小值,最大值)来确定图形显示的缩放系数。这是我到目前为止的内容:

def get_minmax(data):
    x = (min([x for x,_ in data]), max([x for x,_ in data]))
    y = (min([y for _,y in data]), max([y for _,y in data]))
    return x, y

在我的特定情况下,此代码的明显不佳不是问题(我正在处理大约7个项目的一小部分),但我喜欢(相对)好可读性。不过,我想知道是否有一种解决方案具有很高的可读性和性能。

3 个答案:

答案 0 :(得分:1)

决定详细说明我的一句话。我从您的个人资料中看到您来自C ++,所以让我们计算一下复杂性:

def get_minmax(data):
    x = (min([x for x,_ in data]), max([x for x,_ in data]))
    y = (min([y for _,y in data]), max([y for _,y in data]))
    return x, y

[使用len(data) = n]

寻找minmax应该总是O(n)。这是对的。但这并没有说明常量。是100 * n吗?只是n?十亿* n?

拥有大量数据,这可能会有所作为。

让我们检查一下:

  • [x for x,_ in data]等都是通过data的1个传递。 (+内存使用量,是的!)
  • minmax都需要1次通过

这精确地给出了8次通过- 8 * n


这是不可思议的。正确,Python使用了理解,但是有多种类型的原因。

简介:生成器理解。

它们基本上与列表推导相同,但是它们的计算是延迟的(生成器/迭代器)。如何制作它们?只需用方括号代替括号即可。 (如果理解是函数中唯一的参数,则只需一组括号。):

def get_minmax(data):
    x = (min(x for x,_ in data), max(x for x,_ in data))
    y = (min(y for _,y in data), max(y for _,y in data))
    return x, y

这消除了创建列表的麻烦,因此没有额外的n尺寸内存!仅剩4个传递(最小和最大)= 4n


这仍然给我们留下了所有的最小值和最大值...为了减少这种情况,我们需要执行传统的for循环。列表理解很棒,但是它们不能做任何事情-尤其是不能合并4个函数,每个函数都要遍历列表!

def get_minmax(data):
    x_min, x_max = data[0][0], data[0][0]
    y_min, y_max = data[0][1], data[0][1]
    for x_data, y_data in data: # don't care about the first element, because if you do data[1:], you'll make a copy of the list!
        if x_data < x_min: x_min = x_data
        elif x_data > x_max: x_max = x_data
        if y_data < y_min: y_min = y_data
        elif y_data > y_max: y_max = y_data
    return (x_min, x_max), (y_min, y_max)

从形式上讲,这使我们获得了一次通过。但是,我们在其中做了很多工作。

通过次数较少,但操作次数保持不变。也许平均价格要低一些,因为没有进行比较?

但是悲观的复杂性仍然是4n-我们只获得了一次通行证,但是在循环中做了4件事!


因此应该对生成器使用理解能力,还是仅使用简单的for循环??在特定情况下,最好对其进行度量。

这实际上取决于您在循环中执行的操作及其所保存的数据。

答案 1 :(得分:0)

通过在获取最小值和最大值之前将x和y列表理解设置为变量,可以将列表理解的次数减少一半。

x = [x for x,_ in data]

然后在min函数中使用它,

但是您根本不需要列表

x = min(data, key=lambda x: x[0])[0], max(data, key=lambda x: x[0])[0]

尽管如此,仍然有很多迭代在进行,因此对于较大的列表,它总是要花费一些处理时间

答案 2 :(得分:0)

我将其重新实现为以下内容。它有一个明显的循环(在minmax中有几个隐藏的短循环)。

def get_minmax(data):
    min_x = data[0][0]
    max_x = data[0][0]
    min_y = data[0][1]
    max_y = data[0][1]
    for p in data[1:]:
        min_x = min(min_x, p[0])
        max_x = max(max_x, p[1])
        min_y = min(min_y, p[0])
        max_y = max(max_x, p[1])
    return (min_x, max_x), (min_y, max_y)

我发现,阅读起来很复杂,因为有太多相似的符号和太多语法正确的位置,以至于很容易出错(变成语义错误)。可以通过以下类似的方法删除隐藏的循环,但这对于阅读而言甚至更糟:

def get_minmax(data):
    min_x = data[0][0]
    max_x = data[0][0]
    min_y = data[0][1]
    max_y = data[0][1]
    for p in data[1:]:
        if min_x > p[0]:
            min_x = p[0]
        if max_x < p[0]:
            max_x = p[0]
        if min_y > p[1]:
            min_y = p[1]
        if max_y < p[1]:
            max_y = p[1]
    return (min_x, max_x), (min_y, max_y)

编辑

在阅读h4z3's answer之后,我将以下内容作为性能和可读性之间的一个很好的折衷方案:

def get_minmax(data):
    min_x, min_y = data[0]
    max_x, max_y = data[0]
    for x, y in data:
        min_x = min(x, min_x)
        max_x = max(x, max_x)
        min_y = min(y, min_y)
        max_y = max(y, max_y)
    return (min_x, max_x), (min_y, max_y)

它将循环中的元组解压缩为名称,并(有意地)使用自比较和自赋值来获得更具可读性的代码。