Przekształć „listę krotek” w płaską listę lub macierz

82

W Sqlite polecenie „select..from” zwraca wynik „output”, który drukuje (w Pythonie):

>>print output
[(12.2817, 12.2817), (0, 0), (8.52, 8.52)]

Wygląda na to, że jest to lista krotek. Chciałbym albo przekonwertować „wyjście” w prostą tablicę 1D (= lista w Pythonie, jak sądzę):

[12.2817, 12.2817, 0, 0, 8.52, 8.52]

lub matryca 2x3:

12.2817 12.2817
0          0 
8.52     8.52

do odczytania przez „wyjście [i] [j]”

Polecenie flatten nie działa dla pierwszej opcji, a nie mam pomysłu na drugą ... :)

Czy mógłbyś mi podpowiedzieć? Szybka rzecz byłaby świetna, ponieważ rzeczywiste dane są znacznie większe (tutaj jest tylko prosty przykład).

Garth
źródło
2
[(12.2817, 12.2817), (0, 0), (8.52, 8.52)]to już macierz 3x2!? czy coś przegapiłem?
mouad
Zobacz to pytanie
Joel Cornett
1
dla funkcji spłaszczania sprawdź receptury modułu itertools, istnieje już przykład funkcji spłaszczenia: docs.python.org/library/itertools.html#recipes
mouad
4
[item for sublist in output for item in sublist]działa doskonale i ma tę zaletę, że Twoje wewnętrzne krotki mogą być również listami; bardziej ogólnie dowolna kombinacja wewnętrznych i zewnętrznych prac
iterowalnych

Odpowiedzi:

125

Zdecydowanie najszybsze (i najkrótsze) opublikowane rozwiązanie:

list(sum(output, ()))

Około 50% szybciej niż itertoolsrozwiązanie i około 70% szybciej niż maprozwiązanie.

Joel Cornett
źródło
8
@Joel fajnie, ale zastanawiam się jak to działa? list(output[0]+output[1]+output[2])daje pożądany rezultat, ale list(sum(output))nie. Czemu? Jaką „magię” robi ()?
Kyss Tao
9
Ok, powinienem był przeczytać instrukcję g . Wygląda na to, że sum(sequence[, start])suma dodaje wartość startdomyślną, 0a nie zaczynając od tego, sequence[0]czy istnieje, a następnie dodaje resztę elementów. Przepraszam, że przeszkadzam.
Kyss Tao
3
Jest to dobrze znany anty-wzorzec: nie używaj go sumdo łączenia sekwencji, skutkuje to algorytmem czasu kwadratowego. Rzeczywiście, sumfunkcja będzie narzekać, jeśli spróbujesz to zrobić za pomocą łańcuchów!
juanpa.arrivillaga
@ juanpa.arrivillaga: uzgodniono. Jest bardzo niewiele przypadków użycia, w których byłoby to preferowane.
Joel Cornett
9
Tak, szybko, ale całkowicie tępo. Musiałbyś zostawić komentarz, co tak naprawdę robi :(
CpILL
42

Podejście oparte na listach, które działa z typami iterowalnymi i jest szybsze niż inne pokazane tutaj metody.

flattened = [item for sublist in l for item in sublist]

lto lista do spłaszczenia (nazywana outputw przypadku PO)


testy timeit:

l = list(zip(range(99), range(99)))  # list of tuples to flatten

Rozumienie listy

[item for sublist in l for item in sublist]

wynik timeit = 7,67 µs ± 129 ns na pętlę

Lista ext () metoda

flattened = []
list(flattened.extend(item) for item in l)

wynik timeit = 11 µs ± 433 ns na pętlę

suma()

list(sum(l, ()))

wynik timeit = 24,2 µs ± 269 ns na pętlę

Gman
źródło
1
Musiałem użyć tego na dużym zbiorze danych, metoda rozumienia listy była zdecydowanie najszybsza!
nbeuchat
Dokonałem niewielkiej zmiany w rozwiązaniu .extend i teraz działa trochę lepiej. sprawdź to w swoim czasie, aby porównać
Totoro
24

W Pythonie 2.7 i wszystkich wersjach Python3 można itertools.chainspłaszczyć listę elementów iteracyjnych. Albo za pomocą *składni, albo metody klasowej.

>>> t = [ (1,2), (3,4), (5,6) ]
>>> t
[(1, 2), (3, 4), (5, 6)]
>>> import itertools
>>> list(itertools.chain(*t))
[1, 2, 3, 4, 5, 6]
>>> list(itertools.chain.from_iterable(t))
[1, 2, 3, 4, 5, 6]
Thruston
źródło
11

Aktualizacja : spłaszczanie przy użyciu rozszerzenia, ale bez zrozumienia i bez używania listy jako iteratora (najszybsze)

Po sprawdzeniu kolejnej odpowiedzi na to, która zapewniła szybsze rozwiązanie dzięki zrozumieniu listy z dual for , zrobiłem trochę poprawkę i teraz działa lepiej, najpierw wykonanie listy (...) przeciągało się przez duży procent czasu, a potem zmieniało listę zrozumienie dla prostej pętli również nieco bardziej ogolone.

Nowe rozwiązanie to:

l = []
for row in output: l.extend(row)

Starsze:

Spłaszczanie z mapą / rozszerzeniem:

l = []
list(map(l.extend, output))

Spłaszczanie ze zrozumieniem listy zamiast mapy

l = []
list(l.extend(row) for row in output)

trochę czasu na nowe rozszerzenie i poprawę uzyskaną poprzez usunięcie listy (...) dla [...]:

import timeit
t = timeit.timeit
o = "output=list(zip(range(1000000000), range(10000000))); l=[]"
steps_ext = "for row in output: l.extend(row)"
steps_ext_old = "list(l.extend(row) for row in output)"
steps_ext_remove_list = "[l.extend(row) for row in output]"
steps_com = "[item for sublist in output for item in sublist]"

print("new extend:      ", t(steps_ext, setup=o, number=10))
print("old extend w []: ", t(steps_ext_remove_list, setup=o, number=10))
print("comprehension:   ", t(steps_com, setup=o, number=10,))
print("old extend:      ", t(steps_ext_old, setup=o, number=10))

>>> new extend:       4.502427191007882
>>> old extend w []:  5.281140706967562
>>> comprehension:    5.54302118299529
>>> old extend:       6.840151469223201    
Totoro
źródło
9

użyj itertoolsłańcucha:

>>> import itertools
>>> list(itertools.chain.from_iterable([(12.2817, 12.2817), (0, 0), (8.52, 8.52)]))
[12.2817, 12.2817, 0, 0, 8.52, 8.52]
Charles Beattie
źródło
7
>>> flat_list = []
>>> nested_list = [(1, 2, 4), (0, 9)]
>>> for a_tuple in nested_list:
...     flat_list.extend(list(a_tuple))
... 
>>> flat_list
[1, 2, 4, 0, 9]
>>> 

możesz łatwo przejść z listy krotek do pojedynczej listy, jak pokazano powyżej.

cobie
źródło
7

Lub możesz spłaszczyć listę w ten sposób:

reduce(lambda x,y:x+y, map(list, output))
Maria Zverina
źródło
reduce(lambda x,y:x+y, output)wydaje się działać bezpośrednio konwertując na długą krotkę (którą można przekonwertować na listę). Dlaczego warto korzystać map(list, output)z reduce()połączenia? Może jest to bardziej zgodne z faktem, że krotki są niezmienne, a listy są zmienne .
Paul Rougieux,
5

Właśnie po to numpyzostało stworzone, zarówno z punktu widzenia struktur danych, jak i szybkości.

import numpy as np

output = [(12.2817, 12.2817), (0, 0), (8.52, 8.52)]
output_ary = np.array(output)   # this is your matrix 
output_vec = output_ary.ravel() # this is your 1d-array
Joshua Cook
źródło
2

W przypadku dowolnych list zagnieżdżonych (na wszelki wypadek):

def flatten(lst):
    result = []
    for element in lst: 
        if hasattr(element, '__iter__'):
            result.extend(flatten(element))
        else:
            result.append(element)
    return result

>>> flatten(output)
[12.2817, 12.2817, 0, 0, 8.52, 8.52]
cval
źródło