Cięcie listy na N prawie równej długości partycji [duplikat]

to pytanie ma już odpowiedzi tutaj : dzielenie listy na N części o w przybliżeniu równej długości (32 odpowiedzi) Zamknięty 3 lata temu .

Szukam szybkiego, czystego, pythonicznego sposobu na podzielenie listy na dokładnie N prawie równych partycji.

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)

Jest tu kilka odpowiedzi iteracja nad plasterkami listy , które są bardzo zbliżone do tego, czego chcę, z wyjątkiem tego, że koncentrują się na rozmiarze listy, i zależy mi na liczbie list (niektóre z nich również nie mają żadnego). Są trywialnie nawrócone, oczywiście, ale jestem Szukam najlepszej praktyki.

Podobnie, ludzie wskazali tutaj świetne rozwiązania jak podzielić listę na równe kawałki? dla bardzo podobnego problemu, ale bardziej interesuje mnie liczba partycji niż konkretny rozmiar, o ile jest w granicach 1. Ponownie, to jest banalnie kabriolet, ale szukam najlepszych praktyk.

Author: martineau, 2010-04-17

5 answers

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

Wersja Pythona 3:

def partition(lst, n):
    division = len(lst) / n
    return [lst[round(division * i):round(division * (i + 1))] for i in range(n)]
 22
Author: João Silva,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-30 20:25:05

Tylko inne ujęcie, które działa tylko wtedy, gdy [[1,3,5],[2,4]] jest akceptowalną partycją, w twoim przykładzie.

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

To odpowiada przykładowi podanemu w przykładzie @ Daniel Stutzbach:

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]]
 29
Author: Manuel Zubieta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-13 19:51:39

Oto wersja podobna do Daniela: dzieli się tak równomiernie, jak to możliwe, ale umieszcza wszystkie większe partycje na początku:

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

Unika również użycia arytmetyki float, ponieważ zawsze sprawia mi to dyskomfort. :)

Edit: przykład, aby pokazać kontrast z rozwiązaniem Daniela Stutzbacha

>>> print [len(x) for x in partition(range(105), 10)]
[11, 11, 11, 11, 11, 10, 10, 10, 10, 10]
 28
Author: Mark Dickinson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-17 21:26:16

Poniżej jest jeden sposób.

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

Jeśli len (lst) nie może być równomiernie podzielony przez n, ta wersja rozdzieli dodatkowe elementy w mniej więcej równych odstępach czasu. Na przykład:

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

Kod może być prostszy, jeśli nie masz nic przeciwko temu, że wszystkie 11 są na początku lub na końcu.

 4
Author: Daniel Stutzbach,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-17 20:55:42

Ta odpowiedź zapewnia funkcję split(list_, n, max_ratio), dla ludzi którzy chcą podzielić swoją listę na n kawałki z co najwyżej max_ratio stosunek długości części. Pozwala na większą zmienność niż pytanie "co najwyżej 1 różnica w długości części".

Działa poprzez pobieranie próbek n długości części w pożądanym zakresie proporcji [1, max_ratio) , umieszczając je po sobie tworząc " łamaną trzymać "z właściwych odległości między "break points", ale źle Długość całkowita. Skalowanie złamany kij do pożądanej długości daje nam przybliżone pozycje punktów przerwania, które chcemy. Aby uzyskać liczbę całkowitą break points wymaga późniejszego zaokrąglenia.

Niestety, roundings mogą spiskować, aby kawałki były zbyt krótkie, i pozwól przekroczyć max_ratio. Zobacz na dole tej odpowiedzi dla przykład.

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

Możesz wypróbować tak:

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

Przykład złego wyniku:

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')]
 0
Author: Esteis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-24 20:15:21