【问题标题】:How can I izip in python with sorted output?如何在 python 中使用排序输出进行 izip 压缩?
【发布时间】:2012-05-26 20:04:41
【问题描述】:

由于没有更好的名字,我想在 Python 中做一个“izip_sorted”。该函数的输入是许多迭代,每个迭代。输出是具有排序输出的单个迭代。

print([x for x in izip_sorted([0,4,8], [1,3,5], [12,12,42],[])])

编辑:这是一个简单的例子。真正的用途将是大约 40 个输入迭代,每个迭代大约有 100000 个元素。每个元素都是一个类,存储一个字典并实现__cmp__,以便对元素进行排序。数据太大,无法一次全部读取。

应该打印

[0, 1, 3, 4, 5, 8, 12, 12, 42]

我有一个解决方案,但我是 python 新手,我不知道它非常 Pythonic。这可以改进吗?只有 1 个元素发生变化的排序似乎很浪费......

def izip_sorted(*iterables):
    """
    Return an iterator that outputs the values from the iterables, in sort order
    izip_sort('ABF', 'D', 'CE') --> A B C D E F
    """
    iterators = [iter(it) for it in iterables]
    current_iterators = []
    for it in iterators:
        try:
            current_iterators.append((next(it), it))
        except StopIteration:
            pass
    current_iterators.sort(key=lambda x: x[0])
    while(current_iterators):
        yield current_iterators[0][0]
        try:
            current_iterators[0] = (next(current_iterators[0][1]), current_iterators[0][1])
            current_iterators.sort(key=lambda x: x[0])
        except StopIteration:
            current_iterators = current_iterators[1:]

【问题讨论】:

  • 这是一个真正具有误导性的名称,因为它与 izip() 函数没有任何关系。

标签: sorting iterator python yield


【解决方案1】:

我认为您正在寻找的是链接可迭代的所有值,然后对它们进行排序。为此,我们可以简单地将itertools.chain()the sorted() builtin 结合起来:

>>> from itertools import chain
>>> sorted(chain([0,4,8], [1,3,5], [12,12,42],[]))
[0, 1, 3, 4, 5, 8, 12, 12, 42]

如果你有一个可迭代的列表,你会想要使用itertools.chain.from_iterable()

>>> iterables = [[0,4,8], [1,3,5], [12,12,42],[]]
>>> sorted(chain.from_iterable(iterables))
[0, 1, 3, 4, 5, 8, 12, 12, 42]

编辑:对于您的确切问题,假设您知道输入列表已排序,并且每个输入列表的最后一项不会大于下一个列表的第一项,我们只需根据第一个值进行排序(使用sorted()的关键函数)然后将它们链接起来。

>>> from operator import itemgetter
>>> iterables = [[0,4,8], [1,3,5], [12,12,42]]
>>> sorted(iterables, key=itemgetter(0))
[[0, 4, 8], [1, 3, 5], [12, 12, 42]]
>>> list(chain.from_iterable(sorted(iterables, key=itemgetter(0))))
[0, 4, 8, 1, 3, 5, 12, 12, 42]

问题是,您提供给我们的数据不符合这些规则,因此答案不正确。

【讨论】:

  • 没有必要通过调用 list() 来构造一个全新的列表
  • @MrFooz 这纯粹是为了示例,以便您可以看到结果。编辑:Derp,sorted(),当然会返回一个列表。编辑。我对使用list() 包装示例以显示输出变得过于热心了。我的错。
  • 我编辑了这个问题。我的示例代码使用的是小列表,但真正的代码试图对 40 个可迭代对象进行排序,每个对象大约有 100000 个元素,每个元素都是一个包含大约 20 个项目的字典。它不适合内存,这就是我首先使用可迭代对象的原因!
  • @Eyal 正如我之前所说,itertools.chain() 是一个生成器,因此很懒——它一次只会处理一个项目。也就是说,sorted() 不是,但要对您的值进行排序,您需要让它们可用。没有办法懒惰地对某些东西进行排序,因为您必须知道其他值才能知道任何一个值应该放在哪里。
  • @Lattyware 这就是为什么我指定每个可迭代的 排序的。查看每个可迭代对象的第一个元素就足够了。
【解决方案2】:

如果输入没有排序,那么它们都必须被实现(本质上,从可迭代变为列表)。不查看数据就无法排序。 LattyWare's solution 是最pythonic的。

另一方面,如果已知输入的可迭代对象已排序,则可以使用heapq.merge

>>> from heapq import merge
>>> merge(*iterables)

【讨论】:

  • 在我的辩护中,heapq.merge 的代码与我的没有太大不同,尽管 merge() 使用堆进行排序,当只有 1 个元素可能出错时(正如我所怀疑的) )。我还没有完全离开!
  • 可惜不支持自定义按键功能。
  • @AlexB:不支持自定义键功能其实没什么大不了的;只需使用旧的 decorate-sort-undecorate 模式,就可以做到不失懒惰。
  • @LieRyan 我最终只是从可迭代对象中发出元组,第一项是我需要排序的键。
【解决方案3】:

这不是zipzip 将几个迭代压缩成一个可迭代的产生元组。你的功能很简单:

sorted(itertools.chain(*iterables))

【讨论】:

  • 如果你建议使用*iterables,你最好使用chain.from_iterable()
  • 我编辑了这个问题。我没有使用链,因为实际上列表很长,有很多,而且列表的元素很大。事实上,仅仅将元素读入内存就会让我的电脑死机。
  • @Eyal itertools.chain() 是一个生成器 - 它一次只能处理一个项目。
  • @Lattyware 那么它将如何工作?如果最小的项在最后一个迭代器中,是否需要先从以前的迭代器中获取所有项并将它们存储在某个地方,然后再输出最后一个?如果不先将链的所有输出放入内存,我看不出如何对链的输出进行排序。
  • @Eyal 我基本上已经就我的问题做出了回应,我不想在两个问题上重复讨论,所以看看那里。
【解决方案4】:

你也可以这样做:

sorted([item for iterable in iterables for item in iterable])

【讨论】:

  • 是的,但是列表理解解决方案的效率不如itertools.chain(),而且可读性较差。
猜你喜欢
  • 2015-09-03
  • 2015-08-13
  • 2016-03-17
  • 1970-01-01
  • 2018-07-29
  • 2021-07-29
  • 1970-01-01
  • 2021-09-30
  • 1970-01-01
相关资源
最近更新 更多