2016-02-16 19 views
11

Ho un file molto grande netCDF che sto leggendo utilizzando netCDF4 in pythonAccelerare la lettura di file molto grande netcdf in python

non riesco a leggere questo file in una sola volta in quanto le sue dimensioni (1200 x 720 x 1440) sono troppo grandi per l'intero file per essere in memoria in una volta. La prima dimensione rappresenta il tempo, mentre i successivi 2 rappresentano rispettivamente la latitudine e la longitudine.

import netCDF4 
nc_file = netCDF4.Dataset(path_file, 'r', format='NETCDF4') 
for yr in years: 
    nc_file.variables[variable_name][int(yr), :, :] 

Tuttavia, la lettura di un anno alla volta è terribilmente lenta. Come faccio ad accelerare per i casi d'uso di seguito?

--edit

Il chunksize è 1

  1. posso leggere una serie di anni: nc_file.variables [nome_variabile] [0: 100,:,:]

  2. ci sono diversi casi d'uso:

    per anno negli anni:

    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), :, :]) 
+0

Sei sicuro che la lettura di qualsiasi altra questione (ad esempio l'intero file in una sola volta) sarebbe più veloce? Puoi provare con un file ritagliato? –

+0

Qualsiasi [profilazione essenziale] (http://stackoverflow.com/questions/582336/how-can-you-profile-a-python-script) è stata completata? –

+0

Stai facendo qualcosa con i dati dell'anno una volta che l'hai letto? Puoi leggere un intervallo di anni, ad es. '[1997: 2007,:,:]'? – hpaulj

risposta

17

Consiglio vivamente di dare un'occhiata ai progetti xarray e dask. L'utilizzo di questi potenti strumenti ti consentirà di suddividere facilmente il calcolo in blocchi. Questo porta due vantaggi: puoi calcolare dati che non si adattano alla memoria e puoi utilizzare tutti i core della tua macchina per prestazioni migliori. È possibile ottimizzare le prestazioni scegliendo opportunamente la dimensione del blocco (vedere documentation).

È possibile caricare i vostri dati da netCDF facendo qualcosa di semplice come

import xarray as xr 
ds = xr.open_dataset(path_file) 

Se si desidera pezzo i dati in anni lungo la dimensione tempo, poi si specifica il parametro chunks (supponendo che coordinano l'anno è chiamato 'l'anno'):

ds = xr.open_dataset(path_file, chunks={'year': 10}) 

Dal momento che le altre coordinate non appaiono nel chunks dict, quindi un unico pezzo verrà utilizzato per loro. (Vedi maggiori dettagli nella documentazione here.). Questo sarà utile per il tuo primo requisito, in cui desideri moltiplicare ogni anno con un array 2D. Si potrebbe fare semplicemente:

ds['new_var'] = ds['var_name'] * arr_2d 

Ora, xarray e dask sono il risultato di calcolo pigramente. Al fine di attivare il calcolo effettivo, si può semplicemente chiedere xarray per salvare il risultato al netCDF:

ds.to_netcdf(new_file) 

Il calcolo viene attivato attraverso dask, che si prende cura di dividere la trasformazione in pezzi e, quindi, consente di lavorare con dati che non si adattano alla memoria. Inoltre, dask si occuperà di utilizzare tutti i core del processore per i pezzi di calcolo.

I progetti xarray e dask non gestiscono ancora bene le situazioni in cui i blocchi non "si allineano" bene per il calcolo parallelo. Dato che in questo caso ci siamo suddivisi solo nella dimensione "anno", non ci aspettiamo problemi.

Se si desidera aggiungere file insieme due diverse netCDF, è semplice come:

ds1 = xr.open_dataset(path_file1, chunks={'year': 10}) 
ds2 = xr.open_dataset(path_file2, chunks={'year': 10}) 
(ds1 + ds2).to_netcdf(new_file) 

Ho fornito un esempio completamente funzionante utilizzando a dataset available online.

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 ... 
+0

grazie @Pedro, sarà più vicino a xarray – user308827

2

Controllare suddivisione in blocchi di file. ncdump -s <infile> darà la risposta. Se la dimensione del blocco nella dimensione temporale è maggiore di una, dovresti leggere la stessa quantità di anni contemporaneamente, altrimenti stai leggendo diversi anni contemporaneamente dal disco e ne utilizzi solo uno alla volta. Quanto lento è lento? Max pochi secondi per timestep suona ragionevole per un array di queste dimensioni. Fornire maggiori informazioni su ciò che si fa con i dati in un secondo momento potrebbe fornirci ulteriori indicazioni su dove potrebbe essere il problema.

+0

grazie @kakk11, chunksize = 1 – user308827

+0

1 nella dimensione temporale e in altre dimensioni? Puoi anche chiarire quanto lento è "lento" nel tuo caso! – kakk11

+0

la dimensione del blocco è 720 e 1440 per le altre dimensioni. Ci vuole una frazione di secondo per ogni iterazione del ciclo. Ma si aggiunge quando è necessario iterare oltre 1200 anni – user308827

1

Questo è un pò Hacky, ma può essere la soluzione più semplice:

Leggi sottoinsiemi del file in memoria, poi cPickle (https://docs.python.org/3/library/pickle.html) il file su disco per un utilizzo futuro. Caricare i dati da una struttura di dati decapitati è probabile che sia più veloce dell'analisi di netCDF ogni volta.

+0

È molto probabile che scrivere/leggere hdf5 con compressione blosc, come in PyTables, sia effettivamente più veloce di cPickle. Per non parlare delle dimensioni del file che possono diventare molto grandi per i dati numerici non compressi! – kakk11