{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Intro to HDF5 data model\n", "\n", "* High-level overview of the HDF5 file strudcture and basic tools\n", "\n", "\n", "## What's HDF5?\n", "\n", "* HDF5 = Hierarchical Data Format Version 5\n", "* A file format optimized for numeric data\n", "* A hierarquichal structure to store information (like folders)\n", "* A self-describing container: Metadata + Data\n", "* A library with several functionalities (tools)\n", "* High level from user side (easy access) / Low level from machine side (binary, compressible)\n", "* Fast I/O, parallel reading/writing (!), very good for HPC\n", "* Data can be read/written in chuncks, in-memory, out-of-memory \n", "\n", "Read more: [https://www.hdfgroup.org/solutions/hdf5/](https://www.hdfgroup.org/solutions/hdf5/) \n", "\n", "## How popular is it?\n", "* Matlab `*.m` files **are** HDF5!\n", "* NetCDF4 files **are** HDF5!\n", "* ICESat-2 data comes in HDF5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Write data to HDF5\n", "\n", "Let's create some fake data" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "%matplotlib inline" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[-1.17810300e+00 4.92165922e-01 -6.28310766e-01 -1.01736856e+00\n", " -2.75882985e-02 -1.31115622e+00 -1.03739686e+00 7.85862355e-01\n", " -9.57980538e-01 -9.83571900e-03 -9.77938307e-01 -5.63877665e-01\n", " -1.54177295e+00 -2.68832360e-01 6.50000763e-01 4.54624034e-02\n", " -1.39600018e+00 2.77440145e-01 -3.30264003e-01 9.43053364e-03\n", " 8.49028964e-01 -2.16018101e+00 -2.98947778e-01 1.41343155e-03\n", " 4.56584580e-01 -4.56783842e-01 1.31501647e+00 -1.84023042e+00\n", " 3.92644928e-01 -2.10216420e-01 -5.35963880e-01 4.64478172e-01\n", " 6.13842854e-01 4.31984257e-01 -1.35627322e+00 4.59049557e-01\n", " 8.28192468e-01 -1.94771227e+00 -9.61864020e-01 3.10717381e-01\n", " -1.13429020e+00 -3.97000355e-01 -1.58854125e+00 5.99333245e-01\n", " -2.19463457e-01 7.00161994e-01 -1.63350082e+00 2.78043299e-01\n", " -5.43529821e-01 -1.68733339e+00 -1.01407576e+00 -1.60827872e-01\n", " -4.75821670e-01 7.80836557e-02 1.28097527e+00 -6.25748168e-01\n", " 7.26383363e-01 -2.58838384e-01 -7.95933765e-01 2.83112535e-01\n", " -4.52171829e-01 3.43425599e-01 4.94187468e-01 4.30020408e-01\n", " 1.37670976e+00 -3.98152974e-01 -1.64975516e+00 -8.82258059e-01\n", " -1.14014004e+00 1.47517602e+00 1.40312980e+00 5.21577238e-01\n", " -2.86945618e-01 -2.78985873e-01 1.02115612e+00 1.06796687e+00\n", " 1.91093691e-01 -1.12905869e-01 1.66054019e+00 6.29917226e-01\n", " -1.44744817e-01 1.82389879e+00 -3.35419482e-01 -9.43489833e-01\n", " 1.28524826e+00 5.75652004e-01 -2.07645535e-01 -1.36201854e+00\n", " -8.65084772e-01 -3.50592633e-01 -1.42603270e+00 1.34828537e+00\n", " 4.94861483e-01 -1.13146199e+00 1.07440999e+00 -1.30961518e+00\n", " 1.40722007e-01 -5.96393171e-02 3.99602528e-01 6.16327589e-01]\n" ] } ], "source": [ "import h5py\n", "import numpy as np\n", "\n", "x = np.random.randn(100)\n", "y = np.random.randn(100)\n", "z = np.random.randn(100)\n", "\n", "print(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Save 1D arrays to file" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "#!rm myfile.h5 # remove old file if previously written\n", "#!rm data/*_gt* # remove existing data just in case\n", "\n", "with h5py.File('myfile.h5', 'w') as f: # open file in write mode\n", " f['x'] = x # write data\n", " f['y'] = y\n", " f['z'] = z" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "myfile.h5\r\n" ] } ], "source": [ "!ls *.h5 # Check the file was created" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**NOTE:** \n", "**The HDF5 library comes with some useful command-line tools** \n", "**There is no need to write code to inspect an HDF5 file!**" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x Dataset {100}\r\n", "y Dataset {100}\r\n", "z Dataset {100}\r\n" ] } ], "source": [ "!h5ls myfile.h5 # inspect the file w/command-line tools" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "OBS: More sophysticated command-line tools below.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Read data from HDF5\n", "\n", "Load data (in memory) vs. get pointer (out of memory)" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "x (in mem): [-1.17810300e+00 4.92165922e-01 -6.28310766e-01 -1.01736856e+00\n", " -2.75882985e-02 -1.31115622e+00 -1.03739686e+00 7.85862355e-01\n", " -9.57980538e-01 -9.83571900e-03 -9.77938307e-01 -5.63877665e-01\n", " -1.54177295e+00 -2.68832360e-01 6.50000763e-01 4.54624034e-02\n", " -1.39600018e+00 2.77440145e-01 -3.30264003e-01 9.43053364e-03\n", " 8.49028964e-01 -2.16018101e+00 -2.98947778e-01 1.41343155e-03\n", " 4.56584580e-01 -4.56783842e-01 1.31501647e+00 -1.84023042e+00\n", " 3.92644928e-01 -2.10216420e-01 -5.35963880e-01 4.64478172e-01\n", " 6.13842854e-01 4.31984257e-01 -1.35627322e+00 4.59049557e-01\n", " 8.28192468e-01 -1.94771227e+00 -9.61864020e-01 3.10717381e-01\n", " -1.13429020e+00 -3.97000355e-01 -1.58854125e+00 5.99333245e-01\n", " -2.19463457e-01 7.00161994e-01 -1.63350082e+00 2.78043299e-01\n", " -5.43529821e-01 -1.68733339e+00 -1.01407576e+00 -1.60827872e-01\n", " -4.75821670e-01 7.80836557e-02 1.28097527e+00 -6.25748168e-01\n", " 7.26383363e-01 -2.58838384e-01 -7.95933765e-01 2.83112535e-01\n", " -4.52171829e-01 3.43425599e-01 4.94187468e-01 4.30020408e-01\n", " 1.37670976e+00 -3.98152974e-01 -1.64975516e+00 -8.82258059e-01\n", " -1.14014004e+00 1.47517602e+00 1.40312980e+00 5.21577238e-01\n", " -2.86945618e-01 -2.78985873e-01 1.02115612e+00 1.06796687e+00\n", " 1.91093691e-01 -1.12905869e-01 1.66054019e+00 6.29917226e-01\n", " -1.44744817e-01 1.82389879e+00 -3.35419482e-01 -9.43489833e-01\n", " 1.28524826e+00 5.75652004e-01 -2.07645535e-01 -1.36201854e+00\n", " -8.65084772e-01 -3.50592633e-01 -1.42603270e+00 1.34828537e+00\n", " 4.94861483e-01 -1.13146199e+00 1.07440999e+00 -1.30961518e+00\n", " 1.40722007e-01 -5.96393171e-02 3.99602528e-01 6.16327589e-01]\n", "y (on disk): \n", "\n", "x (in mem): \n", "y (on disk): \n", "\n", "x (in mem): (100,)\n", "y (on disk): (100,)\n" ] } ], "source": [ "with h5py.File('myfile.h5', 'r') as f: # open file\n", " x = f['x'][:] # read data into memory\n", " y = f['y'] # get pointer to data on disk\n", " \n", " print('x (in mem): ', x)\n", " print('y (on disk):', y)\n", " print('')\n", " print('x (in mem): ', type(x))\n", " print('y (on disk):', type(y))\n", " print('')\n", " print('x (in mem): ', x.shape)\n", " print('y (on disk):', y.shape) # same info from out-of-memory array" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Append data to HDF5\n", "\n", "Let's add some data with specific paths (groups)" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "with h5py.File('myfile.h5', 'a') as f:\n", " f['/path/to/data/vec'] = z**2\n", " f['/path/to/data/mat'] = z.reshape(10,10)\n", " \n", " # NOTE: 'path', 'to' and 'data' are groups\n", " # 'vec' and 'mat' are datasets" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "/ Group\r\n", "/path Group\r\n", "/path/to Group\r\n", "/path/to/data Group\r\n", "/path/to/data/mat Dataset {10, 10}\r\n", "/path/to/data/vec Dataset {100}\r\n", "/x Dataset {100}\r\n", "/y Dataset {100}\r\n", "/z Dataset {100}\r\n" ] } ], "source": [ "# Inspect file from the command line\n", "!h5ls -r myfile.h5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now our HDF5 file has some structure!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Add metadata to HDF5\n", "\n", "Let's first inpect the metadata added by default" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "HDF5 \"myfile.h5\" {\r\n", "GROUP \"/\" {\r\n", " GROUP \"path\" {\r\n", " GROUP \"to\" {\r\n", " GROUP \"data\" {\r\n", " DATASET \"mat\" {\r\n", " DATATYPE H5T_IEEE_F64LE\r\n", " DATASPACE SIMPLE { ( 10, 10 ) / ( 10, 10 ) }\r\n", " }\r\n", " DATASET \"vec\" {\r\n", " DATATYPE H5T_IEEE_F64LE\r\n", " DATASPACE SIMPLE { ( 100 ) / ( 100 ) }\r\n", " }\r\n", " }\r\n", " }\r\n", " }\r\n", " DATASET \"x\" {\r\n", " DATATYPE H5T_IEEE_F64LE\r\n", " DATASPACE SIMPLE { ( 100 ) / ( 100 ) }\r\n", " }\r\n", " DATASET \"y\" {\r\n", " DATATYPE H5T_IEEE_F64LE\r\n", " DATASPACE SIMPLE { ( 100 ) / ( 100 ) }\r\n", " }\r\n", " DATASET \"z\" {\r\n", " DATATYPE H5T_IEEE_F64LE\r\n", " DATASPACE SIMPLE { ( 100 ) / ( 100 ) }\r\n", " }\r\n", "}\r\n", "}\r\n" ] } ], "source": [ "# Inspect Metadate from the commaand line\n", "!h5dump -H myfile.h5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's add our own metadata" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "with h5py.File('myfile.h5', 'a') as f:\n", " g = f['/path'] # pointer to group 'path'\n", " d = f['/path/to/data/mat'] # pointer to dataset 'mat'\n", " \n", " # Metadata for the group\n", " g.attrs['Description'] = 'This is a group'\n", " g.attrs['Author'] = 'Your name'\n", " g.attrs['email'] = 'yourname@domain.com'\n", " \n", " # Metadata for the data\n", " d.attrs['Description'] = 'This is an array'\n", " d.attrs['Date'] = '2019-06-01'\n", " d.attrs['Version'] = '1.2'" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "HDF5 \"myfile.h5\" {\r\n", "GROUP \"/\" {\r\n", " GROUP \"path\" {\r\n", " ATTRIBUTE \"Author\" {\r\n", " DATATYPE H5T_STRING {\r\n", " STRSIZE H5T_VARIABLE;\r\n", " STRPAD H5T_STR_NULLTERM;\r\n", " CSET H5T_CSET_UTF8;\r\n", " CTYPE H5T_C_S1;\r\n", " }\r\n", " DATASPACE SCALAR\r\n", " }\r\n", " ATTRIBUTE \"Description\" {\r\n", " DATATYPE H5T_STRING {\r\n", " STRSIZE H5T_VARIABLE;\r\n", " STRPAD H5T_STR_NULLTERM;\r\n", " CSET H5T_CSET_UTF8;\r\n", " CTYPE H5T_C_S1;\r\n", " }\r\n", " DATASPACE SCALAR\r\n", " }\r\n", " ATTRIBUTE \"email\" {\r\n", " DATATYPE H5T_STRING {\r\n", " STRSIZE H5T_VARIABLE;\r\n", " STRPAD H5T_STR_NULLTERM;\r\n", " CSET H5T_CSET_UTF8;\r\n", " CTYPE H5T_C_S1;\r\n", " }\r\n", " DATASPACE SCALAR\r\n", " }\r\n", " GROUP \"to\" {\r\n", " GROUP \"data\" {\r\n", " DATASET \"mat\" {\r\n", " DATATYPE H5T_IEEE_F64LE\r\n", " DATASPACE SIMPLE { ( 10, 10 ) / ( 10, 10 ) }\r\n", " ATTRIBUTE \"Date\" {\r\n", " DATATYPE H5T_STRING {\r\n", " STRSIZE H5T_VARIABLE;\r\n", " STRPAD H5T_STR_NULLTERM;\r\n", " CSET H5T_CSET_UTF8;\r\n", " CTYPE H5T_C_S1;\r\n", " }\r\n", " DATASPACE SCALAR\r\n", " }\r\n", " ATTRIBUTE \"Description\" {\r\n", " DATATYPE H5T_STRING {\r\n", " STRSIZE H5T_VARIABLE;\r\n", " STRPAD H5T_STR_NULLTERM;\r\n", " CSET H5T_CSET_UTF8;\r\n", " CTYPE H5T_C_S1;\r\n", " }\r\n", " DATASPACE SCALAR\r\n", " }\r\n", " ATTRIBUTE \"Version\" {\r\n", " DATATYPE H5T_STRING {\r\n", " STRSIZE H5T_VARIABLE;\r\n", " STRPAD H5T_STR_NULLTERM;\r\n", " CSET H5T_CSET_UTF8;\r\n", " CTYPE H5T_C_S1;\r\n", " }\r\n", " DATASPACE SCALAR\r\n", " }\r\n", " }\r\n", " DATASET \"vec\" {\r\n", " DATATYPE H5T_IEEE_F64LE\r\n", " DATASPACE SIMPLE { ( 100 ) / ( 100 ) }\r\n", " }\r\n", " }\r\n", " }\r\n", " }\r\n", " DATASET \"x\" {\r\n", " DATATYPE H5T_IEEE_F64LE\r\n", " DATASPACE SIMPLE { ( 100 ) / ( 100 ) }\r\n", " }\r\n", " DATASET \"y\" {\r\n", " DATATYPE H5T_IEEE_F64LE\r\n", " DATASPACE SIMPLE { ( 100 ) / ( 100 ) }\r\n", " }\r\n", " DATASET \"z\" {\r\n", " DATATYPE H5T_IEEE_F64LE\r\n", " DATASPACE SIMPLE { ( 100 ) / ( 100 ) }\r\n", " }\r\n", "}\r\n", "}\r\n" ] } ], "source": [ "# Inspect Metadata from the commaand line\n", "!h5dump -H myfile.h5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Inspect HDF5 from Python\n", "\n", "Let's do the same as above but using Python code" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "f = h5py.File('myfile.h5', 'r') # keep it open\n", "\n", "# Inspect base groups quickly\n", "print(f.keys())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Inspect the full structure w/metadata" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "path\n", " Author: Your name\n", " Description: This is a group\n", " email: yourname@domain.com\n", "path/to\n", "path/to/data\n", "path/to/data/mat\n", " Date: 2019-06-01\n", " Description: This is an array\n", " Version: 1.2\n", "path/to/data/vec\n", "x\n", "y\n", "z\n" ] } ], "source": [ "def print_attrs(name, obj):\n", " print(name)\n", " for key,val in obj.attrs.items():\n", " print(\" %s: %s\" % (key, val))\n", "\n", "f.visititems(print_attrs) " ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "f.close()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Create an extendable dataset\n", "\n", "Create an empty container (called `grids`) extendable in the 3rd dim" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "with h5py.File('myfile.h5', 'a') as f:\n", " dset = f.create_dataset(\"grids\", (10,10,5), maxshape=(10,10,None), dtype='f4', compression='gzip')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Check that our created container has an infinity dimension" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "/ Group\r\n", "/grids Dataset {10, 10, 5/Inf}\r\n", "/path Group\r\n", "/path/to Group\r\n", "/path/to/data Group\r\n", "/path/to/data/mat Dataset {10, 10}\r\n", "/path/to/data/vec Dataset {100}\r\n", "/x Dataset {100}\r\n", "/y Dataset {100}\r\n", "/z Dataset {100}\r\n" ] } ], "source": [ "!h5ls -r myfile.h5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Create a bunch of 2D grids to save to our empty container" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(5, 10, 10)\n" ] } ], "source": [ "mygrids = [np.random.randn(10,10) for _ in range(5)]\n", "\n", "print(np.shape(mygrids)) # 5 grids of 10 by 10" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Save grids one at a time and close the file" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [], "source": [ "with h5py.File('myfile.h5', 'a') as f:\n", " grids = f['grids']\n", " \n", " for k,g in enumerate(mygrids):\n", " grids[:,:,k] = g" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Read in (select) specific grids with fancy indexing" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(10, 10, 3)\n" ] } ], "source": [ "with h5py.File('myfile.h5', 'r') as f:\n", " mygrids = f['grids'][:,:,[0,2,4]] # 3 grids out of 5\n", " \n", "print(np.shape(mygrids))" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "%matplotlib inline\n", "import matplotlib.pyplot as plt\n", "\n", "# Plot each grid to check dimensions are right\n", "[plt.matshow(mygrids[:,:,k]) for k in range(mygrids.shape[2])]\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Final thoughts on HDF5\n", "\n", "- Many small files is usually more practical than a few large ones\n", "- Read/Write is faster on smaller files (faster queries)\n", "- Network transfer is usually faster with smaller files\n", "- Storing a lot of data into a single file is susceptible to corruption\n", "- Many small files simplifies (embarrasingly) parallelization" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.3" } }, "nbformat": 4, "nbformat_minor": 2 }