numpy und Teilen durch Null

mit matplotlib, NumPy, pandas, SciPy, SymPy und weiteren mathematischen Programmbibliotheken.
Antworten
bords0
User
Beiträge: 234
Registriert: Mittwoch 4. Juli 2007, 20:40

Ich habe eine Menge von eindimensionalen arrays (Vektoren), die alle die Länge n + 1 haben. Könnte so etwas sein wie "zurückgelegter Weg", "Entfernung vom Endpunkt", "Zeit übrig", usw.

Die Länge ergibt sich aus der Erzeugung, der Speicherung und der Weiterverarbeitung dieser Vektoren. Manche dieser Vektoren haben als letzte Stelle immer 0 (alles andere ergibt inhaltlich keinen Sinn). Nun muss ich manchmal zwei dieser Vektoren dividieren, wobei beide Vektoren an der letzten Stelle eine 0 haben. Am sinnvollsten wäre es, wenn das Ergebnis an der letzten Stelle auch eine 0 hätte.

Gibt es da best practices oder so dafür? Ich habe folgende Varianten ausprobiert, wobei die erste ein RuntimeWarning ausspuckt, was nicht akzeptabel ist (kann man bestimmt irgendwie unterdrücken?):

Code: Alles auswählen

import numpy as np

n = 5
a = np.array([3, 3, 3, 2, 1, 0], dtype=float)
b = np.array([5, 4, 3, 2, 1, 0], dtype=float)

# 1. naiv
c = a / b
print(c)
# [0.6  0.75 1.   1.   1.    nan]
# RuntimeWarning: invalid value encountered in true_divide

# 2. nur die letzte Stelle nicht teilen, in zero-array hinein
c = np.zeros(n + 1)
c[:-1] = a[:-1] / b[:-1]
print(c)
# [0.6  0.75 1.   1.   1.   0.  ]

# 3. nur die letzte Stelle nicht teilen, Null anhängen
c = np.append(a[:-1] / b[:-1], 0.0)
print(c)
# [0.6  0.75 1.   1.   1.   0.  ]

# 4. Oder nie durch Nullen teilen, in zero-array hinein
c = np.divide(a, b, out=np.zeros(n + 1), where=b != 0.0)
print(c)
# [0.6  0.75 1.   1.   1.   0.  ]
(Bei der 1. Variante könnte/müsste man noch den letzten Wert durch 0 ersetzen, je nach Geschmack - da bin ich mir noch nicht sicher.)
%timeit liefert, dass die erste Variante die schnellste ist, es folgt die zweite, und die numpy-Funktionen sind am langsamsten.

Code: Alles auswählen

In [31]: %timeit a / b
<magic-timeit>:1: RuntimeWarning: invalid value encountered in true_divide
1.85 µs ± 231 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [32]: %%timeit
    ...: c = np.zeros(n + 1)
    ...: c[:-1] = a[:-1] / b[:-1]
3.02 µs ± 453 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [33]: %timeit c = np.append(a[:-1] / b[:-1], 0.0)
6.68 µs ± 304 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [34]: %timeit c = np.divide(a, b, out=np.zeros(n + 1), where=b != 0)
4.29 µs ± 230 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
Ich tendiere zur 2. Variante, aber die erste Variante mit Unterdrückung der Fehlermeldung an genau dieser Stelle könnte ich mir auch vorstellen. Aber vielleicht ist es ganz anders noch besser?
tonikae
User
Beiträge: 90
Registriert: Sonntag 23. Februar 2020, 10:27

Eine Divsion durch Null ist mathematisch grundsätzlich nicht möglich
Das gibt immer und überall eine Fehlermeldung
Das übliche Standardverfahren(wenn man weiss dass die Null als Divisor auftreten kann),
ist vor der Division prüfen ob der Divisor Null ist. und die Divsion dann zu unterlassen.
rogerb
User
Beiträge: 878
Registriert: Dienstag 26. November 2019, 23:24

@bords0,
Manche dieser Vektoren haben als letzte Stelle immer 0 (alles andere ergibt inhaltlich keinen Sinn)
Ich bin mir nicht sicher ob ich dich richtig verstehe.
Wenn die letzte Zahl immer eine '0' ist, warum sollte sie dann überhaupt dort "mitgeschleppt" werden? Kann es sein, dass das irgend ein "Workaround" für etwas ist, was schon vorher nicht ordentlich behandelt wurde?

Wenn es um "best practices" geht, scheint mir der erste Ansatz zu sein, dass man sich überlegt, warum es überhaupt zu dieser Inkonsistenz kommt.
Ist die Bedeutung der letzten "0" eine andere als die übrigen Zahlen?

Ansonsten, wenn es sich nicht vermeiden lässt, währe wohl die 4. Variante die Standardvariante, da sie "0" an jeder beliebigen Stelle behandeln kann. Das währe also der allgemeingültige Ansatz.
Da du aber schon weißt, dass die "0" immer an der letzten Stelle steht, kannst du das eben schneller durch eine der ersten Varianten erreichen.
bords0
User
Beiträge: 234
Registriert: Mittwoch 4. Juli 2007, 20:40

rogerb hat geschrieben: Sonntag 25. Juli 2021, 12:46
Manche dieser Vektoren haben als letzte Stelle immer 0 (alles andere ergibt inhaltlich keinen Sinn)
Ich bin mir nicht sicher ob ich dich richtig verstehe.
Wenn die letzte Zahl immer eine '0' ist, warum sollte sie dann überhaupt dort "mitgeschleppt" werden? Kann es sein, dass das irgend ein "Workaround" für etwas ist, was schon vorher nicht ordentlich behandelt wurde?
Ich betrachte n + 1 Zeitpunkte, und so etwas wie "restliche Zeit" und "restlicher Weg" haben dann halt eine 0 am Schluss. Das ist ein sinnvoller Wert.
Wenn es um "best practices" geht, scheint mir der erste Ansatz zu sein, dass man sich überlegt, warum es überhaupt zu dieser Inkonsistenz kommt.
Ist die Bedeutung der letzten "0" eine andere als die übrigen Zahlen?
Keine Inkonsistenz, gleiche Bedeutung. Nur der Quotient hat nicht wirklich eine Bedeutung, am sinnvollsten ist "0".
Ansonsten, wenn es sich nicht vermeiden lässt, währe wohl die 4. Variante die Standardvariante, da sie "0" an jeder beliebigen Stelle behandeln kann. Das währe also der allgemeingültige Ansatz.
Da du aber schon weißt, dass die "0" immer an der letzten Stelle steht, kannst du das eben schneller durch eine der ersten Varianten erreichen.
Bei der 4. Variante ist eher nicht schön, dass sie auch Nullen an anderen Stellen behandelt. Da sollten eigentlich keine auftreten. Deshalb funktioniert sie zwar identisch zu den anderen Varianten, aber wenn woanders doch mal eine 0 auftauchen sollte ist das ein Fehler, und das würde ich gerne mitbekommen - eine RuntimeWarning wäre dann eigentlich genau das, was ich mir wünschen würde.

Irgendeinen Kompromiss werde ich wohl machen müssen ... :-(
Antworten