我最近应用this解决方案来平均每N行矩阵。
虽然解决方案通常起作用,但在应用于7x1阵列时遇到了问题。我注意到问题出在使用empty_rows = df['columnname'].isnull()
运算符时。
举一个小例子:
-=
输出:
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
不同。直到现在我才想到这两种方式完全相同。有什么区别?
为什么我提到的用于求和矩阵中每N行的方法是有用的,例如对于7x4矩阵而不是7x1阵列?
答案 0 :(得分:79)
注意:在版本1.13.0以后不再存在共享内存的NumPy阵列上使用就地操作(请参阅详细信息here)。这两个操作将产生相同的结果。此答案仅适用于早期版本的NumPy。
在计算中使用数组进行变异会导致意外结果!
在问题的示例中,使用-=
减法修改a
的第二个元素,然后立即在第三个元素的操作中使用修改的第二个元素a
。
以下是a[1:] -= a[:-1]
一步一步发生的事情:
a
是包含数据[1, 2, 3]
的数组。
我们对这些数据有两种看法:a[1:]
为[2, 3]
,a[:-1]
为[1, 2]
。
就地减法-=
开始。从a[:-1]
的第一个元素中减去a[1:]
的第一个元素1。这已将a
修改为[1, 1, 3]
。现在我们知道a[1:]
是数据[1, 3]
的视图,a[:-1]
是数据[1, 1]
的视图(数组a
的第二个元素已经改变了。
a[:-1]
现在是[1, 1]
,NumPy现在必须从{{1}的第二个元素中减去它的第二个元素,它是1 (不再是2!) }}。这使得a[1:]
成为值a[1:]
的视图。
[1, 2]
现在是一个值为a
的数组。
[1, 1, 2]
没有此问题,因为b[1:] = b[1:] - b[:-1]
首先创建 new 数组,然后将此数组中的值分配给b[1:] - b[:-1]
。在减法期间,它不会修改b[1:]
本身,因此视图b
和b[1:]
不会更改。
一般建议是避免将一个视图与另一个视图重叠,如果它们重叠。这包括运算符b[:-1]
,-=
等,并使用通用函数中的*=
参数(如out
和np.subtract
)来回写其中一个数组。
答案 1 :(得分:43)
在内部,区别在于:
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对象应用切片操作会在它们上创建视图,因此使用它们可以直接访问"原始"的内存。对象
答案 2 :(得分:11)
The docs说:
Python中增强赋值背后的想法是它不是 只是一种更简单的方式来编写存储的常见做法 左手操作数中的二进制操作的结果,但也是a 有问题的左手操作数的方式,知道它应该 “自己”操作,而不是创建一个修改后的副本 本身。
作为一个拇指规则,增强减法(x-=y
)为x.__isub__(y)
, IN - 场操作 IF 可能,正常减法时( x = x-y
)是x=x.__sub__(y)
。在像整数这样的非可变对象上它是等价的。但是对于像数组或列表这样的可变类,如在你的例子中,它们可能是非常不同的东西。