에서 NumPy와 수동 모양 변경 () 함수에 대한, 그것은 말한다
>>> a = np.zeros((10, 2))
# A transpose make the array non-contiguous
>>> b = a.T
# Taking a view makes it possible to modify the shape without modifying the
# initial object.
>>> c = b.view()
>>> c.shape = (20)
AttributeError: incompatible shape for a non-contiguous array
내 질문은 다음과 같습니다.
- 연속 및 비 연속 배열이란 무엇입니까? 인접 메모리 블록 이란 무엇입니까?
- 이 둘 사이에 성능 차이가 있습니까? 둘 중 하나를 언제 사용해야합니까?
- 전치가 배열을 비 연속적으로 만드는 이유는 무엇입니까?
- 왜
c.shape = (20)
오류가 발생incompatible shape for a non-contiguous array
합니까?
답변 해 주셔서 감사합니다!
답변
연속 배열은 깨지지 않은 메모리 블록에 저장된 배열 일뿐입니다. 배열의 다음 값에 액세스하려면 다음 메모리 주소로 이동합니다.
2D 배열을 고려하십시오 arr = np.arange(12).reshape(3,4)
. 다음과 같이 보입니다.
컴퓨터 메모리에의 값은 arr
다음과 같이 저장됩니다.
이 수단은, arr
A는 C 연속 때문에 어레이 행이 메모리의 연속 블록으로 저장된다. 다음 메모리 주소는 해당 행의 다음 행 값을 보유합니다. 한 열 아래로 이동하려면 세 블록을 건너 뛰면됩니다 (예 : 0에서 4로 건너 뛰는 것은 1,2, 3을 건너 뛰는 것을 의미합니다).
배열을로 전치 arr.T
한다는 것은 인접한 행 항목이 더 이상 인접한 메모리 주소에 없기 때문에 C 연속성이 손실됨을 의미합니다. 그러나, arr.T
이다 포트란 연속 때문에 열이 메모리의 연속 블록에는 :
성능 측면에서, 서로 옆에있는 메모리 주소에 액세스하는 것은 더 “확산”된 주소에 액세스하는 것보다 훨씬 빠릅니다 (RAM에서 값을 가져 오면 여러 인접 주소를 가져 와서 CPU에 캐시 할 수 있음). 연속 배열에 대한 작업이 종종 더 빠르다는 것을 의미합니다.
C 연속 메모리 레이아웃의 결과로 행 단위 연산은 일반적으로 열 단위 연산보다 빠릅니다. 예를 들어, 일반적으로
np.sum(arr, axis=1) # sum the rows
다음보다 약간 빠릅니다.
np.sum(arr, axis=0) # sum the columns
마찬가지로 포트란 연속 배열의 경우 열에 대한 작업이 약간 더 빠릅니다.
마지막으로 새 모양을 할당하여 Fortran 연속 배열을 평면화 할 수없는 이유는 무엇입니까?
>>> arr2 = arr.T
>>> arr2.shape = 12
AttributeError: incompatible shape for a non-contiguous array
이것이 가능해 지려면 NumPy는 다음 arr.T
과 같이 행을 합쳐야합니다.
( shape
속성을 직접 설정하는 것은 C 순서를 가정합니다. 즉 NumPy는 작업을 행 단위로 수행하려고합니다.)
이것은 불가능합니다. 모든 축에 대해 NumPy 는 배열의 다음 요소에 도달하기 위해 일정한 스트라이드 길이 (이동할 바이트 수) 를 가져야합니다 . arr.T
이러한 방식으로 평면화 하려면 배열의 연속 값을 검색하기 위해 메모리에서 앞뒤로 건너 뛰어야합니다.
arr2.reshape(12)
대신 작성 하면 NumPy는 arr2의 값을 새 메모리 블록에 복사합니다 (이 모양에 대한 원래 데이터에 대한 뷰를 반환 할 수 없기 때문에).
답변
12 개의 다른 배열 값이있는이 예제가 도움이 될 것입니다.
In [207]: x=np.arange(12).reshape(3,4).copy()
In [208]: x.flags
Out[208]:
C_CONTIGUOUS : True
F_CONTIGUOUS : False
OWNDATA : True
...
In [209]: x.T.flags
Out[209]:
C_CONTIGUOUS : False
F_CONTIGUOUS : True
OWNDATA : False
...
C order
값은 생성 된 순서에 있습니다. 전치 사람이 아니다
In [212]: x.reshape(12,) # same as x.ravel()
Out[212]: array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
In [213]: x.T.reshape(12,)
Out[213]: array([ 0, 4, 8, 1, 5, 9, 2, 6, 10, 3, 7, 11])
둘 다 1d 뷰를 얻을 수 있습니다.
In [214]: x1=x.T
In [217]: x.shape=(12,)
의 모양 x
도 변경할 수 있습니다.
In [220]: x1.shape=(12,)
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-220-cf2b1a308253> in <module>()
----> 1 x1.shape=(12,)
AttributeError: incompatible shape for a non-contiguous array
그러나 조옮김의 모양은 변경할 수 없습니다. 는 data
여전히 0,1,2,3,4...
로서 액세스 액세스 할 수없는 위해, 0,4,8...
1 차원 배열이다.
그러나 복사본은 x1
변경할 수 있습니다.
In [227]: x2=x1.copy()
In [228]: x2.flags
Out[228]:
C_CONTIGUOUS : True
F_CONTIGUOUS : False
OWNDATA : True
...
In [229]: x2.shape=(12,)
보는 strides
것도 도움이 될 수 있습니다. strides는 다음 값에 도달하기 위해 얼마나 멀리 (바이트) 이동해야 하는지를 나타냅니다. 2d 배열의 경우 2 개의 stride 값이 있습니다.
In [233]: x=np.arange(12).reshape(3,4).copy()
In [234]: x.strides
Out[234]: (16, 4)
다음 행으로 이동하려면 16 바이트 단계, 다음 열만 4.
In [235]: x1.strides
Out[235]: (4, 16)
조옮김은 보폭의 순서를 전환합니다. 다음 행은 4 바이트, 즉 다음 숫자입니다.
In [236]: x.shape=(12,)
In [237]: x.strides
Out[237]: (4,)
모양을 변경하면 보폭도 변경됩니다. 버퍼를 한 번에 4 바이트 씩 이동하면됩니다.
In [238]: x2=x1.copy()
In [239]: x2.strides
Out[239]: (12, 4)
비록 x2
처럼 보이는 x1
, 그것은 다른 순서 값으로 자신의 데이터 버퍼를 가지고 있습니다. 다음 열은 이제 4 바이트 이상이고 다음 행은 12 (3 * 4)입니다.
In [240]: x2.shape=(12,)
In [241]: x2.strides
Out[241]: (4,)
에서처럼 x
모양을 1d로 변경하면 보폭이 (4,)
.
의 경우 x1
데이터를 0,1,2,...
순서대로 사용하면를 줄 1d 보폭이 없습니다 0,4,8...
.
__array_interface__
배열 정보를 표시하는 또 다른 유용한 방법입니다.
In [242]: x1.__array_interface__
Out[242]:
{'strides': (4, 16),
'typestr': '<i4',
'shape': (4, 3),
'version': 3,
'data': (163336056, False),
'descr': [('', '<i4')]}
x1
데이터 버퍼 주소로써 동일하게 나타날 x
이 데이터를 공유하는로. x2
버퍼 주소가 다릅니다.
order='F'
매개 변수를 copy
및 reshape
명령에 추가하여 실험 할 수도 있습니다 .