2016-01-27 16 views
88

Gần đây tôi đã áp dụng giải pháp this để lấy trung bình mỗi N hàng ma trận. Mặc dù giải pháp hoạt động nói chung tôi gặp vấn đề khi được áp dụng cho một mảng 7x1. Tôi đã nhận thấy rằng vấn đề là khi sử dụng toán tử -=. Để thực hiện một ví dụ nhỏ:Sự khác biệt giữa a - = b và a = a - b trong Python

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 

mà kết quả đầu ra:

[1 1 2] 
[1 1 1] 

Vì vậy, trong trường hợp của một mảng a -= b tạo ra một kết quả khác nhau hơn a = a - b. Tôi nghĩ cho đến bây giờ hai cách này giống hệt nhau. Sự khác biệt là gì?

Phương pháp tôi đề cập đến để tính tổng N hàng trong ma trận đang hoạt động, ví dụ: cho ma trận 7x4 nhưng không phải cho mảng 7x1?

Trả lời

79

Lưu ý: sử dụng các thao tác tại chỗ trên các mảng NumPy chia sẻ bộ nhớ không còn là vấn đề trong phiên bản 1.13.0 trở đi (xem chi tiết here). Hai hoạt động sẽ tạo ra kết quả tương tự. Câu trả lời này chỉ áp dụng cho các phiên bản trước của NumPy.


Mảng đột biến trong khi chúng đang được sử dụng trong tính toán có thể dẫn đến kết quả không mong muốn!

Trong ví dụ trong câu hỏi, trừ với -= đổi các yếu tố thứ hai của a và sau đó ngay lập tức sử dụng mà sửa đổi Yếu tố thứ hai trong hoạt động trên các yếu tố thứ ba của a.

Đây là những gì xảy ra với a[1:] -= a[:-1] từng bước:

  • a là mảng với các dữ liệu [1, 2, 3].

  • Chúng tôi có hai chế độ xem trên dữ liệu này: a[1:][2, 3]a[:-1][1, 2].

  • Các tại chỗ trừ -= bắt đầu. Phần tử đầu tiên của a[:-1], 1, được trừ từ phần tử đầu tiên của a[1:]. Điều này đã sửa đổi a thành [1, 1, 3]. Bây giờ chúng ta có mà a[1:] là một cái nhìn của dữ liệu [1, 3], và a[:-1] là một cái nhìn của dữ liệu [1, 1] (phần tử thứ hai của mảng a đã được thay đổi).

  • a[:-1] tại là [1, 1] và NumPy bây giờ phải trừ yếu tố thứ hai của nó đó là 1 (không 2 nữa!) Từ phần tử thứ hai của a[1:]. Điều này làm cho a[1:] chế độ xem giá trị [1, 2].

  • a hiện là một mảng có giá trị [1, 1, 2].

b[1:] = b[1:] - b[:-1] không có vấn đề này vì b[1:] - b[:-1] tạo ra một mới mảng đầu tiên và sau đó gán các giá trị trong mảng này để b[1:]. Nó không tự sửa đổi b trong khi trừ, do đó, các chế độ xem b[1:]b[:-1] không thay đổi.


Lời khuyên chung là tránh sửa đổi một chế độ xem tại chỗ khác nếu chúng trùng lặp. Điều này bao gồm các toán tử -=, *=, v.v. và sử dụng tham số out trong các chức năng phổ quát (như np.subtractnp.multiply) để ghi lại một trong các mảng.

+4

Tôi thích câu trả lời này nhiều hơn cho câu trả lời hiện được chấp nhận. Nó sử dụng ngôn ngữ rất rõ ràng để hiển thị hiệu quả của việc sửa đổi các đối tượng biến đổi tại chỗ. Quan trọng hơn, đoạn cuối cùng trực tiếp nhấn mạnh tầm quan trọng của việc sửa đổi tại chỗ cho các chế độ xem chồng chéo, nên là bài học đưa về nhà từ câu hỏi này. – Reti43

42

Bên trong, sự khác biệt là điều này:

a[1:] -= a[:-1] 

tương đương với điều này:

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))) 

trong khi điều này:

b[1:] = b[1:] - b[:-1] 

bản đồ này:

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))) 

Trong một số trường hợp, __sub__()__isub__() hoạt động theo cách tương tự. Nhưng các đối tượng có thể thay đổi sẽ biến đổi và tự trả lại khi sử dụng __isub__(), trong khi chúng phải trả về một đối tượng mới với __sub__().

Áp dụng các thao tác cắt trên các đối tượng có khối u tạo ra các chế độ xem trên chúng, vì vậy việc sử dụng chúng trực tiếp truy cập vào bộ nhớ của đối tượng "gốc".

11

The docs nói:

Ý tưởng đằng sau nhiệm vụ tăng cường bằng Python là nó không phải là chỉ là một cách dễ dàng hơn để viết thực tế phổ biến của lưu trữ các kết quả của một phép toán hai ngôi trong trái của nó toán hạng, nhưng cũng là một cách cho toán hạng bên trái được đề cập đến để biết rằng cần hoạt động 'trên chính nó', thay vì tạo bản sao sửa đổi của chính bản thân số .

Như một quy tắc ngón tay cái, phép trừ tăng cường (x-=y) là x.__isub__(y), cho TRÊN hoạt động -place NẾU có thể, khi phép trừ bình thường (x = x-y) là x=x.__sub__(y). Trên các đối tượng không thể thay đổi giống như các số nguyên thì nó tương đương nhau. Nhưng đối với những người có thể thay đổi như mảng hoặc danh sách, như trong ví dụ của bạn, chúng có thể là những thứ rất khác nhau.

Các vấn đề liên quan