Lecture accélérée d'un très grand fichier netcdf en python
j'ai un très grand fichier netCDF que je suis en train de lire en utilisant netCDF4 en python
Je ne peux pas lire ce fichier tout d'un coup car ses dimensions (1200 x 720 x 1440) sont trop grandes pour que le fichier entier soit en mémoire à la fois. La première dimension représente le temps et les deux suivantes la latitude et la longitude respectivement.
import netCDF4
nc_file = netCDF4.Dataset(path_file, 'r', format='NETCDF4')
for yr in years:
nc_file.variables[variable_name][int(yr), :, :]
cependant, lire un an à la fois est extrêmement lent. Comment puis-je accélérer cela pour les cas d'utilisation ci-dessous?
-- EDIT
le chunksize est 1
je peux lire une série d'années: nc_file.variables [variable_name][0:100, :, :]
Il y a plusieurs cas d'utilisation:
pour an dans les années:
numpy.ma.sum(nc_file.variables[variable_name][int(yr), :, :])
# Multiply each year by a 2D array of shape (720 x 1440)
for yr in years:
numpy.ma.sum(nc_file.variables[variable_name][int(yr), :, :] * arr_2d)
# Add 2 netcdf files together
for yr in years:
numpy.ma.sum(nc_file.variables[variable_name][int(yr), :, :] +
nc_file2.variables[variable_name][int(yr), :, :])
3 réponses
je recommande fortement que vous prenez un coup d'oeil à l' xarray
et dask
projets. L'utilisation de ces outils puissants vous permettent de diviser le calcul en morceaux. Cela apporte deux avantages: vous pouvez calculer sur des données qui ne tient pas en mémoire, et vous pouvez utiliser tous les cœurs de votre machine pour une meilleure performance. Vous pouvez optimiser les performances en choisissant de manière appropriée la taille du morceau (voir documentation).
vous pouvez charger vos données à partir de netCDF en faisant quelque chose d'aussi simple que
import xarray as xr
ds = xr.open_dataset(path_file)
si vous voulez fractionner vos données en années le long de la dimension temporelle, alors vous spécifiez le chunks
paramètre (en supposant que la coordonnée de l'année est nommée 'année'):
ds = xr.open_dataset(path_file, chunks={'year': 10})
Depuis les autres coordonnées n'apparaissent pas dans le chunks
DCT, puis un seul morceau sera utilisé pour eux. (Plus de détails dans la documentation ici.). Ce sera utile pour votre première exigence, où vous voulez multiplier chaque année par un tableau 2D. Il vous suffit de faire:
ds['new_var'] = ds['var_name'] * arr_2d
Maintenant, xarray
et dask
Calculez votre résultat paresseusement. Afin de déclencher le calcul final, vous pouvez simplement demander xarray
pour sauvegarder votre résultat sur netCDF:
ds.to_netcdf(new_file)
Le calcul est déclenché par dask
, qui s'occupe de séparer le traitement en morceaux, et permet ainsi de travailler avec des données qui ne tiennent pas en mémoire. En outre, dask
s'occupera de l'utilisation de tous vos cœurs de processeur pour le calcul des morceaux.
xarray
et dask
les projets ne gèrent toujours pas bien les situations où les morceaux ne "s'alignent" pas bien pour le calcul parallèle. Puisque, dans ce cas, nous n'avons travaillé que dans la dimension "année", nous nous attendons à n'avoir aucun problème.
si vous voulez ajouter deux fichiers netCDF différents ensemble, c'est aussi simple comme:
ds1 = xr.open_dataset(path_file1, chunks={'year': 10})
ds2 = xr.open_dataset(path_file2, chunks={'year': 10})
(ds1 + ds2).to_netcdf(new_file)
j'ai fourni un exemple entièrement fonctionnel en utilisant un jeu de données disponible en ligne.
In [1]:
import xarray as xr
import numpy as np
# Load sample data and strip out most of it:
ds = xr.open_dataset('ECMWF_ERA-40_subset.nc', chunks = {'time': 4})
ds.attrs = {}
ds = ds[['latitude', 'longitude', 'time', 'tcw']]
ds
Out[1]:
<xarray.Dataset>
Dimensions: (latitude: 73, longitude: 144, time: 62)
Coordinates:
* latitude (latitude) float32 90.0 87.5 85.0 82.5 80.0 77.5 75.0 72.5 ...
* longitude (longitude) float32 0.0 2.5 5.0 7.5 10.0 12.5 15.0 17.5 20.0 ...
* time (time) datetime64[ns] 2002-07-01T12:00:00 2002-07-01T18:00:00 ...
Data variables:
tcw (time, latitude, longitude) float64 10.15 10.15 10.15 10.15 ...
In [2]:
arr2d = np.ones((73, 144)) * 3.
arr2d.shape
Out[2]:
(73, 144)
In [3]:
myds = ds
myds['new_var'] = ds['tcw'] * arr2d
In [4]:
myds
Out[4]:
<xarray.Dataset>
Dimensions: (latitude: 73, longitude: 144, time: 62)
Coordinates:
* latitude (latitude) float32 90.0 87.5 85.0 82.5 80.0 77.5 75.0 72.5 ...
* longitude (longitude) float32 0.0 2.5 5.0 7.5 10.0 12.5 15.0 17.5 20.0 ...
* time (time) datetime64[ns] 2002-07-01T12:00:00 2002-07-01T18:00:00 ...
Data variables:
tcw (time, latitude, longitude) float64 10.15 10.15 10.15 10.15 ...
new_var (time, latitude, longitude) float64 30.46 30.46 30.46 30.46 ...
In [5]:
myds.to_netcdf('myds.nc')
xr.open_dataset('myds.nc')
Out[5]:
<xarray.Dataset>
Dimensions: (latitude: 73, longitude: 144, time: 62)
Coordinates:
* latitude (latitude) float32 90.0 87.5 85.0 82.5 80.0 77.5 75.0 72.5 ...
* longitude (longitude) float32 0.0 2.5 5.0 7.5 10.0 12.5 15.0 17.5 20.0 ...
* time (time) datetime64[ns] 2002-07-01T12:00:00 2002-07-01T18:00:00 ...
Data variables:
tcw (time, latitude, longitude) float64 10.15 10.15 10.15 10.15 ...
new_var (time, latitude, longitude) float64 30.46 30.46 30.46 30.46 ...
In [6]:
(myds + myds).to_netcdf('myds2.nc')
xr.open_dataset('myds2.nc')
Out[6]:
<xarray.Dataset>
Dimensions: (latitude: 73, longitude: 144, time: 62)
Coordinates:
* time (time) datetime64[ns] 2002-07-01T12:00:00 2002-07-01T18:00:00 ...
* latitude (latitude) float32 90.0 87.5 85.0 82.5 80.0 77.5 75.0 72.5 ...
* longitude (longitude) float32 0.0 2.5 5.0 7.5 10.0 12.5 15.0 17.5 20.0 ...
Data variables:
tcw (time, latitude, longitude) float64 20.31 20.31 20.31 20.31 ...
new_var (time, latitude, longitude) float64 60.92 60.92 60.92 60.92 ...
Vérifier le découpage du fichier. ncdump -s <infile>
va donner la réponse. Si la taille du morceau dans la dimension de temps est plus grande qu'un, vous devriez lire la même quantité d'années à la fois, sinon vous lisez plusieurs années à la fois à partir du disque et en utilisant seulement un à la fois.
Comment lent, c'est lent? Max quelques secondes par timestep semble raisonnable pour un tableau de cette taille.
Donner plus d'informations sur ce que vous faites avec les données plus tard peut nous donner plus de conseils sur l'endroit où le problème peut être.
C'est un Peu Hacky, mais peut-être la solution la plus simple:
Lire des sous-ensembles de fichier dans la mémoire, puis cPickle (https://docs.python.org/3/library/pickle.html) le fichier sur le disque pour une utilisation ultérieure. Le chargement de vos données à partir d'une structure de données est susceptible d'être plus rapide que l'analyse netCDF à chaque fois.