द्वारा अजगर समूह


125

मान लें कि मेरे पास एक डेटा जोड़ी है जहां सूचकांक 0 मान है और सूचकांक 1 प्रकार है:

input = [
          ('11013331', 'KAT'), 
          ('9085267',  'NOT'), 
          ('5238761',  'ETH'), 
          ('5349618',  'ETH'), 
          ('11788544', 'NOT'), 
          ('962142',   'ETH'), 
          ('7795297',  'ETH'), 
          ('7341464',  'ETH'), 
          ('9843236',  'KAT'), 
          ('5594916',  'ETH'), 
          ('1550003',  'ETH')
        ]

मैं उन्हें उनके प्रकार (पहली अनुक्रमित स्ट्रिंग द्वारा) के रूप में इस तरह से समूहित करना चाहता हूं:

result = [ 
           { 
             type:'KAT', 
             items: ['11013331', '9843236'] 
           },
           {
             type:'NOT', 
             items: ['9085267', '11788544'] 
           },
           {
             type:'ETH', 
             items: ['5238761', '962142', '7795297', '7341464', '5594916', '1550003'] 
           }
         ] 

मैं इसे एक कुशल तरीके से कैसे प्राप्त कर सकता हूं?

जवाबों:


153

इसे 2 चरणों में करें। सबसे पहले, एक शब्दकोश बनाएं।

>>> input = [('11013331', 'KAT'), ('9085267', 'NOT'), ('5238761', 'ETH'), ('5349618', 'ETH'), ('11788544', 'NOT'), ('962142', 'ETH'), ('7795297', 'ETH'), ('7341464', 'ETH'), ('9843236', 'KAT'), ('5594916', 'ETH'), ('1550003', 'ETH')]
>>> from collections import defaultdict
>>> res = defaultdict(list)
>>> for v, k in input: res[k].append(v)
...

फिर, उस शब्दकोश को अपेक्षित प्रारूप में परिवर्तित करें।

>>> [{'type':k, 'items':v} for k,v in res.items()]
[{'items': ['9085267', '11788544'], 'type': 'NOT'}, {'items': ['5238761', '5349618', '962142', '7795297', '7341464', '5594916', '1550003'], 'type': 'ETH'}, {'items': ['11013331', '9843236'], 'type': 'KAT'}]

यह itertools.groupby के साथ भी संभव है लेकिन इसके लिए पहले इनपुट को छांटना पड़ता है।

>>> sorted_input = sorted(input, key=itemgetter(1))
>>> groups = groupby(sorted_input, key=itemgetter(1))
>>> [{'type':k, 'items':[x[0] for x in v]} for k, v in groups]
[{'items': ['5238761', '5349618', '962142', '7795297', '7341464', '5594916', '1550003'], 'type': 'ETH'}, {'items': ['11013331', '9843236'], 'type': 'KAT'}, {'items': ['9085267', '11788544'], 'type': 'NOT'}]

ध्यान दें कि ये दोनों कुंजियों के मूल क्रम का सम्मान नहीं करते हैं। यदि आपको ऑर्डर रखने की आवश्यकता है तो आपको ऑर्डरडीड की आवश्यकता है।

>>> from collections import OrderedDict
>>> res = OrderedDict()
>>> for v, k in input:
...   if k in res: res[k].append(v)
...   else: res[k] = [v]
... 
>>> [{'type':k, 'items':v} for k,v in res.items()]
[{'items': ['11013331', '9843236'], 'type': 'KAT'}, {'items': ['9085267', '11788544'], 'type': 'NOT'}, {'items': ['5238761', '5349618', '962142', '7795297', '7341464', '5594916', '1550003'], 'type': 'ETH'}]

यह कैसे किया जा सकता है यदि इनपुट टपल में एक कुंजी और दो या दो से अधिक मान हों, जैसे: [('11013331', 'red', 'KAT'), ('9085267', 'blue' 'KAT')]जहां टपल का अंतिम तत्व कुंजी है और पहले दो मान के रूप में। परिणाम इस तरह होना चाहिए: परिणाम = [{प्रकार: 'कैट', आइटम: [(11013331 ', लाल), (' 9085267 ', नीला)]}]
user1144616

1
from operator import itemgetter
बौमन

1
चरण 1 को आयात के बिना किया जा सकता है:d= {}; for k,v in input: d.setdefault(k, []).append(v)
21

मैं अजगर में एक MapReduce कार्यक्रम पर काम कर रहा हूं, बस सोच रहा हूं कि शब्दकोशों या बाहरी पुस्तकालय जैसे पंडों से निपटने के बिना किसी सूची में मूल्यों द्वारा समूह बनाने का कोई तरीका है? यदि नहीं, तो मैं अपने परिणाम में वस्तुओं और प्रकार से कैसे छुटकारा पा सकता हूं?
कोरोश

54

पायथन के अंतर्निहित itertoolsमॉड्यूल में वास्तव में एक groupbyफ़ंक्शन होता है, लेकिन इसके लिए समूहित किए जाने वाले तत्वों को पहले क्रमबद्ध किया जाना चाहिए जैसे कि समूहित किए जाने वाले तत्व सूची में सन्निहित हैं:

from operator import itemgetter
sortkeyfn = itemgetter(1)
input = [('11013331', 'KAT'), ('9085267', 'NOT'), ('5238761', 'ETH'), 
 ('5349618', 'ETH'), ('11788544', 'NOT'), ('962142', 'ETH'), ('7795297', 'ETH'), 
 ('7341464', 'ETH'), ('9843236', 'KAT'), ('5594916', 'ETH'), ('1550003', 'ETH')] 
input.sort(key=sortkeyfn)

अब इनपुट जैसा दिखता है:

[('5238761', 'ETH'), ('5349618', 'ETH'), ('962142', 'ETH'), ('7795297', 'ETH'),
 ('7341464', 'ETH'), ('5594916', 'ETH'), ('1550003', 'ETH'), ('11013331', 'KAT'),
 ('9843236', 'KAT'), ('9085267', 'NOT'), ('11788544', 'NOT')]

groupby2-टुपल्स का एक क्रम देता है, फॉर्म का (key, values_iterator)। हम जो चाहते हैं, वह इसे उन बिंदुओं की सूची में बदलना है जहां 'प्रकार' कुंजी है, और 'आइटम' मानों के द्वारा लौटाए गए tuples के 0'th तत्वों की एक सूची है। ऐशे ही:

from itertools import groupby
result = []
for key,valuesiter in groupby(input, key=sortkeyfn):
    result.append(dict(type=key, items=list(v[0] for v in valuesiter)))

अभी result आपका वांछित तानाशाही है, जैसा कि आपके प्रश्न में कहा गया है।

आप इस पर विचार कर सकते हैं, हालांकि, इस प्रकार से कुंजीबद्ध, और मूल्यों की सूची वाले प्रत्येक मूल्य। अपने वर्तमान स्वरूप में, किसी विशेष प्रकार के मानों को खोजने के लिए, आपको उस सूची पर पुनरावृत्त करना होगा, जिसमें मिलान वाले 'टाइप' कुंजी को खोजने के लिए सूची मिल जाए, और उसके बाद 'आइटम' तत्व प्राप्त करें। यदि आप 1-आइटम डिकेट की एक सूची के बजाय एकल हुक का उपयोग करते हैं, तो आप एक विशेष प्रकार के लिए आइटम को मास्टर हुक में एकल कुंजीदार लुकअप के साथ पा सकते हैं। का उपयोग कर groupby, यह इस तरह दिखेगा:

result = {}
for key,valuesiter in groupby(input, key=sortkeyfn):
    result[key] = list(v[0] for v in valuesiter)

resultअब इसमें यह resहुकुम है (यह @ केनीटीएम के जवाब में मध्यवर्ती डिफ़ॉल्ट के समान है ):

{'NOT': ['9085267', '11788544'], 
 'ETH': ['5238761', '5349618', '962142', '7795297', '7341464', '5594916', '1550003'], 
 'KAT': ['11013331', '9843236']}

(यदि आप इसे एक-लाइनर के लिए कम करना चाहते हैं, तो आप कर सकते हैं:

result = dict((key,list(v[0] for v in valuesiter)
              for key,valuesiter in groupby(input, key=sortkeyfn))

या newfangled ताना-बोध रूप का उपयोग कर:

result = {key:list(v[0] for v in valuesiter)
              for key,valuesiter in groupby(input, key=sortkeyfn)}

मैं अजगर में एक MapReduce कार्यक्रम पर काम कर रहा हूं, बस सोच रहा हूं कि शब्दकोशों या बाहरी पुस्तकालय जैसे पंडों से निपटने के बिना किसी सूची में मूल्यों द्वारा समूह बनाने का कोई तरीका है? यदि नहीं, तो मैं अपने परिणाम में वस्तुओं और प्रकार से कैसे छुटकारा पा सकता हूं?
कोरोश

@Kourosh - एक नए प्रश्न के रूप में पोस्ट करें, लेकिन "आइटम से छुटकारा पाने और मेरे परिणाम में टाइप करें", और "शब्दकोशों से निपटने के बिना" का क्या अर्थ है यह इंगित करना सुनिश्चित करें।
पॉलएमसीजी

7

मुझे भी पंडों का साधारण समूह बनाना पसंद था । यह बड़े डेटा सेट के लिए शक्तिशाली, सरल और सबसे पर्याप्त है

result = pandas.DataFrame(input).groupby(1).groups


3

यह उत्तर @ PaulMcG के उत्तर के समान है लेकिन इनपुट को छांटने की आवश्यकता नहीं है।

कार्यात्मक प्रोग्रामिंग में उन लोगों के लिए, groupByएक पंक्ति में लिखा जा सकता है (आयात सहित नहीं!), और इसके विपरीत itertools.groupbyइनपुट को छांटने की आवश्यकता नहीं है:

from functools import reduce # import needed for python3; builtin in python2
from collections import defaultdict

def groupBy(key, seq):
 return reduce(lambda grp, val: grp[key(val)].append(val) or grp, seq, defaultdict(list))

(कारण ... or grp यह lambdaहै कि इसके reduce()लिए काम करने के लिए, lambdaअपने पहले तर्क को वापस करने की आवश्यकता है; क्योंकि list.append()हमेशा इच्छा हमेशा लौटती Noneहैorgrp । यानी यह अजगर के प्रतिबंध के आसपास पाने के लिए एक हैक है कि एक लंबोदर केवल एक ही अभिव्यक्ति का मूल्यांकन कर सकता है।)

यह एक ऐसा प्रतिफल देता है जिसकी कुंजियाँ दिए गए फ़ंक्शन का मूल्यांकन करके पाई जाती हैं और जिनके मूल्य मूल क्रम में मूल वस्तुओं की एक सूची है। ओपी के उदाहरण के लिए, इस रूप में कॉल करने से groupBy(lambda pair: pair[1], input)यह वापस आ जाएगा:

{'KAT': [('11013331', 'KAT'), ('9843236', 'KAT')],
 'NOT': [('9085267', 'NOT'), ('11788544', 'NOT')],
 'ETH': [('5238761', 'ETH'), ('5349618', 'ETH'), ('962142', 'ETH'), ('7795297', 'ETH'), ('7341464', 'ETH'), ('5594916', 'ETH'), ('1550003', 'ETH')]}

और @ पॉलएमसीजी के उत्तर के अनुसार ओपी के अनुरोधित प्रारूप को सूची बोध में लपेटकर पाया जा सकता है। तो यह यह करेंगे:

result = {key: [pair[0] for pair in values],
          for key, values in groupBy(lambda pair: pair[1], input).items()}

बहुत कम कोड, फिर भी समझ में आता है। यह भी अच्छा है क्योंकि यह पहिया को सुदृढ़ नहीं करता है।
देवकंडे

2

निम्नलिखित फ़ंक्शन जल्दी से ( कोई छंटनी की आवश्यकता नहीं) समूह किसी भी सूचकांक की कुंजी द्वारा किसी भी लंबाई के tuples:

# given a sequence of tuples like [(3,'c',6),(7,'a',2),(88,'c',4),(45,'a',0)],
# returns a dict grouping tuples by idx-th element - with idx=1 we have:
# if merge is True {'c':(3,6,88,4),     'a':(7,2,45,0)}
# if merge is False {'c':((3,6),(88,4)), 'a':((7,2),(45,0))}
def group_by(seqs,idx=0,merge=True):
    d = dict()
    for seq in seqs:
        k = seq[idx]
        v = d.get(k,tuple()) + (seq[:idx]+seq[idx+1:] if merge else (seq[:idx]+seq[idx+1:],))
        d.update({k:v})
    return d

आपके प्रश्न के मामले में, आपके द्वारा समूहित की जाने वाली कुंजी का सूचकांक 1 है, इसलिए:

group_by(input,1)

देता है

{'ETH': ('5238761','5349618','962142','7795297','7341464','5594916','1550003'),
 'KAT': ('11013331', '9843236'),
 'NOT': ('9085267', '11788544')}

जो वास्तव में आपके द्वारा पूछे गए आउटपुट नहीं है, लेकिन आपकी आवश्यकताओं के अनुरूप हो सकते हैं।


मैं अजगर में एक MapReduce कार्यक्रम पर काम कर रहा हूं, बस सोच रहा हूं कि शब्दकोशों या बाहरी पुस्तकालय जैसे पंडों से निपटने के बिना किसी सूची में मूल्यों द्वारा समूह बनाने का कोई तरीका है? यदि नहीं, तो मैं अपने परिणाम में वस्तुओं और प्रकार से कैसे छुटकारा पा सकता हूं?
कोरोश

0
result = []
# Make a set of your "types":
input_set = set([tpl[1] for tpl in input])
>>> set(['ETH', 'KAT', 'NOT'])
# Iterate over the input_set
for type_ in input_set:
    # a dict to gather things:
    D = {}
    # filter all tuples from your input with the same type as type_
    tuples = filter(lambda tpl: tpl[1] == type_, input)
    # write them in the D:
    D["type"] = type_
    D["itmes"] = [tpl[0] for tpl in tuples]
    # append D to results:
    result.append(D)

result
>>> [{'itmes': ['9085267', '11788544'], 'type': 'NOT'}, {'itmes': ['5238761', '5349618', '962142', '7795297', '7341464', '5594916', '1550003'], 'type': 'ETH'}, {'itmes': ['11013331', '9843236'], 'type': 'KAT'}]
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.