Numpy 배열에서 요소 이동
이 질문은 하단에 자체 답변이 포함되어 있습니다.미리 할당된 어레이를 사용합니다.
수년 전 이 질문의 후속 조치로, numpy에 표준 "이동" 함수가 있습니까?문서에서 아무것도 보이지 않습니다.
다음은 제가 찾고 있는 간단한 버전입니다.
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])
어제 fastrolling_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
위의 제안이 답입니다.미리 할당된 어레이를 사용합니다.
numpy가 아니라 Scipy는 당신이 원하는 시프트 기능을 정확히 제공합니다.
import numpy as np
from scipy.ndimage import shift
xs = np.array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])
shift(xs, 3, cval=np.NaN)
에서 값이 "" "" "" "" "" "" "" "" "" "" ""와 같은 입니다.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])
가장 빠른 시프트 구현을 복사하여 붙여넣으려는 사용자에게는 벤치마크와 결론이 있습니다(끝 참조).또한 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의 세 번째 솔루션입니다.
벤치마크 및 Numba 소개
요약
- 인정된 답변(
scipy.ndimage.interpolation.shift
)는 이 페이지에 나열된 솔루션 중 가장 느린 솔루션입니다. - Numba(@numba.njit)는 어레이 크기가 ~25.000보다 작을 때 성능을 약간 향상시킵니다.
- 어레이 크기가 큰 경우(250.000보다 큰 경우) "모든 방법"이 동일하게 좋습니다.
- 가장 빠른 옵션은 다음과 같습니다.
의
필요한 이동량입니다. - 아래는 이 페이지(2020-07-11)에 나열된 모든 다양한 방법의 타이밍 그림이며, 상수 시프트 = 10을 사용합니다.보다시피 어레이 크기가 작은 일부 방법은 최적의 방법보다 +2000% 이상의 시간을 사용합니다.
최상의 옵션이 포함된 세부 벤치마크
- 선택하세요.
shift4_numba
(아래 정의) 좋은 올라운드를 원하는 경우
코드
3.1 shift4_numba
- 우수한 만능 재료, 어레이 크기에 상관없이 최대 20% 쓰기 가능
- 중간 크기의 어레이에서 최적의 방법: ~ 500 < N < 20.000.
- 주의: Numbajit (just in time 컴파일러)는 장식된 함수를 두 번 이상 호출하는 경우에만 성능을 향상시킵니다.첫 번째 통화는 일반적으로 후속 통화보다 3-4배 더 오래 걸립니다.미리 컴파일된 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
- 소형인 경우 최적의 옵션(N <= 300..1500) 어레이 크기입니다.임계값은 필요한 이동량에 따라 달라집니다.
- 모든 어레이 크기에서 우수한 성능 제공. 가장 빠른 솔루션 대비 최대 + 50%.
- 주의: Numbajit (just in time 컴파일러)는 장식된 함수를 두 번 이상 호출하는 경우에만 성능을 향상시킵니다.첫 번째 통화는 일반적으로 후속 통화보다 3-4배 더 오래 걸립니다.미리 컴파일된 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
- 어레이 크기 ~ 20.000 < N < 250.000인 최적의 방법
- 과 동일
shift5_numba
@numba.njit 장식기를 제거하기만 하면 됩니다.
4 부록
4.1 사용방법에 대한 세부사항
shift_scipy
:scipy.ndimage.interpolation.shift
(scip 1.4.1) - 승인된 답변의 옵션으로, 가장 느린 대안임이 분명합니다.shift1
:np.roll
그리고.out[:num] xnp.nan
아이언맨 마크 20 & gzc에 의해shift2
:np.roll
그리고.np.put
아이언맨 마크20에 의해shift3
:np.pad
그리고.slice
gzc 단위로shift4
:np.concatenate
그리고.np.full
틀림없이shift5
하기 2회 용사result[slice] = x
틀림없이shift#_numba
@numba.njit 장식된 이전 버전.
그shift2
그리고.shift3
에는 현재 numba(0.50.1)에서 지원하지 않는 함수가 포함되어 있습니다.
4.2 기타 시험결과
4.2.1 상대적 시간, 모든 방법
4.2.2 원재료명, 모든 방법
4.2.3 원시 타이밍, 몇 가지 최적 방법
- 소규모 어레이를 사용한 물리적 타이밍, 일정한 시프트(10), 몇 가지 최적의 방법
- 소규모 어레이를 사용한 물리적 타이밍, 10% 이동, 몇 가지 최적의 방법
- 대규모 어레이를 사용한 물리적 타이밍, 지속적인 시프트(10), 몇 가지 최적의 방법
- 대규모 어레이를 사용한 물리적 타이밍, 10% 이동, 몇 가지 최상의 방법
당신이 원하는 것을 해주는 단일 기능은 없습니다.변화에 대한 당신의 정의는 대부분의 사람들이 하는 것과 약간 다릅니다.배열을 이동하는 방법은 일반적으로 루프됩니다.
>>>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번 통화했습니다.
저는 각각이 실제 데이터로 어떻게 수행되는지 보기 위해 타이밍을 실험할 것입니다.
변환할 수 있습니다.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.])
판다와 함께 할 수도 있습니다.
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)
이 예에서, 판다를 사용하는 것은 Scipy보다 약 8배 더 빠릅니다.
numpy의 원라이너를 원하지만 성능에 대해 크게 걱정하지 않는다면 다음을 시도해 보십시오.
np.sum(np.diag(the_array,1),0)[:-1]
설명:np.diag(the_array,1)
대각선에서 한 번에 배열을 사용하여 행렬을 만듭니다.np.sum(...,0)
행렬을 열 단위로 합합니다....[:-1]
원래 배열의 크기에 해당하는 요소를 사용합니다.컴퓨터를 가지고 노는 것.1
그리고.:-1
매개 변수가 다른 방향으로 이동할 수 있기 때문입니다.
판다 라이브러리와 같은 numba 및 음의 이동 값을 지원하는 간단하고 효과적인 방법.인수에서 원래 배열이 손상되는 것을 방지하고 정수 배열과 함께 작동합니다.
import numpy as np
from numba import njit
@njit
def numba_shift(arr_: np.ndarray, shift: np.int32 = 1) -> np.ndarray:
arr = arr_.copy().astype(np.float64)
if shift > 0:
arr[shift:] = arr[:-shift]
arr[:shift] = np.nan
else:
arr[:shift] = arr[-shift:]
arr[shift:] = np.nan
return arr
예:
ar = np.array([1,2,3,4,5,6])
numba_shift(ar,-1)
array([ 2., 3., 4., 5., 6., nan])
시간:
%timeit numba_shift(ar,-1)
1.02 µs ± 9.42 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
참고: numba가 필요하지 않으면 numpy만 사용하여 라인을 삭제합니다.@njit
그리고 numba 가져오기.
아마도요.np.roll
당신에게 필요한 것입니다.
arr = np.arange(10)
shift = 2 # shift length
arr_1 = np.roll(arr, shift=shift)
arr_1[:shift] = np.nan
내 해결책은 다음과 같습니다.np.roll
및 마스킹된 배열:
import numpy as np
import numpy.ma as ma # this is for masked array
def shift(arr, shift):
r_arr = np.roll(arr, shift=shift)
m_arr = ma.masked_array(r_arr,dtype=float)
if shift > 0: m_arr[:shift] = ma.masked
else: m_arr[shift:] = ma.masked
return m_arr.filled(np.nan)
기본적으로, 저는 그냥.np.roll
배열을 이동하려면 다음을 사용합니다.ma.masked_array
불필요한 요소를 유효하지 않은 것으로 표시하고 유효하지 않은 위치를 다음으로 채웁니다.np.nan
설정합니다.dtype
float
으로 채워지도록.np.nan
아무 문제도 일으키지 않을 것입니다.
In [11]: shift(arr, 3)
Out[11]: array([nan, nan, nan, 0., 1., 2., 3., 4., 5., 6.])
In [12]: shift(arr, -3)
Out[12]: array([ 3., 4., 5., 6., 7., 8., 9., nan, nan, nan])
대답의 .shift5
)는배열을
def shift(array, offset, constant_values=0):
"""Returns copy of array shifted by offset, with fill using constant."""
array = np.asarray(array)
offset = np.atleast_1d(offset)
assert len(offset) == array.ndim
new_array = np.empty_like(array)
def slice1(o):
return slice(o, None) if o >= 0 else slice(0, o)
new_array[tuple(slice1(o) for o in offset)] = (
array[tuple(slice1(-o) for o in offset)])
for axis, o in enumerate(offset):
new_array[(slice(None),) * axis +
(slice(0, o) if o >= 0 else slice(o, None),)] = constant_values
return new_array
여기에 numpy의 특별한 내장 함수를 사용하지 않고 numba와 호환되는 2차원 솔루션이 있습니다.
def shift(array, dy, dx):
n, m = array.shape[:2]
e = np.zeros((n, m))
if dy > 0 and dx > 0:
e[dy:, dx:] = array[:-dy, :-dx]
return e
elif dy > 0 and dx < 0:
e[dy:, :dx] = array[:-dy, -dx:]
return e
elif dy < 0 and dx > 0:
e[:dy, dx:] = array[-dy:, :-dx]
return e
elif dy < 0 and dx < 0:
e[:dy, :dx] = array[-dy:, -dx:]
return e
elif dy < 0 and dx == 0:
e[:dy, :] = array[-dy:, :]
return e
elif dy > 0 and dx == 0:
e[dy:, :] = array[:-dy, :]
return e
elif dy == 0 and dx < 0:
e[:, :dx] = array[:, -dx:]
return e
elif dy == 0 and dx > 0:
e[:, dx:] = array[:, :-dx]
return e
더 빠른 솔루션이 있습니다. @gzc의 벤치마크 솔루션에 2개의 벤치마크를 추가했습니다.
def shift6(arr, num, fill_value=np.nan):
for _ in range(num):
darr.appendleft(fill_value)
def shift7(arr, num, fill_value=np.nan):
darr = deque(arr)
for _ in range(num):
darr.appendleft(fill_value)
darr = deque(arr)
def benchmark_shift6():
shift6(arr, 3)
def benchmark_shift7():
shift6(arr, 3)
benchmark_set = ['benchmark_shift1', 'benchmark_shift2', 'benchmark_shift3', 'benchmark_shift4', 'benchmark_shift5', 'benchmark_shift6', 'benchmark_shift7']
그리고 제 노트북의 출력은 제안된 다른 솔루션보다 훨씬 우수합니다.
%s time: ('benchmark_shift1', 0.08232757700170623)
%s time: ('benchmark_shift2', 0.0934765400015749)
%s time: ('benchmark_shift3', 0.14349375600431813)
%s time: ('benchmark_shift4', 0.03575193700089585)
%s time: ('benchmark_shift5', 0.01389261399890529)
%s time: ('benchmark_shift6', 0.0025887360025080852)
%s time: ('benchmark_shift7', 0.0024806019937386736)
코드를 사례에 쏟지 않고 수행하는 한 가지 방법
배열 포함:
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
언급URL : https://stackoverflow.com/questions/30399534/shift-elements-in-a-numpy-array
'programing' 카테고리의 다른 글
access_token JWT에서 데이터를 추가하는 방법 (0) | 2023.08.07 |
---|---|
대상 'x86_64-apple-ios-simulator'에 대한 모듈을 찾을 수 없습니다. (0) | 2023.08.07 |
파일이 PowerShell의 심볼 링크인지 확인합니다. (0) | 2023.08.07 |
보조 테이블을 통한 재귀 선택 사용 (0) | 2023.08.07 |
Linux에서 도커를 중지하는 방법 (0) | 2023.08.07 |