Source code for pyunicorn.climate.tsonis

# This file is part of pyunicorn.
# Copyright (C) 2008--2024 Jonathan F. Donges and pyunicorn authors
# URL: <https://www.pik-potsdam.de/members/donges/software-2/software>
# License: BSD (3-clause)
#
# Please acknowledge and cite the use of this software and its authors
# when results are used in publications or published elsewhere.
#
# You can use the following reference:
# J.F. Donges, J. Heitzig, B. Beronov, M. Wiedermann, J. Runge, Q.-Y. Feng,
# L. Tupikina, V. Stolbova, R.V. Donner, N. Marwan, H.A. Dijkstra,
# and J. Kurths, "Unified functional network and nonlinear time series analysis
# for complex systems science: The pyunicorn package"

"""
Provides classes for generating and analyzing complex climate networks.
"""

import numpy as np

from .climate_network import ClimateNetwork
from .climate_data import ClimateData
from ..core.cache import Cached


# TODO: Reconsider storage of correlation matrix without taking absolute value.
[docs] class TsonisClimateNetwork(ClimateNetwork): """ Encapsulates a Tsonis climate network. Construct a static climate network following Tsonis et al. from the Pearson correlation matrix at zero lag. Hence, Tsonis climate networks are undirected due to the symmetry of the correlation matrix. References: [Tsonis2004]_, [Tsonis2006]_, [Tsonis2008b]_, [Tsonis2008c]_. """ # # Defines internal methods #
[docs] def __init__(self, data, threshold=None, link_density=None, non_local=False, node_weight_type="surface", winter_only=True, silence_level=0): """ Initialize an instance of TsonisClimateNetwork. .. note:: Either threshold **OR** link_density have to be given! Possible choices for ``node_weight_type``: - None (constant unit weights) - "surface" (cos lat) - "irrigation" (cos**2 lat) :type data: :classL`.ClimateData` :arg data: The climate data used for network construction. :arg float threshold: The threshold of similarity measure, above which two nodes are linked in the network. :arg float link_density: The networks's desired link density. :arg bool non_local: Determines, whether links between spatially close nodes should be suppressed. :arg str ode_weight_type: The type of geographical node weight to be used. :arg bool winter_only: Determines, whether only data points from the winter months (December, January and February) should be used for analysis. Possibly, this further suppresses the annual cycle in the time series. :arg int silence_level: The inverse level of verbosity of the object. """ if silence_level <= 1: print("Generating a Tsonis climate network...") self.silence_level = silence_level # Set instance variables self.data = data """(ClimateData) - The climate data used for network construction.""" self.N = self.data.grid.N self._set_winter_only(winter_only) ClimateNetwork.__init__(self, grid=self.data.grid, similarity_measure=self.similarity_measure(), threshold=threshold, link_density=link_density, non_local=non_local, directed=False, node_weight_type=node_weight_type, silence_level=silence_level)
[docs] def __str__(self): """ Return a string representation of TsonisClimateNetwork. **Example:** >>> print(TsonisClimateNetwork.SmallTestNetwork()) TsonisClimateNetwork: ClimateNetwork: GeoNetwork: Network: undirected, 6 nodes, 6 links, link density 0.400. Geographical boundaries: time lat lon min 0.0 0.00 2.50 max 9.0 25.00 15.00 Threshold: 0.5 Local connections filtered out: False Use only data points from winter months: False """ return (f'TsonisClimateNetwork:\n{ClimateNetwork.__str__(self)}\n' f'Use only data points from winter months: ' f'{self.winter_only()}')
# # Methods for testing purposes #
[docs] @staticmethod def SmallTestNetwork(): """ Return a 6-node undirected test climate network from a test data set. **Example:** >>> r(TsonisClimateNetwork.SmallTestNetwork().adjacency) array([[0, 0, 1, 0, 1, 0], [0, 0, 0, 1, 0, 1], [1, 0, 0, 0, 1, 0], [0, 1, 0, 0, 0, 1], [1, 0, 1, 0, 0, 0], [0, 1, 0, 1, 0, 0]]) :rtype: Network instance """ return TsonisClimateNetwork(data=ClimateData.SmallTestData(), threshold=0.5, winter_only=False, silence_level=2)
# # Defines methods to calculate the correlation matrix #
[docs] def _calculate_correlation(self, anomaly): """ Return the correlation matrix at zero lag. :type anomaly: 2D Numpy array (time, index) :arg anomaly: the anomaly time series from which to calculate the correlation matrix at zero lag. :rtype: 2D Numpy array (index, index) :return: the correlation matrix at zero lag. """ if self.silence_level <= 1: print("Calculating correlation matrix at zero lag from anomaly " "values...") # Cast to float32 type to save memory since correlation coefficients # are not needed in high floating point precision. correlation = np.corrcoef(anomaly.transpose()).astype("float32") return correlation
[docs] def calculate_similarity_measure(self, anomaly): """ Encapsulate the calculation of the correlation matrix at zero lag. **Example:** >>> TsonisClimateNetwork.SmallTestNetwork()._calculate_correlation( ... anomaly=ClimateData.SmallTestData().anomaly()) array([[ 1. , -0.25377226, -1. , 0.25377226, 1. , -0.25377226], [-0.25377226, 1. , 0.25377226, -1. , -0.25377226, 1. ], [-1. , 0.25377226, 1. , -0.25377226, -1. , 0.25377226], [ 0.25377226, -1. , -0.25377226, 1. , 0.25377226, -1. ], [ 1. , -0.25377226, -1. , 0.25377226, 1. , -0.25377226], [-0.25377226, 1. , 0.25377226, -1. , -0.25377226, 1. ]], dtype=float32) :type anomaly: 2D Numpy array (time, index) :arg anomaly: the anomaly time series from which to calculate the correlation matrix at zero lag. :rtype: 2D Numpy array (index, index) :return: the correlation matrix at zero lag. """ return self._calculate_correlation(anomaly)
[docs] @Cached.method() def correlation(self): """ Return the correlation matrix at zero lag. **Example:** >>> TsonisClimateNetwork.SmallTestNetwork().correlation() array([[ 1. , 0.25377226, 1. , 0.25377226, 1. , 0.25377226], [ 0.25377226, 1. , 0.25377226, 1. , 0.25377226, 1. ], [ 1. , 0.25377226, 1. , 0.25377226, 1. , 0.25377226], [ 0.25377226, 1. , 0.25377226, 1. , 0.25377226, 1. ], [ 1. , 0.25377226, 1. , 0.25377226, 1. , 0.25377226], [ 0.25377226, 1. , 0.25377226, 1. , 0.25377226, 1. ]], dtype=float32) :rtype: 2D Numpy array (index, index) :return: the correlation matrix at zero lag. """ corr = self.similarity_measure() self.set_link_attribute('correlation', abs(corr)) return corr
[docs] def winter_only(self): """ Indicate, if only winter months were used for network generation. **Example:** >>> TsonisClimateNetwork.SmallTestNetwork().winter_only() False :return bool: whether only winter months were used for network generation. """ return self._winter_only
[docs] def _set_winter_only(self, winter_only): """ Toggle use of exclusively winter data points for network generation. :arg bool winter_only: Indicates, whether only winter months were used for network generation. """ self._winter_only = winter_only if winter_only: winter_anomaly = self.data.anomaly_selected_months([0, 1, 11]) correlation = self._calculate_correlation(winter_anomaly) else: correlation = self._calculate_correlation(self.data.anomaly()) self._similarity_measure = correlation
[docs] def set_winter_only(self, winter_only): """ Toggle use of exclusively winter data points for network generation. Also explicitly re(generates) the instance of TsonisClimateNetwork. **Example:** >>> net = TsonisClimateNetwork.SmallTestNetwork() >>> net.set_winter_only(winter_only=False) >>> net.n_links 6 :arg bool winter_only: Indicates, whether only winter months were used for network generation. """ self._set_winter_only(winter_only) self._regenerate_network()
# # Defines methods to calculate weighted network measures #
[docs] def correlation_weighted_average_path_length(self): """ Return correlation weighted average path length. **Example:** >>> TsonisClimateNetwork.SmallTestNetwork().\ correlation_weighted_average_path_length() 1.0 :return float: the correlation weighted average path length. """ self.correlation() return self.average_path_length('correlation')
[docs] def correlation_weighted_closeness(self): """ Return correlation weighted closeness. **Example:** >>> TsonisClimateNetwork.SmallTestNetwork().\ correlation_weighted_closeness() array([ 0.25, 0.25, 0.25, 0.25, 0.25, 0.25]) :rtype: 1D Numpy array [index] :return: the correlation weighted closeness sequence. """ self.correlation() return self.closeness('correlation')
[docs] def local_correlation_weighted_vulnerability(self): """ Return correlation weighted vulnerability. **Example:** >>> TsonisClimateNetwork.SmallTestNetwork().\ local_correlation_weighted_vulnerability() array([ 0., 0., 0., 0., 0., 0.]) :rtype: 1D Numpy array [index] :return: the correlation weighted vulnerability sequence. """ self.correlation() return self.local_vulnerability('correlation')