【问题标题】:Vectorized assignment in NumpyNumpy 中的向量化赋值
【发布时间】:2018-12-08 02:49:27
【问题描述】:

假设我有一个大型 2D numpy 数组,例如1000x1000 个元素。我还有两个长度为 L 的一维整数数组和一个相同长度的浮点一维数组。如果我想根据整数数组简单地将浮点数分配到原始数组中的不同位置,我可以这样写:

mat = np.zeros((1000,1000))
int1 = np.random.randint(0,999,size=(50000,))
int2 = np.random.randint(0,999,size=(50000,))
f = np.random.rand(50000)
mat[int1,int2] = f

但是如果有冲突,即多个浮点数对应于单个位置,除了最后一个之外的所有浮点数都将被覆盖。有没有办法以某种方式聚合所有的碰撞,例如落在同一位置的所有花车的平均值或中位数?我想利用矢量化并希望避免解释器循环。

谢谢!

【问题讨论】:

  • 考虑ufunc .at 方法,例如np.add.at indexing with array
  • 如果你想要平均值并且没有条目可以更新的最大次数,你需要一个 3D 数组来存储所有值,然后在最后取平均值。跨度>

标签: python arrays numpy


【解决方案1】:

根据 hpaulj 的建议,以下是在发生碰撞时获取平均值的方法:

import numpy as np

mat = np.zeros((2,2))
int1 = np.zeros(2, dtype=int)
int2 = np.zeros(2, dtype=int)
f = np.array([0,1])

np.add.at(mat, [int1, int2], f)
n = np.zeros((2,2))
np.add.at(n, [int1, int2], 1)
mat[int1, int2] /= n[int1, int2]
print(mat)

array([[0.5, 0. ],
       [0. , 0. ]])

【讨论】:

  • 非常聪明高效!不过没有中位数的版本吗?
  • 我也玩过中位数的小游戏,但想不出一个简单的方法来获得它。 (并不意味着它不存在:)。主要原因是您需要保留所有碰撞的列表来计算中位数,这迫使您(我相信)使用与 numpy 向量化不能很好集成的 python 列表......
【解决方案2】:

您可以在pandas 中操作您的数据,然后进行分配。

开始
mat = np.zeros((1000,1000))
a = np.random.randint(0,999,size=(50000,))
b = np.random.randint(0,999,size=(50000,))
c = np.random.rand(50000)

你可以定义一个函数

def get_aggregated_collisions(a,b,c):
    df = pd.DataFrame({'x':a, 'y':b, 'v':c})
    df['coord'] = df[['x','y']].apply(tuple,1)
    d = df.groupby('coord').agg({"v":'mean','x':'first', 'y':'first'}).to_dict('list')
    return d

然后

d = get_aggregated_collisions(a,b,c)
mat[d['x'], d['y']] = d['v']

整个操作(包括生成矩阵,np.random 等)运行良好

1.05 s ± 30.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

创建tuple 坐标背后的想法是有一个可散列的选项来按坐标对值进行分组。也许还有更聪明的方法可以做到这一点:) 随时接受建议。

【讨论】:

  • 您不需要创建tuple。只需根据xy 列进行分组。
  • @CindyAlmighty 是的,确实如此! :) 只需将"mean" 更改为"meadian" 或您可能想要的任何操作。
  • @Tai 这两天没睡哈哈:谢谢指点。我不会编辑以免您的答案变得多余。谢谢;}
  • @RafaelC 听起来你日子不好过。随意编辑您的答案,为用户提供更好的信息。我不介意。
【解决方案3】:

我的试验基于 RafaelC 的回答。

先在["x", "y"]上做groupby,然后每组取meanmedian,最后用reset_index()重置索引。

import pandas as np
# setup
mat = np.zeros((1000,1000))
a = np.random.randint(0,999,size=(50000,))
b = np.random.randint(0,999,size=(50000,))
c = np.random.rand(50000)
# Start here
df = pd.DataFrame({"x":a, "y":b, "val":c})
v = df.groupby(["x", "y"]).mean().reset_index()
mat[v["x"], v["y"]] += v["val"]

如果需要中位数,将v修改为

v = df.groupby(["x", "y"]).median().reset_index()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-24
    • 2020-11-02
    相关资源
    最近更新 更多