在不包含列表理解的情况下更改过滤后的二维数组中的元素,同时保持对原始元素的引用
Changing elements in a filtered 2d array without list comprehension while keeping reference to original
我不确定如何表达这个问题,但这是我正在尝试做的事情。
arr_first = np.array([[0,0,0,0],[0,0,0,0],[1,1,1,0],[1,1,1,0],[1,1,1,0],[1,1,2,0],[1,1,2,0],[2,2,2,0]])
arr_second = np.array([[0,0,0],[1,1,1],[1,1,2],[2,2,2]])
我正在尝试通过 arr_second
的前三个元素过滤 arr_first
,导致...
[array([0, 0, 0, 0]), array([0, 0, 0, 0])]
[array([1, 1, 1, 0]), array([1, 1, 1, 0]), array([1, 1, 1, 0])]
[array([1, 1, 2, 0]), array([1, 1, 2, 0])]
[array([2, 2, 2, 0])]
然后,使用过滤后的二维数组,将 32 添加到每个二维数组中的一个数组的第四个元素,如下所示:
[[ 0 0 0 0]
[ 0 0 0 32]
[ 1 1 1 0]
[ 1 1 1 0]
[ 1 1 1 32]
[ 1 1 2 0]
[ 1 1 2 32]
[ 2 2 2 32]]
并将该数据保存到原始 arr_first
。
我目前使用的方法是使用 python 列表理解语法:
for i in range(len(arr_second)):
filtered = [row for row in arr_first if
arr_second[i][0] == row[0] and arr_second[i][1] == row[1] and arr_second[i][2] == row[2]]
choosen_block = random.choice(filtered)
choosen_block[3] += 32
print(arr_first)
这可行,但在大型数据集中可能会非常慢。因此,我尝试使用 numpy 的 in1d:
进行过滤
for i in range(len(arr_second)):
filtered = arr_first[np.in1d(arr_first[:, 0], arr_second[i][0]) &
np.in1d(arr_first[:, 1], arr_second[i][1]) &
np.in1d(arr_first[:, 2], arr_second[i][2])]
choosen_block = random.choice(filtered)
choosen_block[3] += 32
但此方法的问题是更改不再保存在 arr_first
中,这与列表理解方法不同,因为 arr_first
不再通过引用传递到 filtered
.
我想知道是否有人可以通过使 filtered
中的更改也出现在 arr_first
中而不是必须制作另一个列表并附加循环来解决这个问题filtered
到它。
您可以使用 Pandas 到 groupby
、sample
,并更新 arr_first
。
import pandas as pd
df = pd.DataFrame(arr_first)
inner_len = len(arr_first[0,:])
update_amt = 32
update_ix = 3
df.iloc[(df.groupby(list(range(inner_len)))
.apply(lambda x: x.sample().index.values[0]).values),
update_ix] += update_amt
arr_first
[[ 0 0 0 0]
[ 0 0 0 32]
[ 1 1 1 0]
[ 1 1 1 32]
[ 1 1 1 0]
[ 1 1 2 32]
[ 1 1 2 0]
[ 2 2 2 32]]
解释
Pandas 让我们可以根据唯一的行值集对 arr_first
进行分组,例如[1,1,1,0]
。我将 groupby
过程缩写为 range()
,但命令实际上只是说:"Group by column 0, then column 1, then column 2, then column 3"。这有效地按 arr_first
中每一行的完整值集进行了分组。这似乎有效地模仿了您通过 arr_second
中的值匹配 arr_first
行的方法。
一旦我们得到了组中的行,我们就可以 sample
每组中的其中一行,并获取它的索引。
然后,将选定的索引用于添加更新步骤。
即使我们正在更新 df
,arr_first
也会更新,因为它在 df
的创建过程中(有点)通过引用传递.
我倾向于在 Pandas 中思考,但可能有与这些步骤等效的 Numpy。
以下是使您的方法奏效的方法。
首先,为什么 list comp 可以就地工作,而 in1d 不能?列表 comp 对 arr_first 的各个行进行操作,每个这样的行都是一个 "view",即对 arr_first 的引用。相比之下,in1d soln 创建一个掩码,然后将其应用于阵列。使用掩码是 "fancy" 或 "advanced" 索引的一种形式。由于 orig 数组花式索引所指的子集通常不能用偏移量和步幅表示,因此这会强制复制,之后无论您做什么都不会影响 orig 数组。
一个简单的解决方法是不敷面膜。而是将其转换为行索引向量并直接在此向量上使用 random.choice:
import numpy as np
import random
arr_first = np.array([[0,0,0,0],[0,0,0,0],[1,1,1,0],[1,1,1,0],[1,1,1,0],[1,1,2,0],[1,1,2,0],[2,2,2,0]])
arr_second = np.array([[0,0,0],[1,1,1],[1,1,2],[2,2,2]])
for i in range(len(arr_second)):
filtered_idx = np.where(np.in1d(arr_first[:, 0], arr_second[i][0]) &
np.in1d(arr_first[:, 1], arr_second[i][1]) &
np.in1d(arr_first[:, 2], arr_second[i][2]))[0]
choosen_block = random.choice(filtered_idx)
arr_first[choosen_block, 3] += 32
print(arr_first)
示例输出:
[[ 0 0 0 0]
[ 0 0 0 32]
[ 1 1 1 32]
[ 1 1 1 0]
[ 1 1 1 0]
[ 1 1 2 0]
[ 1 1 2 32]
[ 2 2 2 32]]
我不确定如何表达这个问题,但这是我正在尝试做的事情。
arr_first = np.array([[0,0,0,0],[0,0,0,0],[1,1,1,0],[1,1,1,0],[1,1,1,0],[1,1,2,0],[1,1,2,0],[2,2,2,0]])
arr_second = np.array([[0,0,0],[1,1,1],[1,1,2],[2,2,2]])
我正在尝试通过 arr_second
的前三个元素过滤 arr_first
,导致...
[array([0, 0, 0, 0]), array([0, 0, 0, 0])]
[array([1, 1, 1, 0]), array([1, 1, 1, 0]), array([1, 1, 1, 0])]
[array([1, 1, 2, 0]), array([1, 1, 2, 0])]
[array([2, 2, 2, 0])]
然后,使用过滤后的二维数组,将 32 添加到每个二维数组中的一个数组的第四个元素,如下所示:
[[ 0 0 0 0]
[ 0 0 0 32]
[ 1 1 1 0]
[ 1 1 1 0]
[ 1 1 1 32]
[ 1 1 2 0]
[ 1 1 2 32]
[ 2 2 2 32]]
并将该数据保存到原始 arr_first
。
我目前使用的方法是使用 python 列表理解语法:
for i in range(len(arr_second)):
filtered = [row for row in arr_first if
arr_second[i][0] == row[0] and arr_second[i][1] == row[1] and arr_second[i][2] == row[2]]
choosen_block = random.choice(filtered)
choosen_block[3] += 32
print(arr_first)
这可行,但在大型数据集中可能会非常慢。因此,我尝试使用 numpy 的 in1d:
进行过滤for i in range(len(arr_second)):
filtered = arr_first[np.in1d(arr_first[:, 0], arr_second[i][0]) &
np.in1d(arr_first[:, 1], arr_second[i][1]) &
np.in1d(arr_first[:, 2], arr_second[i][2])]
choosen_block = random.choice(filtered)
choosen_block[3] += 32
但此方法的问题是更改不再保存在 arr_first
中,这与列表理解方法不同,因为 arr_first
不再通过引用传递到 filtered
.
我想知道是否有人可以通过使 filtered
中的更改也出现在 arr_first
中而不是必须制作另一个列表并附加循环来解决这个问题filtered
到它。
您可以使用 Pandas 到 groupby
、sample
,并更新 arr_first
。
import pandas as pd
df = pd.DataFrame(arr_first)
inner_len = len(arr_first[0,:])
update_amt = 32
update_ix = 3
df.iloc[(df.groupby(list(range(inner_len)))
.apply(lambda x: x.sample().index.values[0]).values),
update_ix] += update_amt
arr_first
[[ 0 0 0 0]
[ 0 0 0 32]
[ 1 1 1 0]
[ 1 1 1 32]
[ 1 1 1 0]
[ 1 1 2 32]
[ 1 1 2 0]
[ 2 2 2 32]]
解释
Pandas 让我们可以根据唯一的行值集对
arr_first
进行分组,例如[1,1,1,0]
。我将groupby
过程缩写为range()
,但命令实际上只是说:"Group by column 0, then column 1, then column 2, then column 3"。这有效地按arr_first
中每一行的完整值集进行了分组。这似乎有效地模仿了您通过arr_second
中的值匹配arr_first
行的方法。一旦我们得到了组中的行,我们就可以
sample
每组中的其中一行,并获取它的索引。然后,将选定的索引用于添加更新步骤。
即使我们正在更新
df
,arr_first
也会更新,因为它在df
的创建过程中(有点)通过引用传递.
我倾向于在 Pandas 中思考,但可能有与这些步骤等效的 Numpy。
以下是使您的方法奏效的方法。
首先,为什么 list comp 可以就地工作,而 in1d 不能?列表 comp 对 arr_first 的各个行进行操作,每个这样的行都是一个 "view",即对 arr_first 的引用。相比之下,in1d soln 创建一个掩码,然后将其应用于阵列。使用掩码是 "fancy" 或 "advanced" 索引的一种形式。由于 orig 数组花式索引所指的子集通常不能用偏移量和步幅表示,因此这会强制复制,之后无论您做什么都不会影响 orig 数组。
一个简单的解决方法是不敷面膜。而是将其转换为行索引向量并直接在此向量上使用 random.choice:
import numpy as np
import random
arr_first = np.array([[0,0,0,0],[0,0,0,0],[1,1,1,0],[1,1,1,0],[1,1,1,0],[1,1,2,0],[1,1,2,0],[2,2,2,0]])
arr_second = np.array([[0,0,0],[1,1,1],[1,1,2],[2,2,2]])
for i in range(len(arr_second)):
filtered_idx = np.where(np.in1d(arr_first[:, 0], arr_second[i][0]) &
np.in1d(arr_first[:, 1], arr_second[i][1]) &
np.in1d(arr_first[:, 2], arr_second[i][2]))[0]
choosen_block = random.choice(filtered_idx)
arr_first[choosen_block, 3] += 32
print(arr_first)
示例输出:
[[ 0 0 0 0]
[ 0 0 0 32]
[ 1 1 1 32]
[ 1 1 1 0]
[ 1 1 1 0]
[ 1 1 2 0]
[ 1 1 2 32]
[ 2 2 2 32]]