2012-07-02 14 views
26

मैं कई सर्वरों से लॉग मर्ज करने की कोशिश कर रहा हूं। प्रत्येक लॉग tuples की एक सूची है (date, count)। date एक से अधिक बार प्रकट हो सकता है, और मैं चाहता हूं कि परिणामी शब्दकोश सभी सर्वरों से सभी गिनती का योग धारण करे।पायथन: मूल्यों के योग() के साथ शब्दों को मर्ज करें

from collections import defaultdict 

a=[("13.5",100)] 
b=[("14.5",100), ("15.5", 100)] 
c=[("15.5",100), ("16.5", 100)] 
input=[a,b,c] 

output=defaultdict(int) 
for d in input: 
     for item in d: 
      output[item[0]]+=item[1] 
print dict(output) 

कौन देता है::

{'14.5': 100, '16.5': 100, '13.5': 100, '15.5': 200} 

जैसी उम्मीद थी

यहाँ मेरी प्रयास, उदाहरण के लिए कुछ डेटा के साथ है।

मैं कोड को देखने वाले सहयोगी के कारण केले जाने जा रहा हूं। वह जोर देती है कि लूप के लिए इन घोंसले के बिना, इसे करने के लिए और अधिक पागल और सुरुचिपूर्ण तरीका होना चाहिए। कोई विचार?

+4

उपयोग 'काउंटर()' –

+2

@AshwiniChaudhary: 'काउंटर()' केवल मौके की गणना करता है, और जैसा कि मान पहले से ही पहले से आबादी वाले हैं, यह इस परिदृश्य के लिए काम नहीं करेगा। –

+0

@ क्रिस्टियनविट्स नीचे मेरा समाधान देखें। –

उत्तर

30

इस से अधिक आसान नहीं मिल करता है, मुझे लगता है:

a=[("13.5",100)] 
b=[("14.5",100), ("15.5", 100)] 
c=[("15.5",100), ("16.5", 100)] 
input=[a,b,c] 

from collections import Counter 

print sum(
    (Counter(dict(x)) for x in input), 
    Counter()) 

ध्यान दें कि Counter (यह भी एक मल्टीसेट के रूप में जाना जाता है) अपने डेटा के लिए सबसे स्वाभाविक डेटा संरचना (सेट का एक प्रकार है जो करने के लिए तत्वों की हो सकती है है एक से अधिक बार, या समकक्ष - अर्थशास्त्र के साथ एक मानचित्र तत्व -> घटना गणना। आप इसे टुपल्स की सूचियों के बजाय पहले स्थान पर इस्तेमाल कर सकते थे।


इसके अलावा

संभव:

from collections import Counter 
from operator import add 

print reduce(add, (Counter(dict(x)) for x in input)) 

का उपयोग sum(seq, initialValue) के बजाय reduce(add, seq) आम तौर पर अधिक लचीला है और आप अनावश्यक प्रारंभिक मूल्य गुजर छोड़ देता है।

ध्यान दें कि आप राशि के बजाय मल्टीसेट्स के चौराहे को खोजने के लिए operator.and_ का भी उपयोग कर सकते हैं।


उपर्युक्त संस्करण बहुत धीमा है, क्योंकि प्रत्येक चरण पर एक नया काउंटर बनाया गया है। आइए इसे ठीक करें।

हम जानते हैं कि Counter+Counter मर्ज किए गए डेटा के साथ एक नया Counter देता है।यह ठीक है, लेकिन हम अतिरिक्त सृजन से बचना चाहते हैं। के Counter.update बजाय उपयोग करते हैं:

अद्यतन (स्वयं, iterable = कोई नहीं, ** kwds) अनबाउंड collections.Counter विधि

dict.update तरह

(), लेकिन उन्हें जगह के बजाय मायने रखता है जोड़ें। स्रोत एक पुनरावर्तनीय, एक शब्दकोश, या एक अन्य काउंटर उदाहरण हो सकता है।

यही वही है जो हम चाहते हैं। आइए इसे reduce के साथ संगत फ़ंक्शन के साथ लपेटें और देखें कि क्या होता है।

def updateInPlace(a,b): 
    a.update(b) 
    return a 

print reduce(updateInPlace, (Counter(dict(x)) for x in input)) 

यह ओपी के समाधान से केवल मामूली धीमी है।

बेंचमार्क: http://ideone.com/7IzSx(अभी तक एक और समाधान के साथ अपडेट किया गया, astynax करने के लिए धन्यवाद)

(इसके अलावा: जो काम करता है lambda x,y: x.update(y) or x द्वारा आप सख्त एक एक लाइनर चाहते हैं, आप updateInPlace जगह ले सकता है वही तरीका और यहां तक ​​कि एक दूसरे को विभाजित करने के लिए भी साबित होता है, लेकिन पठनीयता पर विफल रहता है। नहीं :-))

+2

+1 मुझे वास्तव में यह समाधान पसंद है। – sloth

+1

समय जटिलता के बारे में क्या? क्या यह ओपी के कोड – jerrymouse

+0

से अधिक कुशल है, मुझे ऐसा नहीं लगता है। ओपी का कोड कोई तात्कालिक वस्तु नहीं बनाता है, इसलिए यह आमतौर पर अधिक कुशल होना चाहिए। – Kos

7

आप इस्तेमाल कर सकते हैं itertools 'groupby:

from itertools import groupby, chain 

a=[("13.5",100)] 
b=[("14.5",100), ("15.5", 100)] 
c=[("15.5",100), ("16.5", 100)] 
input = sorted(chain(a,b,c), key=lambda x: x[0]) 

output = {} 
for k, g in groupby(input, key=lambda x: x[0]): 
    output[k] = sum(x[1] for x in g) 

print output 

दो छोरों के बजाय groupby और एक defaultdict के उपयोग के अपने कोड साफ कर देगा।

+2

लैम्ब्डा के बजाय, आप' ऑपरेटर.इटमेटेटर (0) 'में भी ड्रॉप कर सकते हैं :) – Kos

+1

गलत:' groupby', जैसा कि कहा गया है जिस दस्तावेज़ का आप उल्लेख करते हैं, उसे पहले क्रमबद्ध करने की आवश्यकता है! यहां यह काम करता है क्योंकि 'बी [1]' और 'सी [0]' श्रृंखला (ए, बी, सी) में लगातार रहेगा, लेकिन यदि आप 'चेन (ए, सी, बी)' करते हैं, तो परिणाम है सही नहीं है (आपको 'आउटपुट ['15 .5'] ') के लिए 200 के बजाय 100 मिलते हैं ... – Emmanuel

+1

मुझे लगता है कि इसका व्यक्तिगत स्वाद है, लेकिन मुझे डिफॉल्टडिक्ट से पढ़ने के लिए यह कठिन लगता है, और ओपी दृष्टिकोण – fraxel

8
from collections import Counter 


a = [("13.5",100)] 
b = [("14.5",100), ("15.5", 100)] 
c = [("15.5",100), ("16.5", 100)] 

inp = [dict(x) for x in (a,b,c)] 
count = Counter() 
for y in inp: 
    count += Counter(y) 
print(count) 

उत्पादन:

Counter({'15.5': 200, '14.5': 100, '16.5': 100, '13.5': 100}) 

संपादित करें: रूप duncan सुझाव आप एक पंक्ति के साथ इन 3 लाइनों की जगह ले सकता: count = sum((Counter(y) for y in inp), Counter())

:

count = Counter() 
    for y in inp: 
     count += Counter(y) 

द्वारा की जगह

+2

आप 'sum' का उपयोग करके' for' लूप को भी हटा सकते हैं: 'count = sum ((inp में y के लिए काउंटर (y)), काउंटर())' – Duncan

+0

@ डंकन धन्यवाद मैं कभी नहीं जानता था, सुझाव लागू किया गया है । –

1

आप काउंटर या डिफॉल्टडिक्ट का उपयोग कर सकते हैं, या आप मेरे संस्करण का प्रयास कर सकते हैं:

def merge_with(d1, d2, fn=lambda x, y: x + y): 
    res = d1.copy() # "= dict(d1)" for lists of tuples 
    for key, val in d2.iteritems(): # ".. in d2" for lists of tuples 
     try: 
      res[key] = fn(res[key], val) 
     except KeyError: 
      res[key] = val 
    return res 

>>> merge_with({'a':1, 'b':2}, {'a':3, 'c':4}) 
{'a': 4, 'c': 4, 'b': 2} 

या और भी अधिक सामान्य:

def make_merger(fappend=lambda x, y: x + y, fempty=lambda x: x): 
    def inner(*dicts): 
     res = dict((k, fempty(v)) for k, v 
      in dicts[0].iteritems()) # ".. in dicts[0]" for lists of tuples 
     for dic in dicts[1:]: 
      for key, val in dic.iteritems(): # ".. in dic" for lists of tuples 
       try: 
        res[key] = fappend(res[key], val) 
       except KeyError: 
        res[key] = fempty(val) 
     return res 
    return inner 

>>> make_merger()({'a':1, 'b':2}, {'a':3, 'c':4}) 
{'a': 4, 'c': 4, 'b': 2} 

>>> appender = make_merger(lambda x, y: x + [y], lambda x: [x]) 
>>> appender({'a':1, 'b':2}, {'a':3, 'c':4}, {'b':'BBB', 'c':'CCC'}) 
{'a': [1, 3], 'c': [4, 'CCC'], 'b': [2, 'BBB']} 

इसके अलावा, आप dict उपवर्ग और एक __add__ विधि को लागू कर सकते हैं:

+1

धन्यवाद! हालांकि, मूल कोड की तुलना में यह थोड़ा कम स्पष्ट लगता है। –

संबंधित मुद्दे