Iteracja co dwa elementy na liście

209

Jak wykonać analizę forpętli lub listy, aby każda iteracja dawała mi dwa elementy?

l = [1,2,3,4,5,6]

for i,k in ???:
    print str(i), '+', str(k), '=', str(i+k)

Wynik:

1+2=3
3+4=7
5+6=11
jackhab
źródło
2
W przypadku nakładających się par: stackoverflow.com/questions/5434891/…
user202729,

Odpowiedzi:

231

Potrzebujesz implementacji pairwise()(lub grouped()).

W przypadku Python 2:

from itertools import izip

def pairwise(iterable):
    "s -> (s0, s1), (s2, s3), (s4, s5), ..."
    a = iter(iterable)
    return izip(a, a)

for x, y in pairwise(l):
   print "%d + %d = %d" % (x, y, x + y)

Lub bardziej ogólnie:

from itertools import izip

def grouped(iterable, n):
    "s -> (s0,s1,s2,...sn-1), (sn,sn+1,sn+2,...s2n-1), (s2n,s2n+1,s2n+2,...s3n-1), ..."
    return izip(*[iter(iterable)]*n)

for x, y in grouped(l, 2):
   print "%d + %d = %d" % (x, y, x + y)

W Pythonie 3 możesz zastąpić izipgo wbudowaną zip()funkcją i upuścićimport .

Wszystko kredytowej Martineau na jego odpowiedź na moje pytanie , znalazłem to być bardzo skuteczna, ponieważ tylko iteracje raz na listę i nie tworzy żadnych zbędnych list w procesie.

Uwaga : nie należy tego mylić zpairwise przepisem we własnej itertoolsdokumentacji Pythona , która daje wynik s -> (s0, s1), (s1, s2), (s2, s3), ..., jak wskazał @lazyr w komentarzach.

Mały dodatek dla tych, którzy chcieliby sprawdzić typ za pomocą mypy w Pythonie 3:

from typing import Iterable, Tuple, TypeVar

T = TypeVar("T")

def grouped(iterable: Iterable[T], n=2) -> Iterable[Tuple[T, ...]]:
    """s -> (s0,s1,s2,...sn-1), (sn,sn+1,sn+2,...s2n-1), ..."""
    return zip(*[iter(iterable)] * n)
Johnsyweb
źródło
16
Nie należy mylić z funkcją parowania sugerowaną w sekcji receptur itertools , która dajes -> (s0,s1), (s1,s2), (s2, s3), ...
Lauritz V. Thaulow
1
Robi to inaczej. Twoja wersja daje tylko połowę liczby par w porównaniu do itertoolsfunkcji receptury o tej samej nazwie. Oczywiście twoje jest szybsze ...
Sven Marnach
Co? Twoja funkcja i funkcja, o której mówiłem, robią różne rzeczy i taki był sens mojego komentarza.
Lauritz V. Thaulow
5
BĄDŹ OSTROŻNY! Korzystanie z tych funkcji stwarza ryzyko, że nie będziesz iterował ostatnich elementów iteracji. Przykład: lista (pogrupowane ([1,2,3], 2)) >>> [(1, 2)] .. kiedy można się spodziewać [(1,2), (3,)]
egafni
4
@ Erik49: W przypadku określonym w pytaniu nie ma sensu mieć krotki „niekompletnej”. Jeśli chcesz dołączyć niepełną krotkę, możesz użyć izip_longest()zamiast izip(). Np .: list(izip_longest(*[iter([1, 2, 3])]*2, fillvalue=0))-> [(1, 2), (3, 0)]. Mam nadzieję że to pomoże.
Johnsyweb
191

Cóż, potrzebujesz krotkę 2 elementów, więc

data = [1,2,3,4,5,6]
for i,k in zip(data[0::2], data[1::2]):
    print str(i), '+', str(k), '=', str(i+k)

Gdzie:

  • data[0::2] oznacza tworzenie podzbioru kolekcji elementów, które (index % 2 == 0)
  • zip(x,y) tworzy kolekcję krotek z kolekcji xiy tych samych elementów indeksu.
Margus
źródło
8
Można to również rozszerzyć, jeśli wymagane są więcej niż dwa elementy. Na przykładfor i, j, k in zip(data[0::3], data[1::3], data[2::3]):
lifebalance
19
O wiele czystsze niż pobieranie importu i definiowanie funkcji!
kmarsh
7
@kmarsh: Ale działa to tylko na sekwencjach, funkcja działa na każdym iterowalnym; i to wykorzystuje dodatkowe miejsce O (N), funkcja nie; z drugiej strony jest to generalnie szybsze. Istnieją dobre powody, aby wybrać jedno lub drugie; banie się importnie jest jednym z nich.
abarnert
77
>>> l = [1,2,3,4,5,6]

>>> zip(l,l[1:])
[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]

>>> zip(l,l[1:])[::2]
[(1, 2), (3, 4), (5, 6)]

>>> [a+b for a,b in zip(l,l[1:])[::2]]
[3, 7, 11]

>>> ["%d + %d = %d" % (a,b,a+b) for a,b in zip(l,l[1:])[::2]]
['1 + 2 = 3', '3 + 4 = 7', '5 + 6 = 11']
pyanon
źródło
1
To nie działa na Python-3.6.0, ale nadal działa na Python-2.7.10
Hamid
6
@HamidRohani zipzwraca zipobiekt w Pythonie 3, który nie podlega indeksowi. Najpierw należy go przekonwertować na sekwencję ( list, tupleitd.), Ale „niedziałanie” jest trochę rozciągnięte.
vaultah
58

Proste rozwiązanie.

l = [1, 2, 3, 4, 5, 6]

dla i w zakresie (0, len (l), 2):
    print str (l [i]), '+', str (l [i + 1]), '=', str (l [i] + l [i + 1])
taskinoor
źródło
1
co jeśli twoja lista nie jest równa, a chcesz po prostu pokazać ostatnią cyfrę taką, jaka jest?
Hans de Jong
@HansdeJong cię nie dostał. Proszę wyjaśnić trochę więcej.
taskinoor
2
Dzięki. Już wymyśliłem, jak to zrobić. Problem polegał na tym, że jeśli masz listę, która nie zawiera nawet liczby, otrzyma błąd indeksu. Rozwiązano to za pomocą: z wyjątkiem:
Hansa de Jonga
Lub ((l[i], l[i+1])for i in range(0, len(l), 2))dla generatora, można go łatwo modyfikować dla dłuższych krotek.
Basel Shishani,
44

Chociaż wszystkie użyte odpowiedzi zipsą poprawne, uważam, że samodzielne wdrożenie funkcji prowadzi do bardziej czytelnego kodu:

def pairwise(it):
    it = iter(it)
    while True:
        try:
            yield next(it), next(it)
        except StopIteration:
            # no more elements in the iterator
            return

W it = iter(it)części zapewnia, że itw rzeczywistości jest iterator, a nie tylko iterable. Jeśli itjest już iteratorem, linia ta nie ma op-op.

Stosowanie:

for a, b in pairwise([0, 1, 2, 3, 4, 5]):
    print(a + b)
mic_e
źródło
2
To rozwiązanie pozwala na uogólnienie na rozmiar krotek> 2
guilloptero
1
To rozwiązanie działa również, jeśli itjest tylko iteratorem, a nie iteracją. Inne rozwiązania wydają się polegać na możliwości utworzenia dwóch niezależnych iteratorów dla sekwencji.
skyking
Znalazłem to podejście na stackoverflow.com/a/16815056/2480481, zanim zobaczyłem tę odpowiedź. Jest czystszy, łatwiejszy niż obsługa zip ().
m3nda
2
Podoba mi się, że pozwala uniknąć potrojenia wykorzystania pamięci jako przyjętej odpowiedzi.
Kentzo
Nie działa to dobrze z forpętlami w Pythonie 3.5+ ze względu na PEP 479 , który zastępuje wszelkie StopIterationpodniesione w generatorze przez RuntimeError.
sidney,
28

Mam nadzieję, że będzie to jeszcze bardziej elegancki sposób.

a = [1,2,3,4,5,6]
zip(a[::2], a[1::2])

[(1, 2), (3, 4), (5, 6)]
Vivek Srinivasan
źródło
18

Jeśli jesteś zainteresowany wydajnością, zrobiłem mały test porównawczy (używając mojej biblioteki simple_benchmark) w celu porównania wydajności rozwiązań i załączyłem funkcję z jednego z moich pakietów:iteration_utilities.grouper

from iteration_utilities import grouper
import matplotlib as mpl
from simple_benchmark import BenchmarkBuilder

bench = BenchmarkBuilder()

@bench.add_function()
def Johnsyweb(l):
    def pairwise(iterable):
        "s -> (s0, s1), (s2, s3), (s4, s5), ..."
        a = iter(iterable)
        return zip(a, a)

    for x, y in pairwise(l):
        pass

@bench.add_function()
def Margus(data):
    for i, k in zip(data[0::2], data[1::2]):
        pass

@bench.add_function()
def pyanon(l):
    list(zip(l,l[1:]))[::2]

@bench.add_function()
def taskinoor(l):
    for i in range(0, len(l), 2):
        l[i], l[i+1]

@bench.add_function()
def mic_e(it):
    def pairwise(it):
        it = iter(it)
        while True:
            try:
                yield next(it), next(it)
            except StopIteration:
                return

    for a, b in pairwise(it):
        pass

@bench.add_function()
def MSeifert(it):
    for item1, item2 in grouper(it, 2):
        pass

bench.use_random_lists_as_arguments(sizes=[2**i for i in range(1, 20)])
benchmark_result = bench.run()
mpl.rcParams['figure.figsize'] = (8, 10)
benchmark_result.plot_both(relative_to=MSeifert)

wprowadź opis zdjęcia tutaj

Więc jeśli chcesz najszybszego rozwiązania bez zewnętrznych zależności, prawdopodobnie powinieneś po prostu zastosować podejście podane przez Johnysweba (w momencie pisania jest to najbardziej pozytywna i akceptowana odpowiedź).

Jeśli nie przeszkadza ci dodatkowa zależność, to grouperod iteration_utilitiesbędzie prawdopodobnie nieco szybsze.

Dodatkowe przemyślenia

Niektóre podejścia mają pewne ograniczenia, które nie zostały tutaj omówione.

Na przykład kilka rozwiązań działa tylko dla sekwencji (tj. List, ciągów itp.), Na przykład rozwiązania Margus / pyanon / taskinoor, które korzystają z indeksowania, podczas gdy inne rozwiązania działają na dowolnej iterowalnej (to znaczy sekwencje i generatorach, iteratorach), takim jak Johnysweb / mic_e / my solutions.

Następnie Johnysweb dostarczył również rozwiązanie, które działa dla rozmiarów innych niż 2, podczas gdy inne odpowiedzi nie (ok iteration_utilities.grouper pozwala również ustawić liczbę elementów na „grupę”).

Następnie pojawia się pytanie, co powinno się stać, jeśli na liście znajduje się nieparzysta liczba elementów. Czy pozostały element należy odrzucić? Czy listę należy uzupełnić, aby była równa? Czy pozostały element powinien zostać zwrócony jako pojedynczy? Druga odpowiedź nie odnosi się bezpośrednio do tego punktu, jednak jeśli niczego nie przeoczyłem, wszyscy stosują podejście, zgodnie z którym pozostały element powinien zostać odrzucony (z wyjątkiem odpowiedzi Taskinoors - to faktycznie spowoduje wyjątek).

Dzięki grouperniemu możesz zdecydować, co chcesz zrobić:

>>> from iteration_utilities import grouper

>>> list(grouper([1, 2, 3], 2))  # as single
[(1, 2), (3,)]

>>> list(grouper([1, 2, 3], 2, truncate=True))  # ignored
[(1, 2)]

>>> list(grouper([1, 2, 3], 2, fillvalue=None))  # padded
[(1, 2), (3, None)]
MSeifert
źródło
12

Użyj razem poleceń zipi iter:

Uważam, że to rozwiązanie iterjest dość eleganckie:

it = iter(l)
list(zip(it, it))
# [(1, 2), (3, 4), (5, 6)]

Który znalazłem w dokumentacji zip Python 3 .

it = iter(l)
print(*(f'{u} + {v} = {u+v}' for u, v in zip(it, it)), sep='\n')

# 1 + 2 = 3
# 3 + 4 = 7
# 5 + 6 = 11

Aby uogólnić na Nelementy jednocześnie:

N = 2
list(zip(*([iter(l)] * N)))
# [(1, 2), (3, 4), (5, 6)]
Mechanika kwantowa
źródło
10
for (i, k) in zip(l[::2], l[1::2]):
    print i, "+", k, "=", i+k

zip(*iterable) zwraca krotkę z kolejnym elementem każdej iterowalnej.

l[::2] zwraca pierwszy, trzeci, piąty itd. element listy: pierwszy dwukropek wskazuje, że wycinek zaczyna się na początku, ponieważ nie ma za nim liczby, drugi dwukropek jest potrzebny tylko wtedy, gdy chcesz „kroku w wycinku” „(w tym przypadku 2).

l[1::2]robi to samo, ale zaczyna się w drugim elemencie list, więc zwraca drugi, czwarty, szósty itd. element oryginalnej listy.

alexpinho98
źródło
4
Odpowiedź ta została już udzielona przez Margusa dwa lata temu. stackoverflow.com/questions/5389507/…
cababunga
1
1 do wyjaśnienia, jak [number::number]działa składnia. pomocne dla tych, którzy często nie używają Pythona
Alby
2

Z rozpakowaniem:

l = [1,2,3,4,5,6]
while l:
    i, k, *l = l
    print(str(i), '+', str(k), '=', str(i+k))
toaruScar
źródło
Łał! Dlaczego nie mogłem o tym myśleć :) Musisz po prostu poradzić sobie ze sprawą, w której nie ma absolutnej pary (nieparzyste wpisy)
Saurav Kumar
1

Dla każdego może to pomóc, oto rozwiązanie podobnego problemu, ale z nakładającymi się parami (zamiast wzajemnie wykluczających się par).

Z dokumentacji itertools Python :

from itertools import izip

def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return izip(a, b)

Lub bardziej ogólnie:

from itertools import izip

def groupwise(iterable, n=2):
    "s -> (s0,s1,...,sn-1), (s1,s2,...,sn), (s2,s3,...,sn+1), ..."
    t = tee(iterable, n)
    for i in range(1, n):
        for j in range(0, i):
            next(t[i], None)
    return izip(*t)
Chris Malek
źródło
1

możesz użyć pakietu more_itertools .

import more_itertools

lst = range(1, 7)
for i, j in more_itertools.chunked(lst, 2):
    print(f'{i} + {j} = {i+j}')
Scott Ming
źródło
1

Muszę podzielić listę przez liczbę i tak ustawić.

l = [1,2,3,4,5,6]

def divideByN(data, n):
        return [data[i*n : (i+1)*n] for i in range(len(data)//n)]  

>>> print(divideByN(l,2))
[[1, 2], [3, 4], [5, 6]]

>>> print(divideByN(l,3))
[[1, 2, 3], [4, 5, 6]]
Ali Katkar
źródło
1

Można to zrobić na wiele sposobów. Na przykład:

lst = [1,2,3,4,5,6]
[(lst[i], lst[i+1]) for i,_ in enumerate(lst[:-1])]    
>>>[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]

[i for i in zip(*[iter(lst)]*2)]    
>>>[(1, 2), (3, 4), (5, 6)]
Binit Bhagat
źródło
0

Pomyślałem, że to dobre miejsce, by podzielić się moim uogólnieniem na n> 2, które jest po prostu przesuwanym oknem nad iteracją:

def sliding_window(iterable, n):
    its = [ itertools.islice(iter, i, None) 
            for i, iter
            in enumerate(itertools.tee(iterable, n)) ]                               

    return itertools.izip(*its)
Yuval
źródło
0

Tytuł tego pytania jest mylący, wydaje się, że szukasz kolejnych par, ale jeśli chcesz iterować zestaw wszystkich możliwych par, to zadziała:

for i,v in enumerate(items[:-1]):
        for u in items[i+1:]:
Ofek Ron
źródło
0

Używając pisania, aby zweryfikować dane za pomocą narzędzia do analizy statycznej mypy :

from typing import Iterator, Any, Iterable, TypeVar, Tuple

T_ = TypeVar('T_')
Pairs_Iter = Iterator[Tuple[T_, T_]]

def legs(iterable: Iterator[T_]) -> Pairs_Iter:
    begin = next(iterable)
    for end in iterable:
        yield begin, end
        begin = end
Vlad Bezden
źródło
0

Uproszczone podejście:

[(a[i],a[i+1]) for i in range(0,len(a),2)]

jest to przydatne, jeśli twoja tablica jest a i chcesz iterować po niej parami. Aby wykonać iterację na trojaczkach lub więcej, wystarczy zmienić polecenie krokowe „zakres”, na przykład:

[(a[i],a[i+1],a[i+2]) for i in range(0,len(a),3)]

(musisz poradzić sobie z nadmiarem wartości, jeśli długość tablicy i stopień nie pasują)

mchrgr2000
źródło
-1

Tutaj możemy mieć alt_elemmetodę, która może pasować do twojej pętli for.

def alt_elem(list, index=2):
    for i, elem in enumerate(list, start=1):
        if not i % index:
           yield tuple(list[i-index:i])


a = range(10)
for index in [2, 3, 4]:
    print("With index: {0}".format(index))
    for i in alt_elem(a, index):
       print(i)

Wynik:

With index: 2
(0, 1)
(2, 3)
(4, 5)
(6, 7)
(8, 9)
With index: 3
(0, 1, 2)
(3, 4, 5)
(6, 7, 8)
With index: 4
(0, 1, 2, 3)
(4, 5, 6, 7)

Uwaga: powyższe rozwiązanie może nie być wydajne, biorąc pod uwagę operacje wykonywane w func.

Sanket Sudake
źródło
-1
a_list = [1,2,3,4,5,6]
empty_list = [] 
for i in range(0,len(a_list),2):
   empty_list.append(a_list[i]+a_list[i+1])   
print(empty_list)
użytkownik391
źródło