【问题标题】:Update values in numpy array with other values in Python用 Python 中的其他值更新 numpy 数组中的值
【发布时间】:2020-06-03 21:49:12
【问题描述】:

给定以下数组:

a = np.array([[1,2,3],[4,5,6],[7,8,9]])

[[1 2 3]
 [4 5 6]
 [7 8 9]]

如何将某些值替换为其他值?

bad_vals = [4, 2, 6]
update_vals = [11, 1, 8]

我目前使用:

for idx, v in enumerate(bad_vals):
    a[a==v] = update_vals[idx]

这给出了:

[[ 1  1  3]
 [11  5  8]
 [ 7  8  9]]

但是替换具有许多值的大型数组相当慢。有什么好的选择吗?

输入数组可以更改为任何值(list/tupleslist),如果这可能是访问某些快速黑魔法所必需的。

编辑:

基于@Divakar 和@charlysotelo 的出色回答,我使用benchit 包对我的真实用例日期进行了快速比较。我的输入数据数组的 a 或多或少的比率为 100:1 (rows:columns),其中替换值数组的长度按 3 x rows 大小的顺序排列。

功能:

# current approach
def enumerate_values(a, bad_vals, update_vals):
    for idx, v in enumerate(bad_vals):
        a[a==v] = update_vals[idx]
    return a

# provided solution @Divakar
def map_values(a, bad_vals, update_vals):
    N = max(a.max(), max(bad_vals))+1
    mapar = np.empty(N, dtype=int)
    mapar[a] = a
    mapar[bad_vals] = update_vals
    out = mapar[a]
    return out

# provided solution @charlysotelo
def vectorize_values(a, bad_vals, update_vals):
    bad_to_good_map = {}
    for idx, bad_val in enumerate(bad_vals):
        bad_to_good_map[bad_val] = update_vals[idx]
    f = np.vectorize(lambda x: (bad_to_good_map[x] if x in bad_to_good_map else x))
    a = f(a)

    return a

# define benchit input functions
import benchit
funcs = [enumerate_values, map_values, vectorize_values]

# define benchit input variables to bench against
in_ = {
    n: (
        np.random.randint(0,n*10,(n,int(n * 0.01))), # array
        np.random.choice(n*10, n*3,replace=False), # bad_vals
        np.random.choice(n*10, n*3) # update_vals
    ) 
    for n in [300, 1000, 3000, 10000, 30000]
}

# do the bench
# btw: timing of bad approaches (my own function here) take time
t = benchit.timings(funcs, in_, multivar=True, input_name='Len')
t.plot(logx=True, grid=False)

【问题讨论】:

  • 值(正)是整数吗?我们可以因此制作一个类似[0,1,1,3,11,5,8] 的列表(从而定义映射)
  • 您可以使用来自 Fast replacement of values in a numpy array 的答案,方法是根据 bad_vals 和 update_vals 制作字典。
  • @WillemVanOnsem 是的,所有值都是正整数
  • @Divakar,是的,会给!只好睡一会了。。

标签: python arrays numpy set


【解决方案1】:

这是一种基于正数的暗示映射数组方法的方法-

def map_values(a, bad_vals, update_vals):
    N = max(a.max(), max(bad_vals))+1
    mapar = np.empty(N, dtype=int)
    mapar[a] = a
    mapar[bad_vals] = update_vals
    out = mapar[a]
    return out

示例运行 -

In [94]: a
Out[94]: 
array([[1, 2, 1],
       [4, 5, 6],
       [7, 1, 1]])

In [95]: bad_vals
Out[95]: [4, 2, 6]

In [96]: update_vals
Out[96]: [11, 1, 8]

In [97]: map_values(a, bad_vals, update_vals)
Out[97]: 
array([[ 1,  1,  1],
       [11,  5,  8],
       [ 7,  1,  1]])

基准测试

# Original soln
def replacevals(a, bad_vals, update_vals):
    out = a.copy()
    for idx, v in enumerate(bad_vals):
        out[out==v] = update_vals[idx]
    return out

给定样本的二维输入为nxn,而n 样本将被替换。让我们设置具有相同结构的输入数据集。

使用benchit 包(几个基准测试工具打包在一起;免责声明:我是它的作者)对建议的解决方案进行基准测试。

import benchit
funcs = [replacevals, map_values]
in_ = {n:(np.random.randint(0,n*10,(n,n)),np.random.choice(n*10,n,replace=False),np.random.choice(n*10,n)) for n in [3,10,100,1000,2000]}
t = benchit.timings(funcs, in_, multivar=True, input_name='Len')
t.plot(logx=True, save='timings.png')

剧情:

【讨论】:

  • 这是一个非常好的解决方案。对于我的实际用例,它比我的解决方案快 740 倍。谢谢你分享这个。也不错benchit 包。让我试着看看我是否可以在图表中结合其他解决方案(比我的方法快 55 倍)并用它来更新我的答案。再次感谢!
  • @Mattijn 是的,您可以使用函数名称将任何其他方法添加到 funcs = [replacevals, map_values] 中。那样应该很方便。如果您想分享,想看看您的图表。
  • @Divakar--Benchit 看起来很有趣。 benchit 与我使用过的Perfplot 相比如何?有什么优点/缺点?
  • @Divakar--好的,将在我的下一个基准测试中尝试一下。我看到 benchit 有两个优点:1)它在屏幕左上角显示测试环境信息,2)它有一个更好的网格(水平和垂直)来显示结果。
  • @Divakar--谢谢!我能够使用新版本在在线 Python 上运行您的基本测试。需要在 Python 规范文件中添加 `bench = "^0.0.3" 才能加载 benchit 及其依赖项,尽管它仍然会加载 bench-it。
【解决方案2】:

这实际上取决于数组的大小,以及从坏整数到好整数的映射大小。

对于大量的坏到好整数 - 下面的方法更好:

import numpy as np
import time

ARRAY_ROWS = 10000
ARRAY_COLS = 1000

NUM_MAPPINGS = 10000

bad_vals = np.random.rand(NUM_MAPPINGS)
update_vals = np.random.rand(NUM_MAPPINGS)

bad_to_good_map = {}
for idx, bad_val in enumerate(bad_vals):
    bad_to_good_map[bad_val] = update_vals[idx]

# np.vectorize with mapping
# Takes about 4 seconds
a = np.random.rand(ARRAY_ROWS, ARRAY_COLS)
f = np.vectorize(lambda x: (bad_to_good_map[x] if x in bad_to_good_map else x))
print (time.time())
a = f(a)
print (time.time())


# Your way
# Takes about 60 seconds
a = np.random.rand(ARRAY_ROWS, ARRAY_COLS)
print (time.time())
for idx, v in enumerate(bad_vals):
    a[a==v] = update_vals[idx]
print (time.time())

运行上面的代码,np.vectorize(lambda) 方法用了不到 4 秒的时间就完成了——而你的方法用了将近 60 秒。但是,将 NUM_MAPPINGS 设置为 100,您的方法对我来说只需不到一秒 - 比 np.vectorize 方式的 2 秒要快。

【讨论】:

  • 非常感谢您分享您的解决方案,与我在真实数据中的解决方案相比,它提供了 55 倍的加速。 @Divakar 提供的解决方案虽然令人惊叹,但其速度提升了 741 倍。再次感谢!
猜你喜欢
  • 2019-03-04
  • 2021-11-21
  • 1970-01-01
  • 2011-02-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-19
相关资源
最近更新 更多