2010-11-04 14 views
14

J'ai une liste de vecteurs (en Python) que je veux normaliser, tout en supprimant les vecteurs qui avaient à l'origine de petites normes.Variable intermédiaire dans une liste de compréhension pour le filtrage et la transformation simultanés

La liste d'entrée est, par ex.

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

Et j'ai besoin que la sortie soit (x * n, y * n) avec n = (x * 2 + y * 2) ** - 0,5

Si je voulais juste la normes, par exemple, ce serait facile avec une compréhension de la liste:

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

Il serait aussi facile de stocker seulement normalisé x, aussi, par exemple, mais ce que je veux est d'avoir cette variable temporaire « n ", à utiliser dans deux calculs, et le jeter.

Je ne peux pas simplement utiliser une fonction lambda car j'ai aussi besoin du n pour filtrer la liste. Alors, quelle est la meilleure façon?

En ce moment je suis en utilisant cette compréhension de liste imbriquée ici (avec une expression dans la liste intérieure):

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 liste intérieure génère tuples avec une valeur supplémentaire (n), puis-je utiliser ces valeurs pour les calculs et le filtrage. Est-ce vraiment le meilleur moyen? Y a-t-il de terribles inefficacités dont je devrais être conscient?

Répondre

11
Is this really the best way? 

Eh bien, il ne fonctionne efficacement et si vous avez vraiment, vraiment envie d'écrire, alors il est Citations le meilleur que vous pouvez faire.

D'autre part, une simple fonction 4 de ligne ferait la même chose beaucoup plus claire:

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

BTW, il y a un bug dans votre code ou la description - vous dites que vous filtrez des vecteurs courts mais votre code fait le contraire: p

+0

Merci, qui semble agréable. Une fonction d'itérateur est vraiment mieux pour quelque chose de plus compliqué comme ça. – dividebyzero

+0

A propos de la sélection vectorielle, le n est en fait l'inverse de la norme, c'est ** - 0,5, et non ** 0,5. C'est pourquoi la multiplication par n au lieu d'une division.C'est parce que je prévois d'utiliser une fonction spécifique pour calculer approximativement la racine carrée réciproque, au lieu d'utiliser soit l'exponentiation, soit par ex. 1/(sqrt (x)). – dividebyzero

1

Ceci suggère que l'utilisation d'un forloop pourrait être le moyen le plus rapide. Assurez-vous de vérifier les résultats timeit sur votre propre machine, car ces résultats peuvent varier en fonction d'un certain nombre de facteurs (matériel, système d'exploitation, version Python, longueur 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] 

cède ces résultats de 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

Ne confondez pas "meilleur moyen" avec "moyen le plus rapide". Si la performance est le plus gros problème, il devrait probablement utiliser numpy de toute façon. –

+0

@Glenn the Numpy Way est certainement mieux, j'étais juste curieux de savoir comment résoudre cela en utilisant des listes de compréhension, itérateurs, etc ... – dividebyzero

1

Stealing le code de unutbu, voici un test plus incluant une version numpy et la version iterator. Notez que la conversion de la liste en numpy peut coûter du temps.

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) 

et le résultat ...

[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