2010-01-28 16 views
58

मैं एक सूची में लगातार संख्या के समूहों की पहचान करना चाहते हैं में लगातार संख्याओं के समूह की पहचान करें कि ऐसा है, तो:एक सूची

myfunc([2, 3, 4, 5, 12, 13, 14, 15, 16, 17, 20]) 

रिटर्न:

[(2,5), (12,17), 20] 

और क्या सबसे अच्छा सोच रहा था ऐसा करने का तरीका था (विशेष रूप से अगर पाइथन में कुछ अंतर्निहित है)।

संपादित करें: नोट मैं मूल रूप से यह उल्लेख करना भूल गया था कि अलग-अलग संख्याओं को व्यक्तिगत संख्या के रूप में वापस नहीं किया जाना चाहिए, न कि श्रेणियां।

+3

क्या यह वापसी एक स्ट्रिंग मान है? –

+0

आदर्श रूप से ऐसा कुछ पसंद करेंगे जो अलग-अलग प्रकार के रेंज बनाम स्टैंडअलोन संख्याओं का उपयोग करता है। – mikemaccana

उत्तर

5

more_itertools.consecutive_groups संस्करण 4.0 में जोड़ा गया है।

डेमो

import more_itertools as mit 


iterable = [2, 3, 4, 5, 12, 13, 14, 15, 16, 17, 20] 
[list(group) for group in mit.consecutive_groups(iterable)] 
# [[2, 3, 4, 5], [12, 13, 14, 15, 16, 17], [20]] 

कोड

इस उपकरण को लागू करना, हम चाहते हैं कि लगातार संख्या की सीमाओं पाता है एक जनरेटर समारोह बनाते हैं।

def find_ranges(iterable): 
    """Yield range of consecutive numbers.""" 
    for group in mit.consecutive_groups(iterable): 
     group = list(group) 
     if len(group) == 1: 
      yield group[0] 
     else: 
      yield group[0], group[-1] 


iterable = [2, 3, 4, 5, 12, 13, 14, 15, 16, 17, 20] 
list(find_ranges(iterable)) 
# [(2, 5), (12, 17), 20] 

source कार्यान्वयन emulates एक older recipe (के रूप में @Nadia Alramli द्वारा प्रदर्शन)।

नोट: more_itertoolspip install more_itertools के माध्यम से स्थापित एक तृतीय-पक्ष पैकेज है।

+1

बहुत धन्यवाद। अद्यतन पोस्ट देखें। – pylang

3

यह एक मानक समारोह का उपयोग नहीं करता है - यह सिर्फ इनपुट से अधिक iiterates, लेकिन यह काम करना चाहिए: ऐसा करना आवश्यक हो

def myfunc(l): 
    r = [] 
    p = q = None 
    for x in l + [-1]: 
     if x - 1 == q: 
      q += 1 
     else: 
      if p: 
       if q > p: 
        r.append('%s-%s' % (p, q)) 
       else: 
        r.append(str(p)) 
      p = q = x 
    return '(%s)' % ', '.join(r) 

ध्यान दें कि कि इनपुट आरोही क्रम में केवल सकारात्मक संख्या में शामिल है। आपको इनपुट को प्रमाणित करना चाहिए, लेकिन यह कोड स्पष्टता के लिए छोड़ा गया है।

11

मानते हुए अपनी सूची क्रमित है:

>>> from itertools import groupby 
>>> def ranges(lst): 
    pos = (j - i for i, j in enumerate(lst)) 
    t = 0 
    for i, els in groupby(pos): 
     l = len(list(els)) 
     el = lst[t] 
     t += l 
     yield range(el, el+l) 


>>> lst = [2, 3, 4, 5, 12, 13, 14, 15, 16, 17] 
>>> list(ranges(lst)) 
[range(2, 6), range(12, 18)] 
+1

@SilentGhost: वास्तव में सुंदर! – telliott99

+1

'[जे - i for i, j in enumerate (lst)]' चालाक है :-) –

95

संपादित करें 2: ओपी नई आवश्यकता का उत्तर देने के

ranges = [] 
for key, group in groupby(enumerate(data), lambda (index, item): index - item): 
    group = map(itemgetter(1), group) 
    if len(group) > 1: 
     ranges.append(xrange(group[0], group[-1])) 
    else: 
     ranges.append(group[0]) 

आउटपुट:

[xrange(2, 5), xrange(12, 17), 20] 

आप श्रृंखला के साथ xrange जगह ले सकता है या किसी अन्य कस्टम वर्ग।


अजगर डॉक्स इस के लिए एक बहुत साफ recipe है:

from operator import itemgetter 
from itertools import groupby 
data = [2, 3, 4, 5, 12, 13, 14, 15, 16, 17] 
for k, g in groupby(enumerate(data), lambda (i,x):i-x): 
    print map(itemgetter(1), g) 

आउटपुट:

[2, 3, 4, 5] 
[12, 13, 14, 15, 16, 17] 

आपको ठीक उसी उत्पादन प्राप्त करना चाहते हैं, तो आप ऐसा कर सकते हैं:

ranges = [] 
for k, g in groupby(enumerate(data), lambda (i,x):i-x): 
    group = map(itemgetter(1), g) 
    ranges.append((group[0], group[-1])) 

आउट डाल:

[(2, 5), (12, 17)] 

संपादित करें: उदाहरण पहले से ही दस्तावेज में समझाया गया है लेकिन शायद मैं इसे व्याख्या करनी चाहिए और अधिक:

समाधान की कुंजी एक सीमा के साथ differencing है तो कि लगातार संख्या सभी समूह में दिखाई देते हैं।

तो डेटा था: [2, 3, 4, 5, 12, 13, 14, 15, 16, 17] फिर groupby(enumerate(data), lambda (i,x):i-x) निम्नलिखित के बराबर है:

groupby(
    [(0, 2), (1, 3), (2, 4), (3, 5), (4, 12), 
    (5, 13), (6, 14), (7, 15), (8, 16), (9, 17)], 
    lambda (i,x):i-x 
) 

लैम्ब्डा समारोह तत्व मूल्य से तत्व सूचकांक घटा देती है।तो जब आप प्रत्येक आइटम पर लैम्ब्डा लागू करते हैं। , बराबर कुंजी मान द्वारा

[-2, -2, -2, -2, -8, -8, -8, -8, -8, -8] 

GroupBy समूहों तत्वों इसलिए पहले 4 तत्वों एक साथ और बहुत आगे समूह में रखा जाएगा: आप GroupBy निम्नलिखित चाबियाँ मिल जाएगा।

मुझे उम्मीद है कि यह इसे और अधिक पठनीय बनाता है।

+2

+1 दस्तावेज़ों के लिंक के लिए। –

+2

लगभग py3k में काम करता है, सिवाय इसके कि इसे 'लैम्ब्डा x: x [0] -x [1] 'की आवश्यकता होती है। – SilentGhost

+1

+1 वास्तव में बहुत चालाक। लेकिन मुझे लगता है कि मैं कभी नहीं समझूंगा कि अगर मुझे पहले से ही पता नहीं था कि यह क्या करना है। :) –

15

"बेवकूफ" समाधान जो मुझे कुछ हद तक पठनीय लगता है।

x = [2, 3, 4, 5, 12, 13, 14, 15, 16, 17, 22, 25, 26, 28, 51, 52, 57] 

def group(L): 
    first = last = L[0] 
    for n in L[1:]: 
     if n - 1 == last: # Part of the group, bump the end 
      last = n 
     else: # Not part of the group, yield current group and start a new 
      yield first, last 
      first = last = n 
    yield first, last # Yield the last group 


>>>print list(group(x)) 
[(2, 5), (12, 17), (22, 22), (25, 26), (28, 28), (51, 52), (57, 57)] 
+0

मुझे यह जवाब बहुत पसंद है क्योंकि यह अभी तक पठनीय है। हालांकि संख्याओं के बाहर की संख्या को एकल अंकों के रूप में मुद्रित किया जाना चाहिए, न कि tuples (जैसा कि मैं आउटपुट प्रारूपित करता हूं और अलग-अलग संख्याओं के लिए अलग-अलग संख्याओं के लिए अलग स्वरूपण आवश्यकताओं की आवश्यकता होती है। – mikemaccana

+2

दूसरा उत्तर सुंदर और बुद्धिमान दिखता है, लेकिन यह एक मेरे लिए और अधिक समझ में आता है और मेरी जरूरतों के हिसाब से इसे विस्तारित करने की अनुमति देता है। – Benny

1

यहां जवाब है जिसके साथ मैं आया हूं। मैं अन्य लोगों को समझने के लिए कोड लिख रहा हूं, इसलिए मैं परिवर्तनीय नामों और टिप्पणियों के साथ काफी वर्बोज़ हूं।

पहले एक त्वरित सहायक समारोह:

def getpreviousitem(mylist,myitem): 
    '''Given a list and an item, return previous item in list''' 
    for position, item in enumerate(mylist): 
     if item == myitem: 
      # First item has no previous item 
      if position == 0: 
       return None 
      # Return previous item  
      return mylist[position-1] 

और फिर वास्तविक कोड:

def getranges(cpulist): 
    '''Given a sorted list of numbers, return a list of ranges''' 
    rangelist = [] 
    inrange = False 
    for item in cpulist: 
     previousitem = getpreviousitem(cpulist,item) 
     if previousitem == item - 1: 
      # We're in a range 
      if inrange == True: 
       # It's an existing range - change the end to the current item 
       newrange[1] = item 
      else:  
       # We've found a new range. 
       newrange = [item-1,item] 
      # Update to show we are now in a range  
      inrange = True  
     else: 
      # We were in a range but now it just ended 
      if inrange == True: 
       # Save the old range 
       rangelist.append(newrange) 
      # Update to show we're no longer in a range  
      inrange = False 
    # Add the final range found to our list 
    if inrange == True: 
     rangelist.append(newrange) 
    return rangelist 

उदाहरण चलाएँ:

getranges([2, 3, 4, 5, 12, 13, 14, 15, 16, 17]) 

रिटर्न:

[[2, 5], [12, 17]] 
+0

'>>> getranges ([2, 12, 13]) 'आउटपुट:' [[12, 13]] 'क्या वह जानबूझकर था – SilentGhost

+0

तो, यह नहीं था। तो आपका कोड काम नहीं करता है। – SilentGhost

+0

हाँ, मुझे अलग-अलग संख्याओं के लिए ठीक करना होगा (पृष्ठ पर अधिकांश उत्तरों के अनुसार)। अब इस पर काम करना। – mikemaccana

8

यहाँ यह कुछ ऐसा है जो काम करना चाहिए है, जरूरत किसी भी आयात के बिना:

def myfunc(lst): 
    ret = [] 
    a = b = lst[0]       # a and b are range's bounds 

    for el in lst[1:]: 
     if el == b+1: 
      b = el       # range grows 
     else:        # range ended 
      ret.append(a if a==b else (a,b)) # is a single or a range? 
      a = b = el      # let's start again with a single 
    ret.append(a if a==b else (a,b))   # corner case for last single/range 
    return ret 
6

कृपया ध्यान दें कि कोड का उपयोग कर अजगर 3 के रूप में दी तो इस का उपयोग groupby काम नहीं करता।

for k, g in groupby(enumerate(data), lambda x:x[0]-x[1]): 
    group = list(map(itemgetter(1), g)) 
    ranges.append((group[0], group[-1])) 
0
import numpy as np 

myarray = [2, 3, 4, 5, 12, 13, 14, 15, 16, 17, 20] 
sequences = np.split(myarray, np.array(np.where(np.diff(myarray) > 1)[0]) + 1) 
l = [] 
for s in sequences: 
    if len(s) > 1: 
     l.append((np.min(s), np.max(s))) 
    else: 
     l.append(s[0]) 
print(l) 

आउटपुट:

[(2, 5), (12, 17), 20] 
संबंधित मुद्दे