나는 최근 에 행렬의 N 행을 평균화하기 위해이 솔루션을 적용 했습니다 . 솔루션은 일반적으로 작동하지만 7×1 어레이에 적용하면 문제가 발생했습니다. -=
운영자를 사용할 때 문제가 있음을 알았습니다 . 작은 예를 들어 보려면 :
import numpy as np
a = np.array([1,2,3])
b = np.copy(a)
a[1:] -= a[:-1]
b[1:] = b[1:] - b[:-1]
print a
print b
다음을 출력합니다.
[1 1 2]
[1 1 1]
따라서, 어레이의 경우 a -= b
와는 다른 결과를 생성한다 a = a - b
. 지금까지이 두 가지 방법이 똑같다고 생각했습니다. 차이점은 무엇입니까?
매트릭스의 모든 N 행을 합산하는 방법이 7×4 매트릭스에서는 작동하지만 7×1 배열에서는 작동하지 않는 이유는 무엇입니까?
답변
참고 : 메모리를 공유하는 NumPy 배열에서 내부 작업을 사용하는 것은 버전 1.13.0 이상에서 더 이상 문제가되지 않습니다 (자세한 내용은 여기 참조 ). 두 작업은 동일한 결과를 생성합니다. 이 답변은 이전 버전의 NumPy에만 적용됩니다.
계산에 사용되는 동안 배열을 변형하면 예기치 않은 결과가 발생할 수 있습니다!
질문의 예에서 뺄셈은 -=
의 두 번째 요소 a
를 수정 한 다음 의 세 번째 요소에 대한 연산에서 수정 된 두 번째 요소 를 즉시 사용합니다 a
.
a[1:] -= a[:-1]
단계별 로 수행되는 작업 은 다음과 같습니다 .
-
a
데이터가있는 배열입니다[1, 2, 3]
. -
우리는이 데이터 상에 두 개의보기가 있습니다
a[1:]
입니다[2, 3]
,하고a[:-1]
있다[1, 2]
. -
제자리 빼기
-=
가 시작됩니다. 의 첫 번째 요소 인a[:-1]
1이의 첫 번째 요소에서 뺍니다a[1:]
. 이것은로 수정a
되었습니다[1, 1, 3]
. 지금 우리가 가지고있는a[1:]
데이터의 도면[1, 3]
, 및a[:-1]
데이터를 나타내는 도면이다[1, 1]
(어레이의 두 번째 요소는a
변경되었다). -
a[:-1]
이제[1, 1]
NumPy는의 두 번째 요소에서 1 (더 이상 2가 아님) 인 두 번째 요소 를 빼야합니다a[1:]
. 이렇게하면a[1:]
값을 볼 수 있습니다[1, 2]
. -
a
이제 값이있는 배열입니다[1, 1, 2]
.
b[1:] = b[1:] - b[:-1]
때문에이 문제가없는 b[1:] - b[:-1]
만들고 새로운 제 배열하고이 배열에 값을 할당한다 b[1:]
. 그것은 수정하지 않는 b
견해 있도록 뺄셈 동안 자체 b[1:]
및 b[:-1]
변경하지 마십시오.
일반적인 조언은 겹치는 경우 한 뷰를 다른 뷰와 제자리에서 수정하지 않는 것입니다. 여기에는 연산자 -=
, *=
등 이 포함 out
되며 범용 함수 ( np.subtract
및 등 np.multiply
) 에서 매개 변수를 사용 하여 배열 중 하나에 다시 씁니다.
답변
내부적으로 차이점은 다음과 같습니다.
a[1:] -= a[:-1]
다음과 같습니다.
a[1:] = a[1:].__isub__(a[:-1])
a.__setitem__(slice(1, None, None), a.__getitem__(slice(1, None, None)).__isub__(a.__getitem__(slice(1, None, None)))
이 동안 :
b[1:] = b[1:] - b[:-1]
이것에 매핑 :
b[1:] = b[1:].__sub__(b[:-1])
b.__setitem__(slice(1, None, None), b.__getitem__(slice(1, None, None)).__sub__(b.__getitem__(slice(1, None, None)))
어떤 경우에, __sub__()
그리고 __isub__()
비슷한 방식으로 작동합니다. 그러나 변경 가능한 객체는를 사용할 때 변경하고 자신 __isub__()
을 반환해야하며 __sub__()
.
numpy 객체에 슬라이스 작업을 적용하면 뷰가 생성되므로이를 사용하면 “원본”객체의 메모리에 직접 액세스 할 수 있습니다.
답변
문서는 말한다 :
파이썬에서 증강 할당 뒤에있는 아이디어는 이진 연산의 결과를 왼쪽 피연산자에 저장하는 일반적인 관행을 작성하는 것이 더 쉬운 방법 일뿐만 아니라 문제의 왼쪽 피연산자가 다음을 수행하는 방법이라는 것입니다. 자신의 수정 된 사본을 생성하는 대신 ‘자체적으로’작동해야한다는 것을 알고 있어야합니다.
엄지 손가락 법칙으로, 증강 빼기 ( x-=y
)이다 x.__isub__(y)
들면 IN 의 자리 표시 동작 IF 정상 빼기가 (가능 x = x-y
)이다 x=x.__sub__(y)
. 정수와 같은 변경 불가능한 객체에서는 동일합니다. 그러나 예에서와 같이 배열 또는 목록과 같은 변경 가능한 항목의 경우 매우 다를 수 있습니다.