将数据提交到表和数组#

已经了解了如何创建表和数组,以及如何浏览对象树中的数据和元数据。现在,让我们更仔细地研究 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()