移动numpy数组中的元素

时间:2015-05-22 14:36:43

标签: python numpy

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

9 个答案:

答案 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)

基准和Numba简介

1。摘要

  • 可接受的答案(scipy.ndimage.interpolation.shift)是此页面中列出的最慢的解决方案。
  • Numba(@ numba.njit)当数组大小小于〜25.000时,性能会有所提高
  • 当数组大小较大(> 250.000)时,“任何方法”都同样好。
  • 最快的选择确实取决于
    (1)数组的长度
    (2)您需要做的班次量。
  • 下面是此页面(2020-07-11)上列出的所有不同方法的时序图,使用恒定移位=10。可以看到,在数组较小的情况下,某些方法的使用率超过+ 2000%时间胜过最佳方法。

Relative timings, constant shift (10), all methods

2。具有最佳选择的详细基准测试

  • 如果您想获得全方位的好选择,请选择shift4_numba(定义如下)

Relative timings, best methods (Benchmarks)

3。代码

3.1 shift4_numba

  • 全方位的好;最高20%wrt。任何数组大小的最佳方法
  • 中等阵列大小的最佳方法:〜500
  • 注意事项:仅当您多次调用经过修饰的函数时,Numba jit(即时编译器)才会提高性能。第一次通话通常比后续通话时间长3-4倍。
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

  • 具有较小(N <= 300 .. 1500)数组大小的最佳选项。阈值取决于所需的移位量。
  • 在任何阵列大小上均具有良好的性能;与最快的解决方案相比,最大+ 50%。
  • 注意事项:仅当您多次调用经过修饰的函数时,Numba jit(即时编译器)才会提高性能。第一次通话通常比后续通话时间长3-4倍。
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

  • 数组大小约为20.000
  • shift5_numba相同,只需删除@ numba.njit装饰器。

4附录

4.1有关使用方法的详细信息

  • shift_scipyscipy.ndimage.interpolation.shift(scipy 1.4.1)-可接受答案中的选项,显然是最慢的选择。
  • shift1IronManMark20gzcnp.rollout[:num] xnp.nan
  • shift2IronManMark20np.rollnp.put
  • shift3gzcnp.padslice
  • shift4chrisaycocknp.concatenatenp.full
  • shift5:使用chrisaycock的两倍result[slice] = x
  • shift#_numba:@ numba。njit装饰了前一个版本。

shift2shift3包含当前数字(0.50.1)不支持的功能。

4.2其他测试结果

4.2.1相对计时,所有方法

4.2.2原始计时,所有方法

4.2.3原始时间安排,很少有最佳方法

答案 4 :(得分:3)

您可以先使用ndarraySeries转换为DataFramepandas,然后根据需要使用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