একটি তালিকায় অবিচ্ছিন্ন সংখ্যার গোষ্ঠীগুলি সনাক্ত করুন


94

আমি একটি তালিকায় ধারাবাহিক সংখ্যার গ্রুপগুলি সনাক্ত করতে চাই, যাতে:

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

রিটার্নস:

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

এবং ভাবছেন যে এটি করার সর্বোত্তম উপায়টি কী (বিশেষত পাইথনের মধ্যে কিছু অন্তর্নির্মিত আছে)।

সম্পাদনা: দ্রষ্টব্য আমি মূলত উল্লেখ করতে ভুলে গেছি যে পৃথক নম্বরগুলি পৃথক সংখ্যা হিসাবে দেখা উচিত, ব্যাপ্তি নয়।


4
যে রিটার্ন মান একটি স্ট্রিং?
মার্ক বায়ার্স

আদর্শভাবে এমন কিছু পছন্দ করতে চান যা বনাম পৃথক সংখ্যার জন্য পৃথক প্রকারের ব্যবহার করে।
মাইকমেকানা

উত্তর:


53

more_itertools.consecutive_groups ৪.০ সংস্করণে যুক্ত করা হয়েছিল।

ডেমো

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]

উৎস বাস্তবায়ন একটি অনুকরণ সর্বোত্তম রেসিপি (যেমন @Nadia Alramli দ্বারা প্রদর্শিত)।

দ্রষ্টব্য: এর more_itertoolsমাধ্যমে তৃতীয় পক্ষের প্যাকেজ ইনস্টলযোগ্য pip install more_itertools


121

সম্পাদনা 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]

আপনি রেঞ্জ বা অন্য কোনও কাস্টম শ্রেণীর সাথে এক্সরেঞ্জ প্রতিস্থাপন করতে পারেন।


পাইথন ডক্সের খুব ঝরঝরে রেসিপি রয়েছে :

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)]

সম্পাদনা: দস্তাবেজটিতে উদাহরণটি ইতিমধ্যে ব্যাখ্যা করা হয়েছে তবে সম্ভবত আমার এটি আরও ব্যাখ্যা করা উচিত:

সমাধানের কীটি একটি ব্যাপ্তির সাথে পৃথক হয় যাতে একটানা সংখ্যাগুলি সমস্ত গ্রুপে উপস্থিত হয়।

তথ্য হয়: [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]

সমষ্টি কী মান দ্বারা গ্রুপবাই উপাদানগুলিকে গোষ্ঠীভূত করে, তাই প্রথম 4 টি উপাদান এক সাথে আরও গোষ্ঠীভুক্ত করা হবে।

আমি আশা করি এটি এটিকে আরও পাঠযোগ্য করে তোলে।

python 3 সংস্করণ নতুনদের জন্য সহায়ক হতে পারে

প্রথমে প্রয়োজনীয় লাইব্রেরিগুলি আমদানি করুন

from itertools import groupby
from operator import itemgetter

ranges =[]

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

4
এটি প্রয়োজন ব্যতীত প্রায় পাইপেকে কাজ করে lambda x:x[0]-x[1]
সাইলেন্টগোস্ট

আপনি কি দয়া করে মাল্টি-ক্যারেক্টার ভেরিয়েবলের নাম ব্যবহার করতে পারেন? মানচিত্রে () বা গ্রুপবাজি () এর সাথে পরিচিত না এমন ব্যক্তির জন্য, কেজি, আই এবং এক্স এর অর্থ পরিষ্কার নয়।
মাইকমেকানা

4
এটি একই পরিবর্তনশীল নামের সাথে পাইথন ডকুমেন্টেশন থেকে অনুলিপি করা হয়েছিল। আমি এখন নাম পরিবর্তন করেছি।
নাদিয়া আলরামলি

4
আপনাকে এক্সরেঞ্জ / ব্যাপ্তিতে ২ য় নম্বর বৃদ্ধি করতে হবে কারণ এটি অন্তর্ভুক্ত নয়। অন্য কথায় [2,3,4,5] == xrange(2,6), না xrange(2,5)। এটি একটি নতুন অন্তর্ভুক্ত ব্যাপ্তি ডেটা সংজ্ঞায়িত করার উপযুক্ত হতে পারে।
আইসআর্ডার

10
পাইথন 3 প্রথম উদাহরণে একটি সিনট্যাক্স ত্রুটি ছুড়ে দেয়। অজগর 3: কাজ করার জন্য এখানে প্রথম 2 টি লাইন আপডেট হয়েছে:for key, group in groupby(enumerate(data), lambda i: i[0] - i[1]): group = list(map(itemgetter(1), group))
ডেরেক 73

16

"নিষ্পাপ" সমাধান যা আমি কমপক্ষে পঠনযোগ্য হিসাবে খুঁজে পাই।

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)]

আমি এই উত্তরটি অনেক পছন্দ করি কারণ এটি পরিশ্রুত এখনও পাঠযোগ্য। তবে সংখ্যার যে রেঞ্জ বাইরে একক সংখ্যায় পর্যবসিত না tuples (যেমন আমি আউটপুট ফরম্যাট এবং সংখ্যার রেঞ্জ বনাম পৃথক সংখ্যার জন্য বিভিন্ন ফর্ম্যাটিং প্রয়োজনীয়তা থাকবে যেমন প্রিন্ট করা উচিত নয়।
mikemaccana

4
অন্য উত্তরটি দেখতে সুন্দর এবং বুদ্ধিমান লাগছিল, তবে এটি আমার কাছে আরও বোধগম্য এবং আমার মতো একজন শিক্ষানবিশকে আমার চাহিদা অনুযায়ী এটি প্রসারিত করার অনুমতি দিয়েছিল।
বেনি

একক অঙ্ক হিসাবে সীমাহীন টিউপলগুলি মুদ্রণের জন্য একটি তালিকা বোধগম্যতা ব্যবহার করতে পারে: print([i if i[0] != i[1] else i[0] for i in group(x)])
নেক্সাস

14

ধরে নিচ্ছি আপনার তালিকাটি বাছাই করা হয়েছে:

>>> 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)]

4
[j - i for i, j in enumerate(lst)]চতুর :-)
জোচেন রিটেল

9

এখানে এটি এমন কিছু যা কোনও আমদানির প্রয়োজন ছাড়াই কাজ করা উচিত:

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

দয়া করে নোট করুন যে কোডটি ব্যবহার groupbyকরে পাইথন 3 এ দেওয়া কাজ করে না তাই এটি ব্যবহার করুন।

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]))

3

এটি কোনও স্ট্যান্ডার্ড ফাংশন ব্যবহার করে না - এটি কেবল ইনপুটটির উপরেই আলোকিত করে, তবে এটির কাজ করা উচিত:

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)

নোট করুন যে এটির জন্য প্রয়োজনীয় যে ইনপুটটিতে আরোহণের ক্রমে কেবলমাত্র ইতিবাচক সংখ্যা রয়েছে। আপনার ইনপুটটি বৈধ করা উচিত, তবে এই কোডটি স্বচ্ছতার জন্য বাদ দেওয়া হয়েছে।


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]]

>>> getranges([2, 12, 13])আউটপুট: [[12, 13]]। এটা কি ইচ্ছাকৃত ছিল?
সাইলেন্টগোস্ট

হ্যাঁ, আমাকে পৃথক সংখ্যার জন্য ঠিক করতে হবে (পৃষ্ঠার উত্তরগুলির মধ্যে বেশিরভাগ)। এটি এখন কাজ করে।
মাইকমেকানা

আসলে আমি নাদিয়ার উত্তর পছন্দ করি, গ্রুপবাই () মনে হয় যে স্ট্যান্ডার্ড ফাংশনটি আমি চেয়েছিলাম।
মাইকমেকানা

1
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]

1

ব্যবহার groupbyএবং countথেকে itertoolsআমাদের একটি সংক্ষিপ্ত সমাধান দেয়। ধারণাটি হ'ল, ক্রমবর্ধমান ক্রমানুসারে, সূচক এবং মানের মধ্যে পার্থক্য একই থাকবে।

সূচকের উপর নজর রাখতে, আমরা একটি itertools.count ব্যবহার করতে পারি , যা কোড ক্লিনারটিকে ব্যবহার হিসাবে পরিষ্কার করে তোলে enumerate:

from itertools import groupby, count

def intervals(data):
    out = []
    counter = count()

    for key, group in groupby(data, key = lambda x: x-next(counter)):
        block = list(group)
        out.append([block[0], block[-1]])
    return out

কিছু নমুনা আউটপুট:

print(intervals([0, 1, 3, 4, 6]))
# [[0, 1], [3, 4], [6, 6]]

print(intervals([2, 3, 4, 5]))
# [[2, 5]]

0

নম্পি + বোঝার তালিকাগুলি ব্যবহার করে: নম্পি ডিফ
ডিফেন্স ফাংশন সহ, ফলস্বরূপ ইনপুট ভেক্টরগুলি তাদের পার্থক্য একের সমান নয় বলে চিহ্নিত করা যায়। ইনপুট ভেক্টরের শুরু এবং শেষ বিবেচনা করা দরকার।

import numpy as np
data = np.array([2, 3, 4, 5, 12, 13, 14, 15, 16, 17, 20])

d = [i for i, df in enumerate(np.diff(data)) if df!= 1] 
d = np.hstack([-1, d, len(data)-1])  # add first and last elements 
d = np.vstack([d[:-1]+1, d[1:]]).T

print(data[d])

আউটপুট:

 [[ 2  5]   
  [12 17]   
  [20 20]]

দ্রষ্টব্য: স্বতন্ত্র সংখ্যাগুলি আলাদাভাবে আচরণ করা উচিত, (স্বতন্ত্র হিসাবে পৃথক হিসাবে প্রত্যাবর্তন করা হয়েছিল) অনুরোধ বাদ দেওয়া হয়েছিল। ফলাফলের আরও পোস্ট-প্রসেসিংয়ের মাধ্যমে এটি পৌঁছানো যেতে পারে। সাধারণত এটি কোনও উপকার না পেয়ে বিষয়গুলিকে আরও জটিল করে তুলবে।


0

একটি সংক্ষিপ্ত সমাধান যা অতিরিক্ত আমদানি ছাড়াই কাজ করে। এটি যে কোনও পুনরাবৃত্তীয় গ্রহণ করে, অরসেটেড ইনপুটগুলি সাজিয়ে তোলে এবং নকল আইটেমগুলি সরিয়ে দেয়:

def ranges(nums):
    nums = sorted(set(nums))
    gaps = [[s, e] for s, e in zip(nums, nums[1:]) if s+1 < e]
    edges = iter(nums[:1] + sum(gaps, []) + nums[-1:])
    return list(zip(edges, edges))

উদাহরণ:

>>> ranges([2, 3, 4, 7, 8, 9, 15])
[(2, 4), (7, 9), (15, 15)]

>>> ranges([-1, 0, 1, 2, 3, 12, 13, 15, 100])
[(-1, 3), (12, 13), (15, 15), (100, 100)]

>>> ranges(range(100))
[(0, 99)]

>>> ranges([0])
[(0, 0)]

>>> ranges([])
[]

এটি @ ডানসালমোর সমাধানের মতোই যা আমি আশ্চর্যজনক বলে মনে করি, পড়তে এবং প্রয়োগ করা কিছুটা কঠিন হলেও (এটি কোনও ফাংশন হিসাবে দেওয়া হয়নি)।

দ্রষ্টব্য যে এটি সহজেই "traditionalতিহ্যবাহী" খোলা রেঞ্জগুলিকে [start, end)আলাদা করে পরিবর্তন করা যেতে পারে, উদাহরণস্বরূপ, রিটার্নের বিবৃতি পরিবর্তন করে:

    return [(s, e+1) for s, e in zip(edges, edges)]

আমি এই উত্তরটি অনুলিপি করে অন্য একটি প্রশ্ন থেকে উত্তরটির অনুলিপি হিসাবে চিহ্নিত করেছিলাম যাতে এটি আরও সহজ করে তোলা যায় (আমি এখন কেবল এই বিষয়টির জন্য আবার অনুসন্ধান করেছি, প্রথমে এখানে প্রথম সন্ধান পেয়েছি এবং উত্তরগুলিতে সন্তুষ্ট হচ্ছে না) প্রদত্ত)।


0

দ্বারা সংস্করণ মার্ক Byers , অ্যান্ড্রিয়া অম্বু , SilentGhost , নাদিয়া Alramli এবং truppo সহজ এবং দ্রুত। 'ট্রুপ্পো' সংস্করণ আমাকে এমন একটি সংস্করণ লিখতে উত্সাহিত করেছিল যা ১ এর চেয়ে অন্য পদক্ষেপের মাপগুলি পরিচালনা করার সময় একই নিম্পল আচরণ বজায় রাখে (এবং সিঙ্গেলটনের উপাদান হিসাবে তালিকাবদ্ধ যা কোনও প্রদত্ত পদক্ষেপের আকারের সাথে 1 ধাপের বেশি না বাড়ায়)। এটি এখানে দেওয়া হয়

>>> list(ranges([1,2,3,4,3,2,1,3,5,7,11,1,2,3]))
[(1, 4, 1), (3, 1, -1), (3, 7, 2), 11, (1, 3, 1)]
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.