una tuplas metodos llenar listas lista eliminar elementos comparar arreglos agregar python pandas numpy

tuplas - metodos de listas en python



Convierta de manera eficiente una lista desigual de listas en una matriz de contenido mínima rellenada con nan (4)

considere la lista de listas l

l = [[1, 2, 3], [1, 2]]

si convierto esto en una np.array . obtendré una matriz de objetos unidimensional con [1, 2, 3] en la primera posición y [1, 2] en la segunda posición.

print(np.array(l)) [[1, 2, 3] [1, 2]]

Quiero esto en su lugar

print(np.array([[1, 2, 3], [1, 2, np.nan]])) [[ 1. 2. 3.] [ 1. 2. nan]]

Puedo hacer esto con un bucle, pero todos sabemos lo impopulares que son los bucles.

def box_pir(l): lengths = [i for i in map(len, l)] shape = (len(l), max(lengths)) a = np.full(shape, np.nan) for i, r in enumerate(l): a[i, :lengths[i]] = r return a print(box_pir(l)) [[ 1. 2. 3.] [ 1. 2. nan]]

¿Cómo hago esto de una manera rápida y vectorizada?

sincronización

funciones de configuración

%%cython import numpy as np def box_pir_cython(l): lengths = [len(item) for item in l] shape = (len(l), max(lengths)) a = np.full(shape, np.nan) for i, r in enumerate(l): a[i, :lengths[i]] = r return a

def box_divikar(v): lens = np.array([len(item) for item in v]) mask = lens[:,None] > np.arange(lens.max()) out = np.full(mask.shape, np.nan) out[mask] = np.concatenate(v) return out def box_hpaulj(LoL): return np.array(list(zip_longest(*LoL, fillvalue=np.nan))).T def box_simon(LoL): max_len = len(max(LoL, key=len)) return np.array([x + [np.nan]*(max_len-len(x)) for x in LoL]) def box_dawg(LoL): cols=len(max(LoL, key=len)) rows=len(LoL) AoA=np.empty((rows,cols, )) AoA.fill(np.nan) for idx in range(rows): AoA[idx,0:len(LoL[idx])]=LoL[idx] return AoA def box_pir(l): lengths = [len(item) for item in l] shape = (len(l), max(lengths)) a = np.full(shape, np.nan) for i, r in enumerate(l): a[i, :lengths[i]] = r return a def box_pandas(l): return pd.DataFrame(l).values


Esto parece estar cerca de this question , donde el relleno estaba con zeros lugar de NaNs . Se publicaron enfoques interesantes allí, junto con el mine basado en la broadcasting y boolean-indexing . Entonces, simplemente modificaría una línea de mi publicación allí para resolver este caso así:

def boolean_indexing(v, fillval=np.nan): lens = np.array([len(item) for item in v]) mask = lens[:,None] > np.arange(lens.max()) out = np.full(mask.shape,fillval) out[mask] = np.concatenate(v) return out

Ejecución de muestra:

In [32]: l Out[32]: [[1, 2, 3], [1, 2], [3, 8, 9, 7, 3]] In [33]: boolean_indexing(l) Out[33]: array([[ 1., 2., 3., nan, nan], [ 1., 2., nan, nan, nan], [ 3., 8., 9., 7., 3.]]) In [34]: boolean_indexing(l,-1) Out[34]: array([[ 1, 2, 3, -1, -1], [ 1, 2, -1, -1, -1], [ 3, 8, 9, 7, 3]])

He publicado pocos resultados de tiempo de ejecución allí para todos los enfoques publicados en esas preguntas y respuestas, lo que podría ser útil.


Podría escribir esto como una forma de asignación de divisiones en cada una de las sub matrices que se han llenado con un valor predeterminado:

def to_numpy(LoL, default=np.nan): cols=len(max(LoL, key=len)) rows=len(LoL) AoA=np.empty((rows,cols, )) AoA.fill(default) for idx in range(rows): AoA[idx,0:len(LoL[idx])]=LoL[idx] return AoA

Agregué la indexación booleana de Divakar como f4 y agregué a la prueba de sincronización. Al menos en mis pruebas, (Python 2.7 y Python 3.5; Numpy 1.11) no es el más rápido.

El tiempo muestra que izip_longest o f2 es ligeramente más rápido para la mayoría de las listas, pero la asignación de corte (que es f1 ) es más rápida para listas más grandes:

from __future__ import print_function import numpy as np try: from itertools import izip_longest as zip_longest except ImportError: from itertools import zip_longest def f1(LoL): cols=len(max(LoL, key=len)) rows=len(LoL) AoA=np.empty((rows,cols, )) AoA.fill(np.nan) for idx in range(rows): AoA[idx,0:len(LoL[idx])]=LoL[idx] return AoA def f2(LoL): return np.array(list(zip_longest(*LoL,fillvalue=np.nan))).T def f3(LoL): max_len = len(max(LoL, key=len)) return np.array([x + [np.nan]*(max_len-len(x)) for x in LoL]) def f4(LoL): lens = np.array([len(item) for item in LoL]) mask = lens[:,None] > np.arange(lens.max()) out = np.full(mask.shape,np.nan) out[mask] = np.concatenate(LoL) return out if __name__==''__main__'': import timeit for case, LoL in ((''small'', [list(range(20)), list(range(30))] * 1000), (''medium'', [list(range(20)), list(range(30))] * 10000), (''big'', [list(range(20)), list(range(30))] * 100000), (''huge'', [list(range(20)), list(range(30))] * 1000000)): print(case) for f in (f1, f2, f3, f4): print(" ",f.__name__, timeit.timeit("f(LoL)", setup="from __main__ import f, LoL", number=100) )

Huellas dactilares:

small f1 0.245459079742 f2 0.209980010986 f3 0.350691080093 f4 0.332141160965 medium f1 2.45869493484 f2 2.32307982445 f3 3.65722203255 f4 3.55545687675 big f1 25.8796288967 f2 26.6177148819 f3 41.6916451454 f4 41.3140149117 huge f1 262.429639101 f2 295.129109859 f3 427.606887817 f4 441.810388088


Probablemente la versión de lista más rápida usa itertools.zip_longest (puede ser izip_longest en Py2):

In [747]: np.array(list(itertools.zip_longest(*ll,fillvalue=np.nan))).T Out[747]: array([[ 1., 2., 3.], [ 1., 2., nan]])

La zip simple produce:

In [748]: list(itertools.zip_longest(*ll)) Out[748]: [(1, 1), (2, 2), (3, None)]

otro zip ''transpone'':

In [751]: list(zip(*itertools.zip_longest(*ll))) Out[751]: [(1, 2, 3), (1, 2, None)]

A menudo, al comenzar con listas (o incluso una matriz de listas de objetos), es más rápido seguir con los métodos de lista. Hay una sobrecarga sustancial en la creación de una matriz o marco de datos.

Esta no es la primera vez que se hace esta pregunta.

¿Cómo puedo rellenar y / o truncar un vector a una longitud específica usando numpy?

Mi respuesta allí incluye tanto este zip_longest como tu box_pir

Creo que también hay una versión numpy rápida que usa una matriz aplanada, pero no recuerdo los detalles. Probablemente fue dado por Warren o Divakar.

Creo que la versión ''aplanada'' funciona algo en esta línea:

In [809]: ll Out[809]: [[1, 2, 3], [1, 2]] In [810]: sll=np.hstack(ll) # all values in a 1d array In [816]: res=np.empty((2,3)); res.fill(np.nan) # empty target

obtener índices aplanados donde van los valores. Este es el paso crucial. Aquí el uso de r_ es iterativo; la versión rápida probablemente usa cumsum

In [817]: idx=np.r_[0:3, 3:3+2] In [818]: idx Out[818]: array([0, 1, 2, 3, 4]) In [819]: res.flat[idx]=sll In [820]: res Out[820]: array([[ 1., 2., 3.], [ 1., 2., nan]])

================

entonces el enlace que falta es >np.arange() broadcasting

In [897]: lens=np.array([len(i) for i in ll]) In [898]: mask=lens[:,None]>np.arange(lens.max()) In [899]: mask Out[899]: array([[ True, True, True], [ True, True, False]], dtype=bool) In [900]: idx=np.where(mask.ravel()) In [901]: idx Out[901]: (array([0, 1, 2, 3, 4], dtype=int32),)


Tal vez algo como esto? No sé acerca de su hardware, pero significa a 16 ms para 100 bucles para l2 = [lista (rango (20)), lista (rango (30))] * 10000.

from numpy import nan def box(l): max_lenght = len(max(l, key=len)) return [x + [nan]*(max_lenght-len(x)) for x in l]