una sirve recorrer que para palabra metodos listas lista guardar elementos datos como cargar buscar agregar python performance list list-comprehension

sirve - ¿La mejor manera de intercalar dos o más listas en python?



para que sirve una lista en python (7)

Supongamos que tengo una lista:

l=[''a'',''b'',''c'']

Y su lista de sufijos:

l2 = [''a_1'', ''b_1'', ''c_1'']

Me gustaría que la salida deseada fuera:

out_l = [''a'',''a_1'',''b'',''b_2'',''c'',''c_3'']

El resultado es la versión intercalada de las dos listas anteriores.

Puedo escribir regularmente for hacer esto, pero me pregunto si hay una forma más Pythonic (por ejemplo, usando la comprensión de lista o lambda) para hacerlo.

He intentado algo como esto:

list(map(lambda x: x[1]+''_''+str(x[0]+1), enumerate(a))) # this only returns [''a_1'', ''b_2'', ''c_3'']

Además, ¿qué cambios se deberían hacer para el caso general, es decir, para 2 o más listas donde l2 no es necesariamente un derivado de l ?


yield

Puedes usar un generador para una solución elegante. En cada iteración, ceda dos veces: una vez con el elemento original y una vez con el elemento con el sufijo agregado.

El generador tendrá que estar agotado; eso se puede hacer añadiendo una list al final.

def transform(l): for i, x in enumerate(l, 1): yield x yield f''{x}_{i}'' # {}_{}''.format(x, i)

También puede volver a escribir esto utilizando el yield from sintaxis para la delegación del generador:

def transform(l): for i, x in enumerate(l, 1): yield from (x, f''{x}_{i}'') # (x, {}_{}''.format(x, i))

out_l = list(transform(l)) print(out_l) [''a'', ''a_1'', ''b'', ''b_2'', ''c'', ''c_3'']

Si está en versiones anteriores a python-3.6, reemplace f''{x}_{i}'' con ''{}_{}''.format(x, i) .

Generalizando
Considere un escenario general donde tiene N listas del formulario:

l1 = [v11, v12, ...] l2 = [v21, v22, ...] l3 = [v31, v32, ...] ...

Que le gustaría intercalar. Estas listas no necesariamente se derivan unas de otras.

Para manejar las operaciones de intercalado con estas N listas, deberá iterar sobre pares:

def transformN(*args): for vals in zip(*args): yield from vals out_l = transformN(l1, l2, l3, ...)

list.__setitem__

Lo recomendaría desde la perspectiva del rendimiento. Primero asigne espacio para una lista vacía, y luego asigne los elementos de la lista a sus posiciones apropiadas usando la asignación de la lista dividida. l entra en los índices pares, y l'' ( l modificado) va en los índices impares.

out_l = [None] * (len(l) * 2) out_l[::2] = l out_l[1::2] = [f''{x}_{i}'' for i, x in enumerate(l, 1)] # [{}_{}''.format(x, i) ...]

print(out_l) [''a'', ''a_1'', ''b'', ''b_2'', ''c'', ''c_3'']

Este es siempre el más rápido de mis tiempos (abajo).

Generalizando
Para manejar las listas N, asigna iterativamente a rebanadas.

list_of_lists = [l1, l2, ...] out_l = [None] * len(list_of_lists[0]) * len(list_of_lists) for i, l in enumerate(list_of_lists): out_l[i::2] = l

zip + chain.from_iterable

Un enfoque funcional, similar a la solución de @chrisz. Construye pares usando zip y luego itertools.chain usando itertools.chain .

from itertools import chain # [{}_{}''.format(x, i) ...] out_l = list(chain.from_iterable(zip(l, [f''{x}_{i}'' for i, x in enumerate(l, 1)])))

print(out_l) [''a'', ''a_1'', ''b'', ''b_2'', ''c'', ''c_3'']

iterools.chain es ampliamente considerado como el enfoque de aplanamiento de la lista pitónica.

Generalizando
Esta es la solución más sencilla de generalizar, y sospecho que la más eficiente para múltiples listas cuando N es grande.

list_of_lists = [l1, l2, ...] out_l = list(chain.from_iterable(zip(*list_of_lists)))

Actuación

Echemos un vistazo a algunas pruebas de rendimiento para el caso simple de dos listas (una lista con su sufijo). Los casos generales no serán probados ya que los resultados varían ampliamente según los datos.

from timeit import timeit import pandas as pd import matplotlib.pyplot as plt res = pd.DataFrame( index=[''ajax1234'', ''cs0'', ''cs1'', ''cs2'', ''cs3'', ''chrisz'', ''sruthiV''], columns=[10, 50, 100, 500, 1000, 5000, 10000, 50000, 100000], dtype=float ) for f in res.index: for c in res.columns: l = [''a'', ''b'', ''c'', ''d''] * c stmt = ''{}(l)''.format(f) setp = ''from __main__ import l, {}''.format(f) res.at[f, c] = timeit(stmt, setp, number=50) ax = res.div(res.min()).T.plot(loglog=True) ax.set_xlabel("N"); ax.set_ylabel("time (relative)"); plt.show()

Funciones

def ajax1234(l): return [ i for b in [[a, ''{}_{}''.format(a, i)] for i, a in enumerate(l, start=1)] for i in b ] def cs0(l): # this is in Ajax1234''s answer, but it is my suggestion return [j for i, a in enumerate(l, 1) for j in [a, ''{}_{}''.format(a, i)]] def cs1(l): def _cs1(l): for i, x in enumerate(l, 1): yield x yield f''{x}_{i}'' return list(_cs1(l)) def cs2(l): out_l = [None] * (len(l) * 2) out_l[::2] = l out_l[1::2] = [f''{x}_{i}'' for i, x in enumerate(l, 1)] return out_l def cs3(l): return list(chain.from_iterable( zip(l, [f''{x}_{i}'' for i, x in enumerate(l, 1)])) ) def chrisz(l): return [ val for pair in zip(l, [f''{k}_{j+1}'' for j, k in enumerate(l)]) for val in pair ] def sruthiV(l): return [ l[int(i / 2)] + "_" + str(int(i / 2) + 1) if i % 2 != 0 else l[int(i/2)] for i in range(0,2*len(l)) ]

Software

Sistema — Mac OS X High Sierra — Intel Core i7 a 2,4 GHz
Python — 3.6.0
IPython — 6.2.1


(Editado)

Usando la lista de comprensión:

[ l[int(i/2)]+"_"+str(int(i/2)+1) if i%2!=0 else l[int(i/2)] for i in range(0,2*len(l))] # l=[''b'', ''a'', ''d'', ''c''] # output : [''b'', ''b_1'', ''a'', ''a_2'', ''d'', ''d_3'', ''c'', ''c_4'']


Aquí está mi implementación simple

l=[''a'',''b'',''c''] # generate new list with the indices of the original list new_list=l + [''{0}_{1}''.format(i, (l.index(i) + 1)) for i in l] # sort the new list in ascending order new_list.sort() print new_list # Should display [''a'', ''a_1'', ''b'', ''b_2'', ''c'', ''c_3'']


Podrías usar zip :

[val for pair in zip(l, [f''{k}_{j+1}'' for j, k in enumerate(l)]) for val in pair]

Salida:

[''a'', ''a_1'', ''b'', ''b_2'', ''c'', ''c_3'']


Puedes usar una lista de comprensión así:

l=[''a'',''b'',''c''] new_l = [i for b in [[a, ''{}_{}''.format(a, i)] for i, a in enumerate(l, start=1)] for i in b]

Salida:

[''a'', ''a_1'', ''b'', ''b_2'', ''c'', ''c_3'']

Opcional, método más corto:

[j for i, a in enumerate(l, 1) for j in [a, ''{}_{}''.format(a, i)]]


Si desea devolver [["a","a_1"],["b","b_2"],["c","c_3"]] puede escribir

new_l=[[x,"{}_{}".format(x,i+1)] for i,x in enumerate(l)]

Esto no es lo que quiere, en su lugar desea ["a","a_1"]+["b","b_2"]+["c","c_3"] . Esto se puede hacer a partir del resultado de la operación anterior utilizando sum() ; ya que está sumando listas, necesita agregar la lista vacía como un argumento para evitar un error. Así que eso da

new_l=sum(([x,"{}_{}".format(x,i+1)] for i,x in enumerate(l)),[])

No sé cómo esto se compara con la velocidad (probablemente no sea así), pero me resulta más fácil entender lo que está pasando que las otras respuestas basadas en listas de comprensión.


Una solución muy simple:

out_l=[] for i,x in enumerate(l,1): out_l.extend([x,f"{x}_{i}"])