Skip to content

Phase Randomization (Brunner et al. 2019)

Type Nonparametric
Resolution Daily
Sites Univariate
Class PhaseRandomizationGenerator

Overview

Phase randomization generates synthetic daily streamflow by randomizing the Fourier phase spectrum while preserving the amplitude (power) spectrum. This maintains both short-range (daily autocorrelation) and long-range (Hurst phenomenon) temporal dependence. A four-parameter kappa distribution fitted per day-of-year allows extrapolation beyond the observed range.

Algorithm

Preprocessing

  1. Remove leap days - ensures consistent 365-day years.
  2. Create day-of-year index (1-365).
  3. Validate - minimum 730 days (2 complete years), no missing observations.

Fitting

  1. Marginal distribution fitting (if marginal='kappa') - for each day d:
  2. Define moving window of +/- win_h_length days (circular, wraps at year boundary)
  3. Extract all observations in window across all years
  4. Fit four-parameter kappa distribution via L-moment matching
  5. Normal score transform - for each day d:
  6. Rank all observations across years
  7. Generate a standard normal sample of the same size, sort it
  8. Map observed ranks to sorted normal values (rank-matching)
  9. Result: normalized series with approximately N(0,1) marginals per day
  10. Fourier transform of normalized series:
  11. Compute FFT; extract modulus (amplitude) and phases
  12. Store first-half indices and mirror indices for conjugate symmetry

Generation

  1. Phase randomization:
  2. Keep DC component (index 0) unchanged
  3. For positive frequencies: generate random phases from Uniform(-pi, pi)
  4. Construct FT_new[k] = modulus[k] * exp(i * phase_random[k])
  5. Apply conjugate symmetry for negative frequencies
  6. Inverse FFT - produces phase-randomized series in normalized domain.
  7. Back-transform to original distribution - for each day d:
  8. If kappa: generate kappa sample, rank-match against normalized values
  9. If empirical: rank-match directly against observed (no extrapolation)
  10. Non-negativity - replace negative values with Uniform(0, min_obs_d).

Parameters

Parameter Type Default Description
Q_obs pd.Series or pd.DataFrame - Observed daily streamflow with DatetimeIndex
marginal str 'kappa' Marginal distribution: 'kappa' (extrapolation) or 'empirical'
win_h_length int 15 Half-window for daily distribution fitting (total = 2*h + 1 days)
name Optional[str] None Optional name identifier for this generator instance
debug bool False Enable debug logging

Properties Preserved

  • Full power spectrum (all temporal autocorrelations, in expectation)
  • Long-range dependence (Hurst coefficient)
  • Day-of-year marginal distributions (via kappa or empirical)
  • Seasonal patterns

Not preserved: - Phase coherence (randomized by design) - Exact autocorrelations (preserved only in expectation across ensemble)

Limitations

  • Univariate only - no spatial correlations between sites
  • Generated series has same length as observed (no temporal extrapolation)
  • Output excludes February 29 dates
  • Kappa fitting may fail for some days - falls back to adjacent day parameters or empirical distribution
  • Minimum 2 years of data; 10+ recommended for stable kappa fits

References

Primary: Brunner, M.I., Bárdossy, A., and Furrer, R. (2019). Technical note: Stochastic simulation of streamflow time series using phase randomization. Hydrology and Earth System Sciences, 23, 3175-3187. https://doi.org/10.5194/hess-23-3175-2019

See also: - Theiler, J., Eubank, S., Longtin, A., Galdrikian, B., and Farmer, J.D. (1992). Testing for nonlinearity in time series: the method of surrogate data. Physica D, 58, 77-94. - Hosking, J.R.M. (1990). L-moments: Analysis and estimation of distributions using linear combinations of order statistics. Journal of the Royal Statistical Society Series B, 52, 105-124. - Hosking, J.R.M. (1994). The four-parameter kappa distribution. IBM Journal of Research and Development, 38, 251-258.


Implementation: src/synhydro/methods/generation/nonparametric/phase_randomization.py Tests: tests/test_phase_randomization_generator.py