2015-11-19 133 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

相關問題