numpy记录/结构数组是否仅能包含numpy无内容?

0 投票
1 回答
2487 浏览
提问于 2025-04-18 17:08

我有一组复杂的数据,需要进行距离计算。每条记录包含很多不同类型的数据,所以我觉得使用记录数组或结构化数组比较合适。问题是,当我进行距离计算时,scipy的空间距离函数需要数组,而记录数组是numpy的空类型(voids)。我该如何将记录数组变成numpy数组,而不是numpy的空类型呢?下面是我想说的一个非常简单的例子。

import numpy
import scipy.spatial.distance as scidist


input_data = [
    ('340.9', '7548.2', '1192.4', 'set001.txt'),
    ('546.7', '9039.9', '5546.1', 'set002.txt'),
    ('456.3', '2234.8', '2198.8', 'set003.txt'),
    ('332.1', '1144.2', '2344.5', 'set004.txt'),
]

record_array = numpy.array(input_data,
                           dtype=[('d1', 'float64'), ('d2', 'float64'), ('d3', 'float64'), ('file', '|S20')])

以下代码运行失败...

this_fails_and_makes_me_cry = record_array[['d1', 'd2', 'd3']]
scidist.pdist(this_fails_and_makes_me_cry)

我收到这个错误....

Traceback (most recent call last):
  File "/home/someguy/working_datasets/trial003/scrap.py", line 16, in <module>
    scidist.pdist(record_array[['d1', 'd2', 'd3']])
  File "/usr/lib/python2.7/dist-packages/scipy/spatial/distance.py", line 1093, in pdist
    raise ValueError('A 2-dimensional array must be passed.');
ValueError: A 2-dimensional array must be passed.

这个错误发生的原因是这个_fails_and_makes_me_cry是一个numpy的空类型数组。为了让它工作,我每次都得这样转换...

this_works = numpy.array(map(list, record_array[['d1', 'd2', 'd3']]))
scidist.pdist(this_works)

一开始就能创建一个包含numpy数组的记录数组吗?还是说numpy的记录/结构化数组只能用numpy的空类型?如果记录数组能以一种与scipy的空间距离函数兼容的格式来存储数据,那就太方便了,这样我就不用每次都转换了。这可能吗?

1 个回答

3
this_fails_and_makes_me_cry = record_array[['d1', 'd2', 'd3']]

这段代码创建了一个一维的结构化数组,里面有三个字段:d1d2d3。而pdist需要的是一个二维数组。下面是如何从record_array中提取出只有d字段的二维数组的一种方法。

(注意: 如果你想用来计算距离的字段在结构化数组record_array中不是连续的,下面的方法就不适用了。那种情况下请看下面的替代方案。)

首先,我们创建一个新的数据类型(dtype),在这个数据类型中,d1d2d3会合并成一个叫d的字段,这个字段里包含三个浮点数值:

In [61]: dt2 = dtype([('d', 'f8', 3), ('file', 'S20')])

接下来,使用view方法来创建record_array的这个新数据类型的视图:

In [62]: rav = record_array.view(dt2)

In [63]: rav
Out[63]: 
array([([340.9, 7548.2, 1192.4], 'set001.txt'),
       ([546.7, 9039.9, 5546.1], 'set002.txt'),
       ([456.3, 2234.8, 2198.8], 'set003.txt'),
       ([332.1, 1144.2, 2344.5], 'set004.txt')], 
      dtype=[('d', '<f8', (3,)), ('file', 'S20')])

rav并不是一个副本,它只是指向record_array使用的同一块内存。

现在可以访问d字段来获取二维数组:

In [64]: d = rav['d']

In [65]: d
Out[65]: 
array([[  340.9,  7548.2,  1192.4],
       [  546.7,  9039.9,  5546.1],
       [  456.3,  2234.8,  2198.8],
       [  332.1,  1144.2,  2344.5]])

d可以直接传给pdist使用:

In [66]: pdist(d)
Out[66]: 
array([ 4606.75875427,  5409.10137454,  6506.81395539,  7584.32432455,
        8522.8149229 ,  1107.27706108])

需要注意的是,除了把record_array转换成rav,你也可以一开始就用dt2作为record_array的数据类型,然后直接写d = record_array['d']


如果在record_array中用于计算距离的字段不是连续的,你需要先把它们提取到一个新的数组中,使它们变得连续:

In [83]: arr = record_array[['d1','d2','d3']]

然后对arr进行视图处理并重新调整形状,使其变成二维的:

In [84]: d = arr.view(np.float64).reshape(-1,3)

In [85]: d
Out[85]: 
array([[  340.9,  7548.2,  1192.4],
       [  546.7,  9039.9,  5546.1],
       [  456.3,  2234.8,  2198.8],
       [  332.1,  1144.2,  2344.5]])

如果这样做更方便,你可以把这些操作合并成一行:

In [86]: d = record_array[['d1', 'd2', 'd3']].view(np.float64).reshape(-1, 3)

撰写回答