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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 08:22:19  来源:igfitidea点击:

Shift elements in a numpy array

pythonnumpy

提问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 shift2made 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 ndarrayto Seriesor DataFramewith pandasfirst, then you can use shiftmethod as you want.

您可以先转换ndarraySeriesDataFrame使用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 1and :-1as 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