Python 移动 numpy 数组中的元素
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/30399534/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Shift elements in a numpy array
提问by chrisaycock
Following-up from this questionyears ago, is there a canonical "shift" function in numpy? I don't see anything from the documentation.
几年前对这个问题的跟进,numpy 中是否有规范的“移位”函数?我没有从文档中看到任何内容。
Here's a simple version of what I'm looking for:
这是我正在寻找的简单版本:
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)]
Using this is like:
使用这个就像:
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])
This question came from my attempt to write a fast rolling_productyesterday. I needed a way to "shift" a cumulative product and all I could think of was to replicate the logic in np.roll()
.
这个问题来自我昨天尝试编写一个快速滚动产品。我需要一种方法来“转换”累积产品,而我所能想到的就是在np.roll()
.
So np.concatenate()
is much faster than np.r_[]
. This version of the function performs a lot better:
所以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)))
An even faster version simply pre-allocates the array:
更快的版本只是预先分配数组:
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
采纳答案by Ed Smith
Not numpy but scipy provides exactly the shift functionality you want,
不是 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)
where default is to bring in a constant value from outside the array with value cval
, set here to nan
. This gives the desired output,
其中默认值是从数组外部引入一个常量值cval
,这里设置为nan
. 这给出了所需的输出,
array([ nan, nan, nan, 0., 1., 2., 3., 4., 5., 6.])
and the negative shift works similarly,
负移的作用类似,
shift(xs, -3, cval=np.NaN)
Provides output
提供输出
array([ 3., 4., 5., 6., 7., 8., 9., nan, nan, nan])
回答by IronManMark20
There is no single function that does what you want. Your definition of shift is slightly different than what most people are doing. The ways to shift an array are more commonly looped:
没有单一的功能可以满足您的需求。你对转变的定义与大多数人所做的略有不同。移动数组的方法更常见的是循环:
>>>xs=np.array([1,2,3,4,5])
>>>shift(xs,3)
array([3,4,5,1,2])
However, you can do what you want with two functions.
Consider a=np.array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])
:
但是,您可以使用两个函数执行您想要的操作。
考虑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]
After running cProfile on your given function and the above code you provided, I found that the code you provided makes 42 function calls while shift2
made 14 calls when arr is positive and 16 when it is negative. I will be experimenting with timing to see how each performs with real data.
在您给定的函数和您提供的上述代码上运行 cProfile 后,我发现您提供的代码进行了 42 次函数调用,而shift2
当 arr 为正时进行了 14 次调用,当 arr 为负时进行了 16 次调用。我将尝试计时,看看每个人在真实数据上的表现如何。
回答by gzc
For those who want to just copy and paste the fastest implementation of shift, there is a benchmark and conclusion(see the end). In addition, I introduce fill_value parameter and fix some bugs.
对于那些只想复制和粘贴最快的 shift 实现的人,有一个基准和结论(见文末)。另外,我引入了fill_value参数并修复了一些错误。
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 result:
基准结果:
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
Conclusion
结论
shift5 is winner! It's OP's third solution.
shift5 是赢家!这是 OP 的第三个解决方案。
回答by JsonBruce
You can convert ndarray
to Series
or DataFrame
with pandas
first, then you can use shift
method as you want.
您可以先转换ndarray
为Series
或DataFrame
使用pandas
,然后您可以shift
根据需要使用方法。
Example:
例子:
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.])
回答by Ran Aroussi
You can also do this with Pandas:
你也可以用 Pandas 做到这一点:
Using a 2356-long array:
使用 2356 长的数组:
import numpy as np
xs = np.array([...])
Using scipy:
使用 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)
Using 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 this example, using Pandas was about ~8 times faster than Scipy
在这个例子中,使用 Pandas 大约比 Scipy 快 8 倍
回答by Nathan Chappell
If you want a one-liner from numpy and aren't too concerned about performance, try:
如果您想要 numpy 的单线并且不太关心性能,请尝试:
np.sum(np.diag(the_array,1),0)[:-1]
Explanation: np.diag(the_array,1)
creates a matrix with your array one-off the diagonal, np.sum(...,0)
sums the matrix column-wise, and ...[:-1]
takes the elements that would correspond to the size of the original array. Playing around with the 1
and :-1
as parameters can give you shifts in different directions.
说明:np.diag(the_array,1)
使用您的数组对角线创建一个矩阵,np.sum(...,0)
按列对矩阵求和,并...[:-1]
采用与原始数组大小相对应的元素。使用1
和:-1
作为参数可以让您在不同的方向上移动。
回答by Alon Gweta
One way to do it without spilt the code into cases
在不将代码溢出到案例中的情况下做到这一点的一种方法
with 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
with matrix it can be done like this:
使用矩阵可以这样完成:
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