Messwert in Array finden

mit matplotlib, NumPy, pandas, SciPy, SymPy und weiteren mathematischen Programmbibliotheken.
Antworten
sanjo386
User
Beiträge: 7
Registriert: Samstag 4. März 2017, 11:47

Hallo zusammen,

ich möchte gerne einen Messwert in einem Array finden. Die Herausforderung daran ist, dass es Messwerte sind inkl. der natürlichen Streuung und es soll der nächste Wert gefunden werden. Angenommen ich suche den Messwert 12 und das Array sieht [1.5, 2.8, 5.0, 11.9, 12.2, 14.9] aus, dann soll das Ergebnis 11.9 sein. Man könnte abfragen, ob der Messwert kleiner oder größer als der gesuchte Wert ist und sich so herantasten (Newton), aber das erscheint mir etwas aufwendig, zumal hier mehrdimensionale Arrays Messwerte unterschiedlicher Einheiten und Streuungen enthalten. Man müsste also zusätzlich noch die Genauigkeit in Bezug zur Standardabweichung berücksichtigen. Alternativ könnte man von jedem Messwert die Differenz zum gesuchten Wert bilden und die betragsmäßig kleinste Differenz entspricht dem nächsten Messwert, was mir als sehr rechenlastig erscheint.

Was denkt ihr, gibt es vielleicht weitere Möglichkeiten?

Vielen Dank vorab und Gruß
Sanjo386
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wenn die Sortierung immer gegeben ist, kannst du auch mit dem bisect-Modul arbeiten.
Siegfried
User
Beiträge: 24
Registriert: Sonntag 30. April 2017, 14:04

Alternativ mit numpy

[codebox=pys60 file=Unbenannt.txt]
import numpy as np
werte = [1.5, 2.8, 5.0, 11.9, 12.2, 14.9]
suchwert = 12
index=abs(np.array(werte)-suchwert).argmin()
print("Das Listenelement mit der geringsten Abweichung ist:",werte[index],"Listenindex:",index)

[/code]

Funktioniert auch mit mehrdimensionalen Arrays; Voraussetzung ist, daß alle Subarrays einer Dimension gleich groß sind. Beispiel:
[codebox=pys60 file=Unbenannt.txt]
werte = [[1.5, 2.8, 5.0, 11.9, 12.2, 14.9],[8, 9, 10, 11, 12.02, 13]]
[/code]
Siegfried
User
Beiträge: 24
Registriert: Sonntag 30. April 2017, 14:04

@Siegfried:
index ist zwar ein sprechender Name, kann aber zu Konflikten nit der eingebauten index Methode führen. Besser wäre ein Name wir 'ind','idx', 'suchindex' , 'indexx',...
Für mehrdimensionale Arrays funktioniert es so nicht. So kann nur der Listenindex ausgegeben werden. Wenn man auch den Wert haben will, sollte man das Array von vornherein als 'np.'array' definieren

korrigierte Version
[codebox=pys60 file=Unbenannt.txt]
import numpy as np
werte = np.array([[1.5, 2.8, 5.0, 11.9, 12.2, 14.9],[8, 9, 10, 11, 12.02, 13]])
suchwert=12
indexx=abs(werte-suchwert).argmin()
print("Das Listenelement mit der geringsten Abweichung ist:",werte.flatten()[indexx],"Listenindex:",indexx)
[/code]
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

Die Methode `list.index` ist in einem anderen Namensraum, so dass eine lokale Variable `index` überhaupt kein Problem darstellt. Was dagegen ein Problem ist, sind Namen, die aus kryptischen Abkürzungen bestehen, wie `ind`, `idx`, oder sinnlose postfixe enthalten, wie z.B. `indexx`, wo niemand weiß, wofür das `x` steht, oder etwas suggerieren, was gar nicht der Fall ist, wie z.B. `suchindex`, der gar nicht zum Suchen da ist, sondern schon ein `gefundenindex` ist. Am besten nennt man den Index nach dem Messwert, den man gesucht hat, als `index_druck`, `index_temperatur`, ...

Wenn man ein 2d-Array nur dazu hat, um es dann flachzuklopfen, hat man nicht wirklich ein 2d-Array.
Benutzeravatar
snafu
User
Beiträge: 6732
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

sanjo386 hat geschrieben:Angenommen ich suche den Messwert 12 und das Array sieht [1.5, 2.8, 5.0, 11.9, 12.2, 14.9] aus, dann soll das Ergebnis 11.9 sein.
Und dann soll anschließend nicht mehr die 12 verwendet werden, sondern die 11.9, habe ich das richtig verstanden? Man kann also nicht in einem Rutsch mit der 12 das 2d-Array durchlaufen, sondern muss fortwährend den Näherungswert anpassen?
Siegfried
User
Beiträge: 24
Registriert: Sonntag 30. April 2017, 14:04

Über sinnvolle Namensgebung kann man lange streiten. Ich halte `ind` oder `idx` nicht für besonders kryptische Abkürzungen, meine aber, daß `gefundenindex` etwas suggeriert, was gar nicht der Fall ist, da ein Wert gesucht wird, der bei den Meßwerten gar vorhanden ist, ergo auch nicht gefunden werden kann. Ähnlich gering scheint mir der Zusatznutzen von `index_druck` oder `index_temperatur`, da sich die Meßwerte wahrscheinlich alle auf Temperatur, Druck o.ä. beziehen, d.h. der eine gesuchte Meßwert hat genauso einen `index_druck` wie alle anderen Meßwerte auch.

Gemäß Fragestellung sind mehrdimensionale Arrays gegeben. Es kann also keine Rede davon sein, daß sie nur erzeugt werden, um sie anschließend flach zu klopfen.
Benutzeravatar
snafu
User
Beiträge: 6732
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Hier mal der gezeigte Ansatz angewendet auf dem 2d-Array:

Code: Alles auswählen

import numpy as np

def find_nearest_2d(values, needle):
    indices = abs(values - needle).argmin(axis=1)
    return np.array(
        [values[i, j] for i, j in enumerate(indices)]
    )

def main():
    values = [[1.5, 2.8, 5.0, 11.9, 12.2, 14.9],[8, 9, 10, 11, 12.02, 13]]
    print(find_nearest_2d(np.array(values), 12))

if __name__ == '__main__':
    main()
Vielleicht kann man sich den Umweg über die LC sparen, aber dazu fällt mir kein Numpy-Boardmittel ein...
Siegfried
User
Beiträge: 24
Registriert: Sonntag 30. April 2017, 14:04

@snafu: Vielleicht habe ich die Frage falsch verstanden.Ich dachte, es ginge um ein Array (ein- oder mehrdimensional), aus dem ein Wert extrahiert werden soll, der einem Suchwert am nächsten ist. Egal; sollte irgend jemand (wenn auch vielleicht der Fragesteller nicht) dieses Problem haben, könnte man analog zum Tutorial (https://docs.scipy.org/doc/numpy-dev/us ... ssing.html) auch eine `index`-Methode einbauen. Das Problem bei `argmin` ist, das es einen "flach geklopften" Index zurück gibt. Die `index` Methode paßt ihn an die Anzahl der Dimensionen an, d.h. sie funktioniert bei 1,2, ... dimensionalen Arrays.

[codebox=pys60 file=Unbenannt.txt]
import numpy as np

class ndarray_mit_index_funktion(np.ndarray):
def __new__(self, input_array, dtype=float, buffer=None, offset=0, strides=None, order=None):
return np.asarray(input_array, dtype, order).view(self)

def index(self, suchwert):
"""Sucht das erste Vorkommen eines Suchwertes und gibt den Index zurueck
Sollte der Wert nicht im Array vorhanden sein, wird der Index des Wertes
zurueckgegeben, der dem Suchwert am naechsten ist. Funktioniert nicht
fuer Zeichenketten"""
idx=abs(self-suchwert).argmin()
ergebnis=[]
for i in reversed(self.shape):
ergebnis.append(idx % i)
idx = idx // i
return tuple(reversed(ergebnis))

def main():
werte = ndarray_mit_index_funktion\
([[1.5, 2.8, 5.0, 11.9, 12.2, 14.9],[8, 9, 10, 11, 12.02, 13]])
suchindex = werte.index(12)
print("Das Listenelement mit der geringsten Abweichung ist:",werte[suchindex],"Listenindex:",suchindex)

if __name__ == '__main__':
main()

[/code]

Das Problem ist, daß wir nunmehr Funktionen `find_nearest_2D` bzw Methoden `index` haben, unsere Programme immer mehr Zeilen umfassen und dadurch für den Fragesteller der eigentliche Kern (hier Zeile 12) verloren gehen könnte.
BlackJack

@Siegfried: Eine Klasse abzuleiten macht hier nicht wirklich Sinn. Eine Funktion ist *wesentlich* einfacher und ich sehe keinen Vorteil von einer Klasse.

Für die Umrechnung des flachen Index gibt es schon eine fertige Funktion in Numpy.

Den '\' am Zeilenende hättest Du Dir sparen können wenn Du die Klammer nicht auf die nächste Zeile verschoben hättest. Solange es noch ”offene” Klammern gibt, weiss der Compiler auch so dass der Ausdruck noch nicht zuende ist.

Als Funktion ist das ein Einzeiler:

Code: Alles auswählen

import numpy as np

 
def find_nearest_index(values, needle):
    """Sucht das erste Vorkommen eines Suchwertes und gibt den Index zurueck.

    Sollte der Wert nicht im Array vorhanden sein, wird der Index des Wertes
    zurueckgegeben, der dem Suchwert am naechsten ist. Funktioniert nicht
    fuer Zeichenketten.
    """
    return np.unravel_index(abs(values - needle).argmin(), values.shape)

 
def main():
    werte = np.array(
        [[1.5, 2.8, 5.0, 11.9, 12.2, 14.9], [8, 9, 10, 11, 12.02, 13]]
    )
    index = find_nearest_index(werte, 12)
    print(
        'Das Listenelement mit der geringsten Abweichung ist:',
        werte[index],
        'Listenindex:',
        index,
    )


if __name__ == '__main__':
    main()
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

@snafu: das Zeilenweise finden des nächsten Wertes geht auch kurz:

Code: Alles auswählen

indices = abs(values - needle).argmin(axis=1)
nearest = values[range(values.shape[0]), indices]
Siegfried
User
Beiträge: 24
Registriert: Sonntag 30. April 2017, 14:04

@BlackJack: Sehr kompakte und elegante Lösung. Und schon wieder habe ich etwas dazu gelernt. `unravel_index` wird nicht mehr vergessen. Zum Glück hat meine mühsame manuelle Lösung wenigstens die gleichen Ergebnisse gebracht.
Siegfried
User
Beiträge: 24
Registriert: Sonntag 30. April 2017, 14:04

@snafu: Die zeilenweise Suche wird auch im NumPy Tutorial behandelt (https://docs.scipy.org/doc/numpy-dev/us ... of-indices). In diesem offiziellen Tutorial wird übrigens problematischer Weise ein Index in einer Variablen mit der krytischen Abkürzung `ind` abgespeichert.
BlackJack

@Siegfried: Für Quelltexte gelten andere Richtlinien als für interaktive Python-Shell-Sitzungen die dazu auch noch innerhalb eines Tutorials stehen, wo alles ausführlich erklärt wird und kaum ein Name/Wert ausserhalb des jeweiligen, kleinen Ausschnitts der Python-Shell-Sitzung hinaus weiterverwendet wird. Zudem ist das eine Bibliothek für ein Spezialgebiet in dem die Leute an schlechte Namen gewöhnt sind. Was die Namen nicht besser macht. :-)
sanjo386
User
Beiträge: 7
Registriert: Samstag 4. März 2017, 11:47

Diese Zeile

Code: Alles auswählen

4.index=abs(np.array(werte)-suchwert).argmin()
hat mir bereits geholfen. Darüber hinaus waren die weiteren Antworten auch hilfreich!

Gruß
Sanjo
Antworten