感谢大家的帮助。似乎最简洁地解释我的问题的是 zip 返回元组的迭代器,而不是列表,因此是不可变的。您可以在下面看到工作解决方案和原始问题代码。
解决方案:
f = np.array([1,2,3,4,5,6,7,8,9,10])
trials = [[0,1,2], [5,6,7], [8,9]]
trial_averages = []
for i in trials:
a = 0
for j in i:
a += f[j]
a /= len(i)
trial_averages.append(a)
trial_averages = np.array(trial_averages)
print('trials = {0}'.format(trials))
print('averages = {0}'.format(trial_averages))
trials = [[0, 1, 2], [5, 6, 7], [8, 9]]
averages = [ 2. 7. 9.5]
问题:
我正在尝试使用两个数组的zip来修改for循环中numpy数组的元素,并且修改后的数组不会更新。我错过了什么?
我已经创建了一个新的例子,因为我原来的例子似乎并没有说明我的确切问题。
新例子:
f = np.array([1,2,3,4,5,6,7,8,9,10])
trials = [[0,1,2], [5,6,7], [8,9]]
trial_averages = np.zeros(len(trials))
for i, j in zip(trials, trial_averages):
for k in i:
j += f[k]
j /= len(i)
print('trials = {0}'.format(trials))
print('averages = {0}'.format(trial_averages))
print('{0}, {1}'.format(i, j))
输出:
trials = [[0, 1, 2], [5, 6, 7], [8, 9]]
averages = [ 0. 0. 0. 0.]
[8, 9], 9.5
在这种情况下, j 是指向 trial_averages 中元素的指针,我希望在第二个for循环中将其更新为< em> f 指向试验中的列表,然后除以试验中列表的长度,计算值的平均值f 在每次试验中。
旧例子供参考:
工作示例:
A = np.array([1, 2, 3, 4], dtype=float)
B = np.zeros(len(A))
for i, j in zip(A, B):
j = i
print('A = {0}'.format(A))
print('B = {0}'.format(B))
print('{0}, {1}'.format(i, j))
输出:
A = [ 1. 2. 3. 4.]
B = [ 0. 0. 0. 0.]
4.0, 4.0
不会拉链传递指向输入数组中元素的指针吗?不应该分配j = i使B = [1。 2. 3. 4。]?
感谢您的帮助。
答案 0 :(得分:1)
在Python中,了解执行任务时会发生什么很重要。它是为变量分配新对象,还是修改(可变)对象。
在您的代码中j
是1d数组的元素。但是j=i
会将j
分配给i
分配给In [439]: A = np.arange(12).reshape(3,4)
In [440]: B = np.zeros_like(A)
In [441]: for i, j in zip(A,B):
...: print(i, j)
...: j[...] = i
...:
[0 1 2 3] [0 0 0 0]
[4 5 6 7] [0 0 0 0]
[ 8 9 10 11] [0 0 0 0]
In [442]: B
Out[442]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
的同一个对象。这会破坏与迭代产生的对象的所有连接。
相反,迭代2d数组的行:
i
此处,j
和j[...] =...
是1d数组,各自的2d数组的视图。 j
更改B
数组的值,因此会反映在父数组+=
中。
B
是另一个就地操作,对In [444]: for i, j in zip(A,B):
...: j += i
产生相同的更改:
j=i
请注意,这些规则也适用于在列表上进行迭代,但是返回传播到父级的更改是如何不同的。因此,了解j[:]=i
与In [446]: Al = A.tolist(); Bl = B.tolist()
In [447]: for i,j in zip(Al, Bl):
...: j[:] = i[::-1] # assign a reverse list
...: # j = would not work
In [449]: Bl
Out[449]: [[3, 2, 1, 0], [7, 6, 5, 4], [11, 10, 9, 8]]
(而不仅仅是在循环中)会发生什么事情非常重要。同时,不要养成以这种方式使用数组的习惯。尝试在不迭代阵列的情况下完成工作。
列表示例:
In [455]: x=np.arange(3).astype(np.float64)
In [456]: for j in x:
...: print(j,type(j))
...: j += 1
...: print(j)
...:
0.0 <class 'numpy.float64'>
1.0
1.0 <class 'numpy.float64'>
2.0
2.0 <class 'numpy.float64'>
3.0
In [457]: x
Out[457]: array([ 0., 1., 2.])
您尝试更改的值是1d数组的元素。它们实际上是标量(虽然包裹在一个numpy dtype中):
In [462]: x=[0,1,2]
In [463]: for j in x:
...: j += 1
...:
In [464]: x
Out[464]: [0, 1, 2]
或者对于平面列表的元素:
In [469]: x = np.arange(3)
In [470]: y = x[0]
In [471]: y += 1
In [472]: y
Out[472]: 1
In [473]: x
Out[473]: array([0, 1, 2])
In [474]: x[0] += 1
In [475]: x
Out[475]: array([1, 1, 2])
在上面的例子中,我只是更改了2d数组的行。值在循环内发生变化,但它们不会反向传播到源数组。
另一个例子:
j
迭代y=x[0]
更像x[0]+=
案例,而不是PictureBox
案例。
答案 1 :(得分:0)
在for循环中重置索引是个坏主意
其次你循环完成任何事情,因为既不是A也不是B在身体
答案 2 :(得分:0)
Python并没有真正拥有&#34; C风格&#34;指针,但可以指向对象的名称。有时对象可以更改,因此名称引用的数据将更改(可变对象)。有时候对象不能,所以你需要创建一个新对象并重新指向它(不可变对象)。
您在循环中绑定到zip
和i
的{{1}}输出是不可变值。说j
只会在j = i
指向名称 j
,但不会对以前称为{{1}的对象做任何事情}} (它仍然作为i
的一部分存在)。
如果你想复制数组的部分,使用切片,那么Numpy可以非常有效地为你做到这一点(见Indexing — NumPy User Guide):
j
答案 3 :(得分:0)
以下是如何使您的示例以最小的更改(箭头)工作;对于更有原则的方法,请参阅本文的底部:
import numpy as np
f = [1,2,3,4,5,6,7,8,9,10]
trials = [[0,1,2], [5,6,7], [8,9]]
trial_averages = np.zeros(len(trials))
for i, j in zip(trials, trial_averages[:, None]): # <----
for k in i:
j += f[k]
j /= len(i)
print('trials = {0}'.format(trials))
print('averages = {0}'.format(trial_averages))
print('{0}, {1}'.format(i, j))
A = np.array([1, 2, 3, 4], dtype=float)
B = np.zeros(len(A))
for i, j in zip(A, B[:, None]): # <----
j[...] = i # <----
print('A = {0}'.format(A))
print('B = {0}'.format(B))
print('{0}, {1}'.format(i, j))
输出:
trials = [[0, 1, 2], [5, 6, 7], [8, 9]]
averages = [ 2. 7. 9.5]
[8, 9], [ 9.5]
A = [ 1. 2. 3. 4.]
B = [ 1. 2. 3. 4.]
4.0, [ 4.]
正如您在第一个示例中所看到的,使j
变为可变就足够了。在第二个示例中,我们还必须将分配更改为__setitem__
受控制的分配。
创建类似C指针之类的更冗长但更少hackish的方法是0d数组。您可以使用np.nditer
创建返回0d数组而不是裸值的迭代器。在许多方面,它们的行为类似于标量,但您可以使用[()]
“取消引用”它们,然后分配给它们。你的第二个例子看起来像这样,并产生与以前相同的输出:
ro, wo, rw = ['readonly'], ['writeonly'], ['readwrite']
A = np.array([1, 2, 3, 4], dtype=float)
B = np.zeros(len(A))
for i, j in np.nditer([A, B], [], [ro, wo]): # <----
j[()] = i # <----
print('A = {0}'.format(A))
print('B = {0}'.format(B))
print('{0}, {1}'.format(i, j))