[python] 연속 배열과 비 연속 배열의 차이점은 무엇입니까?

에서 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

내 질문은 다음과 같습니다.

  1. 연속 및 비 연속 배열이란 무엇입니까? 인접 메모리 블록 이란 무엇입니까?
  2. 이 둘 사이에 성능 차이가 있습니까? 둘 중 하나를 언제 사용해야합니까?
  3. 전치가 배열을 비 연속적으로 만드는 이유는 무엇입니까?
  4. c.shape = (20)오류가 발생 incompatible shape for a non-contiguous array합니까?

답변 해 주셔서 감사합니다!



답변

연속 배열은 깨지지 않은 메모리 블록에 저장된 배열 일뿐입니다. 배열의 다음 값에 액세스하려면 다음 메모리 주소로 이동합니다.

2D 배열을 고려하십시오 arr = np.arange(12).reshape(3,4). 다음과 같이 보입니다.

여기에 이미지 설명 입력

컴퓨터 메모리에의 값은 arr다음과 같이 저장됩니다.

여기에 이미지 설명 입력

이 수단은, arrA는 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'매개 변수를 copyreshape명령에 추가하여 실험 할 수도 있습니다 .


답변