8

我有numpy.arrays 其中列包含不同的数据类型,并且列也应该具有应用于它们的不同功能。我也有数组中的函数。

比方说:

a = array([[ 1, 2.0, "three"],
           [ 4, 5.0, "six"  ]], dtype=object)

functions_arr = array([act_on_int, act_on_float, act_on_str])

我当然可以通过划分事物来想办法做到这一点,但对我来说最自然的一件事是将其视为与广播的元素乘法,并将函数视为运算符。所以我想做类似的事情

functions_arr*a

并得到效果

array([[act_on_int(1), act_on_float(2.0), act_on_str("three")],
       [act_on_int(4), act_on_float(5.0), act_on_str("six")  ]])

你知道按照这些思路实现目标的方法吗?

编辑:我将问题中数组的定义更改为包括dtype=[object],因为人们指出这对于数组以我想要的方式存储类型很重要。

感谢您的回答和评论!我已经接受了 senderles 的回答,并且觉得这与我的想法非常接近。

由于我似乎对如何将运算视为乘法感到有些困惑,让我用另一个例子来澄清一下:

如您所知,这样的操作:

v = array([1,2,3])
u = array([[5,7,11],
           [13,17,19]])
v*u

v将在行u和产量上广播

array([[ 1*5, 2*7,  3*11],
       [1*13, 2*17, 3*19]])

IE

array([[ 5, 14, 33],
       [13, 34, 57]])

如果我们现在要替换v为例如我们将拥有的del 运算符(以下实际上不是工作 python 代码:)

V = array([(d/dx),(d/dy),(d/dz)])
u = array([[5,7,11],
           [13,17,19]])
V*u

屈服(精神上)

array([[(d/dx)5, (d/dy)7, (d/dz)11]],
       [(d/dx)13,(d/dy)17,(d/dz)19]])

我承认对一堆常数求导并不是最有趣的运算,所以请随意用,和u中的一些符号数学表达式替换。无论如何,我希望这至少让我的推理和标题中关于“(使用 python 函数作为运算符?)”的一点更清楚。xyz

4

3 回答 3

4

正如 Sven Marnach 提醒我的那样,您创建的数组可能是 Python 对象数组。对它们的任何操作都可能比纯numpy操作慢得多。但是,只要您实际上并不期望这会非常快,您就可以很容易地完成您所要求的事情!这与AFoglia的建议没有太大区别,但更接近于您所要求的:

>>> a = numpy.array([[ 1, 2.0, "three"],
...                  [ 4, 5.0, "six"  ]], dtype=object)
>>> funcs = [lambda x: x + 10, lambda x: x / 2, lambda x: x + '!']
>>> apply_vectorized = numpy.vectorize(lambda f, x: f(x), otypes=[object])
>>> apply_vectorized(funcs, a)
array([[11, 1.0, three!],
       [14, 2.5, six!]], dtype=object)

在这里也呼应AFoglia,很有可能您最好使用记录数组-这使您可以随意划分数组,并使用numpy ufunc以更自然的方式使用它-这很多比 Python 函数更快,通常:

rec.array([(1, 2.0, 'three'), (4, 5.0, 'six')], 
      dtype=[('int', '<i8'), ('float', '<f8'), ('str', '|S10')])
>>> a['int']
array([1, 4])
>>> a['float']
array([ 2.,  5.])
>>> a['str']
rec.array(['three', 'six'], 
      dtype='|S10')
>>> a['int'] += 10
>>> a['int']
array([11, 14])
于 2012-07-05T15:22:06.580 回答
3

您正在寻找内置函数zip()

一个简单的例子lists

>>> a=[[ 1, 2.0, "three"],[ 4, 5.0, "six"  ]]

>>> funcs=[lambda x:x**2,lambda y:y*2,lambda z:z.upper()]

>>> [[f(v) for v,f in zip(x,funcs)]for x in a]
[[1, 4.0, 'THREE'], [16, 10.0, 'SIX']]
于 2012-07-05T13:55:08.733 回答
3

它不是广播,因为原始数组只有一维。看起来它有 2 个维度,因为每个元素都有三个成员(一个 int、一个浮点数和一个字符串),但对于 numpy,这只是类型,并且维度数是 1。

它也不是乘法,因为您将函数应用于每个元素。(乘法并不比加法更多,因此functions_arr * a具有误导性的语法。)

尽管如此,您仍然可以编写与您想要的内容类似的东西。我会尝试 numpy.vectorize。未经测试,并假设输出 dtype 与原始数组相同。我想它会像...

def act_on_row(row) :
    return (act_on_int(row["int_field"]),
            act_on_float(row["float_field"]),
            act_on_str(row["str_field"]))

act_on_array = numpy.vectorize(act_on_row, otypes=[a.dtype])

acted_on = act_on_array(a)

我从未尝试过矢量化,而且我不知道使用结构化 dtype 是否很棘手,但这应该可以帮助您入门。

不过,更简单的解决方案是按字段循环遍历数组。

rslt = numpy.empty((len(a),), dtype=a.dtype)

rslt["int_field"] = act_on_int(a["int_field"])
rslt["float_field"] = act_on_float(a["float_field"])
rslt["str_field"] = act_on_str(a["str_field"])

(您可能需要对每个单独的函数进行矢量化,具体取决于它们的作用。)

于 2012-07-05T14:33:30.247 回答