Formato HDF5: Almacenamiento de Big Data Científico
HDF5 (Hierarchical Data Format versión 5) es un formato de archivo binario autodescriptivo diseñado para almacenar y gestionar conjuntos de datos grandes y complejos. Desarrollado por el HDF Group (originalmente en el Centro Nacional de Aplicaciones de Supercomputación), HDF5 es el formato dominante en la computación científica, ingeniería aeroespacial, genómica, modelado climático, física de partículas y cada vez más en el aprendizaje automático. Cuando la NASA almacena telemetría de satélites, el CERN archiva datos de colisiones de partículas, o un framework de aprendizaje profundo guarda un modelo entrenado, HDF5 es frecuentemente el formato elegido.
Lo "jerárquico" en su nombre se refiere a su organización interna similar a un sistema de archivos: un archivo HDF5 es un contenedor autónomo en el que datasets, metadatos y estructuras anidadas complejas coexisten en un árbol de grupos y datasets, todos navegables sin documentación externa.
Por Qué Existe HDF5
Antes de HDF5, los datos científicos se almacenaban típicamente de tres maneras:
- Archivos binarios planos — rápidos pero completamente opacos sin documentación acompañante que describa el diseño de datos, dimensiones, unidades y procedencia
- Archivos de texto/CSV — legibles por humanos pero órdenes de magnitud más lentos para leer y escribir, sin soporte para arrays multidimensionales
- Soluciones específicas de formato — NetCDF (clima), FITS (astronomía), formatos propietarios de vendedores — cada uno requiriendo herramientas especializadas
HDF5 resolvió esta fragmentación proporcionando un formato contenedor de propósito general que es autodescriptivo (todos los metadatos están incrustados junto con los datos), soporta arrays arbitrariamente grandes de cualquier tipo numérico y es accesible a través de librerías en todos los lenguajes de programación científica principales.
Estructura del Archivo HDF5
Un archivo HDF5 se organiza como un grafo acíclico dirigido (DAG) que se asemeja a un sistema de archivos:
/ ← grupo raíz
├── experimento/ ← grupo
│ ├── metadatos ← grupo
│ │ └── parametros ← dataset (dtype estructurado)
│ ├── datos_crudos ← dataset (array float32 3D)
│ └── procesado/ ← grupo
│ ├── espectros ← dataset (array float64 2D)
│ └── timestamps ← dataset (array int64 1D)
├── calibracion/ ← grupo
│ ├── frame_oscuro ← dataset (array uint16 2D)
│ └── campo_plano ← dataset (array float32 2D)
└── resultados/ ← grupo
├── clasificaciones ← dataset (array string 1D)
└── puntuaciones ← dataset (array float32 1D)
Los dos bloques fundamentales son los grupos (contenedores, como directorios) y los datasets (arrays de datos, como archivos). Ambos pueden llevar atributos — pequeños elementos de metadatos adjuntos directamente al objeto.
Datasets: Arrays Multidimensionales
Un dataset HDF5 es un array N-dimensional de un tipo de dato definido:
- Tipos enteros:
int8,int16,int32,int64,uint8,uint16,uint32,uint64 - Punto flotante:
float32(simple),float64(doble) - Números complejos:
complex64,complex128 - Strings: longitud fija o variable, UTF-8
- Tipos compuestos: registros similares a structs de C con campos nombrados de tipos mixtos
- Tipos enumerados: códigos enteros mapeados a valores nombrados
- Referencias: punteros a otros datasets o grupos dentro del mismo archivo
Chunking
Por defecto, HDF5 almacena datasets como bloques contiguos en disco. Para datasets grandes, el chunking divide el dataset en bloques de tamaño fijo almacenados independientemente:
import h5py
import numpy as np
with h5py.File('experimento.h5', 'w') as f:
# Crear un dataset 3D grande, en chunks de 64×64×64
ds = f.create_dataset(
'datos_crudos',
shape=(1024, 1024, 512),
dtype='float32',
chunks=(64, 64, 64),
compression='gzip',
compression_opts=4,
)
# Escribir una rebanada
ds[256:320, 256:320, 128:192] = np.random.rand(64, 64, 64).astype('float32')
El chunking habilita varias capacidades importantes:
- Lecturas parciales — lee solo las rebanadas que necesitas sin cargar todo el dataset en memoria
- Compresión — cada chunk se comprime independientemente, habilitando la descompresión al vuelo de los chunks accedidos
- I/O paralelo — diferentes procesos pueden leer/escribir diferentes chunks simultáneamente
- Datasets redimensionables — los datasets con chunking pueden extenderse en cualquier dimensión
Filtros de Compresión
HDF5 soporta una canalización de filtros de transformación de datos aplicados a cada chunk antes del almacenamiento:
| Filtro | Algoritmo | Mejor para |
|---|---|---|
gzip |
DEFLATE (zlib) | Propósito general, ampliamente soportado |
lzf |
LZF | Compresión rápida, ratio moderado |
szip |
SZIP | Datos científicos de punto flotante |
blosc |
Múltiple (LZ4, Zstd, etc.) | Compresión paralela de alto rendimiento |
shuffle |
Reordenamiento de bytes | Pre-filtro antes de compresión (mejora 2-5×) |
fletcher32 |
Checksum | Verificación de integridad de datos |
Uso de Python h5py
import h5py
import numpy as np
from datetime import datetime
# Crear un nuevo archivo HDF5
with h5py.File('resultados.h5', 'w') as f:
# Atributos a nivel de archivo
f.attrs['creador'] = 'pipeline_experimento v2.3'
f.attrs['creado'] = datetime.utcnow().isoformat()
# Crear grupos
crudo = f.create_group('datos_crudos')
proc = f.create_group('procesado')
meta = f.create_group('metadatos')
# Almacenar un array grande con chunking + compresión
senal = crudo.create_dataset(
'senal',
data=np.random.randn(10000, 256).astype('float32'),
chunks=(500, 256),
compression='gzip',
compression_opts=6,
shuffle=True,
)
senal.attrs['unidades'] = 'microvoltios'
senal.attrs['frecuencia_muestreo_hz'] = 1000
senal.attrs['num_canales'] = 256
# Almacenar strings de longitud variable
nombres_canales = [f'CH{i:03d}' for i in range(256)]
dt = h5py.special_dtype(vlen=str)
crudo.create_dataset('nombres_canales', data=nombres_canales, dtype=dt)
# Almacenar un dataset compuesto (estructurado)
dtype = np.dtype([
('timestamp', 'int64'),
('tipo_evento', 'S16'),
('valor', 'float64'),
])
eventos = np.array([
(1710000000000, b'estimulo', 1.0),
(1710000001500, b'respuesta', 0.7),
], dtype=dtype)
meta.create_dataset('eventos', data=eventos)
# Leer de vuelta selectivamente
with h5py.File('resultados.h5', 'r') as f:
# Leer solo filas 1000-2000 (datos de 1 GB, solo cargando 8 MB)
subset = f['datos_crudos/senal'][1000:2000, :]
print(f"Forma: {subset.shape}, dtype: {subset.dtype}")
# Leer atributos
fm = f['datos_crudos/senal'].attrs['frecuencia_muestreo_hz']
print(f"Frecuencia de muestreo: {fm} Hz")
# Recorrer todo el árbol de archivos
def imprimir_arbol(nombre, obj):
sangria = ' ' * nombre.count('/')
tipo = 'DATASET' if isinstance(obj, h5py.Dataset) else 'GRUPO '
print(f"{sangria}{tipo} {nombre}")
f.visititems(imprimir_arbol)
HDF5 en Aprendizaje Profundo
HDF5 se usa extensamente en aprendizaje automático:
Keras/TensorFlow: el formato de modelo .h5 o .keras usa HDF5 para almacenar pesos de capas, JSON de arquitectura y estado del optimizador:
# Guardar un modelo Keras
model.save('modelo.h5')
# Cargar un modelo Keras
from tensorflow import keras
modelo = keras.models.load_model('modelo.h5')
# Inspeccionar la estructura HDF5
with h5py.File('modelo.h5', 'r') as f:
print(list(f.keys())) # ['model_config', 'model_weights', 'training_config']
Datasets de entrenamiento grandes: cuando los datasets son demasiado grandes para la memoria, los datasets HDF5 sirven como arrays mapeados en memoria:
with h5py.File('imagenet_train.h5', 'r') as f:
imagenes = f['imagenes'] # forma: (1281167, 3, 224, 224), float32
etiquetas = f['etiquetas'] # forma: (1281167,), int32
# Iterar en mini-lotes sin cargar los 500 GB
for i in range(0, len(etiquetas), 32):
lote_x = imagenes[i:i+32]
lote_y = etiquetas[i:i+32]
# entrenar en el lote...
HDF5 vs NetCDF vs Zarr
| Característica | HDF5 | NetCDF4 | Zarr |
|---|---|---|---|
| Fundamento | Autónomo | Construido sobre HDF5 | Almacenes de chunks |
| Nativo en la nube | ⚠️ Parcial | ⚠️ Parcial | ✅ Diseñado para ello |
| Escrituras paralelas | ✅ MPI | ✅ MPI | ✅ Nativo |
| Autodescriptivo | ✅ Sí | ✅ Sí | ✅ Sí |
| Almacenes de objetos (S3) | ❌ Pobre | ❌ Pobre | ✅ Excelente |
NetCDF4 está construido sobre HDF5 — añade convenciones para datos geofísicos (variables de coordenadas, convenciones CF) mientras usa HDF5 como motor de almacenamiento. Zarr es un formato más nuevo diseñado para almacenes de objetos en la nube.
Conclusión
La combinación de HDF5 de estructura autodescriptiva, arrays multidimensionales de tipo arbitrario, compresión flexible y escalabilidad probada a datasets de escala petabyte lo ha hecho indispensable en la computación científica durante más de dos décadas. Su organización jerárquica hace que los datasets experimentales complejos sean navegables sin documentación externa, sus características de chunking y compresión lo hacen práctico para datasets mucho más grandes que la RAM disponible, y su soporte de I/O paralelo habilita flujos de trabajo HPC que procesan terabytes en minutos. Ya sea que estés almacenando pesos de redes neuronales, imágenes de satélite, datos de secuenciación genómica o registros de colisiones de física de partículas, HDF5 proporciona la base para el almacenamiento de datos confiable, eficiente y autodocumentado.
Conversiones relacionadas
Conversiones frecuentes del catálogo: