3
votes

Accédez aux éléments d'une matrice par une liste d'indices en Python pour appliquer un max (val, 0.5) à chaque valeur sans boucle for

Je sais comment accéder aux éléments d'un vecteur par des indices en faisant:

Traceback (most recent call last):
  File "main.py", line 43, in <module>
    print(currentGrid[indices])
IndexError: too many indices for array

ce qui donne la bonne réponse: [2 4 6]

Mais j'essaye de faites la même chose en utilisant une matrice 2D, quelque chose comme:

indices = list([(0,0),(1,1),(0,2)])

cela devrait afficher "[0.0 0.9]" pour la valeur en (0,0) et celle en (1,1) dans la matrice. Mais à la place, il affiche "[0,1 0,1]". Aussi si j'essaye d'utiliser 3 index avec:

currentGrid = numpy.array(  [[0,   0.1],
                             [0.9, 0.9],
                             [0.1, 0.1]])
indices = list([(0,0),(1,1)])
print(currentGrid[indices])

J'obtiens maintenant l'erreur suivante:

test = numpy.array([1,2,3,4,5,6])
indices = list([1,3,5])
print(test[indices])

J'ai finalement besoin pour appliquer une simple opération max () sur tous les éléments de ces indices et avoir besoin du moyen le plus rapide de le faire à des fins d'optimisation.

Qu'est-ce que je fais mal? Comment puis-je accéder à des éléments spécifiques d'une matrice pour effectuer des opérations sur eux de manière très efficace (sans utiliser la compréhension de liste ni une boucle).


1 commentaires

Aujourd'hui, j'ai accidentellement trouvé la solution à votre problème. Voir le Edit 4 dans ma réponse.


3 Réponses :


4
votes

Le problème est la disposition des indices que vous passez au tableau. Si votre tableau est bidimensionnel, vos indices doivent être deux listes, l'une contenant les indices verticaux et l'autre les index horizontaux. Par exemple:

idx_i, idx_j = zip(*[(0, 0), (1, 1), (0, 2)])
print currentGrid[idx_j, idx_i]
# [0.0, 0.9, 0.1]

Notez que le premier élément lors de l'indexation des tableaux est la dernière dimension, par exemple: (y, x). Je suppose que vous avez défini le vôtre comme (x, y) sinon vous obtiendrez une IndexError


2 commentaires

Merci pour votre réponse. Je comprends que l'utilisation de deux listes est la bonne façon d'accéder aux éléments par index. Mais est-il possible alors de faire une opération sur chacun de ces éléments sans utiliser une boucle for? J'essaye de trouver un moyen moins intensif en CPU vers un max (val, 0.5) sur tous ces éléments


Si vous ne vous souciez que des valeurs extraites, vous pouvez utiliser la fonction map ou une compréhension de liste. Les deux sont bien plus rapides qu'une boucle for , mais évitez les fonctions lambda + map ( voir ça ). Si vous voulez utiliser ces valeurs "à l'intérieur" du tableau, numpy peut avoir une fonction qui peut mapper une fonction à ces positions.



0
votes

Les index 2D doivent être accédés comme ceci:

print(currentGrid[indices[:,0], indices[:,1]])

Les index de ligne et les index de colonne doivent être passés séparément sous forme de listes.


1 commentaires

Merci, vous avez raison, cela fonctionne. Mais est-il possible de faire un max (valeur, 0,5) sur tous ces éléments sans utiliser une boucle for?



1
votes

Il existe déjà de bonnes réponses à votre problème. Voici juste une solution rapide et sale pour votre code particulier:

maxfunction = numpy.vectorize(lambda i: max(i,0.5))
print(maxfunction(currentGrid[newIndices]))

Edit:

Si vous ne voulez pas utiliser une boucle for, vous devez faire le suivant:

Supposons que vous ayez 3 valeurs de votre matrice 2D (avec les dimensions x1 et x2 auxquelles vous voulez accéder. Les valeurs ont le "coordonnées" (indices) V1 (x11 | x21), V2 (x12 | x22), V3 (x13 | x23) . Ensuite, pour chaque dimension de votre matrice (2 dans votre cas) vous avez besoin pour créer une liste avec les indices de cette dimension de vos points. Dans cet exemple, vous créeriez une liste avec les indices x1 : [x11, x12, x13] et une liste avec les indices x2 de vos points: [x21, x22, x23] . Ensuite, vous combinez ces listes et les utilisez comme index pour la matrice:

import pandas as pd
maxValues = pd.Series(currentGrid[newIndices]).apply(lambda x: max(x,0.5))

ou comment vous l'écrivez:

originalIndices = [(0,0),(1,1),(2,0)]

x1 = []
x2 = []

for i in originalIndices:
    x1.append(i[0])
    x2.append(i[1])

newIndices = [x1,x2]
print(currentGrid[newIndices])

Maintenant avec les points que vous avez utilisés ((0,0), ( 1,1), (2,0)) - veuillez noter que vous devez utiliser (2,0) au lieu de (0,2), car il serait hors de portée sinon:

maxValue = max(currentGrid[indices])

Cela vous donnera 0, 0.9, 0.1 . Et sur cette liste, vous pouvez ensuite appliquer la commande max () si vous le souhaitez (juste pour considérer toute votre question):

indices = list([(0,1,2),(0,1,0)])
print(currentGrid[indices])

Edit2:

Ici un exemple comment vous pouvez transformer votre liste d'index d'origine pour lui donner la forme correcte:

indices = list([(x11,x12,x13),(x21,x22,x23)])

Edit3:

Je ne sais pas si vous pouvez applique max (x, 0.5) à un tableau numpy en utilisant une boucle. Mais vous pouvez utiliser Pandas à la place. Vous pouvez convertir votre liste en une série pandas puis appliquer une fonction lambda:

indices = [[x11,x12,x13],[x21,x22,x23]]

Cela vous donnera un tableau pandas contenant 0.5,0.9,0.5 code >, que vous pouvez simplement renvoyer dans une liste maxValues ​​= list (maxValues) .

Juste une remarque: en arrière-plan, vous aurez toujours une sorte de boucle en cours, également avec cette commande. Je doute que vous obtiendrez de bien meilleures performances grâce à cela. Si vous voulez vraiment améliorer les performances, utilisez une boucle for, avec numba (il vous suffit d'ajouter un décorateur à votre fonction) et l'exécuter en parallèle. Ou vous pouvez utiliser la bibliothèque multiprocessing et la fonction Pool , voir ici . Juste pour vous donner de l'inspiration.

Edit4:

J'ai vu par hasard cette page aujourd'hui, qui permet de faire exactement ce que vous voulez avec Numpy . La solution (à considérer dans le vecteur newIndices de mon Edit2) à votre problème est:

for i in indices:
    print(currentGrid[i[0],i[1]])


2 commentaires

Merci pour votre réponse mais j'ai précisé que je ne souhaitais pas utiliser de boucle for


Oui, ça aide beaucoup! Merci beaucoup. Savez-vous s'il est possible d'appliquer le maximum individuellement sur chaque valeur? Je suppose que je n'ai pas été assez clair. Je veux faire quelque chose comme val = max (val, 0,5) pour chaque valeur à chaque index. Est-ce possible sans utiliser une boucle for pour des problèmes d'optimisation?