from __future__ import division
import numpy as np
import pandas as pd
from scipy import stats
from scipy.stats import norm
def mk_test(x, alpha=0.05):
"""
This function is derived from code originally posted by Sat Kumar Tomer
(satkumartomer@gmail.com)
See also: http://vsp.pnnl.gov/help/Vsample/Design_Trend_Mann_Kendall.htm
The purpose of the Mann-Kendall (MK) test (Mann 1945, Kendall 1975, Gilbert
1987) is to statistically assess if there is a monotonic upward or downward
trend of the variable of interest over time. A monotonic upward (downward)
trend means that the variable consistently increases (decreases) through
time, but the trend may or may not be linear. The MK test can be used in
place of a parametric linear regression analysis, which can be used to test
if the slope of the estimated linear regression line is different from
zero. The regression analysis requires that the residuals from the fitted
regression line be normally distributed; an assumption not required by the
MK test, that is, the MK test is a non-parametric (distribution-free) test.
Hirsch, Slack and Smith (1982, page 107) indicate that the MK test is best
viewed as an exploratory analysis and is most appropriately used to
identify stations where changes are significant or of large magnitude and
to quantify these findings.
Input:
x: a vector of data
alpha: significance level (0.05 default)
Output:
trend: tells the trend (increasing, decreasing or no trend)
h: True (if trend is present) or False (if trend is absence)
p: p value of the significance test
z: normalized test statistics
Examples
--------
>>> x = np.random.rand(100)
>>> trend,h,p,z = mk_test(x,0.05)
"""
n = len(x)
s = 0
for k in range(n - 1):
for j in range(k + 1, n):
s += np.sign(x[j] - x[k])
unique_x, tp = np.unique(x, return_counts=True)
g = len(unique_x)
if n == g:
var_s = (n * (n - 1) * (2 * n + 5)) / 18
else:
var_s = (n * (n - 1) * (2 * n + 5) - np.sum(tp * (tp - 1) * (2 * tp + 5))) / 18
if s > 0:
z = (s - 1) / np.sqrt(var_s)
elif s < 0:
z = (s + 1) / np.sqrt(var_s)
else:
z = 0
p = 2 * (1 - norm.cdf(abs(z)))
h = abs(z) > norm.ppf(1 - alpha / 2)
if (z < 0) and h:
trend = 'decreasing'
elif (z > 0) and h:
trend = 'increasing'
else:
trend = 'no trend'
return trend, h, p, z
if __name__ == '__main__':
stanames = list(pd.read_excel('工作簿9.xlsx', sheet_name=None))
with pd.ExcelWriter('result.xlsx') as writer:
for sta in stanames:
print('正在处理' + sta)
data = pd.read_excel('工作簿9.xlsx', sheet_name=sta, header=1)
trend1, h1, p1, z1 = mk_test(data[data.columns[1]])
trend2, h2, p2, z2 = mk_test(data[data.columns[2]])
trend3, h3, p3, z3 = mk_test(data[data.columns[3]])
trend4, h4, p4, z4 = mk_test(data[data.columns[4]])
df = pd.DataFrame([[trend1, trend2, trend3, trend4], [z1, z2, z3, z4], [p1, p2, p3, p4]],
index=['Trend', 'Z-value', 'P-value'],
columns=data.columns[1:])
df.to_excel(writer, sheet_name=sta, header=data.columns[1:])
|