#!/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = 'Bruno Stuyts'
# Native Python packages
# 3rd party packages
import numpy as np
from scipy.stats import t
# Project imports
from groundhog.general.validation import Validator
CONSTANT_VALUE = {
'data': {'type': 'list', 'elementtype': 'float', 'order': None, 'unique': False, 'empty_allowed': False},
'mode': {'type': 'string', 'options': ('Low', 'Mean'), 'regex': None},
'cov': {'type': 'float', 'min_value': 0, 'max_value': 10.0},
'confidence': {'type': 'float', 'min_value': 0.1, 'max_value': 0.99999},
}
CONSTANT_VALUE_ERRORRETURN = {
'n': np.nan,
't_nminus1': np.nan,
'kn': np.nan,
'Xk': np.nan,
}
[docs]
@Validator(CONSTANT_VALUE, CONSTANT_VALUE_ERRORRETURN)
def constant_value(data, mode='Low', cov=np.nan, confidence=0.95, **kwargs):
"""
Selects the characteristic value from a set of measurements using Eurocode 7 rules.
For a local low value, the 5% fractile is taken. For a mean value, a 95% confidence value or the mean is taken.
The selection process assumes that the parameter under consideration is stationary and is normally distributed.
For lognormally distributed parameters a transformation to the logarithm is required.
:param data: List or numpy array with the measurements. The number of measurements is derived from the length.
:param mode: Determines whether a local low value ``"Low"`` or mean vaue ``"Mean"`` needs to be taken
:param cov: Coefficient of variation (:math:`V_x = \\sigma / \\mu`) (given as the ratio of standard deviation to the mean, not in percent). If CoV is unknown, leave blank.
:param confidence: Confidence level used for calculations (default = 95%)
.. math::
X_k = X_{mean} \\cdot \\left( 1 - k_n \\cdot V_x \\right)
V_x \\text{unknown}: k_{n,mean} = t_{n-1}^{0.95} \\sqrt{ \\frac{1}{n} }, \\ k_{n,low} = t_{n-1}^{0.95} \\sqrt{ \\frac{1}{n} + 1}
V_x \\text{known}: k_{n,mean} = 1.64 \\sqrt{ \\frac{1}{n} }, \\ k_{n,low} = 1.64 \\sqrt{ \\frac{1}{n} + 1}
:returns: Dictionary with the following keys:
- 'n': Number of datapoints
- 't_nminus1': Student-t factor
- 'kn': kn value
- 'Xk': Characteristic value of the parameter under consideration
"""
n = data.__len__()
r = n - 1
t_nminus1 = t.ppf(confidence, r)
if mode == 'Low':
if np.isnan(cov):
# COV unknown
kn = t_nminus1 * np.sqrt((1 / n) + 1)
cov = np.array(data).std() / np.array(data).mean()
else:
# COV known
kn = 1.64 * np.sqrt((1 / n) + 1)
elif mode == "Mean":
if np.isnan(cov):
# COV unknown
kn = t_nminus1 * np.sqrt(1 / n)
cov = np.array(data).std() / np.array(data).mean()
else:
# COV known
kn = 1.64 * np.sqrt(1 / n)
else:
raise ValueError("Mode %s is not defined, use 'Low' or 'Mean'" % mode)
Xk = np.array(data).mean() * (1 - kn * cov)
return {
'n': n,
't_nminus1': t_nminus1,
'kn': kn,
'Xk': Xk,
}
LINEAR_TREND = {
'data': {'type': 'list', 'elementtype': 'float', 'order': None, 'unique': False, 'empty_allowed': False},
'depths': {'type': 'list', 'elementtype': 'float', 'order': None, 'unique': False, 'empty_allowed': False},
'requested_depths': {'type': 'list', 'elementtype': 'float', 'order': None, 'unique': False, 'empty_allowed': False},
'mode': {'type': 'string', 'options': ('Low', 'Mean'), 'regex': None},
'confidence': {'type': 'float', 'min_value': 0.1, 'max_value': 0.99999},
}
LINEAR_TREND_ERRORRETURN = {
'n': np.nan,
't_nminus2': np.nan,
's1': np.nan,
's2': np.nan,
'b': np.nan,
'Xk': None
}
[docs]
@Validator(LINEAR_TREND, LINEAR_TREND_ERRORRETURN)
def linear_trend(data, depths, requested_depths, mode='Low', confidence=0.95, **kwargs):
"""
Selects the characteristic value from a set of measurements using Eurocode 7 rules.
A linear trend is assumed in the data.
For a local low value, the 5% fractile is taken. For a mean value, a 95% confidence value or the mean is taken.
The selection process assumes that the parameter under consideration is stationary and, when de-trended, is normally distributed.
For lognormally distributed parameters a transformation to the logarithm is required.
:param data: List or numpy array with the measurements. The number of measurements is derived from the length.
:param depths: List or numpy array with the depths. The number of depths needs to be identical to the number of measurements.
:param requested_depths: List or numpy array with the depths where the characteristic value is requested.
:param mode: Determines whether a local low value ``"Low"`` or mean vaue ``"Mean"`` needs to be taken
:param confidence: Confidence level used for calculations (default = 95%)
.. math::
x^{*} = \\bar{x} + b ( z - \\bar{z} )
\\bar{x} = \\frac{1}{n} \\left( x_1 + x_2 + ... + x_n \\right)
\\bar{z} = \\frac{1}{n} \\left( z_1 + z_2 + ... + z_n \\right)
b = \\frac{\sum_{i=1}^n (x_i - \\bar{x}) (z_i - \\bar{z})}{\sum_{i=1}^n (z_i - \\bar{z})^2}
\\text{Mean value}
s_1 = \\sqrt{ \\frac{1}{n-2} \\left( \\frac{1}{n} + \\frac{(z- \\bar{z})^2}{\\sum_{i=1}^{n} (z_i - \\bar{z})^2} \\right) \\sum_{i=1}^n \\left[ (x_i - \\bar{x}) - b (z_i - \\bar{z}) \\right]^2 }
X_k = \\left[ \\bar{x} + b (z - \\bar{z}) \\right] - t_{n-2}^{0.95} s_1
\\text{Local low value}
s_2 = \\sqrt{ \\frac{1}{n-2} \\left(1+ \\frac{1}{n} + \\frac{(z- \\bar{z})^2}{\\sum_{i=1}^{n} (z_i - \\bar{z})^2} \\right) \\sum_{i=1}^n \\left[ (x_i - \\bar{x}) - b (z_i - \\bar{z}) \\right]^2 }
X_k = \\left[ \\bar{x} + b (z - \\bar{z}) \\right] - t_{n-2}^{0.95} s_2
:returns: Dictionary with the following keys:
- 'n': Number of datapoints
- 't_nminus1': Student-t factor
- 'kn': kn value
- 'Xk': Characteristic values (Numpy array) of the parameter under consideration at the requested depths
"""
n = data.__len__()
r = n - 2
t_nminus2 = t.ppf(confidence, r)
data_mean = np.array(data).mean()
depth_mean = np.array(depths).mean()
b_array_nominator = np.cumsum((np.array(data) - data_mean) * (np.array(depths) - depth_mean))[-1]
b_array_denominator = np.cumsum((np.array(depths) - depth_mean) ** 2)[-1]
b = b_array_nominator / b_array_denominator
final_part = np.cumsum(
((np.array(data) - data_mean) - b * (np.array(depths) - depth_mean)) ** 2)[-1]
if mode == 'Low':
s1 = np.nan
s2 = np.sqrt(
(1 / (n - 2)) *
(1 + (1 / n) + (((np.array(requested_depths) - depth_mean) ** 2) / b_array_denominator)) *
final_part
)
Xk = (data_mean + b * (np.array(requested_depths) - depth_mean)) - t_nminus2 * s2
elif mode == "Mean":
s1 = np.sqrt(
(1 / (n - 2)) *
((1 / n) + (((np.array(requested_depths) - depth_mean) ** 2) / b_array_denominator)) *
final_part
)
s2 = np.nan
Xk = (data_mean + b * (np.array(requested_depths) - depth_mean)) - t_nminus2 * s1
else:
raise ValueError("Mode %s is not defined, use 'Low' or 'Mean'" % mode)
return {
'n': n,
't_nminus2': t_nminus2,
's1': s1,
's2': s2,
'b': b,
'Xk': Xk
}