| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance |
| # with the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, |
| # software distributed under the License is distributed on an |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| # KIND, either express or implied. See the License for the |
| # specific language governing permissions and limitations |
| # under the License. |
| |
| ''' |
| Classes: |
| Metric - Abstract Base Class from which all metrics must inherit. |
| ''' |
| |
| from abc import ABCMeta, abstractmethod |
| import ocw.utils as utils |
| import numpy |
| import numpy.ma as ma |
| from scipy.stats import mstats |
| |
| class Metric(object): |
| '''Base Metric Class''' |
| __metaclass__ = ABCMeta |
| |
| |
| class UnaryMetric(Metric): |
| '''Abstract Base Class from which all unary metrics inherit.''' |
| __metaclass__ = ABCMeta |
| |
| @abstractmethod |
| def run(self, target_dataset): |
| '''Run the metric for a given target dataset. |
| |
| :param target_dataset: The dataset on which the current metric will |
| be run. |
| :type target_dataset: :class:`dataset.Dataset` |
| |
| :returns: The result of evaluating the metric on the target_dataset. |
| ''' |
| |
| |
| class BinaryMetric(Metric): |
| '''Abstract Base Class from which all binary metrics inherit.''' |
| __metaclass__ = ABCMeta |
| |
| @abstractmethod |
| def run(self, ref_dataset, target_dataset): |
| '''Run the metric for the given reference and target datasets. |
| |
| :param ref_dataset: The Dataset to use as the reference dataset when |
| running the evaluation. |
| :type ref_dataset: :class:`dataset.Dataset` |
| |
| :param target_dataset: The Dataset to use as the target dataset when |
| running the evaluation. |
| :type target_dataset: :class:`dataset.Dataset` |
| |
| :returns: The result of evaluation the metric on the reference and |
| target dataset. |
| ''' |
| |
| |
| class Bias(BinaryMetric): |
| '''Calculate the bias between a reference and target dataset.''' |
| |
| def run(self, ref_dataset, target_dataset): |
| '''Calculate the bias between a reference and target dataset. |
| |
| .. note:: |
| Overrides BinaryMetric.run() |
| |
| :param ref_dataset: The reference dataset to use in this metric run. |
| :type ref_dataset: :class:`dataset.Dataset` |
| |
| :param target_dataset: The target dataset to evaluate against the |
| reference dataset in this metric run. |
| :type target_dataset: :class:`dataset.Dataset` |
| |
| :returns: The difference between the reference and target datasets. |
| :rtype: :class:`numpy.ndarray` |
| ''' |
| return calc_bias(target_dataset.values,ref_dataset.values) |
| |
| class SpatialPatternTaylorDiagram(BinaryMetric): |
| ''' Calculate the target to reference ratio of spatial standard deviation and pattern correlation''' |
| |
| def run(self, ref_dataset, target_dataset): |
| '''Calculate two metrics to plot a Taylor diagram to compare spatial patterns |
| |
| .. note:: |
| Overrides BinaryMetric.run() |
| |
| :param ref_dataset: The reference dataset to use in this metric run. |
| :type ref_dataset: :class:`dataset.Dataset` |
| |
| :param target_dataset: The target dataset to evaluate against the |
| reference dataset in this metric run. |
| :type target_dataset: :class:`dataset.Dataset` |
| |
| :returns: standard deviation ratio, pattern correlation coefficient |
| :rtype: :float:'float','float' |
| ''' |
| return ma.array([calc_stddev_ratio(target_dataset.values, ref_dataset.values), calc_correlation(target_dataset.values, ref_dataset.values)]) |
| |
| |
| class TemporalStdDev(UnaryMetric): |
| '''Calculate the standard deviation over the time.''' |
| |
| def run(self, target_dataset): |
| '''Calculate the temporal std. dev. for a datasets. |
| |
| .. note:: |
| Overrides UnaryMetric.run() |
| |
| :param target_dataset: The target_dataset on which to calculate the |
| temporal standard deviation. |
| :type target_dataset: :class:`dataset.Dataset` |
| |
| :returns: The temporal standard deviation of the target dataset |
| :rtype: :class:`ndarray` |
| ''' |
| return calc_stddev(target_dataset.values, axis=0) |
| |
| |
| class StdDevRatio(BinaryMetric): |
| '''Calculate the standard deviation ratio between two datasets.''' |
| |
| def run(self, ref_dataset, target_dataset): |
| '''Calculate the standard deviation ratio. |
| |
| .. note:: |
| Overrides BinaryMetric.run() |
| |
| :param ref_dataset: The reference dataset to use in this metric run. |
| :type ref_dataset: :class:`dataset.Dataset` |
| |
| :param target_dataset: The target dataset to evaluate against the |
| reference dataset in this metric run. |
| :type target_dataset: :class:`dataset.Dataset` |
| |
| :returns: The standard deviation ratio of the reference and target |
| ''' |
| |
| return calc_stddev_ratio(target_dataset.values, ref_dataset.values) |
| |
| |
| class PatternCorrelation(BinaryMetric): |
| '''Calculate the correlation coefficient between two datasets''' |
| |
| def run(self, ref_dataset, target_dataset): |
| '''Calculate the correlation coefficient between two dataset. |
| |
| .. note:: |
| Overrides BinaryMetric.run() |
| |
| :param ref_dataset: The reference dataset to use in this metric run. |
| :type ref_dataset: :class:`dataset.Dataset` |
| |
| :param target_dataset: The target dataset to evaluate against the |
| reference dataset in this metric run. |
| :type target_dataset: :class:`dataset.Dataset` |
| |
| :returns: The correlation coefficient between a reference and target dataset. |
| ''' |
| # stats.pearsonr returns correlation_coefficient, 2-tailed p-value |
| # We only care about the correlation coefficient |
| # Docs at http://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.pearsonr.html |
| |
| return calc_correlation(target_dataset.values, ref_dataset.values) |
| |
| |
| class TemporalCorrelation(BinaryMetric): |
| '''Calculate the temporal correlation coefficients and associated |
| confidence levels between two datasets, using Pearson's correlation.''' |
| |
| def run(self, reference_dataset, target_dataset): |
| '''Calculate the temporal correlation coefficients and associated |
| confidence levels between two datasets, using Pearson's correlation. |
| |
| .. note:: |
| Overrides BinaryMetric.run() |
| |
| :param reference_dataset: The reference dataset to use in this metric |
| run |
| :type reference_dataset: :class:`dataset.Dataset` |
| |
| :param target_dataset: The target dataset to evaluate against the |
| reference dataset in this metric run |
| :type target_dataset: :class:`dataset.Dataset` |
| |
| :returns: A 2D array of temporal correlation coefficients and a 2D |
| array of confidence levels associated with the temporal correlation |
| coefficients |
| ''' |
| num_times, num_lats, num_lons = reference_dataset.values.shape |
| coefficients = ma.zeros([num_lats, num_lons]) |
| for i in numpy.arange(num_lats): |
| for j in numpy.arange(num_lons): |
| coefficients[i, j] = calc_correlation( |
| target_dataset.values[:, i, j], |
| reference_dataset.values[:, i, j]) |
| return coefficients |
| |
| |
| class TemporalMeanBias(BinaryMetric): |
| '''Calculate the bias averaged over time.''' |
| |
| def run(self, ref_dataset, target_dataset): |
| '''Calculate the bias averaged over time. |
| |
| .. note:: |
| Overrides BinaryMetric.run() |
| |
| :param ref_dataset: The reference dataset to use in this metric run. |
| :type ref_dataset: :class:`dataset.Dataset` |
| |
| :param target_dataset: The target dataset to evaluate against the |
| reference dataset in this metric run. |
| :type target_dataset: :class:`dataset.Dataset` |
| |
| :returns: The mean bias between a reference and target dataset over time. |
| ''' |
| |
| return calc_bias(target_dataset.values,ref_dataset.values, average_over_time=True) |
| |
| class RMSError(BinaryMetric): |
| '''Calculate the Root Mean Square Difference (RMS Error), with the mean |
| calculated over time and space.''' |
| |
| def run(self, reference_dataset, target_dataset): |
| '''Calculate the Root Mean Square Difference (RMS Error), with the mean |
| calculated over time and space. |
| |
| .. note:: |
| Overrides BinaryMetric.run() |
| |
| :param reference_dataset: The reference dataset to use in this metric |
| run |
| :type reference_dataset: :class:`dataset.Dataset` |
| |
| :param target_dataset: The target dataset to evaluate against the |
| reference dataset in this metric run |
| :type target_dataset: :class:`dataset.Dataset` |
| |
| :returns: The RMS error, with the mean calculated over time and space |
| ''' |
| |
| return calc_rmse(target_dataset.values, reference_dataset.values) |
| |
| def calc_bias(target_array, reference_array, average_over_time = False): |
| ''' Calculate difference between two arrays |
| |
| :param target_array: an array to be evaluated, as model output |
| :type target_array: :class:'numpy.ma.core.MaskedArray' |
| |
| :param reference_array: an array of reference dataset |
| :type reference_array: :class:'numpy.ma.core.MaskedArray' |
| |
| :param average_over_time: if True, calculated bias is averaged for the axis=0 |
| :type average_over_time: 'bool' |
| |
| :returns: Biases array of the target dataset |
| :rtype: :class:'numpy.ma.core.MaskedArray' |
| ''' |
| |
| bias = target_array - reference_array |
| if average_over_time: |
| return ma.average(bias, axis=0) |
| else: |
| return bias |
| |
| def calc_stddev(array, axis=None): |
| ''' Calculate a sample standard deviation of an array along the array |
| |
| :param array: an array to calculate sample standard deviation |
| :type array: :class:'numpy.ma.core.MaskedArray' |
| |
| :param axis: Axis along which the sample standard deviation is computed. |
| :type axis: 'int' |
| |
| :returns: sample standard deviation of array |
| :rtype: :class:'numpy.ma.core.MaskedArray' |
| ''' |
| |
| if isinstance(axis, int): |
| return ma.std(array, axis=axis, ddof=1) |
| else: |
| return ma.std(array, ddof=1) |
| |
| |
| def calc_stddev_ratio(target_array, reference_array): |
| ''' Calculate ratio of standard deivations of the two arrays |
| |
| :param target_array: an array to be evaluated, as model output |
| :type target_array: :class:'numpy.ma.core.MaskedArray' |
| |
| :param reference_array: an array of reference dataset |
| :type reference_array: :class:'numpy.ma.core.MaskedArray' |
| |
| :param average_over_time: if True, calculated bias is averaged for the axis=0 |
| :type average_over_time: 'bool' |
| |
| :returns: (standard deviation of target_array)/(standard deviation of reference array) |
| :rtype: :class:'float' |
| ''' |
| |
| return calc_stddev(target_array)/calc_stddev(reference_array) |
| |
| def calc_correlation(target_array, reference_array): |
| '''Calculate the correlation coefficient between two arrays. |
| |
| :param target_array: an array to be evaluated, as model output |
| :type target_array: :class:'numpy.ma.core.MaskedArray' |
| |
| :param reference_array: an array of reference dataset |
| :type reference_array: :class:'numpy.ma.core.MaskedArray' |
| |
| :returns: pearson's correlation coefficient between the two input arrays |
| :rtype: :class:'numpy.ma.core.MaskedArray' |
| ''' |
| |
| return mstats.pearsonr(reference_array.flatten(), target_array.flatten())[0] |
| |
| def calc_rmse(target_array, reference_array): |
| ''' Calculate ratio of standard deivations of the two arrays |
| |
| :param target_array: an array to be evaluated, as model output |
| :type target_array: :class:'numpy.ma.core.MaskedArray' |
| |
| :param reference_array: an array of reference dataset |
| :type reference_array: :class:'numpy.ma.core.MaskedArray' |
| |
| :param average_over_time: if True, calculated bias is averaged for the axis=0 |
| :type average_over_time: 'bool' |
| |
| :returns: root mean square error |
| :rtype: :class:'float' |
| ''' |
| |
| return (ma.mean((calc_bias(target_array, reference_array))**2))**0.5 |