Bonjour à tous!
Le lancement du cours
«Développeur Web Python» approche, respectivement, nous partageons toujours des articles intéressants et nous invitons à nos leçons ouvertes, où vous pouvez regarder du matériel intéressant, rencontrer des
enseignants et leur poser des questions.
C'est parti!
HDF5 permet un stockage efficace de grandes quantités de donnéesLorsque vous travaillez avec de gros volumes de données, expérimentales ou simulées, leur stockage dans plusieurs fichiers texte n'est pas très efficace. Parfois, vous devez accéder à un sous-ensemble spécifique de données et vous souhaitez le faire rapidement. Dans ces situations, le format HDF5 résout les deux problèmes grâce à une bibliothèque intégrée hautement optimisée. HDF5 est largement utilisé dans les environnements scientifiques et possède une excellente implémentation en Python conçue pour fonctionner avec NumPy dès la sortie de l'emballage.
Le format HDF5 prend en charge les fichiers de toute taille, et chaque fichier a une structure interne qui vous permet de rechercher un ensemble de données spécifique. Cela peut être considéré comme un fichier séparé avec sa propre structure hiérarchique, ainsi qu'un ensemble de dossiers et de sous-dossiers. Par défaut, les données sont stockées au format binaire et la bibliothèque est compatible avec différents types de données. L'une des options les plus importantes pour le format HDF5 est qu'il vous permet de joindre des métadonnées à chaque élément de la structure, ce qui le rend idéal pour créer des fichiers hors ligne.

En Python, une interface au format HDF5 peut être construite à l'aide du package h5py. L'une des caractéristiques les plus intéressantes de ce package est que les données sont lues à partir d'un fichier uniquement lorsque cela est nécessaire. Imaginez que vous ayez un très grand tableau qui ne rentre pas dans votre RAM disponible. Par exemple, vous pouvez générer un tableau sur un ordinateur avec des spécifications différentes, contrairement à celui que vous utilisez pour l'analyse des données. Le format HDF5 vous permet de choisir les éléments du tableau à lire avec une syntaxe équivalente à NumPy. Ensuite, vous pouvez travailler avec des données stockées sur le disque dur, et non dans la RAM, sans modifications importantes du code existant.
Dans cet article, nous verrons comment vous pouvez utiliser h5py pour stocker et récupérer des données depuis votre disque dur. Nous discuterons de différentes façons de stocker des données et comment optimiser le processus de lecture. Tous les exemples qui apparaissent dans cet article sont également disponibles dans notre
référentiel Github .
L'installationLe format HDF5 est pris en charge par le
groupe HDF et il est basé sur des normes open source, ce qui signifie que vos données seront toujours disponibles, même si le groupe disparaît. La prise en charge de Python est fournie via le package
h5py , qui peut être installé via pip. N'oubliez pas que vous devez utiliser l'environnement
virtuel pour tester:
pip install h5py
cette commande installera également NumPy s'il n'est pas dans votre environnement.
Si vous recherchez un outil graphique pour examiner le contenu de vos fichiers HDF5, vous pouvez installer la
visionneuse HDF5 . Il est écrit en Java, il devrait donc fonctionner sur presque tous les ordinateurs.
Stockage et lecture de données de basePassons maintenant à l'utilisation de la bibliothèque HDF5. Nous allons créer un nouveau fichier et y enregistrer un tableau NumPy aléatoire.
import h5py import numpy as np arr = np.random.randn(1000) with h5py.File('random.hdf5', 'w') as f: dset = f.create_dataset("default", data=arr)
Les premières lignes sont assez simples: nous importons les packages h5py et NumPy et créons un tableau avec des valeurs aléatoires. Nous ouvrons le fichier random.hdf5 avec l'autorisation d'écriture w, ce qui signifie que si un fichier du même nom existe déjà, il sera écrasé. Si vous souhaitez enregistrer le fichier tout en pouvant y écrire, vous pouvez l'ouvrir avec l'attribut a au lieu de w. Nous créons un ensemble de données appelé par défaut et définissons les données comme un tableau aléatoire créé précédemment. Les jeux de données sont les dépositaires de nos données, principalement des blocs de construction au format HDF5.
Une note
Si vous n'êtes pas familier avec l'instruction with, je dois noter qu'il s'agit d'un moyen pratique d'ouvrir et de fermer des fichiers. Même si une erreur se produit à l'intérieur
with
, le fichier sera fermé. Si pour une raison quelconque vous n'utilisez pas
with
, n'oubliez jamais d'ajouter la commande
f.close()
à la fin. L'instruction
with
fonctionne avec tous les fichiers, pas seulement les fichiers HDF.
Nous pouvons lire les données de la même manière que nous lisons le fichier NumPy:
with h5py.File('random.hdf5', 'r') as f: data = f['default'] print(min(data)) print(max(data)) print(data[:15])
Nous ouvrons le fichier avec l'attribut de lecture r et restaurons les données en accédant directement à l'ensemble de données appelé par défaut. Si vous ouvrez le fichier et ne savez pas quels jeux de données sont disponibles, vous pouvez les obtenir:
for key in f.keys(): print(key)
Après avoir lu le jeu de données souhaité, vous pouvez l'utiliser comme si vous utilisiez un tableau NumPy. Par exemple, vous pouvez trouver les valeurs maximale et minimale ou sélectionner les 15 premières valeurs du tableau. Ces exemples simples, cependant, cachent beaucoup de choses qui se produisent sous le capot, et ils doivent être discutés pour comprendre tout le potentiel du HDF5.
Dans l'exemple ci-dessus, vous pouvez utiliser les données comme un tableau. Par exemple, vous pouvez vous référer au troisième élément en entrant des données [2], ou vous pouvez obtenir une plage de valeurs de données [1: 3]. Veuillez noter: les données ne sont pas un tableau, c'est un ensemble de données. Vous pouvez le voir en tapant
print(type(data))
. Les jeux de données fonctionnent d'une manière complètement différente des tableaux, car leurs informations sont stockées sur le disque dur et ils ne les chargent pas dans la RAM si nous ne les utilisons pas. Le code suivant, par exemple, ne fonctionnera pas:
f = h5py.File('random.hdf5', 'r') data = f['default'] f.close() print(data[1])
L'erreur qui apparaît est un peu lourde, mais la dernière ligne est très utile:
ValueError: Not a dataset (not a dataset)
L'erreur signifie que nous essayons d'accéder à un ensemble de données auquel nous n'avons plus accès. C'est un peu déroutant, mais cela se produit parce que nous avons fermé le fichier et que nous ne sommes donc plus autorisés à accéder à la deuxième valeur des données. Lorsque nous avons attribué f ['default'] à des données variables, nous ne lisons pas réellement les données du fichier, nous générons plutôt un pointeur vers l'emplacement des données sur le disque dur. Par contre, ce code fonctionnera:
f = h5py.File('random.hdf5', 'r') data = f['default'][:] f.close() print(data[10])
Veuillez noter que la seule différence est que nous avons ajouté [:] après avoir lu l'ensemble de données. De nombreux autres manuels s'attardent sur de tels exemples, sans même démontrer le plein potentiel du format HDF5 avec le package h5py. En raison des exemples que nous avons examinés jusqu'à présent, vous vous demandez peut-être: pourquoi utiliser HDF5 si l'enregistrement de fichiers NumPy vous offre les mêmes fonctionnalités? Plongeons-nous dans les fonctionnalités du format HDF5.
Lecture sélective à partir de fichiers HDF5Jusqu'à présent, nous avons vu que lorsque nous lisons un ensemble de données, nous ne lisons pas encore les données du disque, au lieu de cela, nous créons un lien vers un endroit spécifique sur le disque dur. Nous pouvons voir ce qui se passe si, par exemple, nous lisons explicitement les 10 premiers éléments d'un ensemble de données:
with h5py.File('random.hdf5', 'r') as f: data_set = f['default'] data = data_set[:10] print(data[1]) print(data_set[1])
Nous avons divisé le code en différentes lignes pour le rendre plus explicite, mais vous pouvez être plus synthétique dans vos projets. Dans les lignes ci-dessus, nous lisons d'abord le fichier, puis lisons l'ensemble de données par défaut. Nous attribuons les 10 premiers éléments de l'ensemble de données à la variable de données. Après avoir fermé le fichier (quand il se termine), nous pouvons accéder aux valeurs stockées dans les données, mais data_set générera une erreur. Notez que nous ne lisons sur le disque que lorsque nous accédons explicitement aux 10 premiers éléments d'un ensemble de données. Si vous regardez les types data et data_set, vous verrez qu'ils sont vraiment différents. Le premier est un tableau NumPy et le second est un DataSet h5py.
Le même comportement est pertinent dans des scénarios plus complexes. Créons un nouveau fichier, cette fois avec deux ensembles de données, et sélectionnons les éléments de l'un en fonction des éléments de l'autre. Commençons par créer un nouveau fichier et stocker des données; cette partie est la plus simple:
import h5py import numpy as np arr1 = np.random.randn(10000) arr2 = np.random.randn(10000) with h5py.File('complex_read.hdf5', 'w') as f: f.create_dataset('array_1', data=arr1) f.create_dataset('array_2', data=arr2)
Nous avons deux ensembles de données appelés array_1 et array_2, chacun contenant un tableau NumPy aléatoire. Nous voulons lire les valeurs de array_2 qui correspondent aux éléments où les valeurs de array_1 sont positives. Nous pouvons essayer de faire quelque chose comme ça:
with h5py.File('complex_read.hdf5', 'r') as f: d1 = f['array_1'] d2 = f['array_2'] data = d2[d1>0]
mais cela ne fonctionnera pas. d1 est un ensemble de données et ne peut pas être comparé à un entier. La seule façon est de lire réellement les données du disque, puis de les comparer. Par conséquent, nous obtenons quelque chose comme ceci:
with h5py.File('complex_read.hdf5', 'r') as f: d1 = f['array_1'] d2 = f['array_2'] data = d2[d1[:]>0]
Le premier ensemble de données d1 est complètement chargé en mémoire lorsque nous faisons d1 [:], mais à partir du deuxième ensemble de données d2, nous ne prenons que quelques éléments. Si l'ensemble de données d1 était trop volumineux pour être entièrement chargé en mémoire, nous pourrions travailler à l'intérieur d'une boucle.
with h5py.File('complex_read.hdf5', 'r') as f: d1 = f['array_1'] d2 = f['array_2'] data = [] for i in range(len(d1)): if d1[i] > 0: data.append(d2[i]) print('The length of data with a for loop: {}'.format(len(data)))
Bien sûr, il y a des problèmes avec l'efficacité de la lecture par élément et de l'ajout d'éléments à la liste, mais c'est un très bon exemple de l'un des plus grands avantages de l'utilisation de HDF5 par rapport aux fichiers texte ou NumPy. À l'intérieur de la boucle, nous chargeons un seul élément en mémoire. Dans notre exemple, chaque élément est simplement un nombre, mais cela peut être n'importe quoi: du texte à l'image ou à la vidéo.
Comme toujours, en fonction de votre application, vous devez décider si vous souhaitez lire l'intégralité du tableau en mémoire ou non. Parfois, vous exécutez des simulations sur un ordinateur spécifique avec une grande quantité de mémoire, mais vous n'avez pas les mêmes caractéristiques sur votre ordinateur portable et vous êtes obligé de lire des morceaux de vos données. N'oubliez pas que la lecture à partir du disque dur est relativement lente, surtout si vous utilisez le disque dur au lieu des disques SDD ou même plus si vous lisez à partir d'un lecteur réseau.
Écriture sélective sur des fichiers HDF5Dans les exemples ci-dessus, nous avons ajouté des données à l'ensemble de données dès sa création. Cependant, pour de nombreuses applications, vous devez enregistrer les données lors de la génération. HDF5 vous permet d'enregistrer des données de la même manière que vous les lisez. Voyons comment créer un ensemble de données vide et y ajouter des données.
arr = np.random.randn(100) with h5py.File('random.hdf5', 'w') as f: dset = f.create_dataset("default", (1000,)) dset[10:20] = arr[50:60]
Les deux premières lignes sont les mêmes qu'avant, à l'exception de
create_dataset
. Nous n'ajoutons pas de données lors de leur création, nous créons simplement un ensemble de données vide pouvant contenir jusqu'à 1000 éléments. Avec la même logique que précédemment, lorsque nous lisons certains éléments d'un ensemble de données, nous n'écrivons sur le disque que lorsque nous attribuons des valeurs à certains éléments de la variable dset. Dans l'exemple ci-dessus, nous n'affectons des valeurs qu'à un sous-ensemble du tableau, avec des indices de 10 à 19.
AvertissementCe que vous écrivez sur le disque n'est pas entièrement vrai lorsque vous attribuez des valeurs à un ensemble de données. Le moment exact dépend de plusieurs facteurs, dont l'état du système d'exploitation. Si le programme se ferme trop tôt, il peut arriver que tout ne soit pas enregistré. Il est très important de toujours utiliser la méthode
close()
, et si vous écrivez par étapes, vous pouvez également utiliser
flush()
pour forcer l'entrée. L'utilisation de avec évite de nombreux problèmes d'écriture.
Si vous lisez le fichier et imprimez les 20 premières valeurs de l'ensemble de données, vous verrez que ce sont tous des zéros, à l'exception des indices 10 à 19. Il y a une erreur courante qui peut entraîner un mal de tête notable. Le code suivant n'enregistrera rien sur le disque:
arr = np.random.randn(1000) with h5py.File('random.hdf5', 'w') as f: dset = f.create_dataset("default", (1000,)) dset = arr
Cette erreur provoque toujours beaucoup de problèmes, car vous ne comprendrez pas que vous n'avez rien écrit tant que vous n'essayez pas de lire le résultat. Le problème ici est que vous ne spécifiez pas où vous souhaitez stocker les données, vous écrasez simplement la variable dset avec un tableau NumPy. Étant donné que l'ensemble de données et le tableau ont la même longueur, vous devez utiliser dset [:] = arr. Cette erreur se produit plus souvent que vous ne le pensez, et comme elle n'est techniquement pas fausse, vous ne verrez aucune erreur affichée sur le terminal et vos données seront des zéros.
Jusqu'à présent, nous avons toujours travaillé avec des tableaux unidimensionnels, mais nous n'y sommes pas limités. Par exemple, supposons que nous voulons utiliser un tableau 2D, nous pouvons simplement faire:
dset = f.create_dataset('default', (500, 1024))
ce qui nous permet de stocker des données dans un tableau 500x1024. Pour utiliser un jeu de données, nous pouvons utiliser la même syntaxe qu'auparavant, mais en tenant compte de la deuxième dimension:
dset[1,2] = 1 dset[200:500, 500:1024] = 123
Spécifiez les types de données pour optimiser l'espaceJusqu'à présent, nous n'avons examiné que la pointe de l'iceberg de ce que HDF5 a à offrir. En plus de la longueur des données que vous souhaitez conserver, vous pouvez spécifier le type de données pour optimiser l'espace.
La documentation h5py contient une liste de tous les types pris en charge, nous n'en montrons ici que quelques-uns. Dans le même temps, nous travaillerons avec plusieurs ensembles de données dans un seul fichier.
with h5py.File('several_datasets.hdf5', 'w') as f: dset_int_1 = f.create_dataset('integers', (10, ), dtype='i1') dset_int_8 = f.create_dataset('integers8', (10, ), dtype='i8') dset_complex = f.create_dataset('complex', (10, ), dtype='c16') dset_int_1[0] = 1200 dset_int_8[0] = 1200.1 dset_complex[0] = 3 + 4j
Dans l'exemple ci-dessus, nous avons créé trois ensembles de données différents, chacun ayant un type différent. Entiers de 1 octet, entiers de 8 octets et nombres complexes de 16 octets. Nous ne stockons qu'un seul numéro, même si nos ensembles de données peuvent contenir jusqu'à 10 éléments. Vous pouvez lire les valeurs et voir ce qui a été réellement enregistré. Il convient de noter ici qu'un entier de 1 octet doit être arrondi à 127 (au lieu de 1200), et un entier de 8 octets doit être arrondi à 1200 (au lieu de 1200.1).
Si vous avez déjà programmé dans des langages comme C ou Fortran, vous savez probablement ce que signifient différents types de données. Cependant, si vous avez toujours travaillé avec Python, vous n'avez peut-être rencontré aucun problème sans déclarer explicitement le type de données avec lequel vous travaillez. Il est important de se rappeler que le nombre d'octets vous indique combien de nombres différents vous pouvez enregistrer. Si vous utilisez 1 octet, vous disposez de 8 bits et vous pouvez donc stocker 2 ^ 8 nombres différents. Dans l'exemple ci-dessus, les entiers sont à la fois positifs, négatifs et 0. Lorsque vous utilisez des entiers de 1 octet, vous pouvez stocker des valeurs de -128 à 127, au total ce sont 2 ^ 8 nombres possibles. Cela équivaut à utiliser 8 octets, mais avec une large gamme de nombres.
Le type de données sélectionné affectera sa taille. Voyons d'abord comment cela fonctionne avec un exemple simple. Créons trois fichiers, chacun avec un jeu de données pour 100 000 éléments, mais avec différents types de données. Nous y enregistrerons les mêmes données, puis comparerons leurs tailles. Nous créons un tableau aléatoire à attribuer à chaque ensemble de données pour remplir la mémoire. N'oubliez pas que les données seront converties au format spécifié dans l'ensemble de données.
arr = np.random.randn(100000) f = h5py.File('integer_1.hdf5', 'w') d = f.create_dataset('dataset', (100000,), dtype='i1') d[:] = arr f.close() f = h5py.File('integer_8.hdf5', 'w') d = f.create_dataset('dataset', (100000,), dtype='i8') d[:] = arr f.close() f = h5py.File('float.hdf5', 'w') d = f.create_dataset('dataset', (100000,), dtype='f16') d[:] = arr f.close()
Lorsque vous vérifiez la taille de chaque fichier, vous obtiendrez quelque chose comme:
Fichier | Taille (b) |
---|
entier_1 | 102144 |
entier_9 | 802144 |
flotter | 1602144 |
La relation entre la taille et le type de données est claire. Lorsque vous passez d'entiers de 1 octet à 8 octets, la taille du fichier augmente de 8 fois, de même, lorsque vous passez à 16 octets, cela prend environ 16 fois plus d'espace. Mais l'espace n'est pas le seul facteur important à considérer; vous devez également tenir compte du temps nécessaire pour écrire des données sur le disque. Plus vous avez à écrire, plus cela prendra de temps. Selon votre application, il peut être crucial d'optimiser la lecture et l'écriture des données.
Remarque: si vous utilisez un mauvais type de données, vous risquez également de perdre des informations. Par exemple, si vous avez des entiers de 8 octets et que vous les stockez en tant qu'entiers de 1 octet, leurs valeurs seront tronquées. Lorsque vous travaillez en laboratoire, des appareils qui créent différents types de données sont souvent disponibles. Certaines cartes DAQ ont 16 bits, certaines caméras fonctionnent avec 8 bits, mais certaines peuvent fonctionner avec 24. Il est important de faire attention aux types de données, mais c'est aussi quelque chose que les développeurs Python peuvent ne pas prendre en compte, car vous n'avez pas besoin de déclarer le type.
Il est également intéressant de se rappeler que le tableau NumPy par défaut sera flottant avec 8 octets (64 bits) par élément. Cela peut être un problème si, par exemple, vous initialisez un tableau avec des zéros pour stocker des données, qui ne devraient être que de 2 octets. Le type du tableau lui-même ne changera pas, et si vous enregistrez les données lors de la création de l'ensemble de données (en ajoutant data = my_array), le format par défaut sera "f8", qui est un tableau, mais pas des données réelles,
Penser aux types de données n'est pas quelque chose qui se produit régulièrement si vous travaillez avec Python dans des applications simples. Cependant, vous devez savoir que des types de données existent et quel impact ils peuvent avoir sur vos résultats. Vous pouvez avoir de gros disques durs et vous ne vous souciez pas vraiment du stockage de fichiers, mais lorsque vous vous souciez de la vitesse à laquelle vous enregistrez, il n'y a pas d'autre moyen que d'optimiser tous les aspects de votre code, y compris les types de données.
Compression des donnéesLors de l'enregistrement des données, vous pouvez choisir la compression à l'aide de différents algorithmes. Le package h5py prend en charge plusieurs filtres de compression, tels que GZIP, LZF et SZIP. Lorsque vous utilisez l'un des filtres de compression, les données seront traitées sur leur chemin vers le disque et, à la lecture, elles seront décompressées. Par conséquent, il n'y a pas de modifications spéciales dans le code. Nous pouvons répéter la même expérience, en enregistrant différents types de données, mais en utilisant un filtre de compression. Notre code ressemble à ceci:
import h5py import numpy as np arr = np.random.randn(100000) with h5py.File('integer_1_compr.hdf5', 'w') as f: d = f.create_dataset('dataset', (100000,), dtype='i1', compression="gzip", compression_opts=9) d[:] = arr with h5py.File('integer_8_compr.hdf5', 'w') as f: d = f.create_dataset('dataset', (100000,), dtype='i8', compression="gzip", compression_opts=9) d[:] = arr with h5py.File('float_compr.hdf5', 'w') as f: d = f.create_dataset('dataset', (100000,), dtype='f16', compression="gzip", compression_opts=9) d[:] = arr
Nous avons choisi gzip car il est pris en charge sur toutes les plateformes. Les options compression_opts spécifient le niveau de compression. Plus le niveau est élevé, moins les données prennent de place, mais plus le processeur doit fonctionner longtemps. Le niveau de compression par défaut est 4. Nous pouvons voir les différences dans nos fichiers en fonction du niveau de compression:
Tapez | Pas de compression | Compression 9 | Compression 4 |
---|
entier_1 | 102144 | 28016 | 30463 |
entier_8 | 802144 | 43329 | 57971 |
flotter | 1602144 | 1469580 | 1469868 |
L'effet de la compression sur des tableaux de données entiers est beaucoup plus visible que sur des jeux de données à virgule flottante. Je vous laisse le soin de comprendre pourquoi la compression a si bien fonctionné dans les deux premiers cas, mais pas dans le dernier. À titre indicatif: vous devez vérifier les données que vous stockez réellement.
La lecture des données compressées ne modifie aucun des codes décrits ci-dessus. La bibliothèque principale HDF5 se chargera d'extraire les données des ensembles de données compressées à l'aide de l'algorithme approprié. Par conséquent, si vous implémentez la compression pour l'enregistrement, vous n'avez pas besoin de modifier le code que vous utilisez pour la lecture.
La compression des données est un outil supplémentaire que vous devez considérer avec tous les autres aspects du traitement des données. Vous devez tenir compte du temps processeur supplémentaire et du taux de compression efficace afin d'évaluer les avantages de la compression des données dans votre propre application. Le fait qu'il soit transparent au code en aval le rend incroyablement facile à tester et à trouver la meilleure solution.
Redimensionner les jeux de donnéesLorsque vous travaillez sur une expérience, il est parfois impossible de déterminer la taille de vos données. Imaginez que vous enregistrez un film, peut-être que vous l'arrêterez après une seconde, peut-être après une heure. Heureusement, HDF5 vous permet de redimensionner des ensembles de données à la volée avec un faible coût de calcul. La longueur de l'ensemble de données peut être dépassée jusqu'à la taille maximale. Cette taille maximale est spécifiée lors de la création de l'ensemble de données à l'aide du mot clé maxshape:
import h5py import numpy as np with h5py.File('resize_dataset.hdf5', 'w') as f: d = f.create_dataset('dataset', (100, ), maxshape=(500, )) d[:100] = np.random.randn(100) d.resize((200,)) d[100:200] = np.random.randn(100) with h5py.File('resize_dataset.hdf5', 'r') as f: dset = f['dataset'] print(dset[99]) print(dset[199])
Tout d'abord, vous créez un ensemble de données pour stocker 100 valeurs et définissez la taille maximale sur 500 valeurs. Après avoir enregistré le premier lot de valeurs, vous pouvez développer l'ensemble de données pour enregistrer les 100 suivantes. Vous pouvez répéter la procédure jusqu'à obtenir un ensemble de données avec 500 valeurs.
Il en va de même pour les tableaux de formes diverses, toute taille de la matrice à N dimensions peut être modifiée. Vous pouvez vérifier que les données ont été enregistrées correctement en lisant le fichier et en imprimant deux éléments sur la ligne de commande.Vous pouvez également redimensionner l'ensemble de données à un stade ultérieur, vous n'avez pas besoin de le faire dans la même session lorsque vous avez créé le fichier. Par exemple, vous pouvez faire quelque chose comme ça (notez que nous ouvrons un fichier avec un attribut afin de ne pas détruire le fichier précédent): with h5py.File('resize_dataset.hdf5', 'a') as f: dset = f['dataset'] dset.resize((300,)) dset[:200] = 0 dset[200:300] = np.random.randn(100) with h5py.File('resize_dataset.hdf5', 'r') as f: dset = f['dataset'] print(dset[99]) print(dset[199]) print(dset[299])
, , 200 200 299. , , .
, , , . 2D-, , — , 2D-. 3- HDF-, . , :
with h5py.File('movie_dataset.hdf5', 'w') as f: d = f.create_dataset('dataset', (1024, 1024, 1), maxshape=(1024, 1024, None )) d[:,:,0] = first_frame d.resize((1024,1024,2)) d[:,:,1] = second_frame
1024x1024 , . , , . maxshape None.
(Chunks), . (chunk) , .. . , , . , :
dset = f.create_dataset("chunked", (1000, 1000), chunks=(100, 100))
, dset [0: 100,0: 100] . dset [200: 300, 200: 300], dset [100: 200, 400: 500] . . h5py, :
(Chunking) . 10 KiB 1 MiB, . , , .
(auto-chunking), . , maxshape. :
dset = f.create_dataset("autochunk", (1000, 1000), chunks=True)
(Groups). HDF5, , . (groups), , . , :
import numpy as np import h5py arr = np.random.randn(1000) with h5py.File('groups.hdf5', 'w') as f: g = f.create_group('Base_Group') gg = g.create_group('Sub_Group') d = g.create_dataset('default', data=arr) dd = gg.create_dataset('default', data=arr)
Base_Group , Sub_Group. default . , , :
with h5py.File('groups.hdf5', 'r') as f: d = f['Base_Group/default'] dd = f['Base_Group/Sub_Group/default'] print(d[1]) print(dd[1])
, : Base_Group/default Base_Group/Sub_Group/default. , , , , . — keys():
with h5py.File('groups.hdf5', 'r') as f: for k in f.keys(): print(k)
, , for-. , . visit(), :
def get_all(name): print(name) with h5py.File('groups.hdf5', 'r') as f: f.visit(get_all)
,
get_all
, , name. visit,
get_all.
visit , , None, . , , Sub_Group,
get_all
:
def get_all(name): if 'Sub_Group' in name: return name with h5py.File('groups.hdf5', 'r') as f: g = f.visit(get_all) print(g)
visit , , None, , get_all. Sub_Group, get_all , Sub_Group . , g , , :
with h5py.File('groups.hdf5', 'r') as f: g_name = f.visit(get_all) group = f[g_name]
. — , visititems, : name object. :
def get_objects(name, obj): if 'Sub_Group' in name: return obj with h5py.File('groups.hdf5', 'r') as f: group = f.visititems(get_objects) data = group['default'] print('First data element: {}'.format(data[0]))
visititems , , , . , , . . , , .
HDF5, HDF5, , . , , , , , , .. . , , 200x300x250. , , , , — , .
HDF5 -. .
import time import numpy as np import h5py import os arr = np.random.randn(1000) with h5py.File('groups.hdf5', 'w') as f: g = f.create_group('Base_Group') d = g.create_dataset('default', data=arr) g.attrs['Date'] = time.time() g.attrs['User'] = 'Me' d.attrs['OS'] = os.name for k in g.attrs.keys(): print('{} => {}'.format(k, g.attrs[k])) for j in d.attrs.keys(): print('{} => {}'.format(j, d.attrs[j]))
, attrs . , , . , . , , , update:
with h5py.File('groups.hdf5', 'w') as f: g = f.create_group('Base_Group') d = g.create_dataset('default', data=arr) metadata = {'Date': time.time(), 'User': 'Me', 'OS': os.name,} f.attrs.update(metadata) for m in f.attrs.keys(): print('{} => {}'.format(m, f.attrs[m]))
, , hdf5, . , . hdf5, . Python -. JSON, , , , pickle.
import json with h5py.File('groups_dict.hdf5', 'w') as f: g = f.create_group('Base_Group') d = g.create_dataset('default', data=arr) metadata = {'Date': time.time(), 'User': 'Me', 'OS': os.name,} m = g.create_dataset('metadata', data=json.dumps(metadata))
, . , . , json.dumps, . , HDF5. , json.loads:
Python
with h5py.File('groups_dict.hdf5', 'r') as f: metadata = json.loads(f['Base_Group/metadata'][()]) for k in metadata: print('{} => {}'.format(k, metadata[k]))
json , . YAML, XML .. , , , attr , , .
HDF5, . , , , . HDF , , , , , . , HDF .
HDF5 . , , . , . . SQL,
HDFql , SQL HDF5.
. , , - , , . , . , , .
HDF5 — , . , , , , . HDF5 — , , .
LA FIN
,
.