Dask Support#
If you handle large datasets that exceed memory capacity, xeofs is designed to work with dask-backed
xarray objects from end-to-end. By default, xeofs computes models eagerly, which in some
cases can lead to better performance. However, it is also possible to build and fit models “lazily”, meaning
no computation will be carried out until the user calls .compute(). To enable lazy computation, specify
compute=False when initializing the model.
Note
Importantly, xeofs never loads the input dataset(s) into memory.
Lazy Evaluation#
There are a few tricks, and features that need to be explicitly disabled for lazy evaluation to work. First
is the check_nans option, which skips checking for full or isolated NaNs in the data. In this case,
the user is responsible for ensuring that the data is free of NaNs by first applying e.g. .dropna()
or .fillna(). Second is that lazy mode is incompatible with assessing the fit of a rotator class during
evaluation, becaue the entire dask task graph must be built up front. Therefore, a lazy rotator model will
run out to the full max_iter regardless of the specified rtol. For that reason it is recommended to
reduce the number of iterations.
As an example, the following lazily creates a rotated EOF model for a 10GB dataset in about a second, which can
then be evaluated later using .compute().
Warning
Remember that dask allows you to compute out-of-memory datasets by writing intermediate results to your disk. However, computing a singular value decomposition (SVD) typically requires more memory during computation than the size of the input dataset. In this case, about twice the size of the dataset (~20 GB) was written to disk during the SVD computation. Usually, these results are written to /tmp/ on Linux machines. You can change the default directory by configuring dask, for example, using: dask.config.set({"temporary_directory": "/your/temporary/directory"})
import dask.array as da
import numpy as np
import xarray as xr
from xeofs.single import EOF, EOFRotator
data = xr.DataArray(
da.random.random((5000, 720, 360), chunks=(100, 100, 100)),
dims=["time", "lon", "lat"],
coords={
"time": xr.date_range("2000-01-01", periods=5000, freq="D"),
"lon": np.linspace(-180, 180, 720),
"lat": np.linspace(-90, 90, 360),
},
)
model = EOF(n_modes=5, check_nans=False, compute=False)
model.fit(data, dim="time")
rotator = EOFRotator(compute=False, max_iter=20)
rotator.fit(model)
# Later, you can compute the model
# rotator.compute()
Note
A standard laptop (Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz) with four cores, each using 3 GB of memory, needs about 15 minutes to compute the PCA.