2012-05-02 31 views
11

Tôi có một danh sách các số nguyên ...danh sách nhóm của ints bởi liên tục chuỗi

[1,2,3,4,5,8,9,10,11,200,201,202] 

Tôi muốn nhóm chúng thành một danh sách liệt kê trong đó mỗi sublist chứa số nguyên có thứ tự đã không bị phá vỡ. Như thế này ...

[[1,5],[8,11],[200,202]] 

Tôi có một công việc khá phiền phức xung quanh ...

lSequenceOfNum = [1,2,3,4,5,8,9,10,11,200,201,202] 

lGrouped = [] 
start = 0 
for x in range(0,len(lSequenceOfNum)): 
    if x != len(lSequenceOfNum)-1: 
     if(lSequenceOfNum[x+1] - lSequenceOfNum[x]) > 1: 
      lGrouped.append([lSequenceOfNum[start],lSequenceOfNum[x]]) 
      start = x+1 

    else: 
     lGrouped.append([lSequenceOfNum[start],lSequenceOfNum[x]]) 
print lGrouped 

Nó là tốt nhất tôi có thể làm. Có cách nào "pythonic" hơn để làm điều này? Cảm ơn ..

+0

Hãy suy nghĩ về nó trong điều kiện của nơi nhảy là thay vì nơi phạm vi là. Bạn có thể lưu trữ các kết quả trong một mảng đơn giản của ints trong đó mỗi mục là một chỉ mục tương ứng với một bước nhảy trong mảng ban đầu. Tôi nghĩ rằng điều này đơn giản hơn ... và về cơ hội, điều này sẽ có thể sử dụng lại hoặc mã thư viện, bạn có thể đóng gói tất cả những điều đó trong các hoạt động của một lớp. – djechlin

+0

Tôi khá chắc chắn đây là một bản sao mặc dù tôi không thể tìm kiếm nó ngay bây giờ. – jamylak

Trả lời

12

Giả sử danh sách sẽ luôn luôn được theo thứ tự tăng dần:

from itertools import groupby, count 

numberlist = [1,2,3,4,5,8,9,10,11,200,201,202] 

def as_range(g): 
    l = list(g) 
    return l[0], l[-1] 

print [as_range(g) for _, g in groupby(numberlist, key=lambda n, c=count(): n-next(c))] 
+0

Hoàn hảo! Cảm ơn. – b10hazard

0

mã giả (với off-by-one lỗi để sửa chữa):

jumps = new array; 
for idx from 0 to len(array) 
if array[idx] != array[idx+1] then jumps.push(idx); 

Tôi nghĩ rằng đây thực sự là một trường hợp nó làm cho tinh thần để làm việc với các chỉ số (như trong C, trước khi java/python/perl/etc được cải tiến dựa trên điều này) thay vì các đối tượng trong mảng.

0

Dưới đây là một phiên bản nên dễ đọc:

def close_range(el, it): 
    while True: 
     el1 = next(it, None) 
     if el1 != el + 1: 
      return el, el1 
     el = el1 

def compress_ranges(seq): 
    iterator = iter(seq) 
    left = next(iterator, None) 
    while left is not None: 
     right, left1 = close_range(left, iterator) 
     yield (left, right) 
     left = left1 

list(compress_ranges([1, 2, 3, 4, 5, 8, 9, 10, 11, 200, 201, 202])) 
4

tôi nhận ra tôi đã overcomplicated này một chút, dễ dàng hơn để chỉ cần đếm theo cách thủ công so với sử dụng máy phát điện hơi phức tạp:

def ranges(seq): 
    start, end = seq[0], seq[0] 
    count = start 
    for item in seq: 
     if not count == item: 
      yield start, end 
      start, end = item, item 
      count = item 
     end = item 
     count += 1 
    yield start, end 

print(list(ranges([1,2,3,4,5,8,9,10,11,200,201,202]))) 

Sản xuất:

[(1, 5), (8, 11), (200, 202)] 

Phương pháp này là khá nhanh:

Phương pháp này (và cái cũ, họ thực hiện gần như giống hệt nhau):

python -m timeit -s "from test import ranges" "ranges([1,2,3,4,5,8,9,10,11,200,201,202])" 
1000000 loops, best of 3: 0.47 usec per loop 

Jeff Mercado's Method:

python -m timeit -s "from test import as_range; from itertools import groupby, count" "[as_range(g) for _, g in groupby([1,2,3,4,5,8,9,10,11,200,201,202], key=lambda n, c=count(): n-next(c))]" 
100000 loops, best of 3: 11.1 usec per loop 

Đó là nhanh hơn 20 lần - mặc dù, một cách tự nhiên, trừ khi vấn đề tốc độ không phải là mối quan tâm thực sự.


My giải pháp cũ sử dụng máy phát điện:

import itertools 

def resetable_counter(start): 
    while True: 
     for i in itertools.count(start): 
      reset = yield i 
      if reset: 
       start = reset 
       break 

def ranges(seq): 
    start, end = seq[0], seq[0] 
    counter = resetable_counter(start) 
    for count, item in zip(counter, seq): #In 2.x: itertools.izip(counter, seq) 
     if not count == item: 
      yield start, end 
      start, end = item, item 
      counter.send(item) 
     end = item 
    yield start, end 

print(list(ranges([1,2,3,4,5,8,9,10,11,200,201,202]))) 

Sản xuất:

[(1, 5), (8, 11), (200, 202)] 
+0

Bạn có chắc chắn rằng nó hoạt động không? – Abhijit

+0

@Abhijit Khá chắc chắn, tôi đã thử nghiệm nó. Bạn đã tìm thấy nó thất bại? –

+0

Cũng không chắc chắn, nhưng o/p không phải là những gì nó nên được mong đợi. Bạn có thể vui lòng xem [IDEONE RUN] này (http://ideone.com/3sCnZ) – Abhijit

2

Bạn có thể làm điều này một cách hiệu quả trong ba bước

cho

list1=[1,2,3,4,5,8,9,10,11,200,201,202] 

Tính gián đoạn

 [1,2,3,4,5,8,9,10,11 ,200,201,202] 
-  [1,2,3,4,5,8,9 ,10 ,11 ,200,201,202] 
---------------------------------------- 
     [1,1,1,1,3,1,1 ,1 ,189,1 ,1] 
(index) 1 2 3 4 5 6 7 8 9 10 11 
       *   * 
rng = [i+1 for i,e in enumerate((x-y for x,y in zip(list1[1:],list1))) if e!=1] 
>>> rng 
[5, 9] 

Thêm ranh giới

rng = [0] + rng + [len(list1)] 
>>> rng 
[0, 5, 9,12] 

tại tính liên tục thực tế dao động

[(list1[i],list1[j-1]) for i,j in zip(list2,list2[1:])] 
[(1, 5), (8, 11), (200, 202)] 

LB    [0, 5, 9, 12] 
UB    [0, 5, 9, 12] 
    ----------------------- 
indexes (LB,UB-1) (0,4) (5,8) (9,11) 
+0

+1, giải thích tốt :) –

0

câu hỏi tương tự:
Python - find incremental numbered sequences with a list comprehension
Pythonic way to convert a list of integers into a string of comma-separated ranges

input = [1, 2, 3, 4, 8, 10, 11, 12, 17] 

i, ii, result = iter(input), iter(input[1:]), [[input[0]]] 
for x, y in zip(i,ii): 
    if y-x != 1: 
     result.append([y]) 
    else: 
     result[-1].append(y) 

>>> result 
[[1, 2, 3, 4], [8], [10, 11, 12], [17]] 

>>> print ", ".join("-".join(map(str,(g[0],g[-1])[:len(g)])) for g in result) 
1-4, 8, 10-12, 17 

>>> [(g[0],g[-1])[:len(g)] for g in result] 
[(1, 4), (8,), (10, 12), (17,)] 
1

Câu hỏi đặt ra là khá cũ, nhưng tôi nghĩ tôi sẽ chia sẻ giải pháp của tôi anyway

Giả sử import numpy as np

a = [1,2,3,4,5,8,9,10,11,200,201,202] 

np.split(a, array(np.add(np.where(diff(a)>1),1)).tolist()[0]) 
Các vấn đề liên quan