从this question年前开始跟进,是否有规范的转变"在numpy?我从the documentation看不到任何内容。
这是我正在寻找的简单版本:
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)]
使用它就像:
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])
这个问题来自我昨天write a fast rolling_product的尝试。我需要一种方法来转移"累积产品,我能想到的是复制np.roll()
中的逻辑。
所以np.concatenate()
比np.r_[]
快得多。此版本的功能执行得更好:
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)))
更快的版本只需预先分配数组:
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
答案 0 :(得分:57)
不是numpy但是scipy提供了你想要的转换功能,
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)
其中default是从数组外部引入值为cval
的常量值,此处设置为nan
。这给出了所需的输出,
array([ nan, nan, nan, 0., 1., 2., 3., 4., 5., 6.])
负面转变同样有效,
shift(xs, -3, cval=np.NaN)
提供输出
array([ 3., 4., 5., 6., 7., 8., 9., nan, nan, nan])
答案 1 :(得分:31)
对于那些只想复制和粘贴最快的shift实现的人,有一个基准和结论(见最后)。另外,我介绍了fill_value参数并修复了一些错误。
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_shift1 time: 0.265238
benchmark_shift2 time: 0.285175
benchmark_shift3 time: 0.473890
benchmark_shift4 time: 0.099049
benchmark_shift5 time: 0.052836
shift5是赢家!这是OP的第三个解决方案。
答案 2 :(得分:7)
没有一种功能可以满足您的需求。您对班次的定义与大多数人的做法略有不同。移动数组的方法通常是循环的:
>>>xs=np.array([1,2,3,4,5])
>>>shift(xs,3)
array([3,4,5,1,2])
但是,您可以使用两个功能执行所需操作。
考虑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]
在您给定的函数和上面提供的代码上运行cProfile之后,我发现您提供的代码进行了42次函数调用,而shift2
在arr为正时进行了14次调用,而在为负时进行了16次调用。 我将尝试计时,看看每个人如何使用真实数据。
答案 3 :(得分:6)
scipy.ndimage.interpolation.shift
)是此页面中列出的最慢的解决方案。
shift4_numba
(定义如下)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)))
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
shift5
shift5_numba
相同,只需删除@ numba.njit装饰器。shift_scipy
:scipy.ndimage.interpolation.shift
(scipy 1.4.1)-可接受答案中的选项,显然是最慢的选择。shift1
:IronManMark20和gzc的np.roll
和out[:num] xnp.nan
shift2
:IronManMark20的np.roll
和np.put
shift3
:gzc的np.pad
和slice
shift4
:chrisaycock的np.concatenate
和np.full
shift5
:使用chrisaycock的两倍result[slice] = x
shift#_numba
:@ numba。njit装饰了前一个版本。 shift2
和shift3
包含当前数字(0.50.1)不支持的功能。
答案 4 :(得分:3)
您可以先使用ndarray
将Series
转换为DataFrame
或pandas
,然后根据需要使用shift
方法。
示例:
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.])
答案 5 :(得分:0)
您也可以使用Pandas来做到这一点:
使用2356长的数组:
import numpy as np
xs = np.array([...])
使用scipy:
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)
使用熊猫:
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)
在此示例中,使用Pandas的速度是Scipy的约8倍
答案 6 :(得分:0)
如果您想从numpy获得单线并且不太担心性能,请尝试:
np.sum(np.diag(the_array,1),0)[:-1]
说明:np.diag(the_array,1)
创建一个矩阵,矩阵的对角线为一个,对角线为np.sum(...,0)
,矩阵按列进行求和,...[:-1]
则采用与原始数组。将1
和:-1
用作参数可以使您向不同的方向移动。
答案 7 :(得分:0)
也许 np.roll
正是您所需要的
arr = np.arange(10)
shift = 2 # shift length
arr_1 = np.roll(arr, shift=shift)
arr_1[:shift] = np.nan
答案 8 :(得分:-1)
一种做到这一点而又不会将代码浪费到案例中的方法
带有数组:
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
使用矩阵可以做到:
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