Перетворіть “список кортежів” у плоский список або матрицю


82

У Sqlite команда "select..from" повертає результати "output", які друкують (у python):

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

Здається, це перелік кортежів. Я хотів би або перетворити "output" у простий 1D-масив (= список, здається, на Python):

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

або матриця 2х3:

12.2817 12.2817
0          0 
8.52     8.52

читати через "вихід [i] [j]"

Команда flatten не виконує роботу для першого варіанту, а для другого я не маю уявлення ... :)

Не могли б ви дати мені підказку? Щось швидке було б чудово, оскільки реальні дані значно більші (ось лише простий приклад).


2
[(12.2817, 12.2817), (0, 0), (8.52, 8.52)]це вже матриця 3x2!? чи я щось пропустив?
mouad


1
для перевірки функції згладжування рецептів модуля itertools вже є приклад функції зрівняння
mouad

4
[item for sublist in output for item in sublist]чудово працює і має ту перевагу, що ваші внутрішні кортежі також можуть бути списками; загальніше будь-яка комбінація внутрішніх та зовнішніх ітеративних робіт
Kyss Tao

Відповіді:


125

Безсумнівно найшвидше (і найкоротше) опубліковане рішення:

list(sum(output, ()))

Приблизно на 50% швидше, ніж itertoolsрозчин, і приблизно на 70% швидше, ніж mapрозчин.


8
@Joel приємно, але цікаво, як це працює? list(output[0]+output[1]+output[2])дає бажаний результат, але list(sum(output))ні. Чому? Яку "магію" робить ()?
Kyss Tao

9
Гаразд, я повинен був прочитати інструкцію g . Здається sum(sequence[, start]): сума додає startзначення за замовчуванням, 0а не просто починаючи з того, sequence[0]якщо воно існує, а потім додаючи решту елементів. Вибачте за неспокій.
Kyss Tao

3
Це добре відомий анти-шаблон: не використовувати sumдля об’єднання послідовностей, це призводить до квадратичного часового алгоритму. Дійсно, sumфункція скаржиться, якщо ви спробуєте зробити це за допомогою рядків!
juanpa.arrivillaga

@ juanpa.arrivillaga: погоджено. Дуже мало випадків використання, коли це було б кращим.
Джоел Корнетт,

9
Так, швидко, але абсолютно тупо. Вам потрібно було б залишити коментар щодо того, що він насправді робить :(
CpILL

42

Підхід до розуміння списку, який працює з типами Iterable і швидший за інші методи, показані тут.

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

l- це список, який потрібно вирівняти (називається outputу випадку з OP)


timeit тести:

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

Розуміння списку

[item for sublist in l for item in sublist]

timeit результат = 7,67 мкс ± 129 нс на петлю

Метод розширення списку ()

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

timeit результат = 11 мкс ± 433 нс на петлю

сума ()

list(sum(l, ()))

timeit результат = 24,2 мкс ± 269 нс на петлю


1
Мені довелося використовувати це на великому наборі даних, метод розуміння списку був безумовно найшвидшим!
nbeuchat

Я трохи змінив рішення .extend і тепер працює трохи краще. перевірте це за часом, щоб порівняти
Тоторо

24

У Python 2.7 та всіх версіях Python3 ви можете використовувати itertools.chainдля згладжування списку ітерацій. Або з *синтаксисом, або з методом класу.

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

11

Оновлення : Згладжування за допомогою розширення, але без розуміння та без використання списку як ітератора (найшвидший)

Перевіривши наступну відповідь на це, що забезпечило швидше рішення за допомогою розуміння списку, dual forя трохи підправив, і тепер він працює ефективніше, спочатку виконання списку (...) тягнуло великий відсоток часу, потім змінювало список розуміння для простої петлі, поголеної також трохи більше.

Новим рішенням є:

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

Старіші:

Зрівняння з картою / розширенням:

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

Вирівнювання з розумінням списку замість карти

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

трохи часу для нового розширення та вдосконалення, отриманого шляхом простого видалення списку (...) для [...]:

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    

9

itertoolsланцюжок використання :

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

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

Ви можете легко перейти зі списку кортежів до одного списку, як показано вище.


7

Або ви можете згладити список так:

reduce(lambda x,y:x+y, map(list, output))

reduce(lambda x,y:x+y, output)здається, працює безпосередньо, перетворюючи в довгий кортеж (який можна перетворити на список). Навіщо використовувати map(list, output)всередині reduce()дзвінка? Можливо, це більше відповідає тому факту, що кортежі незмінні, списки можна змінювати .
Paul Rougieux

5

Це те, numpyдля чого було створено як із структур даних, так і з точки зору швидкості.

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

2

У випадку довільних вкладених списків (про всяк випадок):

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]
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.