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

  1. je peux lire une série d'années: nc_file.variables [variable_name][0:100, :, :]

  2. 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), :, :])
12
demandé sur Pedro M Duarte 2016-02-16 05:57:38

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 ...
21
répondu Pedro M Duarte 2016-02-21 03:15:10

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.

2
répondu kakk11 2016-02-16 11:09:06

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.

1
répondu knite 2016-02-19 07:18:18