Transforma la "lista de tuplas" en una lista plana o una matriz


82

Con Sqlite, un comando "select..from" devuelve los resultados "salida", que imprime (en python):

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

Parece ser una lista de tuplas. Me gustaría convertir la "salida" en una matriz 1D simple (= lista en Python, supongo):

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

o una matriz de 2x3:

12.2817 12.2817
0          0 
8.52     8.52

para ser leído a través de "salida [i] [j]"

El comando aplanar no hace el trabajo para la primera opción, y no tengo idea de la segunda ... :)

¿Podrías darme una pista? Algo rápido sería genial ya que los datos reales son mucho más grandes (aquí hay solo un ejemplo simple).


2
[(12.2817, 12.2817), (0, 0), (8.52, 8.52)]ya es una matriz de 3x2!? o me perdí algo?
mouad


1
para las recetas del módulo de comprobación de la función aplanar itertools ya hay un ejemplo de función aplanar: docs.python.org/library/itertools.html#recipes
mouad

4
[item for sublist in output for item in sublist]funciona perfectamente y tiene la ventaja de que sus tuplas internas también pueden ser listas; más generalmente, cualquier combinación de trabajos iterables internos y externos
Kyss Tao

Respuestas:


125

Con mucho, la solución más rápida (y más corta) publicada:

list(sum(output, ()))

Aproximadamente un 50% más rápido que la itertoolssolución y aproximadamente un 70% más rápido que la mapsolución.


8
@Joel agradable, pero me pregunto cómo funciona. list(output[0]+output[1]+output[2])da el resultado deseado pero list(sum(output))no. ¿Por qué? ¿Qué "magia" hace ()?
Kyss Tao

9
Ok, debería haber leído el manual g . Parece sum(sequence[, start]): sum agrega startcuál es el valor predeterminado en 0lugar de comenzar desde sequence[0]si existe y luego agregar el resto de los elementos. Lo siento por molestarte.
Kyss Tao

3
Este es un anti-patrón bien conocido: no lo use sumpara concatenar secuencias, da como resultado un algoritmo de tiempo cuadrático. De hecho, ¡la sumfunción se quejará si intenta hacer esto con cadenas!
juanpa.arrivillaga

@ juanpa.arrivillaga: de acuerdo. Hay muy pocos casos de uso en los que esto sea preferible.
Joel Cornett

9
Sí, rápido pero completamente obtuso. Tendría que dejar un comentario sobre lo que realmente está haciendo :(
CpILL

42

Enfoque de comprensión de listas que funciona con tipos Iterables y es más rápido que otros métodos que se muestran aquí.

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

les la lista para aplanar (llamada outputen el caso del OP)


pruebas timeit:

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

Comprensión de listas

[item for sublist in l for item in sublist]

timeit resultado = 7,67 µs ± 129 ns por bucle

Método de extensión de lista ()

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

timeit resultado = 11 µs ± 433 ns por bucle

suma()

list(sum(l, ()))

timeit resultado = 24,2 µs ± 269 ns por bucle


1
Tuve que usar esto en un gran conjunto de datos, ¡el método de comprensión de listas fue, con mucho, el más rápido!
nbeuchat

Hice un pequeño cambio en la solución .extend y ahora funciona un poco mejor. compruébalo en tu horarioit para comparar
Totoro

24

En Python 2.7, y todas las versiones de Python3, puede usar itertools.chainpara aplanar una lista de iterables. O con la *sintaxis o el método de clase.

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

Actualización : Aplanar usando extender pero sin comprensión y sin usar la lista como iterador (más rápido)

Después de verificar la siguiente respuesta a esto que proporcionó una solución más rápida a través de una comprensión de lista dual for, hice un pequeño ajuste y ahora funciona mejor, primero la ejecución de la lista (...) fue arrastrando un gran porcentaje de tiempo, luego cambiando una lista la comprensión de un bucle simple también se afeitó un poco más.

La nueva solución es:

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

Mayor:

Aplanamiento con mapa / extender:

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

Acoplamiento con comprensión de lista en lugar de mapa

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

algún tiempo es para una nueva extensión y la mejora obtenida simplemente eliminando la lista (...) para [...]:

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

itertoolscadena de uso :

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

podría pasar fácilmente de una lista de tuplas a una lista única como se muestra arriba.


7

O puede aplanar la lista de esta manera:

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

reduce(lambda x,y:x+y, output)parece funcionar directamente convirtiendo a una tupla larga (que se puede convertir en una lista). ¿Por qué usar map(list, output)dentro de la reduce()llamada? Tal vez esté más en línea con el hecho de que las tuplas son inmutables, las listas son mutables .
Paul Rougieux

5

Esto es para lo que numpyse hizo, tanto desde una perspectiva de estructura de datos como de velocidad.

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

En caso de listas anidadas arbitrarias (por si acaso):

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]
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.