在for循环中使用zip分配给numpy数组

时间:2017-12-20 22:28:07

标签: python arrays numpy

感谢大家的帮助。似乎最简洁地解释我的问题的是 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。]?

感谢您的帮助。

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

此处,jj[...] =...是1d数组,各自的2d数组的视图。 j更改B数组的值,因此会反映在父数组+=中。

B是另一个就地操作,对In [444]: for i, j in zip(A,B): ...: j += i 产生相同的更改:

j=i

请注意,这些规则也适用于在列表上进行迭代,但是返回传播到父级的更改是如何不同的。因此,了解j[:]=iIn [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;指针,但可以指向对象的名称。有时对象可以更改,因此名称引用的数据将更改(可变对象)。有时候对象不能,所以你需要创建一个新对象并重新指向它(不可变对象)。

您在循环中绑定到zipi的{​​{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))