Подтвердить что ты не робот

Python: разбиение списка на n разделов почти равной длины

Я ищу быстрый, чистый, pythonic способ разделить список на ровно n почти равных разделов.

partition([1,2,3,4,5],5)->[[1],[2],[3],[4],[5]]
partition([1,2,3,4,5],2)->[[1,2],[3,4,5]] (or [[1,2,3],[4,5]])
partition([1,2,3,4,5],3)->[[1,2],[3,4],[5]] (there are other ways to slice this one too)

Здесь есть несколько ответов Итерация по фрагментам списка, которые работают очень близко к тому, что я хочу, за исключением того, что они ориентированы на размер списка, и мне очень нравится о количестве списков (некоторые из них также помещаются вместе с None). Очевидно, это тривиально, но я ищу наилучшую практику.

Точно так же люди указали на отличные решения здесь Как вы разбиваете список на четные куски? для очень похожей проблемы, но меня больше интересует количество разделов, чем определенный размер, до тех пор, пока он находится в пределах 1. Опять же, это тривиально конвертируемо, но я ищу наилучшую практику.

4b9b3361

Ответ 1

def partition(lst, n):
    division = len(lst) / float(n)
    return [ lst[int(round(division * i)): int(round(division * (i + 1)))] for i in xrange(n) ]

>>> partition([1,2,3,4,5],5)
[[1], [2], [3], [4], [5]]
>>> partition([1,2,3,4,5],2)
[[1, 2, 3], [4, 5]]
>>> partition([1,2,3,4,5],3)
[[1, 2], [3, 4], [5]]
>>> partition(range(105), 10)
[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [11, 12, 13, 14, 15, 16, 17, 18, 19, 20], [21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31], [32, 33, 34, 35, 36, 37, 38, 39, 40, 41], [42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52], [53, 54, 55, 56, 57, 58, 59, 60, 61, 62], [63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73], [74, 75, 76, 77, 78, 79, 80, 81, 82, 83], [84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94], [95, 96, 97, 98, 99, 100, 101, 102, 103, 104]]

Версия Python 3:

def partition(lst, n):
    division = len(lst) / n
    return [lst[round(division * i):round(division * (i + 1))] for i in range(n)]

Ответ 2

Здесь версия, аналогичная версии Daniel: она делится как можно более равномерно, но в начале ставит все большие разделы:

def partition(lst, n):
    q, r = divmod(len(lst), n)
    indices = [q*i + min(i, r) for i in xrange(n+1)]
    return [lst[indices[i]:indices[i+1]] for i in xrange(n)]

Он также избегает использования арифметики float, поскольку это всегда делает меня неудобным.:)

Изменить: пример, просто чтобы показать контраст с решением Даниэля Шутцбаха

>>> print [len(x) for x in partition(range(105), 10)]
[11, 11, 11, 11, 11, 10, 10, 10, 10, 10]

Ответ 3

Как раз другой подход, который работает только в том случае, если [[1,3,5],[2,4]] является допустимым разделом, в вашем примере.

def partition ( lst, n ):
    return [ lst[i::n] for i in xrange(n) ]

Это удовлетворяет примеру, указанному в примере @Даниэля Штуцбаха:

partition(range(105),10)
# [[0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100],
# [1, 11, 21, 31, 41, 51, 61, 71, 81, 91, 101],
# [2, 12, 22, 32, 42, 52, 62, 72, 82, 92, 102],
# [3, 13, 23, 33, 43, 53, 63, 73, 83, 93, 103],
# [4, 14, 24, 34, 44, 54, 64, 74, 84, 94, 104],
# [5, 15, 25, 35, 45, 55, 65, 75, 85, 95],
# [6, 16, 26, 36, 46, 56, 66, 76, 86, 96],
# [7, 17, 27, 37, 47, 57, 67, 77, 87, 97],
# [8, 18, 28, 38, 48, 58, 68, 78, 88, 98],
# [9, 19, 29, 39, 49, 59, 69, 79, 89, 99]]

Ответ 4

Ниже приведен один путь.

def partition(lst, n):
    increment = len(lst) / float(n)
    last = 0
    i = 1
    results = []
    while last < len(lst):
        idx = int(round(increment * i))
        results.append(lst[last:idx])
        last = idx
        i += 1
    return results

Если len (lst) не может быть равномерно разделено на n, эта версия будет распределять дополнительные элементы примерно равными интервалами. Например:

>>> print [len(x) for x in partition(range(105), 10)]
[11, 10, 11, 10, 11, 10, 11, 10, 11, 10]

Код может быть проще, если вы не возражаете против всех 11-х в начале или в конце.

Ответ 5

Этот ответ предоставляет функцию split(list_, n, max_ratio), для людей которые хотят разбить свой список на n штуки не более max_ratio соотношение в куске длины. Это позволяет использовать больше вариантов, чем вопросник "не более 1 разницы в длине куска".

Он работает путем выборки n частей длины в пределах желаемого диапазона отношения [1, max_ratio), помещая их друг за другом, чтобы сформировать "сломанный палкой "с правильными расстояниями между" точками разрыва ", но неправильными Общая длина. Масштабирование сломанной палки на нужную длину дает нам приблизительные позиции точек разрыва, которые мы хотим. Чтобы получить целое число точки останова требуют последующего округления.

К сожалению, округление может сговориться, чтобы сделать куски слишком короткими, и вы можете превысить max_ratio. См. Нижнюю часть этого ответа для Пример.

import random

def splitting_points(length, n, max_ratio):
    """n+1 slice points [0, ..., length] for n random-sized slices.

    max_ratio is the largest allowable ratio between the largest and the
    smallest part.
    """
    ratios = [random.uniform(1, max_ratio) for _ in range(n)]
    normalized_ratios = [r / sum(ratios) for r in ratios]
    cumulative_ratios = [
        sum(normalized_ratios[0:i])
        for i in range(n+1)
    ]
    scaled_distances = [
        int(round(r * length))
        for r in cumulative_ratios
    ]

    return scaled_distances


def split(list_, n, max_ratio):
    """Slice a list into n randomly-sized parts.

    max_ratio is the largest allowable ratio between the largest and the
    smallest part.
    """

    points = splitting_points(len(list_), n, ratio)

    return [
        list_[ points[i] : points[i+1] ]
        for i in range(n)
    ]

Вы можете попробовать это так:

for _ in range(10):
    parts = split('abcdefghijklmnopqrstuvwxyz', 4, 2)
    print([(len(part), part) for part in parts])

Пример плохого результата:

parts = split('abcdefghijklmnopqrstuvwxyz', 10, 2)

# lengths range from 1 to 4, not 2 to 4
[(3, 'abc'),  (3, 'def'), (1, 'g'),
 (4, 'hijk'), (3, 'lmn'), (2, 'op'),
 (2, 'qr'),  (3, 'stu'),  (2, 'vw'),
 (3, 'xyz')]