2013-04-12 11 views
24

Ich setze die Werte mehrerer Elemente in einem 2D-Array, jedoch enthalten meine Daten manchmal mehrere Werte für einen bestimmten Index.Umgang mit doppelten Indizes in NumPy-Zuweisungen

Es scheint, dass der "spätere" Wert immer zugewiesen wird (siehe Beispiele unten), aber ist dieses Verhalten garantiert oder gibt es eine Chance, dass ich inkonsistente Ergebnisse bekomme? Woher weiß ich, dass ich "später" so interpretieren kann, wie ich es in einem vektorisierten Auftrag möchte?

d. H. In meinem ersten Beispiel wird a definitiv immer 4 enthalten und im zweiten Beispiel würde es jemals drucken values[0]?

Sehr einfaches Beispiel:

import numpy as np 
indices = np.zeros(5,dtype=np.int) 
a[indices] = np.arange(5) 
a # array([4]) 

Ein weiteres Beispiel

import numpy as np 

grid = np.zeros((1000, 800)) 

# generate indices and values 
xs = np.random.randint(0, grid.shape[0], 100) 
ys = np.random.randint(0, grid.shape[1], 100) 
values = np.random.rand(100) 

# make sure we have a duplicate index 
print values[0], values[5] 
xs[0] = xs[5] 
ys[0] = ys[5] 

grid[xs, ys] = values 

print "output value is", grid[xs[0], ys[0]] 
# always prints value of values[5] 
+1

Um zu verstehen, was in den Arbeiten von Numpy Arrays passiert, empfehle ich http://scipy-lectures.github.io/advanced/advanced_numpy/ – tom10

+4

Schöne Frage ... Dies ist einer von denen Sie wahrscheinlich warten müssen @seberg um eine sinnvolle Antwort zu bekommen. – Jaime

+3

Ich bezweifle, dass alles garantiert ist, aber einige Experimente mit exotischen Arrays zeigen auf eine einfache Links-Rechts-Schleife über dem Array von Indizes. –

Antwort

12

In NumPy 1.9 und später, dies im Allgemeinen nicht genau definiert sein.

Die aktuelle Implementierung iteriert über alle (übertragenen) phantastischen Indizes (und das Zuordnungsarray) gleichzeitig mit separaten Iteratoren, und diese Iteratoren verwenden alle C-Reihenfolge. Mit anderen Worten, derzeit, ja, du kannst. Da möchtest du es vielleicht genauer wissen. Wenn Sie mapping.c in NumPy vergleichen, die diese Dinge behandelt, werden Sie sehen, dass es PyArray_ITER_NEXT verwendet, das documented in C-Reihenfolge ist.

Für die Zukunft würde ich das Bild anders malen. Ich denke, es wäre gut, alle Indizes + das Zuordnungsarray unter Verwendung des neueren Iterators zu iterieren. Wenn dies getan wird, könnte die Reihenfolge offen bleiben, damit der Iterator den schnellsten Weg wählt. Wenn Sie es für den Iterator offen halten, ist es schwer zu sagen, was passieren würde, aber Sie können nicht sicher sein, dass Ihr Beispiel funktioniert (wahrscheinlich die 1-d-Fall immer noch, aber ...).

Also, soweit ich das beurteilen kann, funktioniert es derzeit, aber es ist undokumentiert (soweit ich weiß), also wenn Sie wirklich denken, dass dies gewährleistet sein sollte, müssten Sie dafür Lobbyarbeit leisten und am besten einige Tests schreiben stellen Sie sicher, dass es garantiert werden kann. Denn zumindest bin ich versucht zu sagen: Wenn es die Dinge schneller macht, gibt es keinen Grund, C-Auftrag zu sichern, aber natürlich gibt es irgendwo einen guten Grund ...

Die eigentliche Frage ist: Warum? willst du das überhaupt? ;)

+0

Vielen Dank für die Antwort. Ich werde eine Begründung aufwerfen, warum ich frage, warum dies in den nächsten Tagen aufkam. Ich halte mich nicht für kompetent genug, um zu sagen, ob es einen wirklich guten Grund gibt, C-Order zu behalten ... – YXD

+0

Ich sah [diese Diskussion] (http://mail.scipy.org/pipermail/numpy-discussion/ 2014-Februar/068810.html über numpy 1.9 und fragte sich, was die Implikationen für den 2D-Fall in dieser Frage waren. Die Motivation kommt von einem Sehproblem, bei dem ich einige 3D-Daten in diskrete Pixelkoordinaten projiziere und effizient die "besten" Daten pro Pixel verfolgen muss, wobei aufgrund der Projektion Kollisionen im Bildraum auftreten können. Mein realer Code erreicht dies, indem zuerst die Daten nach den Kosten/Fehlern sortiert und dann wie in der Frage gezeigt zugewiesen werden. – YXD

5

Ich beantworte Sie nicht direkt Frage, ich würde nur gerne darauf hinweisen, dass auch wenn Sie auf dieses Verhalten konsistent verlassen können, Sie besser nicht.

Bedenken Sie:

a = np.zeros(4) 
x = np.arange(4) 
indices = np.zeros(4,dtype=np.int) 
a[indices] += x 

An diesem Punkt ist es vernünftig anzunehmen, dass a.sum()a ‚s x.sum() + vorherige Summe ist?

assert a.sum() == x.sum() 
--> AssertionError 

a 
= array([ 3., 0., 0., 0.]) 

In Ihrem Fall, wenn auf ein Array mit doppelten Indizes zuweisen, ist das Ergebnis intuitiv: Zuordnung zu dem gleichen Index nimmt mehrere Male Ort, also nur die letzte Aufgabe „sticks“ (es vorherigen überschreibt).

Dies ist jedoch in diesem Beispiel nicht der Fall. Es ist nicht mehr intuitiv.Wenn dies der Fall wäre, hätte die direkte Addition mehrere Male stattgefunden, da die Addition in ihrer Art kumulativ ist.

Also, anders gesagt, riskieren sie in diese Falle erwischt:

  • Sie mit doppelten Indizes arbeiten beginnen
  • Sie sehen alles in Ordnung ist, das Verhalten ist genau so, wie Sie
  • Sie erwarten Hören Sie auf, auf die entscheidende Tatsache zu achten, dass Ihre Operationen doppelte Indizes beinhalten. Es macht schließlich keinen Unterschied, oder?
  • Sie starten die Verwendung der gleichen Indizes in verschiedenen Kontexten, z. wie oben
  • tiefes Bedauern darüber :)

So zitierte @seberg:

Die eigentliche Frage hier ist: Warum wollen Sie das überhaupt? ;)

+1

Das ist definitiv ein interessanter Fall. Und ja, Ihre Stichpunkte sind genau der Grund, warum ich die Frage gestellt habe. In meinen ursprünglichen Beispielen scheint es jedoch offensichtlicher zu sein, was "passieren" sollte. Ich werde in den nächsten Tagen etwas Hintergrund/Kontext veröffentlichen. – YXD

6

Ich weiß, das zufriedenstellend beantwortet wurde, aber ich wollte erwähnen, dass es als der „letzte Wert“ dokumentiert ist (vielleicht informell) im Tentative Numpy Tutorial unter Indexing with Arrays of Indices:

wenn jedoch die Liste der Indizes Wiederholungen enthält, wird die Zuordnung mehrmals getan, hinter dem letzten Wert verlassen:

>>> a = arange(5) 
>>> a[[0,0,2]]=[1,2,3] 
>>> a 
array([2, 1, 3, 3, 4]) 

Dies ist vernünftig genug, aber achten Sie, wenn Sie die + Python wollen = Konstrukt verwenden, da es nicht kann tun, was Sie erwarten:

>>> a = arange(5) 
>>> a[[0,0,2]]+=1 
>>> a 
array([1, 1, 3, 3, 4]) 

Sogar 0 obwohl zweimal in der Liste der Indizes auf, die 0-te Element wird nur einmal erhöht. Dies liegt daran, dass Python a+=1 als a=a+1 entspricht.

2

fand ich einen Weg, um mit mit numpy diesen Vorgang zu tun ist dies eindeutig nicht optimal, aber sein schneller als Looping (mit einem Python for-Schleife)

mit: numpy.bincount

size = 5 
a = np.arange(size) 
index = [0,0,2] 
values = [1,2,3] 
a[index] += values 
a 
[2 1 5 3 4] 

Hexe ist nicht korrekt aber:

size = 5 
a = np.arange(size) 
index = [0,0,2] 
values = [1,2,3] 
result = np.bincount(index, values, size) 
a += result 
a 
[3 1 5 3 4] 

was ist gut!

+2

Vielleicht ist dieser Ansatz besser: http://StackOverflow.com/A/24100418/1758727 – czxttkl