L'exemple Building a huge numpy array using pytables montre créer une grande matrice qui ne tient pourtant pas en mémoire. Il existe des modules qui permet de faire des calcul à partir de données stockées sur disque comme si elles étaient en mémoire.
Le module h5py est un module qui permet d'agréger un grand nombre de données dans un seul fichier et de les nommer comme des fichiers sur un disque. L'exemple suivant crée un seul fichier contenant deux tableaux :
import h5py
import random
hf = h5py.File('example.hdf5','w')
arr = [ random.randint(0,100) for h in range(0,1000) ]
hf["random/f0_100"] = arr
arr = [ random.randint(0,1000) for h in range(0,10000) ]
hf["random/f0_1000"] = arr
hf.close()
hf = h5py.File('example.hdf5','r')
print(hf)
for k in hf :
for k2 in hf[k] :
obj =hf["{0}/{1}".format(k,k2)]
print(k, k2, obj, obj.value.shape)
hf.close()
L'avantage est de pouvoir accéder à une partie d'un ensemble sans que celui-ci ne soit chargé en mémoire :
hf = h5py.File('example.hdf5','r')
print(hf["random/f0_1000"][20:25])
hf.close()
try:
from tables import IsDescription, StringCol, Int64Col, Float32Col, Float64Col
except ImportError as e:
# Parfois cela échoue sur Windows: DLL load failed: La procédure spécifiée est introuvable.
import sys
raise ImportError("Cannot import tables.\n" + "\n".join(sys.path)) from e
class Particle(IsDescription):
name = StringCol(16) # 16-character String
idnumber = Int64Col() # Signed 64-bit integer
pressure = Float32Col() # float (single-precision)
energy = Float64Col() # double (double-precision)
from tables import open_file
h5file = open_file("particule2.h5", mode = "w", title = "Test file")
group = h5file.create_group("/", 'detector', 'Detector information')
table = h5file.create_table(group, 'readout', Particle, "Readout example")
h5file
particle = table.row
for i in range(10):
particle['name'] = 'Particle: %6d' % (i)
particle['pressure'] = float(i*i)
particle['energy'] = float(particle['pressure'] ** 4)
particle['idnumber'] = i * (2 ** 34)
# Insert a new particle record
particle.append()
table.flush()
table_read = h5file.root.detector.readout
pressure = [x['pressure'] for x in table_read.iterrows() if 20 <= x['pressure'] < 50]
pressure
names = [ x['name'] for x in table.where("""(20 <= pressure) & (pressure < 50)""") ]
names
Ces lignes sont extraites du tutoriel. Le module autorise la création de tableaux, toujours sur disque.
h5file.close()
blosc compresse des tableaux numérique. Cela permet de libérer de la mémoire pendant le temps qu'il ne sont pas utilisés. Il est optimisé pour perdre le moins de temps possible en compression / décompression.
import blosc
import numpy as np
a = np.linspace(0, 100, 10000000)
a.shape
packed = blosc.pack_array(a)
type(packed), len(packed)
array = blosc.unpack_array(packed)
type(array)
%timeit blosc.pack_array(a)
%timeit blosc.unpack_array(packed)
Performance en fonction de la dimension.
import time
x = []
t_comp = []
t_dec = []
size = 10
for i in range(1,9):
a = np.linspace(0, 100, size)
t1 = time.perf_counter()
packed = blosc.pack_array(a)
t2 = time.perf_counter()
blosc.unpack_array(packed)
t3 = time.perf_counter()
x.append(len(a))
t_comp.append(t2-t1)
t_dec.append(t3-t2)
print(i, t2-t1, t3-t2)
size *= 10
import matplotlib.pyplot as plt
fig, ax = plt.subplots(1, 1)
ax.plot(x, t_comp, label="compression")
ax.plot(x, t_dec, label="décompression")
ax.set_xlabel("taille")
ax.set_ylabel("time(ms)")
ax.set_xscale("log", nonposx='clip')
ax.set_yscale("log", nonposy='clip')
ax.legend();