假设我有这些数据:
data = [1, 2, 3, -4, -5, 3, 2, 4, -2, 5, 6, -5, -1, 1]
我需要通过元组将其分组到另一个列表中。一个元组由两个列表组成。一个是正数,另一个是负数。应该通过检查它是什么类型的数字来创建元组。最后的负数(我的意思是连续的负数之间没有正数)意味着,其他数字必须进入另一个元组,当它找到另一个最后的负数时,它应该创建另一个元组。
所以规则是这样的:所有找到的数字都被添加到第一个元组中,当它找到负数时,它仍然将它添加到该元组,直到它找到正数(这意味着必须创建新的元组)。
我认为展示比解释更容易。解析data
后,列表应如下所示:
l = [([1, 2, 3], [-4, -5]), ([3, 2, 4], [-2]), ([5, 6], [-5, -1]), ([1], [])]
我创建了一个解决方案,但我想知道它是否非常理想。也许有可能写出一个更优雅的(我想知道性能,是否有更好的方法来编写具有最佳性能的解析器:))?
def neighborhood(iterable):
iterator = iter(iterable)
prev = None
item = iterator.next() # throws StopIteration if empty.
for next in iterator:
yield (prev,item,next)
prev = item
item = next
yield (prev,item,None)
l = []
pos = []
neg = []
for prev, item, next in neighborhood(data):
if item > 0:
pos.append(item)
if not next:
l.append((pos, neg))
else:
neg.append(item)
if next > 0:
l.append((pos, neg))
pos = []
neg = []
elif not next:
l.append((pos, neg))
print l
P.S。我认为if not next
部分只能在主要检查后使用一次。
答案 0 :(得分:8)
我首先使用itertools.groupby
制作包含正/负列表的连续元组列表,然后分组成连续的对。通过利用生成器,这仍然可以通过列表一次完成:
from itertools import groupby, zip_longest
x = (list(v) for k,v in groupby(data, lambda x: x < 0))
l = list(zip_longest(x, x, fillvalue=[]))
这会将l
视为:
[([1, 2, 3], [-4, -5]), ([3, 2, 4], [-2]), ([5, 6], [-5, -1]), ([1], [])]
关于上述代码的几点说明:
初始分组为正/负值会传递给groupby
,这应该是合理的(它的编译代码)。
用于分组成对的zipping-a-generator方法在Python中是一个相当常见的习惯用法。它保证自zip
保证起作用,而不是从左到右消耗迭代。
在Python 2中,使用izip_longest
。
答案 1 :(得分:0)
你可以使用O(n)
解决方案,这个解决方案不如@ajcr那么漂亮,但应该更有效率。
def pos_neg(data):
split = []
for r in data:
if len(split) == 0 or (r > 0 and len(split[-1][-1]) > 0):
split.append(([], []))
if r < 0:
split[-1][-1].append(r)
else:
split[-1][-2].append(r)
return split
data = [1, 2, 3, -4, -5, 3, 2, 4, -2, 5, 6, -5, -1, 1]
print pos_neg(data)
#=> [([1, 2, 3], [-4, -5]), ([3, 2, 4], [-2]), ([5, 6], [-5, -1]), ([1], [])]