Calculating anomalies and climatologies of (satellite) timeseries with pytesmo

The following example shows how you can use pytesmo to calculate anomalies or the climatology of a times eries. Here we use the test data that is provided within the Github repository, but it works the same with all pandas DataFrames or Series.

[1]:
%matplotlib inline

from ascat.read_native.cdr import AscatGriddedNcTs
from pytesmo.time_series import anomaly
from pytesmo.utils import rootdir
import warnings
import matplotlib.pyplot as plt

The first step is to read in the ASCAT data at a single grid point and plot the resulting soil moisture time series:

[2]:
testdata_path = rootdir() / "tests" / "test-data"
ascat_data_folder = testdata_path / "sat" / "ascat" / "netcdf" / "55R22"
ascat_grid_fname = testdata_path / "sat" / "ascat" / "netcdf" / "grid" / "TUW_WARP5_grid_info_2_1.nc"
static_layer_path = testdata_path / "sat" / "h_saf" / "static_layer"


#init the AscatSsmCdr reader with the paths
with warnings.catch_warnings():
    warnings.filterwarnings('ignore') # some warnings are expected and ignored

    ascat_reader = AscatGriddedNcTs(
        ascat_data_folder,
        "TUW_METOP_ASCAT_WARP55R22_{:04d}",
        grid_filename=ascat_grid_fname,
        static_layer_path=static_layer_path
    )

ascat_ts = ascat_reader.read(11.82935429,45.4731369)
ascat_ts["sm"].plot(figsize=(10, 4))
plt.ylabel("Soil moisture (degree of saturation in %)");
../_images/examples_anomalies_3_0.png

This timeseries shows a seasonal pattern of high soil moisture in winter and low soil moisture in summer, so we might be interested in the climatology (long-term mean seasonal pattern) or in anomalies from the climatology or from the current seasonality (calculated via a moving window of 35 days).

This can be done with the calc_climatology and calc_anomaly functions in pytesmo.time_series.anomaly.

Let’s first have a look at the climatology:

[3]:
climatology = anomaly.calc_climatology(ascat_ts["sm"])
climatology
[3]:
1      40.725439
2      41.293034
3      41.988083
4      42.341582
5      42.948949
         ...
362    39.143497
363    39.534965
364    39.734723
365    40.090585
366    40.479103
Length: 366, dtype: float64
[4]:
climatology.plot()
plt.ylabel("Soil moisture (degree of saturation in %)");
../_images/examples_anomalies_6_0.png

The returned climatology is a pandas Series with the day of year as index (ranging from 1 to 366). Here we can see more clearly the pattern we spotted above in the full timeseries.

We can use this climatology to calculate the anomalies from it, e.g. soil moisture signal - climatology:

Calculate anomaly based on moving +- 17 day window:

[5]:
anomaly_clim = anomaly.calc_anomaly(ascat_ts["sm"], climatology=climatology)
anomaly_clim.plot(figsize=(10, 4))
plt.ylabel("Soil moisture (degree of saturation in %)");
../_images/examples_anomalies_9_0.png

We can also base our anomaly calculation on a running mean. This way we can get the short-term anomalies separated from a smoothed signal showing the seasonal contributions. Here we use a window of 35 days, e.g +- 17 days in each direction:

[6]:
anomaly_seasonal = anomaly.calc_anomaly(ascat_ts["sm"], window_size=35)
seasonal = ascat_ts["sm"] - anomaly_seasonal

fig, axes = plt.subplots(nrows=2, figsize=(10, 11))
seasonal.plot(ax=axes[0])
axes[0].set_ylabel("Soil moisture (degree of saturation in %)")
axes[0].set_title("35-day moving average")
anomaly_seasonal.plot(ax=axes[1])
axes[1].set_ylabel("Soil moisture anomaly (degree of saturation in %)")
axes[1].set_title("Anomalies from 35-day moving average");
../_images/examples_anomalies_11_0.png