Python Numpy索引-关于奇怪行为/不一致性的问题
这是一个基于我今天早些时候的一个问题的知识主题。我亲眼目睹了努比行为中的一些奇怪的矛盾 首先,如果运行此代码:Python Numpy索引-关于奇怪行为/不一致性的问题,python,numpy,Python,Numpy,这是一个基于我今天早些时候的一个问题的知识主题。我亲眼目睹了努比行为中的一些奇怪的矛盾 首先,如果运行此代码: A = ones((10,4)) view = A[:,1] view.fill(7) A 这将把第2列改为所有7列,因为数组从0开始索引,而切片只是同一矩阵的视图。太棒了,这正是我想要的 现在,如果运行以下命令: A = ones((10,4)) view = A[:,1:2] view.fill(7) A A = ones((10,4)) view = A[:,(1,2)] v
A = ones((10,4))
view = A[:,1]
view.fill(7)
A
这将把第2列改为所有7列,因为数组从0开始索引,而切片只是同一矩阵的视图。太棒了,这正是我想要的
现在,如果运行以下命令:
A = ones((10,4))
view = A[:,1:2]
view.fill(7)
A
A = ones((10,4))
view = A[:,(1,2)]
view.fill(7)
A
它将具有与第一个示例相同的效果。为什么a:b指定从a到b-1的列?这在语言中有具体的原因吗?如果我输入1:3,那应该是第1、2和3列,而不是第1和第2列
最后,如果您运行以下命令:
A = ones((10,4))
view = A[:,1:2]
view.fill(7)
A
A = ones((10,4))
view = A[:,(1,2)]
view.fill(7)
A
A上没有副作用。看起来,如果使用元组创建视图,它无法正确地在原始矩阵上传播任何其他副作用。有什么见解吗
为什么a:b指定从a到b-1的列
这是一个Python约定。普通列表也是如此,range(a,b)
将返回一个列表,其中包含a
到b-1
,但不包括b
。此约定的好处是,通过a:b
进行切片,其中a
和b
是数字,将返回b-a
元素/行/列,而不是更复杂的a-b+1
看起来,如果使用元组创建视图,它就无法正确地在原始矩阵上传播任何进一步的副作用
这是一种Numpy特性,因为它只能创建基于切片的视图;这些可以有效地实现,而基于元组的切片则不能。您可以使用以下代码段模拟此行为,该代码段显示了Python索引语法在后台的作用:
class FakeArray(object):
def __getitem__(self, idx):
return "You can't change the original FakeArray through me"
def __setitem__(self, idx, val):
print("We could set elements %r to %r here" % (idx, val))
现在试试
>>> A = FakeArray()
>>> A[1:2]
"You can't change the original FakeArray through me"
>>> A[1:2] = 'ham'
We could set elements slice(1, 2, None) to 'ham'
所以
A[1:2]
是A的缩写。uuu getitem\uuuuuuuuuuuuuuuuuuuuuuuuuuuuu(切片(1,2,无))
而A[1:2]='ham'
是A的缩写。uuu setitem\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。因为实际上涉及到两种不同的方法,所以切片的行为可能非常不同,这取决于它是否是赋值语句的一部分。在Numpy的例子中,这种差异与切片
和元组
对象之间存在微妙的相互作用。半开区间的使用并不特定于Numpy,而是在所有Python中使用。列表滑动的工作方式相同,range()
函数也是如此
与闭合间隔相比,使用半开放间隔有几个优点:
对于半开放区间,可以表示空的切片和范围,这对于闭合区间来说是困难的。这通常是有用的
半开放区间[a,b)
的长度由b-a
简单给出,对于封闭区间[a,b]
相邻的间隔更容易表达。假设我们有一个算法,在列表a
中的k
元素块上运行。比较Python中的实现
for i in range(0, len(a), k):
frobnicate(a[i:i + k])
对于封闭区间的实施情况:
for i in range(0, len(a) - 1, k):
frobnicate(a[i:i + k - 1])
代码中将出现大量的-1
,如果第一个间隔的右值等于第二个间隔的左值,则两个间隔相邻的属性将丢失
为什么a:b指定从a到b-1的列
这就是所有Python的工作方式,也是许多编程的传统。它允许许多事情的简单计算。例如,它允许切片长度x[a:a+n]
ben
并允许x[:n]
和x[n:]
将x
分成两部分。你会习惯它,从长远来看,大多数程序员都喜欢它
看起来,如果使用元组创建视图,它就无法正确地在原始矩阵上传播任何进一步的副作用
当你做A[:,(1,2)]
时,你没有视图,只有一个新的数组。当你只做切片时,比如A[:,1:3]
,你的数组的条带仍然有连续的内存,所以有视图是有意义的。当你通过使用iterable(想象一下,为了更好地理解你使用的(0,2)
),拥有像bahvior这样的视图将是低效和尴尬的。Edsger Dijkstra的一本好书:约定是有道理的,但是,我想,如果我想在视图中获取一列及其邻居,我需要执行col:col+2。最后的编辑在元组问题上更有意义。这是一个令人讨厌的白痴ncrasy,因为有时用元组来选择我需要的列更有用。@Bjornen:这是一种折衷。如果Python使用闭合区间约定,则必须用I:len(x)-1而不是I:len(x)来抓取I
。这是一种Numpy的特质。如果你知道像ndarray
这样的类型是如何工作的,那么这并不是那么独特或微妙。@MikeGraham:我想是的,但是np.ndarray
及其视图行为中涉及的各种设计选择对普通人来说并不明显(甚至是相当高级的)用户。事实上,相同的语法给出了一个切片的视图,但一个元组的新数组可以正确地称为一种特殊性,IMHO。只是为了记录,你不是用元组索引ndarray
,而是用ndarray
索引一个ndarray,“选择元组中的所有序列和标量都转换为intp索引数组。”我同意这种“特殊性”在您第一次遇到它时可能会有点混乱,但另一种选择是禁用“奇特索引”,并强制人们使用arra