IT TIP

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

itqueen 2020. 10. 31. 10:23
반응형

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


에서 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. 연속 및 비 연속 배열이란 무엇입니까? 연속 메모리 블록 이란 무엇입니까? 와 같이 C의 연속 메모리 블록과 유사 합니까?
  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,)

the shape of x can also be changed.

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

But the shape of the transpose cannot be changed. The data is still in the 0,1,2,3,4... order, which can't be accessed accessed as 0,4,8... in a 1d array.

But a copy of x1 can be changed:

In [227]: x2=x1.copy()

In [228]: x2.flags
Out[228]: 
  C_CONTIGUOUS : True
  F_CONTIGUOUS : False
  OWNDATA : True
  ...
In [229]: x2.shape=(12,)

Looking at strides might also help. A strides is how far (in bytes) it has to step to get to the next value. For a 2d array, there will be be 2 stride values:

In [233]: x=np.arange(12).reshape(3,4).copy()

In [234]: x.strides
Out[234]: (16, 4)

To get to the next row, step 16 bytes, next column only 4.

In [235]: x1.strides
Out[235]: (4, 16)

Transpose just switches the order of the strides. The next row is only 4 bytes- i.e. the next number.

In [236]: x.shape=(12,)

In [237]: x.strides
Out[237]: (4,)

Changing the shape also changes the strides - just step through the buffer 4 bytes at a time.

In [238]: x2=x1.copy()

In [239]: x2.strides
Out[239]: (12, 4)

Even though x2 looks just like x1, it has its own data buffer, with the values in a different order. The next column is now 4 bytes over, while the next row is 12 (3*4).

In [240]: x2.shape=(12,)

In [241]: x2.strides
Out[241]: (4,)

And as with x, changing the shape to 1d reduces the strides to (4,).

For x1, with data in the 0,1,2,... order, there isn't a 1d stride that would give 0,4,8....

__array_interface__ is another useful way of displaying array information:

In [242]: x1.__array_interface__
Out[242]: 
{'strides': (4, 16),
 'typestr': '<i4',
 'shape': (4, 3),
 'version': 3,
 'data': (163336056, False),
 'descr': [('', '<i4')]}

The x1 data buffer address will be same as for x, with which it shares the data. x2 has a different buffer address.

You could also experiment with adding a order='F' parameter to the copy and reshape commands.

참고 URL : https://stackoverflow.com/questions/26998223/what-is-the-difference-between-contiguous-and-non-contiguous-arrays

반응형