Gibt es nach dieser Frage vor Jahren eine kanonische "Shift" -Funktion in Numpy? Ich sehe nichts aus der Dokumentation .
Hier ist eine einfache Version von dem, wonach ich suche:
def shift(xs, n):
if n >= 0:
return np.r_[np.full(n, np.nan), xs[:-n]]
else:
return np.r_[xs[-n:], np.full(-n, np.nan)]
Dies zu verwenden ist wie folgt:
In [76]: xs
Out[76]: array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])
In [77]: shift(xs, 3)
Out[77]: array([ nan, nan, nan, 0., 1., 2., 3., 4., 5., 6.])
In [78]: shift(xs, -3)
Out[78]: array([ 3., 4., 5., 6., 7., 8., 9., nan, nan, nan])
Diese Frage kam von meinem Versuch, gestern ein schnell rollendes Produkt zu schreiben . Ich brauchte einen Weg, um ein kumulatives Produkt zu "verschieben", und alles, was ich mir vorstellen konnte, war, die Logik zu replizieren np.roll()
.
Ist np.concatenate()
also viel schneller als np.r_[]
. Diese Version der Funktion bietet eine viel bessere Leistung:
def shift(xs, n):
if n >= 0:
return np.concatenate((np.full(n, np.nan), xs[:-n]))
else:
return np.concatenate((xs[-n:], np.full(-n, np.nan)))
Eine noch schnellere Version weist das Array einfach vorab zu:
def shift(xs, n):
e = np.empty_like(xs)
if n >= 0:
e[:n] = np.nan
e[n:] = xs[:-n]
else:
e[n:] = np.nan
e[:n] = xs[-n:]
return e
np.r_[np.full(n, np.nan), xs[:-n]]
ich es durch einenp.r_[[np.nan]*n, xs[:-n]]
andere Bedingung ersetzen könnte , ohne die Notwendigkeit vonnp.full
[np.nan]*n
ist eine einfache Python und wird daher langsamer sein alsnp.full(n, np.nan)
. Nicht für kleinen
, aber es wird von np.r_ in ein numpy-Array umgewandelt, was den Vorteil zunichte macht.[np.nan]*n
ist schneller alsnp.full(n, np.nan)
fürn=[10,1000,10000]
. Sie müssen überprüfen, obnp.r_
ein Treffer erzielt wird.Antworten:
Nicht numpy, sondern scipy bietet genau die gewünschte Shift-Funktionalität.
import numpy as np from scipy.ndimage.interpolation import shift xs = np.array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9.]) shift(xs, 3, cval=np.NaN)
Wenn standardmäßig ein konstanter Wert von außerhalb des Arrays mit Wert eingegeben wird
cval
, setzen Sie hier aufnan
. Dies ergibt die gewünschte Ausgabe,array([ nan, nan, nan, 0., 1., 2., 3., 4., 5., 6.])
und die negative Verschiebung funktioniert ähnlich,
shift(xs, -3, cval=np.NaN)
Bietet Ausgabe
array([ 3., 4., 5., 6., 7., 8., 9., nan, nan, nan])
quelle
Für diejenigen, die nur die schnellste Implementierung von Shift kopieren und einfügen möchten, gibt es einen Benchmark und eine Schlussfolgerung (siehe Ende). Außerdem führe ich den Parameter fill_value ein und behebe einige Fehler.
Benchmark
import numpy as np import timeit # enhanced from IronManMark20 version def shift1(arr, num, fill_value=np.nan): arr = np.roll(arr,num) if num < 0: arr[num:] = fill_value elif num > 0: arr[:num] = fill_value return arr # use np.roll and np.put by IronManMark20 def shift2(arr,num): arr=np.roll(arr,num) if num<0: np.put(arr,range(len(arr)+num,len(arr)),np.nan) elif num > 0: np.put(arr,range(num),np.nan) return arr # use np.pad and slice by me. def shift3(arr, num, fill_value=np.nan): l = len(arr) if num < 0: arr = np.pad(arr, (0, abs(num)), mode='constant', constant_values=(fill_value,))[:-num] elif num > 0: arr = np.pad(arr, (num, 0), mode='constant', constant_values=(fill_value,))[:-num] return arr # use np.concatenate and np.full by chrisaycock def shift4(arr, num, fill_value=np.nan): if num >= 0: return np.concatenate((np.full(num, fill_value), arr[:-num])) else: return np.concatenate((arr[-num:], np.full(-num, fill_value))) # preallocate empty array and assign slice by chrisaycock def shift5(arr, num, fill_value=np.nan): result = np.empty_like(arr) if num > 0: result[:num] = fill_value result[num:] = arr[:-num] elif num < 0: result[num:] = fill_value result[:num] = arr[-num:] else: result[:] = arr return result arr = np.arange(2000).astype(float) def benchmark_shift1(): shift1(arr, 3) def benchmark_shift2(): shift2(arr, 3) def benchmark_shift3(): shift3(arr, 3) def benchmark_shift4(): shift4(arr, 3) def benchmark_shift5(): shift5(arr, 3) benchmark_set = ['benchmark_shift1', 'benchmark_shift2', 'benchmark_shift3', 'benchmark_shift4', 'benchmark_shift5'] for x in benchmark_set: number = 10000 t = timeit.timeit('%s()' % x, 'from __main__ import %s' % x, number=number) print '%s time: %f' % (x, t)
Benchmark-Ergebnis:
benchmark_shift1 time: 0.265238 benchmark_shift2 time: 0.285175 benchmark_shift3 time: 0.473890 benchmark_shift4 time: 0.099049 benchmark_shift5 time: 0.052836
Fazit
shift5 ist der Gewinner! Es ist die dritte Lösung von OP.
quelle
shift5
ist es besser zu schreibenresult[:] = arr
stattresult = arr
, Funktion Verhalten konsistent zu halten.type(np.NAN) is float
. Wenn Sie ein Integer-Array mit diesen Funktionen verschieben, müssen Sie einen Integer-Füllwert angeben.Es gibt keine einzige Funktion, die das tut, was Sie wollen. Ihre Definition von Verschiebung unterscheidet sich geringfügig von der der meisten Menschen. Die Möglichkeiten zum Verschieben eines Arrays werden häufiger wiederholt:
>>>xs=np.array([1,2,3,4,5]) >>>shift(xs,3) array([3,4,5,1,2])
Sie können jedoch mit zwei Funktionen tun, was Sie wollen.
Bedenken Sie
a=np.array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])
:def shift2(arr,num): arr=np.roll(arr,num) if num<0: np.put(arr,range(len(arr)+num,len(arr)),np.nan) elif num > 0: np.put(arr,range(num),np.nan) return arr >>>shift2(a,3) [ nan nan nan 0. 1. 2. 3. 4. 5. 6.] >>>shift2(a,-3) [ 3. 4. 5. 6. 7. 8. 9. nan nan nan]
Nachdem ich cProfile für Ihre angegebene Funktion und den oben angegebenen Code ausgeführt habe, stellte ich fest, dass der von Ihnen bereitgestellte Code 42 Funktionsaufrufe ausführt, während
shift2
14 Aufrufe getätigt werden, wenn arr positiv ist, und 16, wenn er negativ ist.Ich werde mit dem Timing experimentieren, um zu sehen, wie sich jedes mit realen Daten verhält.quelle
np.roll()
; Ich habe die Technik in den Links in meiner Frage verwendet. Gibt es eine Chance für Ihre Implementierung, dass Ihre Funktion für negative Verschiebungswerte funktioniert?np.concatenate()
ist viel schneller alsnp.r_[]
. Ersteres wird schließlichnp.roll()
verwendet.Sie können zuerst
ndarray
inSeries
oderDataFrame
mit konvertierenpandas
, dann können Sie dieshift
Methode verwenden, wie Sie möchten.Beispiel:
In [1]: from pandas import Series In [2]: data = np.arange(10) In [3]: data Out[3]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) In [4]: data = Series(data) In [5]: data Out[5]: 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 dtype: int64 In [6]: data = data.shift(3) In [7]: data Out[7]: 0 NaN 1 NaN 2 NaN 3 0.0 4 1.0 5 2.0 6 3.0 7 4.0 8 5.0 9 6.0 dtype: float64 In [8]: data = data.values In [9]: data Out[9]: array([ nan, nan, nan, 0., 1., 2., 3., 4., 5., 6.])
quelle
Benchmarks & Einführung von Numba
1. Zusammenfassung
scipy.ndimage.interpolation.shift
) ist die langsamste auf dieser Seite aufgeführte Lösung.(1) der Länge Ihrer Arrays
(2) dem Schichtaufwand ab, den Sie ausführen müssen.
2. Detaillierte Benchmarks mit den besten Optionen
shift4_numba
(unten definiert), wenn Sie einen guten Allrounder wollen3. Code
3.1
shift4_numba
import numba @numba.njit def shift4_numba(arr, num, fill_value=np.nan): if num >= 0: return np.concatenate((np.full(num, fill_value), arr[:-num])) else: return np.concatenate((arr[-num:], np.full(-num, fill_value)))
3.2.
shift5_numba
import numba @numba.njit def shift5_numba(arr, num, fill_value=np.nan): result = np.empty_like(arr) if num > 0: result[:num] = fill_value result[num:] = arr[:-num] elif num < 0: result[num:] = fill_value result[:num] = arr[-num:] else: result[:] = arr return result
3.3.
shift5
shift5_numba
, entfernen Sie einfach das @ numba.njit Dekorateur.4 Anhang
4.1 Details zu den verwendeten Methoden
shift_scipy
:scipy.ndimage.interpolation.shift
(scipy 1.4.1) - Die Option aus der akzeptierten Antwort, die eindeutig die langsamste Alternative ist .shift1
:np.roll
undout[:num] xnp.nan
von IronManMark20 & gzcshift2
:np.roll
undnp.put
von IronManMark20shift3
:np.pad
undslice
von gzcshift4
:np.concatenate
undnp.full
von Chrisaycockshift5
: zweimalresult[slice] = x
mit chrisaycockshift#_numba
: @ numba .njit dekorierte Versionen der vorherigen.Die
shift2
undshift3
enthaltenen Funktionen, die von der aktuellen Nummer (0.50.1) nicht unterstützt wurden.4.2 Andere Testergebnisse
4.2.1 Relative Timings, alle Methoden
4.2.2 Raw Timings, alle Methoden
4.2.3 Rohdaten, einige der besten Methoden
quelle
Sie können dies auch mit Pandas tun:
Verwenden eines 2356 langen Arrays:
import numpy as np xs = np.array([...])
Scipy verwenden:
from scipy.ndimage.interpolation import shift %timeit shift(xs, 1, cval=np.nan) # 956 µs ± 77.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Verwenden von Pandas:
import pandas as pd %timeit pd.Series(xs).shift(1).values # 377 µs ± 9.42 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In diesem Beispiel war die Verwendung von Pandas etwa achtmal schneller als die von Scipy
quelle
Series
Technik hat uns auf meinem Computer gekostet, während mein Ansatz weniger als 4 uns gekostet hat.Wenn Sie einen Einzeiler von numpy möchten und sich keine Sorgen um die Leistung machen, versuchen Sie Folgendes:
np.sum(np.diag(the_array,1),0)[:-1]
Erläuterung:
np.diag(the_array,1)
Erstellt eine Matrix mit Ihrem Array einmalig in der Diagonale,np.sum(...,0)
summiert die Matrix spaltenweise und verwendet...[:-1]
die Elemente, die der Größe des ursprünglichen Arrays entsprechen würden. Das Herumspielen mit den Parametern1
und:-1
als kann zu Verschiebungen in verschiedene Richtungen führen.quelle
Eine Möglichkeit, dies zu tun, ohne den Code in Fälle zu verschütten
mit Array:
def shift(arr, dx, default_value): result = np.empty_like(arr) get_neg_or_none = lambda s: s if s < 0 else None get_pos_or_none = lambda s: s if s > 0 else None result[get_neg_or_none(dx): get_pos_or_none(dx)] = default_value result[get_pos_or_none(dx): get_neg_or_none(dx)] = arr[get_pos_or_none(-dx): get_neg_or_none(-dx)] return result
Mit Matrix kann es so gemacht werden:
def shift(image, dx, dy, default_value): res = np.full_like(image, default_value) get_neg_or_none = lambda s: s if s < 0 else None get_pos_or_none = lambda s : s if s > 0 else None res[get_pos_or_none(-dy): get_neg_or_none(-dy), get_pos_or_none(-dx): get_neg_or_none(-dx)] = \ image[get_pos_or_none(dy): get_neg_or_none(dy), get_pos_or_none(dx): get_neg_or_none(dx)] return res
quelle