2013-04-10 25 views
6

我有一個python dictionary,其中包含按鍵'time','power'和'usage'中的3 lists。 所有列表都具有相同數量的元素,並且所有列表均已排序。我想要做的是總結列表'權力'和'用法'的所有元素,它們的索引 對應於列表'時間'中的相同值,以便每次只有一個樣本的權力和使用率單元。如何根據Python中第一個相同的元素求出3個相同大小的排序列表?

例如改造這個字典:

{'time': [1, 2, 2, 3, 4, 4, 5], 
'power': [2, 2, 3, 6, 3, 3, 2], 
'usage': [0, 1, 1, 2, 1, 4, 7]} 

這一個:

{'time': [1, 2, 3, 4, 5], 
'power': [2, 5, 6, 6, 2], 
'usage': [0, 2, 2, 5, 7]} 

已經我寫這段的作品代碼,但我不喜歡 這麼多:

d = {'time':[1,2,2,3,4,4,5], 'power':[0,1,1,2,1,4,7], 'usage':[2,2,3,6,3,3,2]} 
prev = -1 
new_d = {'time':[], 'power': [], 'usage':[]} 
indexes = range(len(d['time'])) 

for i in indexes: 
    if d['time'][i]!=prev: 
    new_d['time'].append(d['time'][i]) 
    new_d['power'].append(d['power'][i]) 
    new_d['usage'].append(d['usage'][i]) 
    else: 
    last_power = len(new_d['power']) - 1 
    last_usage = len(new_d['usage']) - 1 
    new_d['power'][last_power]+=d['power'][i] 
    new_d['usage'][last_usage]+=d['usage'][i] 
    prev=d['time'][i] 

print d 
print new_d 

有沒有蟒蛇的方式來做到這一點更簡單和全面?

+0

其實我有20只不同的列出了字典,功率和使用的詳細信息... – 2013-04-10 12:55:48

+0

不是所有的列表進行排序= P。或者至少'功耗'和'使用率'似乎有一些奇怪的標準。它看起來更像是你的元組''(時間,力量,用法)'存儲爲命名列表。他們通過「時間」字段排序,你想壓縮它。但它仍然不可動搖。如果我把一切都對了,哈哈。你也想「總結」所有其他領域? – luk32 2013-04-10 13:01:53

+0

你做錯了。你應該有一個對象列表,每個對象都有'time','power'和'usage'字段(和其他20個字段)。這將使你的代碼更簡單,更清晰,更加Pythonic,並且使處理更簡單。 – user9876 2013-04-10 13:22:41

回答

1
>>> from itertools import groupby 
>>> from operator import itemgetter 
>>> d = {'usage': [0, 1, 1, 2, 1, 4, 7], 'power': [2, 2, 3, 6, 3, 3, 2], 'time': [1, 2, 2, 3, 4, 4, 5]} 
>>> groups = groupby(zip(d['time'], d['power'], d['usage']), key=itemgetter(0)) 
>>> lists = zip(*[[k] + map(sum, zip(*g)[1:]) for k, g in groups]) 
>>> dict(zip(('time', 'power', 'usage'), lists)) 
{'usage': (0, 2, 2, 5, 7), 'power': (2, 5, 6, 6, 2), 'time': (1, 2, 3, 4, 5)} 

對於按鍵的數量可變的,我已經添加了keys變量,以避免重寫他們:

>>> from itertools import groupby 
>>> from operator import itemgetter 
>>> keys = ('time', 'power', 'usage') 
>>> groups = groupby(zip(*[d[k] for k in keys]), key=itemgetter(0)) 
>>> lists = zip(*[[k] + map(sum, zip(*g)[1:]) for k, g in groups]) 
>>> dict(zip(keys, lists)) 
{'usage': (0, 2, 2, 5, 7), 'power': (2, 5, 6, 6, 2), 'time': (1, 2, 3, 4, 5)} 
+0

我不確定這種方法,因爲首先你沒有辦法確定你將右鍵連接到正確的列表。我有這樣的感覺,越來越多的領域複雜化,但我不確定。 – 2013-04-10 13:55:30

+0

@InbarRose在這個方法中一切都完全確定。鍵的順序決定了。我可以很容易地適應更多的領域,我只是做一個'鍵'變量,所以我不必每個都寫兩次 – jamylak 2013-04-10 13:56:51

+0

至高無上的聖牛! – 2013-04-10 14:01:13

3

一個強大的解決方案,它可以處理任何數量的額外字段 - 由「時間」字段排序(方法):

def aggregate(old_d, sort_key='time'): 
    new_d = dict((k, []) for k in old_d) 
    prev = None 
    curr = None 
    for i in range(len(old_d[sort_key])): 
     curr = old_d[sort_key][i] 
     for key, lst in new_d.iteritems(): # .items() in Python 3+ 
      if prev == curr: 
       if key != sort_key:   
        lst[-1] += old_d[key][i] 
      else: 
       lst.append(old_d[key][i]) 
     prev = curr 
    return new_d 

使用你的字典:

d = {'time': [1, 2, 2, 3, 4, 4, 5], 
    'power': [2, 2, 3, 6, 3, 3, 2], 
    'usage': [0, 1, 1, 2, 1, 4, 7]} 

print aggregate(d) 
>>> 
{'usage': [0, 2, 2, 5, 7], 'power': [2, 5, 6, 6, 2], 'time': [1, 2, 3, 4, 5]} 
+0

是的,好的。這種方式似乎比我的更爲惶惑。 – 2013-04-10 13:10:37

+0

@ThanasisPetsas編輯我的答案,爲任意大小的字典提供強大的解決方案。 – 2013-04-10 13:47:27

+0

謝謝!現在好多了! :) – 2013-04-10 14:02:36

0

這是「pythonian way」:):

d = {'time': [1, 2, 2, 3, 4, 4, 5], 
'power': [2, 2, 3, 6, 3, 3, 2], 
'usage': [0, 1, 1, 2, 1, 4, 7]} 

new_d = {'time' : [], 'power' : [], 'usage' : []} 

for time in set(d['time']): 
    new_d['time'].append(time) 
    new_d['power'].append(sum(value for index, value in enumerate(d['power']) if d['time'][index] == time)) 
    new_d['usage'].append(sum(value for index, value in enumerate(d['usage']) if d['time'][index] == time)) 

print new_d 
+0

這不會更快。對於每一次「時間」,你都要查看每個「權力」和「使用情況」列表......這是一個很大的問題。 – 2013-04-10 13:10:49

+0

酷!我認爲我可以很容易地通過字典鍵進行迭代並執行此操作,因爲我的真實數據是包含20個列表的字典,而不僅僅是3個! :) – 2013-04-10 13:11:46

+0

是的,它很容易擴展。 – 2013-04-10 13:12:21

1

您可以對任意數量的額外字段使用以下方法:

from itertools import groupby 
from operator import itemgetter 

dic = {'time': [1, 2, 2, 3, 4, 4, 5], 
'power': [2, 2, 3, 6, 3, 3, 2], 
'usage': [0, 1, 1, 2, 1, 4, 7]} 

aggrigated = {} 
fields = dic.items() 

for field in fields: 
    aggrigated[field[0]] = [sum(y[1] for y in x) 
           for k,x in groupby(
            zip(fields[0][1], field[1]), 
            key=itemgetter(0)) 
          ] 

改進版借用Ashwini Chaudhary的回答。

+0

謝謝,這太棒了! – 2013-04-10 13:58:59

2

使用itertools.groupbyzip和一些列表理解:

In [55]: dic={'time': [1, 2, 2, 3, 4, 4, 5], 
    ....: 'power': [2, 2, 3, 6, 3, 3, 2], 
    ....: 'usage': [0, 1, 1, 2, 1, 4, 7]} 

In [56]: from itertools import groupby 

In [57]: from operator import itemgetter 

In [58]: zip1=zip(dic['time'],dic['power']) #use `itertools.izip` for performance  

In [59]: [sum(x[1] for x in v) for k,v in groupby(zip1,key=itemgetter(0))] 
Out[59]: [2, 5, 6, 6, 2] 

In [60]: zip2=zip(dic['time'],dic['usage']) 

In [61]: [sum(x[1] for x in v) for k,v in groupby(zip2,key=itemgetter(0))] 
Out[61]: [0, 2, 2, 5, 7] 

In [64]: timee=[k for k,v in groupby(dic['time'])] 

In [65]: timee 
Out[65]: [1, 2, 3, 4, 5] 

zip1[(1, 2), (2, 2), (2, 3), (3, 6), (4, 3), (4, 3), (5, 2)],現在你可以基於所述第一項目組的元件使用itertools.groupby再取每一個元組的第二個元素的總和的返回組。

In [75]: new_time=[k for k,v in groupby(dic['time'])] 

In [76]: new_power=[sum(x[1] for x in v) for k,v in groupby(zip1,key=itemgetter(0))] 

In [77]: new_usage=[sum(x[1] for x in v) for k,v in groupby(zip2,key=itemgetter(0))] 

In [80]: dict(zip(('time','power','usage'),(new_time,new_power,new_usage))) 
Out[80]: {'power': [2, 5, 6, 6, 2], 'time': [1, 2, 3, 4, 5], 'usage': [0, 2, 2, 5, 7]} 
+0

我不確定你在這裏做什麼。新字典在哪裏? – 2013-04-10 14:19:19

+0

@InbarRose你可以通過簡單地將這些列表理解分配給一個新的字典來獲得所需的字典。 – 2013-04-10 14:51:01

+0

這整件事看起來好像不會輕易擴展,它讓我很頭疼,試圖理解代碼與所有[進/出]進行......呃!但是,是的,答案是正確的。 :) – 2013-04-10 14:55:27

1

我會先將這些值組合在一個新的字典中,然後求和。需要多一點空間,但它的方便,快捷:

from collections import defaultdict 
from itertools import groupby 

power = defaultdict(list) 
usage = defaultdict(list) 

for i, time in enumerate(data['time']): 
    power[time].append(data['power'][i]) 
    usage[time].append(data['usage'][i]) 

times = [key for key,group in groupby(data['time'])] 

print { 'time': times, 
      'power' : [sum(power[time]) for time in times], 
      'usage' : [sum(usage[time]) for time in times] 
     } 
+0

謝謝!非常酷的解決方案 – 2013-04-10 13:59:52

1
from itertools import izip 

def m_(time, power, usage): 

    time_, power_, usage_ = [], [], [] 

    for t, p, u in izip(time, power, usage): 

     if not time_: 
      time_.append(t) 
      power_.append(0) 
      usage_.append(0) 

     if time_[-1] == t: 
      power_[-1] += p 
      usage_[-1] += u 
     else: 
      time_.append(t) 
      power_.append(p) 
      usage_.append(u) 

    time[:], power[:], usage[:] = time_, power_, usage_ 

if __name__ == '__main__': 
    d = {'time':[1,2,2,3,4,4,5], 'power':[0,1,1,2,1,4,7], 'usage':[2,2,3,6,3,3,2]} 
    m_(**d) 
    print d 
+0

wooow !!!很酷! – 2013-04-10 13:58:32

+0

thx)但沒有太多功能:/ – indirpir 2013-04-10 14:02:39

+0

是的,非常漂亮的方法,但你可以縮放它嗎? – 2013-04-10 14:03:48

3

這是一個可以處理任意字典的程序....(其中d是您的字典...)

from itertools import groupby, imap 
from operator import itemgetter 

def group_dict_by(mapping, field, agg=sum): 
    grouper = mapping[field] 
    new_grouper = [] 
    accum = {k: [] for k in mapping.viewkeys() - [field]} 
    for key, grp in groupby(enumerate(grouper), itemgetter(1)): 
     new_grouper.append(key) 
     idx = [g[0] for g in grp] 
     for dk, dv in accum.iteritems(): 
      dv.append(agg(imap(mapping[dk].__getitem__, idx))) 

    accum[field] = new_grouper 
    return accum 

print group_dict_by(d, 'time') 
# {'usage': [0, 2, 2, 5, 7], 'power': [2, 5, 6, 6, 2], 'time': [1, 2, 3, 4, 5]} 
+0

這與我製作的答案几乎完全一樣,但決定不發佈。當然,我對'field'和'agg'進行了硬編碼,並且在返回之前,我使用了'defaultdict(list)'而不是普通字典(設置'out.default_factory = None')。 – mgilson 2013-04-10 15:47:48

0

下面是我的問題的確切解決方案。我根據jamylak 的答案做出了它,我認爲這是所有給出的最「蟒蛇」和全面的解決方案。我所做的是修改他的代碼以便與多個字段一起工作,即字典中的多個 列表。我已經接受jamylak的答案,這裏是多領域解決 :

from itertools import groupby    
from operator import itemgetter 

d = {'power': [2, 2, 3, 6, 3, 3, 2], 
    'usage': [0, 1, 1, 2, 1, 4, 7], 
    'time': [1, 2, 2, 3, 4, 4, 5]} 

# construct a list with all the key names (starting from 'time') 
keys = ['time'] + [key for key in d.keys() if key!='time'] 
# construct a list with all the keys' lists (starting from the one of 'time') 
keys_lists = [ d['time'] ] + [d[key] for key in d.keys() if key!='time'] 
groups = groupby(zip(*keys_lists), key=itemgetter(0)) 
lists = zip(*[[k] + map(sum, zip(*g)[1:]) for k, g in groups]) 
new_d = dict(zip((keys), lists)) 
print new_d 
相關問題