2017-10-10 41 views
2

我有一個包含flag(布爾值)和value(整數)的對象列表。每個列表項的值都在增加。每次標記屬性交替時,我想將列表轉換爲只有第一個和最後一個值屬性(間隔)的新列表。在Python中將值列表轉換爲區間列表

換句話說,我想轉換此

[ 
    {'flag': True, 'value': 0}, 
    {'flag': True, 'value': 5}, 
    {'flag': True, 'value': 10}, 
    {'flag': False, 'value': 15}, 
    {'flag': False, 'value': 20}, 
    {'flag': False, 'value': 25}, 
    {'flag': False, 'value': 30}, 
    {'flag': False, 'value': 35}, 
    {'flag': False, 'value': 40}, 
    {'flag': True, 'value': 45}, 
    {'flag': True, 'value': 50}, 
    {'flag': True, 'value': 55}, 
    {'flag': True, 'value': 60}, 
    {'flag': False, 'value': 65}, 
    {'flag': False, 'value': 70}, 
    {'flag': False, 'value': 75}, 
    {'flag': False, 'value': 80}, 
    {'flag': False, 'value': 85}, 
    {'flag': True, 'value': 90}, 
    {'flag': True, 'value': 95} 
] 

到這個(或類似的東西):

[ 
    {'flag': True, 'values': (0, 10)}, 
    {'flag': False, 'values': (15, 40)}, 
    {'flag': True, 'values': (45, 60)}, 
    {'flag': False, 'values': (65, 85)}, 
    {'flag': True, 'values': (90, 95)}, 
] 

什麼是在Python解決這個簡單而優雅的方式?

我希望示例數據不是太混亂。我試過使用for循環和使用reduce來解決這個問題,但都沒有解決方案。

謝謝!

回答

3

您可以使用itertools.groupbyflag的項目進行分組,然後將這些組轉換爲列表並獲取這些列表的第一個和最後一個元素的值。這裏,lst是你的原始列表。

>>> [{"flag": key, "values" : (val[0]["value"], val[-1]["value"])} 
... for key, val in ((key, list(val)) for key, val in itertools.groupby(lst, key=lambda d: d["flag"]))] 
[{'flag': True, 'values': (0, 10)}, 
{'flag': False, 'values': (15, 40)}, 
{'flag': True, 'values': (45, 60)}, 
{'flag': False, 'values': (65, 85)}, 
{'flag': True, 'values': (90, 95)}] 

或者類似的,使用內部列表理解首先提取value鍵,因爲我們必須使用一個內部生成表達該組迭代器轉換反正列出。結果是一樣的。

>>> [{"flag": key, "values" : (vals[0], vals[-1])} 
... for key, vals in ((key, [g["value"] for g in grp]) 
...     for key, grp in itertools.groupby(lst, key=lambda d: d["flag"]))] 

但是,這還遠遠沒有可讀的,所以我建議拆分這分成兩行:

>>> groups = ((key, [g["value"] for g in grp]) 
...   for key, grp in itertools.groupby(lst, key=lambda d: d["flag"])) 
>>> result = [{"flag": key, "values" : (vals[0], vals[-1])} for key, vals in groups] 
+0

找不到然而與蟒蛇工具這樣做,什麼更好的辦法,因爲筆者問這樣做的最Python的方式,我想說的大規模理解列表不會幫助代碼的可讀性,我會爲for循環尋找相同的答案。 –

+0

@ T.Nel認爲這並不像人們所希望的那樣簡單和「pythonic」,但我不認爲帶有循環的「手動」解決方案會更好。最好把它分成幾行。此外,您可以隨時將這些東西包裝成具有良好名稱和記錄的功能。 –

0

大熊貓的解決方案:

j = [ 
{'flag': True, 'value': 0}, {'flag': True, 'value': 5}, 
{'flag': True, 'value': 10}, {'flag': False, 'value': 15}, 
{'flag': False, 'value': 20}, {'flag': False, 'value': 25}, 
{'flag': False, 'value': 30}, {'flag': False, 'value': 35}, 
{'flag': False, 'value': 40}, {'flag': True, 'value': 45}, 
{'flag': True, 'value': 50}, {'flag': True, 'value': 55}, 
{'flag': True, 'value': 60}, {'flag': False, 'value': 65}, 
{'flag': False, 'value': 70}, {'flag': False, 'value': 75}, 
{'flag': False, 'value': 80}, {'flag': False, 'value': 85}, 
{'flag': True, 'value': 90}, {'flag': True, 'value': 95}] 

import pandas as pd 

df = pd.DataFrame(j) 

df['group'] = df['flag'].ne(df['flag'].shift()).cumsum() # column to groupby 
output = df.groupby("group").apply(lambda x: \ 
            {'flag':x["flag"].tolist()[0], 
            'values':(x["value"].min(),x["value"].max())}) 

output.tolist() 

輸出:

[{'flag': True, 'values': (0, 10)}, 
{'flag': False, 'values': (15, 40)}, 
{'flag': True, 'values': (45, 60)}, 
{'flag': False, 'values': (65, 85)}, 
{'flag': True, 'values': (90, 95)}] 

Dataframe看起來像:

flag value group 
0 True 0 1 
1 True 5 1 
2 True 10 1 
3 False 15 2 
4 False 20 2 
5 False 25 2 
6 False 30 2 
7 False 35 2 
8 False 40 2 
9 True 45 3 
... 
0

手動解決方案:

lista = [ 
    {'flag': True, 'value': 0}, {'flag': True, 'value': 5}, {'flag': True, 'value': 10}, 
    {'flag': False, 'value': 15}, {'flag': False, 'value': 20}, {'flag': False, 'value': 25}, 
    {'flag': False, 'value': 30}, {'flag': False, 'value': 35}, {'flag': False, 'value': 40}, 
    {'flag': True, 'value': 45}, {'flag': True, 'value': 50}, {'flag': True, 'value': 55}, 
    {'flag': True, 'value': 60}, {'flag': False, 'value': 65}, {'flag': False, 'value': 70}, 
    {'flag': False, 'value': 75}, {'flag': False, 'value': 80}, {'flag': False, 'value': 85}, 
    {'flag': True, 'value': 90}, {'flag': True, 'value': 95} 
] 
change = False 
output = [] 
p=[0]*2 
flagPast = lista[0]['flag'] 
for index,item in enumerate(lista): 
    if(item['flag'] != flagPast): 
     cp=p[:]  
     output.append(zip(['flag','values'],[flagPast,cp])) 
     p[0]=item['value'] 
     change = True 
    else: 
     change = False 
    if(not(change)): 
     p[1]=item['value'] 
    flagPast = item['flag'] 
    if(index==(len(lista)-1)): 
     p[1]=item['value']   
     cp=p[:]  
     output.append(zip(['flag','values'],[flagPast,cp]))