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

Python 从对列表中获取最小值和最大值,python,list,readability,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 在我的具体案例中,这段代码的明显的性能差不是一个问题(我正在处理一个大约

我需要单个坐标的极值(最小值、最大值),以确定用于确定图形视图大小的缩放因子。这就是我到目前为止所做的:

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项的小列表)。然而,我想知道是否有一种结合可读性和性能的解决方案。

在获得最小值和最大值之前,通过将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]

不管怎样,仍然有很多迭代在进行,因此对于较大的列表,总是需要一些处理时间

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

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

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)

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

决定详细阐述我的注释。我从你的简介中看到你来自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(数据)=n
]

寻找最小值应始终
O(n)
。这是正确的。但这并没有提到常数。是100*n吗?只是n?十亿*n

有了海量数据,这可能会有所不同

让我们检查一下:

  • [x代表x,u在数据中]
    等是一次通过
    数据。(+内存使用,耶!)
  • min
    max
    各需通过一次
这正好提供了8次传递-8*n


这是非音速的。Python使用理解,对吧,但是有多种类型的理解是有原因的

介绍:生成器理解。

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

这样就不用创建列表了,因此没有额外的n大小内存!只剩下4次传球(最小和最大传球)=4n


这仍然留给我们所有的分和最大。。。为了减少这种情况,我们需要对
循环执行传统的
。列表理解很好,但它们不能做任何事情——特别是不能合并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)
def get_minmax(数据):
x_min,x_max=数据[0][0],数据[0][0]
y_min,y_max=数据[0][1],数据[0][1]
对于x_数据,y_data in data:#不关心第一个元素,因为如果执行data[1:],您将创建列表的副本!
如果x_数据x_max:x_max=x_data
如果y_数据y_max:y_max=y_data
返回值(x_最小值,x_最大值),(y_最小值,y_最大值)
正式地说,这给了我们一次机会。然而,我们在它里面做的更多。

通过次数较低,但操作次数保持不变。也许它的平均值要低一点,因为有些比较没有发生

但悲观的复杂性仍然是4n——我们只通过了一次,但在循环中做了4件事


因此,应该对生成器使用理解,或者对
循环使用简单的
在特定情况下,最好对其进行测量。


这实际上取决于你在循环中做了什么,以及循环中保存了什么数据。

对于大列表来说,它的性能似乎很差
你确实要对
数据进行8次检查-首先是制作列表,然后取最小值/最大值。如果你删除
[
]
,它将使用生成器并仅通过
数据
4次。如果您在
for
循环中手动查看
数据
,您可以在一次通过中完成所有操作。为什么所有的否决票都被否决?请解释一下。我知道性能很差,但与我自己的“答案”相比,可读性相当好。到目前为止给出的答案表明没有其他选择。如果大列表和性能是一个问题,请改用NumPy。那么您是否建议为常见情况编写循环手册?@Wolf-也许,您必须将
分成两半
不。这将列出2个列表,然后将其浏览4次(每个列表2次)。这将提供6次通过,而不是原来的8次。减半将使用生成器(根本不创建列表)或在原始数据列表上过滤您的另一个解决方案。@h4z3-是的,我意识到我的意思是它将完成的列表理解数量减半,但显然仍然存在最小/最大函数的迭代这似乎是与我相同的可读性噩梦;)但是感谢您对列表副本
[1:]
的澄清。您也可以只做
x_min=min(x_min,x_data)
而不是ifs以提高可读性。-平均而言,您会丢失由
elif
保存的比较,但是如果您关心它,那么可能会选择其他语言。@Wolf好吧,解包元组是有用的事情之一,可以提高可读性,因为元素有它们的名称!;)但是还要比较循环和生成器(最后一句)。我重新思考了我在评论中写的东西,循环本身并没有那么好。(我刚刚想起我的具体案例,循环更好!)成对的循环看起来不错,也许
min(x_min,x_data)
是一个很好的折衷方案。(交叉评论)
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
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
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)