SPI

The Standardized Precipitation Index (SPI) method fits a gamma distribution to precipitation values and maps the result to a normal distribution (z values), which correspond to the SPI values. Precipitation is usually resampled to one ore more months.

\(spi \geq 2.0\)

extremely wet

\(1.5 \leq spi < 2.0\)

very wet

\(1.0 \leq spi < 1.5\)

moderately wet

\(-1.0 \leq spi < 1.0\)

near normal

\(-1.5 \leq spi < -1.0\)

moderately dry

\(-2.0 \leq spi < -1.5\)

severely dry

\(spi \leq -2.0\)

extremely dry

From European Drought Observatory: The SPI indicator shows the anomalies (deviations from the mean) of the observed total precipitation, for any given location and accumulation period of interest. The magnitude of the departure from the mean is a probabilistic measure of the severity of a wet or dry event. Since SPI can be calculated over different precipitation accumulation periods (typically ranging from 1 to 48 months), the resulting different SPI indicators allow for estimating different potential impacts of a meteorological drought:

  • SPI-1 to SPI-3: When SPI is computed for shorter accumulation periods (e.g.,1 to 3 months), it can be used as an indicator for immediate impacts such as reduced soil moisture, snowpack, and flow in smaller creeks.

  • SPI-3 to SPI-12: When SPI is computed for medium accumulation periods (e.g.,3 to 12 months), it can be used as an indicator for reduced stream flow and reservoir storage.

  • SPI-12 to SPI-48: When SPI is computed for longer accumulation periods (e.g.,12 to 48 months), it can be used as an indicator for reduced reservoir and groundwater recharge.

Tutorial

spi()

Input is a list or numpy.array of precipitation values of any frequency (e.g., weekly, monthly, bimonthly, etc.). The output is an array of same size with calculated SPI values. Gaps in the input data (numpy.nan) are ignored and return a numpy.nan at the same position in the output array.

>>> import numpy as np
>>> import pandas as pd
>>> from caeli.drought_indices import spi, spi_monthly
>>> p = [286.08, 321.11, 260.34, 383.07, 277.56, 150.5, 272.63, 246.31, 254.92, 288.5,
...      267.12, 242.51, 286.56, 285.43, 370.03, 241.54, 233.65, 336.29, 330.73, 360.46,
...      407.13, 381.74, 217.2, 232.68, 418.64, 338.96, 246.49, 391.28, 223.81, 387.61]
>>> spi(p)
array([-0.09871128,  0.40370729, -0.49477568,  1.21005166, -0.22705906,
       -2.5764311 , -0.3025364 , -0.72186805, -0.58150079, -0.06272324,
       -0.38798647, -0.78487656, -0.09155701, -0.10841216,  1.04789177,
       -0.80106745, -0.93443395,  0.6101345 ,  0.53525523,  0.92648763,
        1.50004374,  1.19367916, -1.22268976, -0.95104038,  1.6348398 ,
        0.64580242, -0.71889983,  1.31030572, -1.10512868,  1.26566184])
>>> p = [np.nan, 321.11, 260.34, 383.07, 277.56, 150.5, 272.63, 246.31, 254.92, 288.5,
...      267.12, 242.51, 286.56, 285.43, 370.03, 241.54, 233.65, 336.29, 330.73, 360.46,
...      407.13, 381.74, 217.2, 232.68, 418.64, 338.96, 246.49, 391.28, np.nan, 387.61]
>>> spi(p).round(1)
[ nan  0.4 -0.5  1.2 -0.3 -2.6 -0.3 -0.8 -0.6 -0.1 -0.4 -0.8 -0.1 -0.2
  1.  -0.8 -1.   0.6  0.5  0.9  1.4  1.1 -1.3 -1.   1.6  0.6 -0.8  1.3
  nan  1.2]

spi_monthly()

spi_monthly() requires a pandas.DataFrame as input data with pandas.DatetimeIndex as index in any frequency (e.g., minutely, hourly, subdaily, daily, etc.).

We first generate a pandas.DataFrame with random daily precipitation values as example:

>>> np.random.seed(1)
>>> index = pd.date_range('1990-01-01', '2019-12-31', freq='1d')
>>> values = np.random.normal(1, 0.7, len(index))
>>> values[values < 0] = 0.0
>>> df = pd.DataFrame({'P': values}, index=index)
>>> df
                   P
1990-01-01  2.137042
1990-01-02  0.571771
...              ...
2019-12-30  1.285051
2019-12-31  0.565803

[10957 rows x 1 columns]

Given daily data, calculate monthly SPI:

>>> df_spi = spi_monthly(df).round(1)
>>> with pd.option_context('display.max_rows', 4, 'display.max_columns', 8):
...     print(df_spi)
...
       P01   P02   P03   P04  ...  SPI09  SPI10  SPI11  SPI12
year                          ...
1990  30.3  29.5  35.5  29.4  ...    0.4   -0.3    1.1   -1.4
1991  32.1  28.7  39.4  29.7  ...   -0.4   -0.3    0.1   -0.5
...    ...   ...   ...   ...  ...    ...    ...    ...    ...
2018  32.1  25.5  33.2  33.9  ...    1.9    0.8   -0.9   -0.7
2019  23.9  28.7  30.8  37.6  ...    0.8   -0.7    0.7    1.2

[30 rows x 24 columns]

Given daily data, calculate monthly SPI for accumulated three months:

>>> spi_monthly(df, aggregation=3).round(1)
      P01-03  P02-04  P03-05  P04-06  ...  SPI09-11  SPI10-12  SPI11-01  SPI12-02
year                                  ...
1990    95.3    94.3   100.1    97.4  ...       0.7      -0.5      -0.2      -0.8
1991   100.2    97.8    98.5    91.5  ...      -0.4      -0.5      -0.3      -0.7
...      ...     ...     ...     ...  ...       ...       ...       ...       ...
2018    90.9    92.6    97.0    96.7  ...       1.1      -0.5      -2.3      -1.8
2019    83.3    97.0    99.2    97.4  ...       0.4       0.8       NaN       NaN

Given daily data and list of months, calculate monthly SPI for accumulated three months:

>>> spi_monthly(df, aggregation=3, months=[1, 4, 7, 10]).round(1)
      P01-03  P02-04  P03-05  P04-06  ...  SPI09-11  SPI10-12  SPI11-01  SPI12-02
year                                  ...
1990    95.3    94.3   100.1    97.4  ...       0.7      -0.5      -0.2      -0.8
1991   100.2    97.8    98.5    91.5  ...      -0.4      -0.5      -0.3      -0.7
...      ...     ...     ...     ...  ...       ...       ...       ...       ...
2018    90.9    92.6    97.0    96.7  ...       1.1      -0.5      -2.3      -1.8
2019    83.3    97.0    99.2    97.4  ...       0.4       0.8       NaN       NaN