在PyTables中,如何创建可变长度的嵌套数组?
我正在使用 PyTables 2.2.1 和 Python 2.6,想要创建一个包含可变长度嵌套数组的表格。
我查阅了 PyTables 的文档,教程示例(PyTables 教程 3.8)展示了如何创建一个长度为 1 的嵌套数组。但是在这个例子中,我该如何给数据 'info2/info3/x' 和 'info2/info3/y' 添加可变数量的行呢?
为了让表格结构更容易理解,这里有一个我自己做的例子:
"""Desired Pytable output:
DIEM TEMPUS Temperature Data
5 0 100 Category1 <--||--> Category2
x <--| |--> y z <--|
0 0 0
2 1 1
4 1.33 2.67
6 1.5 4.5
8 1.6 6.4
5 1 99
2 2 0
4 2 2
6 2 4
8 2 6
5 2 96
4 4 0
6 3 3
8 2.67 5.33
Note that nested arrays have variable length.
"""
import tables as ts
tableDef = {'DIEM': ts.Int32Col(pos=0),
'TEMPUS': ts.Int32Col(pos=1),
'Temperature' : ts.Float32Col(pos=2),
'Data':
{'Category1':
{
'x': ts.Float32Col(),
'y': ts.Float32Col()
},
'Category2':
{
'z': ts.Float32Col(),
}
}
}
# create output file
fpath = 'TestDb.h5'
fh = ts.openFile(fpath, 'w')
# define my table
tableName = 'MyData'
fh.createTable('/', tableName, tableDef)
tablePath = '/'+tableName
table = fh.getNode(tablePath)
# get row iterator
row = table.row
for i in xrange(3):
print '\ni=', i
# calc some fake data
row['DIEM'] = 5
row['TEMPUS'] = i
row['Temperature'] = 100-i**2
for j in xrange(5-i):
# Note that nested array has variable number of rows
print 'j=', j,
# calc some fake nested data
val1 = 2.0*(i+j)
val2 = val1/(j+1.0)
val3 = val1 - val2
''' Magic happens here...
How do I write 'j' rows of data to the elements of
Category1 and/or Category2?
In bastardized pseudo-code, I want to do:
row['Data/Category1/x'][j] = val1
row['Data/Category1/y'][j] = val2
row['Data/Category2/z'][j] = val3
'''
row.append()
table.flush()
fh.close()
我在 PyTables 的文档中没有找到任何表明这种结构不可能的迹象……但如果这种结构确实不可能,那我还有什么替代方案来处理可变长度的嵌套列呢?
- EArray?VLArray?如果可以的话,如何将这些数据类型整合到上面描述的结构中?
- 还有其他的想法吗?
任何帮助都非常感谢!
编辑附加信息: 看起来 PyTables 的专家们已经解决了“这种结构是否可能”的问题:
那么有没有人找到创建类似 PyTable 数据结构的方法呢?
再次感谢!
3 个回答
我也遇到过这个问题,最后我选择使用固定大小的数组。我原本想存储的数组长度是变化的,所以我创建了新的数组,给它们设置了正确的固定长度。
我做的事情大概是这样的:
def filled_list(src_list, targ_len):
"""takes a varible len() list and creates a new one with a fixed len()"""
for i in range(targ_len):
try:
yield src_list[i]
except IndexError:
yield 0
src_list = [1,2,3,4,5,6,7,8,9,10,11]
new_list = [x for x in filled_list(src_list, 100)]
这样就解决了我的问题。
我有一个类似的任务:需要处理固定大小的数据,但这些数据的数组长度是可变的。
我最开始尝试用固定大小的 StringCol(64*1024) 字段来存储我的可变长度数据(它们的大小总是小于 64K)。但是这样做速度很慢,而且浪费了很多磁盘空间,尽管使用了 blosc 压缩。
经过几天的研究,我找到了一种解决方案:
(小提示:我们把数组字段存储在单独的 EArray 实例中,每个数组字段对应一个 EArray)
- 我在一个普通的 pytables 表中存储固定大小的数据。
我在这些表中添加了两个额外的字段:arrFieldName_Offset 和 arrFieldName_Length:
class Particle(IsDescription): idnumber = Int64Col() ADCcount = UInt16Col() TDCcount = UInt8Col() grid_i = Int32Col() grid_j = Int32Col() pressure = Float32Col() energy = FloatCol() buffer_Offset = UInt32() # note this field! buffer_Length = UInt32() # and this one too!
我还为每个数组字段创建一个 EArray 实例:
datatype = StringAtom(1) buffer = h5file.createEArray('/detector', 'arr', datatype, (0,), "")
然后我添加与固定大小数据对应的行:
row['idnumber'] = ... ... row['energy'] = ... row['buffer_Offset'] = buffer.nrows # my_buf is a string (I get it from a stream) row['buffer_Length'] = len(my_buf) table.append(row)
好了!把缓冲区添加到数组中。
buffer.append(np.ndarray((len(my_buf),), buffer=my_buf, dtype=datatype))
这就是诀窍。在我的实验中,这种方法比存储不规则的固定大小数组(比如 StringAtom(HUGE_NUMBER))快 2 到 10 倍,生成的数据库也小了几倍(2 到 5 倍)。
获取缓冲区数据很简单。假设 row 是你从数据库中读取的一行数据:
# Open array for reading buffer = h5file.createEArray('/detector', 'Particle.buffer', datatype, (0,), "") ... row = ... ... bufferDataYouNeed = buffer[ row['buffer_Offset'] : row['buffer_Offset'] + row['buffer_Length']]
这是很多刚开始使用PyTables的人常遇到的问题。说实话,这也是我最开始尝试做的事情。到2009年,我觉得这个功能还不支持。你可以看看这里有一个我“总是推荐”的解决方案:
http://www.mail-archive.com/pytables-users@lists.sourceforge.net/msg01207.html
简单来说,就是把每个VLArray放在不同的地方。如果这样做,可能你就不需要VLArrays了。如果你为每个实验(或者其他的)存储单独的VLArrays,你可以在这些VLArrays上保留元数据(确保在重命名、移动等操作中保持同步),或者把它放在一个表里(这样更容易搜索)。
另外,你也可以选择一个时间点作为你的列原子,然后简单地再加一列来记录时间戳。这样可以形成一个“参差不齐”的数组,但在内存中仍然保持一个规律的、重复的(表格)结构。例如:
Trial Data
1 0.4, 0.5, 0.45
2 0.3, 0.4, 0.45, 0.56
变成
Trial Timepoint Data
1 1 0.4
1 2 0.5
...
2 4 0.56
上面的数据是一个单一的数字,但它也可以是,比如说,一个4x5x3的原子。
如果现在PyTables支持嵌套的VLArrays,我当然很想知道!
另外,我觉得h5py支持完整的HDF5功能集,所以如果你真的想要嵌套的数据布局,那里可能会更合适。不过,你会失去很多不错的功能!根据我的经验,初学的神经科学家往往会因为没有利用PyTables智能的布局、分块等选择而导致性能很差。如果你走这条路,请记得反馈一下!