2015-11-19 104 views
4

比方说,我有这样的数据:使用Python创建的元组组从另一个列表中

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我认为部分主要检查后只能使用一次。

回答

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,这应该是合理的表现(它是编译代码)。

  • 用于分组对的压缩生成器方法在Python中是一个相当常见的习惯用法。它保证工作,因为zip保证比从左到右消耗迭代。在Python 2中,使用izip_longest

+0

那里有许多魔力。非常有趣的使用一个迭代器喂两次压缩。 – spectras

+0

看起来很有趣。另外我测试了性能,这个似乎比我的要快。当然这个样本数据非常小,但仍然是。我的解决方案在'〜0.33毫秒内完成,'〜0.03毫秒内完成' – Andrius

+0

也许您应该从'zip'文档中添加这样的引用,以使代码正确:_“从左到右的评估顺序迭代是有保证的。“_ – spectras

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], [])] 
+0

嗯,我测试了所有解决方案之间的时间。你的解决方案似乎需要类似的时间来采矿。因此,至少在这个样本数据中,@ajcr解决方案要快得多。 – Andrius

+0

@Andrius这很奇怪,因为我的基准测试显示ajcr解决方案的速度是前者的两倍 – fl00r

+0

@Andrius timeit显示7.99484586716秒vs 16.8815200329秒 – fl00r

相关问题