2010-11-04 6 views
14

Tengo una lista de vectores (en Python) que quiero normalizar, mientras que al mismo tiempo elimino los vectores que originalmente tenían pequeñas normas.Variable intermedia en una lista de comprensión para filtrado y transformación simultáneos

La lista de entrada es, p.

a = [(1,1),(1,2),(2,2),(3,4)] 

y necesito que la salida sea (x * n, y * n) con n = (x * 2 + y * 2) ** - 0,5

Si sólo necesitaba la normas, por ejemplo, que iba a ser fácil con una lista por comprensión:

an = [ (x**2+y**2)**0.5 for x,y in a ] 

también sería fácil de almacenar sólo normalizados x, también, por ejemplo, pero lo que quiero es tener esta variable temporal "n ", para utilizar en dos cálculos, y tirarlo a la basura.

No puedo usar simplemente una función lambda porque también necesito la n para filtrar la lista. Entonces, ¿cuál es la mejor manera?

En este momento estoy usando esta lista por comprensión anidado aquí (con una expresión en la lista interna):

a = [(1,1),(1,2),(2,2),(3,4)] 

[(x*n,y*n) for (n,x,y) in (((x**2.+y**2.)**-0.5 ,x,y) for x,y in a) if n < 0.4] 

# Out[14]: 
# [(0.70710678118654757, 0.70710678118654757), 
# (0.60000000000000009, 0.80000000000000004)] 

La lista interna genera tuplas con un valor extra (n), y luego utilizar estos valores para los cálculos y el filtrado ¿Es esta realmente la mejor manera? ¿Hay alguna ineficiencia terrible de la que deba tener conocimiento?

Respuesta

11
Is this really the best way? 

Bueno, funciona de manera eficiente y si realmente, realmente desea escribir oneliners entonces es lo mejor que puede hacer.

Por otra parte, una simple función de 4 líneas haría lo mismo mucho más claro:

def normfilter(vecs, min_norm): 
    for x,y in vecs: 
     n = (x**2.+y**2.)**-0.5 
     if min_norm < n: 
      yield (x*n,y*n) 

normalized = list(normfilter(vectors, 0.4)) 

Por cierto, hay un error en el código o descripción - se dice a filtrar los vectores cortos, pero su código hace lo contrario: p

+0

Gracias, que se ve bien. Una función de iterador es realmente mejor para algo más complicado como este. – dividebyzero

+0

Sobre la selección del vector, el n es realmente el recíproco de la norma, es ** - 0.5, y no ** 0.5. Es por eso que la multiplicación por n en lugar de una división.Esto se debe a que planeo usar una función específica para calcular la raíz cuadrada recíproca aproximadamente, en lugar de usar una exponenciación o, por ejemplo, 1/(sqrt (x)). – dividebyzero

1

Esto sugiere que usar un forloop podría ser la manera más rápida. Asegúrese de verificar los resultados de tiempo en su propia máquina, ya que estos resultados pueden variar dependiendo de una serie de factores (hardware, sistema operativo, versión de Python, longitud de a, etc.).

a = [(1,1),(1,2),(2,2),(3,4)] 

def two_lcs(a): 
    an = [ ((x**2+y**2)**0.5, x,y) for x,y in a ] 
    an = [ (x*n,y*n) for n,x,y in an if n < 0.4 ] 
    return an 

def using_forloop(a): 
    result=[] 
    for x,y in a: 
     n=(x**2+y**2)**0.5 
     if n<0.4: 
      result.append((x*n,y*n)) 
    return result 

def using_lc(a):  
    return [(x*n,y*n) 
      for (n,x,y) in (((x**2.+y**2.)**-0.5 ,x,y) for x,y in a) if n < 0.4] 

produce estos resultados TimeIt:

% python -mtimeit -s'import test' 'test.using_forloop(test.a)' 
100000 loops, best of 3: 3.29 usec per loop 
% python -mtimeit -s'import test' 'test.two_lcs(test.a)' 
100000 loops, best of 3: 4.52 usec per loop 
% python -mtimeit -s'import test' 'test.using_lc(test.a)' 
100000 loops, best of 3: 6.97 usec per loop 
+0

No combine la "mejor manera" con la "manera más rápida". Si el rendimiento es el mayor problema, probablemente debería estar usando numpy de todos modos. –

+0

@Glenn the Numpy Way es definitivamente mejor, solo tenía curiosidad sobre cómo resolver eso usando listas de comprensión, iteradores, etc. – dividebyzero

1

el robo de los códigos de unutbu, aquí es una prueba más grande que incluye una versión y la versión numpy iterador. Tenga en cuenta que convertir la lista en numpy puede costar algo de tiempo.

import numpy 

# a = [(1,1),(1,2),(2,2),(3,4)] 
a=[] 
for k in range(1,10): 
    for j in range(1,10): 
     a.append((float(k),float(j))) 

npa = numpy.array(a) 

def two_lcs(a): 
    an = [ ((x**2+y**2)**-0.5, x,y) for x,y in a ] 
    an = [ (x*n,y*n) for n,x,y in an if n < 5.0 ] 
    return an 

def using_iterator(a): 
    def normfilter(vecs, min_norm): 
     for x,y in vecs: 
      n = (x**2.+y**2.)**-0.5 
      if n < min_norm: 
       yield (x*n,y*n) 

    return list(normfilter(a, 5.0)) 

def using_forloop(a): 
    result=[] 
    for x,y in a: 
     n=(x**2+y**2)**-0.5 
     if n<5.0: 
      result.append((x*n,y*n)) 
    return result 

def using_lc(a):  
    return [(x*n,y*n) 
      for (n,x,y) in (((x**2.+y**2.)**-0.5 ,x,y) for x,y in a) if n < 5.0] 


def using_numpy(npa): 
    n = (npa[:,0]**2+npa[:,1]**2)**-0.5 
    where = n<5.0 
    npa = npa[where] 
    n = n[where] 
    npa[:,0]=npa[:,0]*n 
    npa[:,1]=npa[:,1]*n 
    return(npa) 

y el resultado ...

[email protected]:~$ python -mtimeit -s'import test' 'test.two_lcs(test.a)' 
10000 loops, best of 3: 65.8 usec per loop 
[email protected]:~$ python -mtimeit -s'import test' 'test.using_lc(test.a)' 
10000 loops, best of 3: 65.6 usec per loop 
[email protected]:~$ python -mtimeit -s'import test' 'test.using_forloop(test.a)' 
10000 loops, best of 3: 64.1 usec per loop 
[email protected]:~$ python -mtimeit -s'import test' 'test.using_iterator(test.a)' 
10000 loops, best of 3: 59.6 usec per loop 
[email protected]:~$ python -mtimeit -s'import test' 'test.using_numpy(test.npa)' 
10000 loops, best of 3: 48.7 usec per loop 
Cuestiones relacionadas