2016-02-16 25 views
11

Mam bardzo duży plik netcdf że czytam za pomocą netCDF4 w PythoniePrzyspieszenie odczyt bardzo dużych plików netcdf w Pythonie

nie mogę odczytać tego pliku na raz, ponieważ jego wymiarach (1200 x 720 x 1440) są zbyt duże, aby cały plik znajdował się w pamięci naraz. Pierwszy wymiar reprezentuje czas, a kolejne 2 oznaczają odpowiednio szerokość i długość geograficzną.

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

Jednak czytanie jednego roku na raz jest potwornie powolne. Jak mogę to przyspieszyć w poniższych przypadkach użycia?

--edit

chunksize jest 1

  1. można odczytać szereg lat: nc_file.variables [variable_name] [0: 100,:,:]

  2. w latach

    dla yr:: Istnieje kilka przypadków użycia

    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

Czy na pewno czytanie w dowolnej innej sprawie (np. Cały plik naraz) byłoby szybsze? Czy możesz spróbować z obciętym plikiem? –

+0

Czy zrobiono [niezbędne profilowanie] (http://stackoverflow.com/questions/582336/how-can-you-profile-a-python-script)? –

+0

Czy robisz cokolwiek z danymi roku po przeczytaniu? Czy możesz przeczytać zakres lat, np. '[1997: 2007,:,:]'? – hpaulj

Odpowiedz

17

Bardzo polecam przyjrzeć się projektom xarray i dask. Korzystanie z tych potężnych narzędzi pozwoli ci łatwo dzielić obliczenia w porcje. Daje to dwie korzyści: możesz obliczyć dane, które nie mieszczą się w pamięci, i możesz użyć wszystkich rdzeni w swojej maszynie dla lepszej wydajności. Możesz zoptymalizować wydajność, odpowiednio wybierając rozmiar porcji (patrz documentation).

Można załadować dane z netcdf robiąc coś tak prostego jak

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

Jeśli chcesz kawałek danych w latach wzdłuż wymiaru czasu, a następnie określić parametr chunks (zakładając, że współrzędnych roku nazywa się „rok”):

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

Ponieważ pozostałe współrzędne nie pojawiają się w chunks dict, a następnie pojedynczy kawałek zostaną wykorzystane dla nich. (Zobacz więcej szczegółów w dokumentacji here.). Będzie to przydatne w przypadku pierwszego wymogu, w którym chcesz pomnożyć każdego roku przez tablicę 2D. Można by po prostu zrobić:

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

Teraz xarray i dask są obliczeniowej swój wynik leniwie. W celu uruchomienia rzeczywistej obliczeń, można po prostu poprosić xarray aby zapisać swój wynik z powrotem do netcdf:

ds.to_netcdf(new_file) 

Obliczenia zostaje wyzwolony przez dask, która troszczy się o podział przetwarzanie w kawałkach, a tym samym umożliwia pracę z dane, które nie mieszczą się w pamięci. Ponadto, dask zajmie się wykorzystaniem wszystkich rdzeni procesora do obliczania porcji.

Projekty wciąż nie radzą sobie z ładnie sytuacjami, w których porcje nie "wyrównują się" dobrze do obliczeń równoległych. Ponieważ w tym przypadku graliśmy tylko w wymiarze "roku", nie spodziewamy się żadnych problemów.

Jeśli chcesz dodać pliki dwie różne netCDF razem, to jest tak proste, jak:

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

I podajemy przykładowe pełni funkcjonalny korzystając 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

dzięki @Pedro, przyjrzymy się bliżej Xarray – user308827

2

Sprawdź dzielenia na porcje pliku. ncdump -s <infile> udzieli odpowiedzi. Jeśli rozmiar porcji w wymiarze czasu jest większy niż jeden, należy przeczytać tę samą liczbę lat naraz, w przeciwnym razie czytasz kilka lat naraz z dysku i używasz tylko jednego na raz. Jak powolny jest wolny? Maksymalnie kilka sekund na każdy takt brzmi rozsądnie dla tablicy tego rozmiaru. Podanie dodatkowych informacji o tym, co zrobisz z danymi później, może nam dostarczyć więcej wskazówek na temat tego, gdzie może być problem.

+0

thanks @ kakk11, chunksize = 1 – user308827

+0

1 w wymiarze czasu i co to jest w innych wymiarach? Możesz również wyjaśnić, jak powolne jest "wolne" w twoim przypadku! – kakk11

+0

Rozmiar porcji to 720 i 1440 dla innych wymiarów. Zajmuje ułamek sekundy dla każdej iteracji pętli. Ale to się sumuje, kiedy musisz powtórzyć ponad 1200 lat. – user308827

1

To Kinda Hacky, ale może być najprostsze rozwiązanie:

lektura podzbiory pliku do pamięci, a następnie cPickle (https://docs.python.org/3/library/pickle.html) plik z powrotem na dysku do wykorzystania w przyszłości. Ładowanie danych z marynowanej struktury danych jest prawdopodobnie szybsze niż analizowanie netCDF za każdym razem.

+0

Jest całkiem prawdopodobne, że pisanie/czytanie hdf5 z kompresją blosc, tak jak w PyTables, jest w rzeczywistości szybsze niż cPickle. Nie wspominając już o wielkości pliku, który może stać się bardzo duży dla nieskompresowanych danych liczbowych! – kakk11