목록에서 연속 숫자 그룹 식별
목록에서 연속적인 숫자 그룹을 식별하고 싶습니다.
myfunc([2, 3, 4, 5, 12, 13, 14, 15, 16, 17, 20])
보고:
[(2,5), (12,17), 20]
그리고이를 수행하는 가장 좋은 방법이 무엇인지 궁금했습니다 (특히 Python에 내장 된 것이있는 경우).
편집 : 원래 개별 숫자는 범위가 아닌 개별 숫자로 반환되어야한다는 것을 언급하는 것을 잊었습니다.
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]
소스 구현은 에뮬레이트 고전 레시피 (AS @Nadia Alramli 의해 설명 참조).
참고 : more_itertools
은를 통해 설치할 수있는 타사 패키지 pip install more_itertools
입니다.
편집 2 : OP 새로운 요구 사항에 응답하려면
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를 범위 또는 다른 사용자 정의 클래스로 바꿀 수 있습니다.
Python 문서에는 이에 대한 매우 깔끔한 레시피 가 있습니다.
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
)
람다 함수는 요소 값에서 요소 인덱스를 뺍니다. 따라서 각 항목에 람다를 적용 할 때. groupby에 대해 다음 키가 제공됩니다.
[-2, -2, -2, -2, -8, -8, -8, -8, -8, -8]
groupby는 동일한 키 값으로 요소를 그룹화하므로 처음 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]))
내가 적어도 어느 정도 읽을 수있는 "순진한"해결책.
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)]
목록이 정렬되었다고 가정합니다.
>>> 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)]
여기에 가져올 필요없이 작동해야하는 것이 있습니다.
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
사용하는 코드 groupby
는 Python 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]))
이것은 표준 함수를 사용하지 않습니다. 입력에 대해 반복하지만 작동해야합니다.
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)
입력에는 오름차순으로 양수 만 포함되어야합니다. 입력의 유효성을 검사해야하지만 명확성을 위해이 코드는 생략되었습니다.
여기 제가 생각해 낸 답이 있습니다. 다른 사람들이 이해할 수 있도록 코드를 작성하고 있으므로 변수 이름과 주석이 상당히 장황합니다.
먼저 빠른 도우미 기능 :
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]]
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]
Using numpy + comprehension lists:
With numpy diff function, consequent input vector entries that their difference is not equal to one can be identified. The start and end of the input vector need to be considered.
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])
Output:
[[ 2 5]
[12 17]
[20 20]]
Note: The request that individual numbers should be treated differently, (returned as individual, not ranges) was omitted. This can be reached by further post-processing the results. Usually this will make things more complex without gaining any benefit.
A short solution that works without additional imports. It accepts any iterable, sorts unsorted inputs, and removes duplicate items:
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))
Example:
>>> 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([])
[]
This is the same as @dansalmo's solution which I found amazing, albeit a bit hard to read and apply (as it's not given as a function).
Note that it could easily be modified to spit out "traditional" open ranges [start, end)
, by e.g. altering the return statement:
return [(s, e+1) for s, e in zip(edges, edges)]
I copied this answer over from another question that was marked as a duplicate of this one with the intention to make it easier findable (after I just now searched again for this topic, finding only the question here at first and not being satisfied with the answers given).
Using groupby
and count
from itertools
gives us a short solution. The idea is that, in an increasing sequence, the difference between the index and the value will remain the same.
In order to keep track of the index, we can use an itertools.count, which makes the code cleaner as using 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
Some sample output:
print(intervals([0, 1, 3, 4, 6]))
# [[0, 1], [3, 4], [6, 6]]
print(intervals([2, 3, 4, 5]))
# [[2, 5]]
참고URL : https://stackoverflow.com/questions/2154249/identify-groups-of-continuous-numbers-in-a-list
'Programing' 카테고리의 다른 글
OS X Yosemite / El Capitan에서 시작시 MySQL을 자동로드하는 방법 (0) | 2020.10.16 |
---|---|
C에서 .h 파일의 비정상적인 사용 (0) | 2020.10.16 |
CSS를 사용하여 목록의 세 번째 항목마다 스타일을 지정 하시겠습니까? (0) | 2020.10.16 |
색인 / 열거에 대한 액세스 권한으로 Python 목록 이해가 가능합니까? (0) | 2020.10.16 |
각도 UI 부트 스트랩이있는 반응 형 드롭 다운 탐색 모음 (올바른 각도 방식으로 수행됨) (0) | 2020.10.16 |