দুটি ডিক্ট একত্রিত করার কোনও অজগর উপায় আছে (উভয় উপস্থিত কীগুলির জন্য মান যুক্ত করা)?


477

উদাহরণস্বরূপ আমার দুটি ডিক্ট রয়েছে:

Dict A: {'a': 1, 'b': 2, 'c': 3}
Dict B: {'b': 3, 'c': 4, 'd': 5}

আমার দু'টি ডিক্ট মিশ্রনের একটি অজগর উপায় দরকার যা ফলাফল:

{'a': 1, 'b': 5, 'c': 7, 'd': 5}

এর অর্থ হ'ল: যদি কোনও চাবিকাঠি উভয় দিক থেকে প্রদর্শিত হয় তবে তাদের মানগুলি যুক্ত করুন, যদি এটি কেবল একটি ডিকের মধ্যে উপস্থিত হয় তবে এর মানটি রাখুন।

উত্তর:


835

ব্যবহার collections.Counter:

>>> from collections import Counter
>>> A = Counter({'a':1, 'b':2, 'c':3})
>>> B = Counter({'b':3, 'c':4, 'd':5})
>>> A + B
Counter({'c': 7, 'b': 5, 'd': 5, 'a': 1})

কাউন্টারগুলি মূলত একটি সাবক্লাস হয় dict, তাই আপনি এখনও তাদের ধরণের সাথে কীগুলি এবং মানগুলির পুনরাবৃত্তি হিসাবে সাধারণত টাইপটি করতে সাধারণত তাদের সাথে আরও কিছু করতে পারেন।


3
এই জাতীয় একত্রে একাধিক কাউন্টার আছে কি? sum(counters)দুর্ভাগ্যক্রমে, কাজ করে না।
ডাঃ জানু-ফিলিপ গেহর্ক্ক

27
@ জান-ফিলিপ জিহরস্কে: এর sum()সাথে একটি শুরুর মান দিন sum(counters, Counter())
মার্টিজান পিটারস

5
ধন্যবাদ। যাইহোক, এই পদ্ধতিটি মধ্যবর্তী-অবজেক্ট-ক্রিয়েশন দ্বারা প্রভাবিত হয় কারণ সামঞ্জক স্ট্রিংগুলি তাই না?
ডাঃ জানু-ফিলিপ গেহর্ক্ক

6
@ জানু-ফিলিপ জিহরস্কে: আপনার অন্য বিকল্পটি লুপ ব্যবহার করা এবং +=ইন-প্লেস সারসংক্ষেপ করা। res = counters[0]তাহলে for c in counters[1:]: res += c
মার্টিজন পিটারস

3
আমি যে পদ্ধতির পছন্দ! কেউ লাইক প্রক্রিয়াকরণের অভিধান পাসে জিনিষ রাখতে পারেন, এক এছাড়াও ব্যবহার করতে পারে update()পরিবর্তে +=: for c in counters[1:]: res.update(c)
ডাঃ জানু-ফিলিপ গেহরস্কে

119

একটি আরও জেনেরিক সমাধান, যা অ-সংখ্যাগত মানগুলির জন্যও কাজ করে:

a = {'a': 'foo', 'b':'bar', 'c': 'baz'}
b = {'a': 'spam', 'c':'ham', 'x': 'blah'}

r = dict(a.items() + b.items() +
    [(k, a[k] + b[k]) for k in set(b) & set(a)])

বা আরও জেনেরিক:

def combine_dicts(a, b, op=operator.add):
    return dict(a.items() + b.items() +
        [(k, op(a[k], b[k])) for k in set(b) & set(a)])

উদাহরণ স্বরূপ:

>>> a = {'a': 2, 'b':3, 'c':4}
>>> b = {'a': 5, 'c':6, 'x':7}

>>> import operator
>>> print combine_dicts(a, b, operator.mul)
{'a': 10, 'x': 7, 'c': 24, 'b': 3}

27
এছাড়াও আপনি ব্যবহার করতে পারে for k in b.viewkeys() & a.viewkeys(), যখন পাইথন 2.7 ব্যবহার , এবং সেট সৃষ্টির লাফালাফি।
মার্টিজন পিটারস

কেন set(a)টিপসগুলির পরিবর্তে কীগুলির সেটটি ফেরত দেয়? এর যৌক্তিকতা কী?
সরসপরিলা

1
@ হাইফান: কারণ ডিক্টগুলি কেভি-জোড়ির চেয়ে নয়, কীগুলির উপর দিয়ে পুনরাবৃত্তি করে। সিএফ list({..}), for k in {...}ইত্যাদি
জর্জি

2
@ ক্র্যাকারজ্যাক: হ্যাঁ, আমি operator.mulস্পষ্ট করে বলতাম যে এই কোডটি জেনেরিক এবং সংখ্যা যুক্ত করার ক্ষেত্রে সীমাবদ্ধ নয়।
জর্জি

6
আপনি কি পাইথন 3- সামঞ্জস্যপূর্ণ বিকল্প যুক্ত করতে পারেন? {**a, **b, **{k: op(a[k], b[k]) for k in a.keys() & b}}পাইথন 3.5+ এ কাজ করা উচিত।
ভোল্টাহ

66
>>> A = {'a':1, 'b':2, 'c':3}
>>> B = {'b':3, 'c':4, 'd':5}
>>> c = {x: A.get(x, 0) + B.get(x, 0) for x in set(A).union(B)}
>>> print(c)

{'a': 1, 'c': 7, 'b': 5, 'd': 5}

1
for x in set(itertools.chain(A, B))আরও যুক্তিযুক্ত ব্যবহার করবেন না ? ডিকের উপর সেটটি ব্যবহার করা কীগুলি ইতিমধ্যে অনন্য হিসাবে কিছুটা আজেবাজে কথা? আমি জানি কীগুলির সেটটি পাওয়ার আরও একটি উপায় তবে এটি ব্যবহার করার চেয়ে আমি আরও বিভ্রান্তিকর বলে মনে করি itertools.chain(বোঝাচ্ছেন আপনি কী itertools.chainকরেন)
জেরোমজ

45

ভূমিকা: এখানে (সম্ভবত) সেরা সমাধান রয়েছে। তবে আপনাকে এটি জানতে হবে এবং এটি মনে রাখতে হবে এবং কখনও কখনও আপনাকে আশা করতে হবে যে আপনার পাইথনের সংস্করণটি খুব পুরানো নয় বা সমস্যাটি যাই হোক না কেন।

তারপরে সর্বাধিক 'হ্যাকি' সমাধান রয়েছে। এগুলি দুর্দান্ত এবং সংক্ষিপ্ত তবে কখনও কখনও বোঝা, পড়া এবং মনে রাখা শক্ত।

চাকাটি পুনরায় উদ্ভাবনের চেষ্টা করার জন্য একটি বিকল্প রয়েছে। - চাকা পুনরুদ্ধার কেন? - সাধারণত কারণ এটি শেখার সত্যিই ভাল উপায় (এবং কখনও কখনও কেবল কারণ ইতিমধ্যে বিদ্যমান সরঞ্জামটি আপনার পছন্দ মতো এবং / অথবা আপনি যেভাবে চান তা ঠিকভাবে করে না) এবং আপনি যদি না জানেন তবে বা সহজ উপায় আপনার সমস্যার জন্য নিখুঁত সরঞ্জাম মনে রাখবেন না।

সুতরাং , আমি মডিউল (আংশিকভাবে কমপক্ষে) Counterথেকে শ্রেণীর চাকাটি পুনঃস্থাপনের প্রস্তাব করছি collections:

class MyDict(dict):
    def __add__(self, oth):
        r = self.copy()

        try:
            for key, val in oth.items():
                if key in r:
                    r[key] += val  # You can custom it here
                else:
                    r[key] = val
        except AttributeError:  # In case oth isn't a dict
            return NotImplemented  # The convention when a case isn't handled

        return r

a = MyDict({'a':1, 'b':2, 'c':3})
b = MyDict({'b':3, 'c':4, 'd':5})

print(a+b)  # Output {'a':1, 'b': 5, 'c': 7, 'd': 5}

এটি প্রয়োগ করার সম্ভবত অন্য উপায় আছে এবং এটি করার জন্য ইতিমধ্যে সরঞ্জাম রয়েছে তবে জিনিসগুলি কীভাবে মূলত কাজ করবে তা ভিজ্যুয়ালাইজ করা সবসময়ই সুন্দর।


3
আমাদের
ব্রায়ান বি

13
myDict = {}
for k in itertools.chain(A.keys(), B.keys()):
    myDict[k] = A.get(k, 0)+B.get(k, 0)

13

অতিরিক্ত আমদানি ছাড়াই এক !

এগুলি ইএএফপি ( পারফরমেন্সের চেয়ে ক্ষমা চাওয়ার চেয়ে সহজ) নামক একটি অজগর মান । কোডের নীচে সেই পাইথন স্ট্যান্ডার্ডের উপর ভিত্তি করে ।

# The A and B dictionaries
A = {'a': 1, 'b': 2, 'c': 3}
B = {'b': 3, 'c': 4, 'd': 5}

# The final dictionary. Will contain the final outputs.
newdict = {}

# Make sure every key of A and B get into the final dictionary 'newdict'.
newdict.update(A)
newdict.update(B)

# Iterate through each key of A.
for i in A.keys():

    # If same key exist on B, its values from A and B will add together and
    # get included in the final dictionary 'newdict'.
    try:
        addition = A[i] + B[i]
        newdict[i] = addition

    # If current key does not exist in dictionary B, it will give a KeyError,
    # catch it and continue looping.
    except KeyError:
        continue

সম্পাদনা: তার উন্নতির পরামর্শের জন্য জিরজিকে ধন্যবাদ ।


5
এন ^ 2 algorith কাউন্টার পদ্ধতি তুলনায় উল্লেখযোগ্যভাবে ধীর হতে হবে
Joop

@ দেবেশেশাইনি আরও ভাল, তবে তবুও উপ-অনুকূল :) যেমন: আপনার কি সত্যিই বাছাই করা দরকার? এবং তারপরে, দুটি লুপ কেন? আপনার কাছে ইতিমধ্যে নতুন কক্ষে সমস্ত কী রয়েছে, অনুকূল করার জন্য কেবলমাত্র ছোট ইঙ্গিত
জেরজাইক

n ^ 1 অ্যালগরিদম আগের এন ^ 2 অ্যালগরিদমের পরিবর্তে স্থাপন করা হয়েছে @ জুপ
দেবেশ

11

এই ধরণের ক্ষেত্রে অবশ্যই Counter()s এর সংক্ষিপ্ত বিবরণ সর্বাধিক পাইথোনিক উপায় তবে কেবল যদি এটির ইতিবাচক মান হয় । এখানে একটি উদাহরণ রয়েছে এবং আপনি দেখতে পাচ্ছেন যে অভিধানে এর মানটিকে cঅবহেলা করার পরে ফলাফল নেই ।cB

In [1]: from collections import Counter

In [2]: A = Counter({'a':1, 'b':2, 'c':3})

In [3]: B = Counter({'b':3, 'c':-4, 'd':5})

In [4]: A + B
Out[4]: Counter({'d': 5, 'b': 5, 'a': 1})

এটা এ কারণে যে এর Counterগুলি প্রাথমিকভাবে ধনাত্মক পূর্ণসংখ্যা সঙ্গে কাজ করার ডিজাইন করা হয়েছে (ঋণাত্মক গণনা অর্থহীন) চলমান গন্য প্রতিনিধিত্ব করতে। তবে এই ব্যবহারের ক্ষেত্রে সাহায্য করার জন্য পাইথন ন্যূনতম ব্যাপ্তি এবং নিম্নরূপ বিধিনিষেধগুলি নথি করে:

  • কাউন্টার শ্রেণি নিজেই একটি অভিধান সাবক্লাস যা এর কী এবং মানগুলিতে কোনও সীমাবদ্ধতা ছাড়াই। মানগুলি গণনা উপস্থাপনের সংখ্যা হিসাবে লক্ষ্য করা হয় তবে আপনি মান ক্ষেত্রের মধ্যে যে কোনও কিছু সংরক্ষণ করতে পারেন।
  • most_common()পদ্ধতি শুধু তাই মান orderable হতে হবে।
  • ইন-প্লেস অপারেশনের জন্য যেমন c[key] += 1, মান ধরণের জন্য কেবল সমর্থন যোগ এবং বিয়োগফল প্রয়োজন। সুতরাং ভগ্নাংশ, ভাসমান এবং দশমিক কাজ করবে এবং নেতিবাচক মানগুলি সমর্থিত। একই জন্য সত্য update()এবং subtract()যা ইনপুট এবং আউটপুট উভয়ের জন্য নেতিবাচক এবং শূন্য মানগুলির অনুমতি দেয়।
  • মাল্টিসেট পদ্ধতিগুলি কেবল ইতিবাচক মানগুলির সাথে ব্যবহারের ক্ষেত্রে তৈরি করা হয়েছে। ইনপুটগুলি নেতিবাচক বা শূন্য হতে পারে তবে ইতিবাচক মানগুলির সাথে কেবল আউটপুট তৈরি করা হয়। কোনও ধরণের বিধিনিষেধ নেই, তবে মান ধরণের সংযোজন, বিয়োগফল এবং তুলনা সমর্থন করা দরকার।
  • elements()পদ্ধতি পূর্ণসংখ্যা গন্য প্রয়োজন। এটি শূন্য এবং নেতিবাচক গণনা উপেক্ষা করে।

সুতরাং আপনার কাউন্টারকে সংক্ষিপ্ত Counter.updateকরার পরে সেই সমস্যাটি ঘটার জন্য আপনি ইচ্ছা আউটপুট পাওয়ার জন্য ব্যবহার করতে পারেন । এটি এর মতো কাজ করে dict.update()তবে তাদের প্রতিস্থাপনের পরিবর্তে গণনা যোগ করে।

In [24]: A.update(B)

In [25]: A
Out[25]: Counter({'d': 5, 'b': 5, 'a': 1, 'c': -1})

10
import itertools
import collections

dictA = {'a':1, 'b':2, 'c':3}
dictB = {'b':3, 'c':4, 'd':5}

new_dict = collections.defaultdict(int)
# use dict.items() instead of dict.iteritems() for Python3
for k, v in itertools.chain(dictA.iteritems(), dictB.iteritems()):
    new_dict[k] += v

print dict(new_dict)

# OUTPUT
{'a': 1, 'c': 7, 'b': 5, 'd': 5}

অথবা

বিকল্প হিসাবে আপনি কাউন্টারকে ব্যবহার করতে পারেন @ মার্তিজন উপরে উল্লিখিত হয়েছে।


7

আরও জেনেরিক এবং এক্সটেনসিবল উপায়ের জন্য মার্জিক্ট পরীক্ষা করুন । এটি singledispatchএর ধরণের ভিত্তিতে মানগুলি ব্যবহার করে এবং মার্জ করতে পারে।

উদাহরণ:

from mergedict import MergeDict

class SumDict(MergeDict):
    @MergeDict.dispatch(int)
    def merge_int(this, other):
        return this + other

d2 = SumDict({'a': 1, 'b': 'one'})
d2.merge({'a':2, 'b': 'two'})

assert d2 == {'a': 3, 'b': 'two'}

5

অজগর 3.5 থেকে: মার্জিং এবং সংশ্লেষ

@ টোকইনাইজার_ফএসজেকে ধন্যবাদ যে আমাকে একটি মন্তব্যে বলেছিল যে আমি প্রশ্নের পুরোপুরি অর্থ পাইনি (আমি ভেবেছিলাম যে অ্যাডের অর্থ কেবল কী যুক্ত করা হবে যা শেষ পর্যন্ত দুটি স্বৈরশাসকের চেয়ে আলাদা এবং পরিবর্তে, আমি বলতে চাইছি সাধারণ মূল মানগুলি সংক্ষিপ্ত করা উচিত)। সুতরাং আমি মার্জ হওয়ার আগে সেই লুপটি যুক্ত করেছি, যাতে দ্বিতীয় অভিধানে সাধারণ কীগুলির যোগফল থাকে। সর্বশেষ অভিধানটি হ'ল যার অভিধানটি নতুন অভিধানে স্থায়ী হবে যা দুটির সংশ্লেষের ফলস্বরূপ, তাই আমি সমস্যাটি সমাধান করেছি thing অজগর 3.5 এবং নিম্নলিখিত সংস্করণগুলি থেকে সমাধানটি বৈধ।

a = {
    "a": 1,
    "b": 2,
    "c": 3
}

b = {
    "a": 2,
    "b": 3,
    "d": 5
}

# Python 3.5

for key in b:
    if key in a:
        b[key] = b[key] + a[key]

c = {**a, **b}
print(c)

>>> c
{'a': 3, 'b': 5, 'c': 3, 'd': 5}

পুনরায় ব্যবহারযোগ্য কোড

a = {'a': 1, 'b': 2, 'c': 3}
b = {'b': 3, 'c': 4, 'd': 5}


def mergsum(a, b):
    for k in b:
        if k in a:
            b[k] = b[k] + a[k]
    c = {**a, **b}
    return c


print(mergsum(a, b))

অভিধান মার্জ করার এই পদ্ধতিতে এটি সাধারণ কীগুলির জন্য মান যোগ করে না। প্রশ্নে, কী জন্য পছন্দসই মান bহয় 5(2 + + 3), কিন্তু আপনার পদ্ধতি ফিরে আসছে 3
টোকেনাইজার_ফসজে

4

অতিরিক্ত হিসাবে, দয়া করে দ্রষ্টব্য a.update( b )তুলনায় 2x দ্রুতa + b

from collections import Counter
a = Counter({'menu': 20, 'good': 15, 'happy': 10, 'bar': 5})
b = Counter({'menu': 1, 'good': 1, 'bar': 3})

%timeit a + b;
## 100000 loops, best of 3: 8.62 µs per loop
## The slowest run took 4.04 times longer than the fastest. This could mean that an intermediate result is being cached.

%timeit a.update(b)
## 100000 loops, best of 3: 4.51 µs per loop

2
def merge_with(f, xs, ys):
    xs = a_copy_of(xs) # dict(xs), maybe generalizable?
    for (y, v) in ys.iteritems():
        xs[y] = v if y not in xs else f(xs[x], v)

merge_with((lambda x, y: x + y), A, B)

আপনি সহজেই এটি সাধারণ করতে পারেন:

def merge_dicts(f, *dicts):
    result = {}
    for d in dicts:
        for (k, v) in d.iteritems():
            result[k] = v if k not in result else f(result[k], v)

তারপরে এটি যে কোনও সংখ্যক ডিক্ট নিতে পারে।


2

এটি দুটি অভিধানকে মার্জ করার জন্য একটি সহজ সমাধান যেখানে +=মানগুলিতে প্রয়োগ করা যেতে পারে, এটি কেবল একবার অভিধানে পুনরাবৃত্তি করতে হবে

a = {'a':1, 'b':2, 'c':3}

dicts = [{'b':3, 'c':4, 'd':5},
         {'c':9, 'a':9, 'd':9}]

def merge_dicts(merged,mergedfrom):
    for k,v in mergedfrom.items():
        if k in merged:
            merged[k] += v
        else:
            merged[k] = v
    return merged

for dct in dicts:
    a = merge_dicts(a,dct)
print (a)
#{'c': 16, 'b': 5, 'd': 14, 'a': 10}

1

এই সমাধানটি ব্যবহার করা সহজ, এটি একটি সাধারণ অভিধান হিসাবে ব্যবহৃত হয় তবে আপনি যোগফলটি ব্যবহার করতে পারেন।

class SumDict(dict):
    def __add__(self, y):
        return {x: self.get(x, 0) + y.get(x, 0) for x in set(self).union(y)}

A = SumDict({'a': 1, 'c': 2})
B = SumDict({'b': 3, 'c': 4})  # Also works: B = {'b': 3, 'c': 4}
print(A + B)  # OUTPUT {'a': 1, 'b': 3, 'c': 6}

1

কি সম্পর্কে:

def dict_merge_and_sum( d1, d2 ):
    ret = d1
    ret.update({ k:v + d2[k] for k,v in d1.items() if k in d2 })
    ret.update({ k:v for k,v in d2.items() if k not in d1 })
    return ret

A = {'a': 1, 'b': 2, 'c': 3}
B = {'b': 3, 'c': 4, 'd': 5}

print( dict_merge_and_sum( A, B ) )

আউটপুট:

{'d': 5, 'a': 1, 'c': 7, 'b': 5}

0

উপরোক্ত সমাধানগুলি দৃশ্যের জন্য দুর্দান্ত যেখানে আপনার খুব কম সংখ্যক Counterগুলি রয়েছে। আপনার যদি সেগুলির একটি বড় তালিকা থাকে তবে এর মতো কিছু অনেক সুন্দর:

from collections import Counter

A = Counter({'a':1, 'b':2, 'c':3})
B = Counter({'b':3, 'c':4, 'd':5}) 
C = Counter({'a': 5, 'e':3})
list_of_counts = [A, B, C]

total = sum(list_of_counts, Counter())

print(total)
# Counter({'c': 7, 'a': 6, 'b': 5, 'd': 5, 'e': 3})

উপরের সমাধানটি মূলত এসগুলি সংক্ষেপ Counterকরে:

total = Counter()
for count in list_of_counts:
    total += count
print(total)
# Counter({'c': 7, 'a': 6, 'b': 5, 'd': 5, 'e': 3})

এটি একই জিনিসটি করে তবে আমি মনে করি এটি কার্যকরভাবে নীচে কী করছে তা দেখতে এটি সর্বদা সহায়তা করে।


0

অন্য কোন মডিউল বা লিব ছাড়াই একটি ডায়াল্ট এ, বি, সি এক রেখায় মার্জ করা

আমরা তিন dicts আছে

a = {"a":9}
b = {"b":7}
c = {'b': 2, 'd': 90}

সমস্ত একক লাইনের সাথে মার্জ করুন এবং ডিক অবজেক্টটি ব্যবহার করে ফিরিয়ে দিন

c = dict(a.items() + b.items() + c.items())

ফিরতি

{'a': 9, 'b': 2, 'd': 90}

6
প্রশ্নটি আবার পড়ুন, এটি প্রত্যাশিত আউটপুট নয়। এটা আপনার ইনপুট সঙ্গে হওয়া উচিত ছিল: {'a': 9, 'b': 9, 'd': 90}। আপনি "যোগফল" প্রয়োজন মিস করছেন।
প্যাট্রিক মেভিজেক
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.