8

好的,所以在阅读了关于 numpy 结构化数组的教程之后,我可以创建一些简单的示例:

from numpy import array, ones
names=['scalar', '1d-array', '2d-array']
formats=['float64', '(3,)float64', '(2,2)float64']
my_dtype = dict(names=names, formats=formats)
struct_array1 = ones(1, dtype=my_dtype)
struct_array2 = array([(42., [0., 1., 2.], [[5., 6.],[4., 3.]])], dtype=my_dtype)

(我的预期用例将包含三个以上的条目,并且将使用非常长的一维数组。)所以,在我们尝试执行一些基本数学之前,一切都很顺利。我收到以下所有错误:

struct_array1 + struct_array2
struct_array1 * struct_array2
1.0 + struct_array1
2.0 * struct_array2

显然,即使是最简单的结构化数组也不支持简单的运算符(+、-、*、/)。还是我错过了什么?我应该看其他一些软件包(不要说 Pandas,因为这完全是矫枉过正)?这似乎是一个明显的能力,所以我有点傻眼。但是在网上很难找到任何关于这个的喋喋不休。这不会严重限制结构化数组的有用性吗?为什么有人会使用结构数组而不是打包到字典中的数组?是否有技术原因导致这可能难以解决?或者,如果正确的解决方案是执行繁重的重载工作,那么如何在保持操作快速的同时完成呢?

4

3 回答 3

4

numpy结构化数组文档页面上,大多数示例都涉及混合数据类型 - 浮点数、整数和字符串。在 SO 上,大多数结构化数组问题都与从 CSV 文件加载混合数据有关。另一方面,在您的示例中,该结构的主要目的似乎是为“列”命名。

您可以对命名列进行数学运算,例如

struct_array1['scalar']+struct_array2['scalar']
struct_array1['2d-array']+struct_array2['2d-array']

您还可以“迭代”字段:

for n in my_dtype['names']:
    print a1[n]+a2[n]

是的,为此,将这些数组值放入字典或对象的属性中也同样有效。

但是,考虑到 CSV 案例,有时我们想讨论 CSV 或结构化数组的特定“行”,例如struct_array[0]. 这样的“行”是一个值的元组。

在任何情况下,其中的主要数据结构numpy都是数值的多维数组,并且大部分代码都围绕数字数据类型 - float、int 等。结构化数组是对此的概括,使用的元素基本上只是固定的字节集。这些字节的解释方式由dtype.

想想 MATLAB 是如何演变的——首先出现的是矩阵,然后是单元格(如 Python 列表),然后是结构体,最后是类和对象。Python 已经有了列表、字典和对象。numpy添加数组。它不需要重新发明一般的 Python 结构。

我倾向于定义这样的类:

class Foo(object):
    def __init__(self):
        self.scalar = 1
        self._1d_array = np.arange(10)
        self._2d_array = np.array([[1,2],[3,4]])

并仅实现应用程序真正需要的二进制操作。

于 2014-10-13T23:05:04.563 回答
3

对整个数组进行操作的另一种方法是使用文档中描述的 'union' dtype。在您的示例中,您可以通过添加“联合”字段并指定重叠的“偏移量”来扩展数据类型:

from numpy import array, ones, zeros

names=['scalar', '1d-array', '2d-array', 'union']
formats=['float64', '(3,)float64', '(2,2)float64', '(8,)float64']
offsets=[0, 8, 32, 0]
my_dtype = dict(names=names, formats=formats, offsets=offsets)
struct_array3=zeros((4,), dtype=my_dtype)

['union']现在可以将所有数据作为(n,8)数组访问

struct_array3['union'] # == struct_array3.view('(8,)f8')
struct_array3['union'].shape  # (4,8)

您可以对“联合”或任何其他字段进行操作:

struct_array3['union'] += 2
struct_array3['scalar']= 1

'union' 字段可以是另一个兼容的形状,例如'(2,4)float64'. 这种数组的“行”可能如下所示:

array([ (3.0, [0.0, 0.0, 0.0], [[2.0, 2.0], [0.0, 0.0]], 
      [[3.0, 0.0, 0.0, 0.0], [2.0, 2.0, 0.0, 0.0]])], 
      dtype={'names':['scalar','1d-array','2d-array','union'], 
             'formats':['<f8',('<f8', (3,)),('<f8', (2, 2)),('<f8', (2, 4))], 
             'offsets':[0,8,32,0], 
             'itemsize':64})
于 2014-10-24T16:23:19.710 回答
0

Okay, after more research I stumbled upon an answer. (No fault to hpaulj - the question was not posed all that well.) But I wanted to post in case someone else has a similar frustration.

The answer comes from the numpy documentation on ndarray.view. They specifically provide an example in which they "[create] a view on a structured array so it can be used in calculations".

So, I was frustrated that I couldn't operate on my example structured arrays. After all, I "see" my structured array as simply a collection of floating point numbers! Well, in the end all I needed was to inform numpy of this abstraction using "view". The errors in the question can be avoided using:

( struct_array1.view(dtype='float64') + struct_array2.view(dtype='float64') ).view(dtype=my_dtype)
( struct_array1.view(dtype='float64') + struct_array2.view(dtype='float64') ).view(dtype=my_dtype)
( 1.0 + struct_array2.view(dtype='float64') ).view(dtype=my_dtype)
( 2.0 * struct_array2.view(dtype='float64') ).view(dtype=my_dtype)

This is not as elegant as one might want, but at least numpy has the capability.

于 2014-10-17T00:49:27.197 回答