将数据提交到表和数组#
已经了解了如何创建表和数组,以及如何浏览对象树中的数据和元数据。现在,让我们更仔细地研究 PyTables 最强大的功能之一,即如何修改已创建的表和数组。
from pathlib import Path
temp_dir = Path(".temp")
temp_dir.mkdir(exist_ok=True)
from tables import *
h5file = open_file(temp_dir/"tutorial1.h5", mode="a")
向现有表追加数据#
现在,让我们看看如何向磁盘上的现有表添加记录。让我们使用我们熟悉的 readout
对象,并向其中追加一些新值:
table = h5file.root.detector.readout
particle = table.row
for i in range(10, 15):
particle['name'] = f'Particle: {i:6d}'
particle['TDCcount'] = i % 256
particle['ADCcount'] = (i * 256) % (1 << 16)
particle['grid_i'] = i
particle['grid_j'] = 10 - i
particle['pressure'] = float(i*i)
particle['energy'] = float(particle['pressure'] ** 4)
particle['idnumber'] = i * (2 ** 34)
particle.append()
table.flush()
它与填充新表时使用的方法相同。PyTables 知道这个表在磁盘上,当您添加新记录时,它们会被追加到表的末尾。
如果您仔细查看代码,您会看到我们使用了 table.row
属性来创建表行,并用新值填充它。每次调用其 append()
方法时,实际行都会提交到输出缓冲区,并且行指针会递增以指向下一个表记录。当缓冲区满时,数据会保存到磁盘,并且缓冲区会再次用于下一个周期。
警告:在写操作后不要忘记始终调用 flush
方法,否则您的表将不会更新!
让我们查看修改后的表中的一些行,并验证我们的新数据是否已追加:
for r in table.iterrows():
print(
f"{r['name']} | {r['pressure']:11.1f} | {r['energy']:11.4g} "
f"| {r['grid_i']:6d} | {r['grid_j']:6d} | {r['TDCcount']:8d} |"
)
b'Particle: 0' | 0.0 | 0 | 0 | 10 | 0 |
b'Particle: 1' | 1.0 | 1 | 1 | 9 | 1 |
b'Particle: 2' | 4.0 | 256 | 2 | 8 | 2 |
b'Particle: 3' | 9.0 | 6561 | 3 | 7 | 3 |
b'Particle: 4' | 16.0 | 6.554e+04 | 4 | 6 | 4 |
b'Particle: 5' | 25.0 | 3.906e+05 | 5 | 5 | 5 |
b'Particle: 6' | 36.0 | 1.68e+06 | 6 | 4 | 6 |
b'Particle: 7' | 49.0 | 5.765e+06 | 7 | 3 | 7 |
b'Particle: 8' | 64.0 | 1.678e+07 | 8 | 2 | 8 |
b'Particle: 9' | 81.0 | 4.305e+07 | 9 | 1 | 9 |
b'Particle: 10' | 100.0 | 1e+08 | 10 | 0 | 10 |
b'Particle: 11' | 121.0 | 2.144e+08 | 11 | -1 | 11 |
b'Particle: 12' | 144.0 | 4.3e+08 | 12 | -2 | 12 |
b'Particle: 13' | 169.0 | 8.157e+08 | 13 | -3 | 13 |
b'Particle: 14' | 196.0 | 1.476e+09 | 14 | -4 | 14 |
修改数据表#
到目前为止,我们只是读取和写入(追加)值到我们的表中。但有时你需要在数据被保存到磁盘后对其进行修改(这在你为了适应你的目标需要修改现实世界的数据时尤其正确)。让我们看看如何修改已存在于我们现有数据表中的值。我们将从粒子表的第一行开始修改单个单元格:
print("Before modif-->", table[0])
Before modif--> (0, 0, 0., 0, 10, 0, b'Particle: 0', 0.)
table.cols.TDCcount[0] = 1
print("After modifying first row of ADCcount-->", table[0])
table.cols.energy[0] = 2
print("After modifying first row of energy-->", table[0])
After modifying first row of ADCcount--> (0, 1, 0., 0, 10, 0, b'Particle: 0', 0.)
After modifying first row of energy--> (0, 1, 2., 0, 10, 0, b'Particle: 0', 0.)
也可以修改整列的范围:
table.cols.TDCcount[2:5] = [2,3,4]
print("After modifying slice [2:5] of TDCcount-->", table[0:5])
table.cols.energy[1:9:3] = [2,3,4]
print("After modifying slice [1:9:3] of energy-->", table[0:9])
After modifying slice [2:5] of TDCcount--> [( 0, 1, 2.0000e+00, 0, 10, 0, b'Particle: 0', 0.)
( 256, 1, 1.0000e+00, 1, 9, 17179869184, b'Particle: 1', 1.)
( 512, 2, 2.5600e+02, 2, 8, 34359738368, b'Particle: 2', 4.)
( 768, 3, 6.5610e+03, 3, 7, 51539607552, b'Particle: 3', 9.)
(1024, 4, 6.5536e+04, 4, 6, 68719476736, b'Particle: 4', 16.)]
After modifying slice [1:9:3] of energy--> [( 0, 1, 2.0000000e+00, 0, 10, 0, b'Particle: 0', 0.)
( 256, 1, 2.0000000e+00, 1, 9, 17179869184, b'Particle: 1', 1.)
( 512, 2, 2.5600000e+02, 2, 8, 34359738368, b'Particle: 2', 4.)
( 768, 3, 6.5610000e+03, 3, 7, 51539607552, b'Particle: 3', 9.)
(1024, 4, 3.0000000e+00, 4, 6, 68719476736, b'Particle: 4', 16.)
(1280, 5, 3.9062500e+05, 5, 5, 85899345920, b'Particle: 5', 25.)
(1536, 6, 1.6796160e+06, 6, 4, 103079215104, b'Particle: 6', 36.)
(1792, 7, 4.0000000e+00, 7, 3, 120259084288, b'Particle: 7', 49.)
(2048, 8, 1.6777216e+07, 8, 2, 137438953472, b'Particle: 8', 64.)]
请检查这些数值是否已正确修改!
提示
请记住,TDCcount
列是第二列,能量是第三列。有关修改列的更多信息,请查阅 tables.Column.__setitem__()
。
PyTables 还允许您同时修改完整的行集。作为这些功能的演示,请参见下一个示例:
table.modify_rows(start=1, step=3,
rows=[(1, 2, 3.0, 4, 5, 6, 'Particle: None', 8.0),
(2, 4, 6.0, 8, 10, 12, 'Particle: None*2', 16.0)])
print("After modifying the complete third row-->", table[0:5])
After modifying the complete third row--> [( 0, 1, 2.000e+00, 0, 10, 0, b'Particle: 0', 0.)
( 1, 2, 3.000e+00, 4, 5, 6, b'Particle: None', 8.)
(512, 2, 2.560e+02, 2, 8, 34359738368, b'Particle: 2', 4.)
(768, 3, 6.561e+03, 3, 7, 51539607552, b'Particle: 3', 9.)
( 2, 4, 6.000e+00, 8, 10, 12, b'Particle: None*2', 16.)]
如您所见,modify_rows()
函数调用修改了第二行和第五行,并返回了被修改的行数。
除了 modify_rows()
之外,还有另一种方法叫做 modify_column()
,用于修改特定列。
最后,还有一种通常比上述方法更便捷的方法来修改表。这种新方法使用与每个表关联的 tables.Row.update()
方法,因此它适用于表迭代器。请看以下例子:
for row in table.where('TDCcount <= 2'):
row['energy'] = row['TDCcount']*2
row.update()
print("After modifying energy column (where TDCcount <=2)-->", table[0:4])
After modifying energy column (where TDCcount <=2)--> [( 0, 1, 2.000e+00, 0, 10, 0, b'Particle: 0', 0.)
( 1, 2, 4.000e+00, 4, 5, 6, b'Particle: None', 8.)
(512, 2, 4.000e+00, 2, 8, 34359738368, b'Particle: 2', 4.)
(768, 3, 6.561e+03, 3, 7, 51539607552, b'Particle: 3', 9.)]
备注
使用这种方法更新表格(即使用 tables.Row.update()
)既方便又高效。请确保广泛使用它。
请注意:目前,如果循环被 break
语句中断,tables.Row.update()
将不会生效(表不会被更新)。可能的解决方案是在 break
语句之前手动刷新行内部缓冲区,通过调用 row._flushModRows()
。
修改数组中的数据#
接下来,我们将学习如何修改数组对象中的数据。基本方法是使用 tables.Array.__setitem__()
特殊方法。让我们看看如何修改 pressureObject
数组上的数据:
pressureObject = h5file.root.columns.pressure
print("Before modif-->", pressureObject[:])
pressureObject[0] = 2
print("First modif-->", pressureObject[:])
pressureObject[1:3] = [2.1, 3.5]
print("Second modif-->", pressureObject[:])
pressureObject[::2] = [1,2]
print("Third modif-->", pressureObject[:])
Before modif--> [25. 36. 49.]
First modif--> [ 2. 36. 49.]
Second modif--> [2. 2.1 3.5]
Third modif--> [1. 2.1 2. ]
因此,总的来说,你可以使用任何组合的扩展切片(多维)。
唯一的例外是,你不能使用负值作为步长来引用你想要修改的索引。
从表中删除行#
我们将通过从表中删除一些行来结束本教程。假设我们想删除第 5 到第 9 行(包括第 9 行):
table.remove_rows(5,10)
5
remove_rows()
方法用于删除指定范围 (start, stop)
内的行,并返回实际被移除的行数。
请记得在完成操作后关闭文件:
h5file.close()