#import statsmodels.api as sm
import numpy as np

# 回归函数，已通过sma.py模块中的函数代替，暂留备份
#
# def regress2(_x, _y, _method_type_1 = "ordinary least square",
#              _method_type_2 = "reduced major axis",
#              _weight_x = [], _weight_y = [], _need_intercept = True):
#     # Regression Type II based on statsmodels
#     # Type II regressions are recommended if there is variability on both x and y
#     # It's computing the linear regression type I for (x,y) and (y,x)
#     # and then average relationship with one of the type II methods
#     #
#     # INPUT:
#     #   _x <np.array>
#     #   _y <np.array>
#     #   _method_type_1 <str> method to use for regression type I:
#     #     ordinary least square or OLS <default>
#     #     weighted least square or WLS
#     #     robust linear model or RLM
#     #   _method_type_2 <str> method to use for regression type II:
#     #     major axis
#     #     reduced major axis <default> (also known as geometric mean)
#     #     arithmetic mean
#     #   _need_intercept <bool>
#     #     True <default> add a constant to relation (y = a x + b)
#     #     False force relation by 0 (y = a x)
#     #   _weight_x <np.array> containing the weigth of x
#     #   _weigth_y <np.array> containing the weigth of y
#     #
#     # OUTPUT:
#     #   slope
#     #   intercept
#     #   r
#     #   std_slope
#     #   std_intercept
#     #   predict
#     #
#     # REQUIRE:
#     #   numpy
#     #   statsmodels
#     #
#     # The code is based on the matlab function of MBARI.
#     # AUTHOR: Nils Haentjens
#     # REFERENCE: https://www.mbari.org/products/research-software/matlab-scripts-linear-regressions/
#
#     # Check input
#     if _method_type_2 != "reduced major axis" and _method_type_1 != "ordinary least square":
#         raise ValueError("'" + _method_type_2 + "' only supports '" + _method_type_1 + "' method as type 1.")
#
#     # Set x, y depending on intercept requirement
#     if _need_intercept:
#         x_intercept = sm.add_constant(_x)
#         y_intercept = sm.add_constant(_y)
#
#     # Compute Regression Type I (if type II requires it)
#     if (_method_type_2 == "reduced major axis" or
#         _method_type_2 == "geometric mean"):
#         if _method_type_1 == "OLS" or _method_type_1 == "ordinary least square":
#             if _need_intercept:
#                 [intercept_a, slope_a] = sm.OLS(_y, x_intercept).fit().params
#                 [intercept_b, slope_b] = sm.OLS(_x, y_intercept).fit().params
#             else:
#                 slope_a = sm.OLS(_y, _x).fit().params
#                 slope_b = sm.OLS(_x, _y).fit().params
#         elif _method_type_1 == "WLS" or _method_type_1 == "weighted least square":
#             if _need_intercept:
#                 [intercept_a, slope_a] = sm.WLS(
#                     _y, x_intercept, weights=1. / _weight_y).fit().params
#                 [intercept_b, slope_b] = sm.WLS(
#                     _x, y_intercept, weights=1. / _weight_x).fit().params
#             else:
#                 slope_a = sm.WLS(_y, _x, weights=1. / _weight_y).fit().params
#                 slope_b = sm.WLS(_x, _y, weights=1. / _weight_x).fit().params
#         elif _method_type_1 == "RLM" or _method_type_1 == "robust linear model":
#             if _need_intercept:
#                 [intercept_a, slope_a] = sm.RLM(_y, x_intercept).fit().params
#                 [intercept_b, slope_b] = sm.RLM(_x, y_intercept).fit().params
#             else:
#                 slope_a = sm.RLM(_y, _x).fit().params
#                 slope_b = sm.RLM(_x, _y).fit().params
#         else:
#             raise ValueError("Invalid literal for _method_type_1: " + _method_type_1)
#
#     # Compute Regression Type II
#     if (_method_type_2 == "reduced major axis" or
#         _method_type_2 == "geometric mean"):
#         # Transpose coefficients
#         if _need_intercept:
#             intercept_b = -intercept_b / slope_b
#         slope_b = 1 / slope_b
#         # Check if correlated in same direction
#         #if np.sign(slope_a) != np.sign(slope_b):
#         #    raise RuntimeError('Type I regressions of opposite sign.')
#         # Compute Reduced Major Axis Slope
#         slope = np.sign(slope_a) * np.sqrt(slope_a * slope_b)
#         if _need_intercept:
#             # Compute Intercept (use mean for least square)
#             if _method_type_1 == "OLS" or _method_type_1 == "ordinary least square":
#                 intercept = np.mean(_y) - slope * np.mean(_x)
#             else:
#                 intercept = np.median(_y) - slope * np.median(_x)
#         else:
#             intercept = 0
#         # Compute r
#         r = np.sign(slope_a) * np.sqrt(slope_a / slope_b)
#         # Compute predicted values
#         predict = slope * _x + intercept
#         # Compute standard deviation of the slope and the intercept
#         n = len(_x)
#         diff = _y - predict
#         Sx2 = np.sum(np.multiply(_x, _x))
#         den = n * Sx2 - np.sum(_x) ** 2
#         s2 = np.sum(np.multiply(diff, diff)) / (n - 2)
#         std_slope = np.sqrt(n * s2 / den)
#         if _need_intercept:
#             std_intercept = np.sqrt(Sx2 * s2 / den)
#         else:
#             std_intercept = 0
#     elif (_method_type_2 == "Pearson's major axis" or
#           _method_type_2 == "major axis"):
#         if not _need_intercept:
#             raise ValueError("Invalid value for _need_intercept: " + str(_need_intercept))
#         xm = np.mean(_x)
#         ym = np.mean(_y)
#         xp = _x - xm
#         yp = _y - ym
#         sumx2 = np.sum(np.multiply(xp, xp))
#         sumy2 = np.sum(np.multiply(yp, yp))
#         sumxy = np.sum(np.multiply(xp, yp))
#         slope = ((sumy2 - sumx2 + np.sqrt((sumy2 - sumx2)**2 + 4 * sumxy**2)) /
#                  (2 * sumxy))
#         intercept = ym - slope * xm
#         # Compute r
#         r = sumxy / np.sqrt(sumx2 * sumy2)
#         # Compute standard deviation of the slope and the intercept
#         n = len(_x)
#         std_slope = (slope / r) * np.sqrt((1 - r ** 2) / n)
#         sigx = np.sqrt(sumx2 / (n - 1))
#         sigy = np.sqrt(sumy2 / (n - 1))
#         std_i1 = (sigy - sigx * slope) ** 2
#         std_i2 = (2 * sigx * sigy) + ((xm ** 2 * slope * (1 + r)) / r ** 2)
#         std_intercept = np.sqrt((std_i1 + ((1 - r) * slope * std_i2)) / n)
#         # Compute predicted values
#         predict = slope * _x + intercept
#     elif _method_type_2 == "arithmetic mean":
#         if not _need_intercept:
#             raise ValueError("Invalid value for _need_intercept: " + str(_need_intercept))
#         n = len(_x)
#         sg = np.floor(n / 2)
#         # Sort x and y in order of x
#         sorted_index = sorted(range(len(_x)), key=lambda i: _x[i])
#         x_w = np.array([_x[i] for i in sorted_index])
#         y_w = np.array([_y[i] for i in sorted_index])
#         x1 = x_w[1:sg + 1]
#         x2 = x_w[sg:n]
#         y1 = y_w[1:sg + 1]
#         y2 = y_w[sg:n]
#         x1m = np.mean(x1)
#         x2m = np.mean(x2)
#         y1m = np.mean(y1)
#         y2m = np.mean(y2)
#         xm = (x1m + x2m) / 2
#         ym = (y1m + y2m) / 2
#         slope = (x2m - x1m) / (y2m - y1m)
#         intercept = ym - xm * slope
#         # r (to verify)
#         r = []
#         # Compute predicted values
#         predict = slope * _x + intercept
#         # Compute standard deviation of the slope and the intercept
#         std_slope = []
#         std_intercept = []
#
#     # Return all that
#     return {"slope": float(slope), "intercept": intercept, "r": r,
#             "std_slope": std_slope, "std_intercept": std_intercept,
#             "predict": predict}


