【问题标题】:How can I pass different types of parameters (ex: array) into a functional class?如何将不同类型的参数(例如:数组)传递给函数类?
【发布时间】:2018-03-26 21:30:37
【问题描述】:

我正在尝试学习如何按类对函数进行分组。例如,我尝试编写一个广义最小二乘法来找到一组 (x,y) 坐标之间的最佳拟合线的方程。对于我的特殊情况,我选择了一条简单的线y = x + 5,因此斜率应该接近1,y 截距应该接近5。在下面的编码解决方案中运行我的尝试会产生错误TypeError: set_x() takes 1 positional argument but 2 were given,尽管我正在尝试传递一个 x 点数组。如何规避此错误?

import numpy as np
from scipy.optimize import minimize


class GeneralizedLeastSquares:

    def __init__(self, residuals=None, parameters=None, x=None, y_true=None, y_fit=None, weights=None, method=None):
        self.residuals = residuals
        self.parameters = parameters
        self.x = x
        self.y_true = y_true
        self.y_fit = y_fit
        self.weights = weights
        self.method = method

    def set_residuals(self, residuals):
        self.residuals = residuals

    def set_parameters(self, parameters):
        self.parameters = parameters

    def set_x(self, x):
        self.x = x

    def set_y_true(self, y_true):
        self.y_true = y_true

    def set_y_fit(self, y_fit):
        self.y_fit = y_fit

    def set_weights(self, weights):
        self.weights = weights

    def set_method(self, method):
        self.method = method

    def get_residuals(self):
        return [(self.y_true[idx] - self.y_fit[idx])**2 for idx in range(len(self.y_true)) if len(self.y_true) == len(self.y_fit) ]

    def get_parameters(self):
        return self.parameters

    def get_x(self):
        return self.x

    def get_y_true(self):
        return self.y_true

    def get_y_fit(self):
        return [self.parameters[0] * self.x[idx] + self.parameters[1] for idx in range(len(self.x))]

    def get_weights(self):
        return self.weights

    def update_weights(self):
        inverse_residuals = [1/self.residuals[idx] for idx in range(len(residuals))]
        inverse_residuals_abs = [abs(inverse_residual) for inverse_residual in inverse_residuals]
        residual_abs_total = sum(inverse_residuals_abs)
        return [inverse_residuals_abs[idx]/residual_abs_total for idx in range(len(inverse_residuals_abs))]

    def get_method(self):
        return self.method

    def get_error_by_residuals(self):
        return sum([self.weights[idx] * self.residuals[idx] for idx in range(len(self.residuals))])

    def get_error_by_std_mean(self):
        return np.std(self.y_true)/np.sqrt(len(self.y_true))

    def get_linear_fit(self):
        """

        """
        if self.parameters == 'estimate':
            slope_init = (self.y_true[-1] - self.y_true[0]) / (self.x[-1] - self.x[0])
            b_init = np.mean([self.y_true[-1] - slope_init * self.x[-1], self.y_true[0] - slope_init * self.x[0]])
            self.parameters = [slope_init, b_init]
        elif not isinstance(self.parameters, (list, np.ndarray)):
            raise ValueError("parameters = 'estimate' or [slope, y-intercept]")
        meths = ['residuals', 'std of mean']
        funcs = [get_error_by_residuals, get_error_by_std_mean]
        func = dict(zip(meths, funcs))[self.method]
        res = minimize(func, x0=self.parameters, args=(self,), method='Nelder-Mead')
        self.parameters = [res.x[0], res.x[1]]
        self.y_fit = get_y_fit(self)
        self.residuals = get_residuals(self)
        self.weights = update_weights(self)
        return self.parameters, self.y_fit, self.residuals, self.weights

x = np.linspace(0, 4, 5)
y_true = np.linspace(5, 9, 5) ## using slope=1, y-intercept=5
y_actual = np.array([4.8, 6.2, 7, 8.1, 8.9]) ## test data
GLS = GeneralizedLeastSquares()
GLS.set_x(x)
GLS.set_y_true(y_actual)
GLS.set_weights(np.ones(len(x)))
GLS.set_parameters('estimate')
# GLS.set_parameters([1.2, 4.9])
GLS.set_method('residuals')
results = GLS.get_linear_fit()
print(results)

【问题讨论】:

  • 类应该总是继承一些东西,即使你只是继承了基类object

标签: python-3.x class oop parameter-passing instance


【解决方案1】:

你的方法没有参数。应该是:

def set_x(self, x):
    self.x = x

在 get/set 方法中包装属性是一种非常 Java / 过时的做事方式。访问类之外的基础属性要容易得多。 IE。而不是:GLS.set_x(12),考虑更多 Pythonic:GLS.x = 12。这样您就不必为每个属性编写 get 和 set 方法。

此外,将对象的繁重提升方法 get_linear_fit 放在 __call__ 方法中可能更有意义。这样,您只需键入 GLS() 而不是 GLS.get_linear_fit() 即可运行回归

【讨论】:

  • 我将set_x(self) 更改为set_x(self, x)。但我现在收到错误TypeError: get_linear_fit() takes 1 positional argument but 2 were given。我可以看到我这样做的方式已经过时了,尽管我需要在课堂上玩更多的东西才能舒服地这样做。关于您最后的评论,__call__ 方法在放置和书写方面是否与__str__ 方法相似?
  • @mikey __call____str__ 不同,它只是运行对象方法的快捷方式,无需给出方法名称。我暂时不会担心这个。您的问题是您将 GLS 对象作为参数传递给 get_linear_fit 方法。这不是必需的。只需调用不带任何参数的方法results = GLS.get_linear_fit()
  • 当我到达那里时,我会和__call__ 一起玩。我按照你的建议 (results = GLS.get_linear_fit()) 做了,现在收到“NameError: name 'get_error_by_residuals' is not defined”。
  • 是的,这整行没有意义:res = minimize(func, x0=self.parameters, args=(self,), method='Nelder-Mead')。对于最小化,您需要一个函数来找到最小值,在这种情况下,它应该是一个返回“误差平方和”的函数,对吗?您需要编写一个函数来返回输入的 squee 错误总和。如果你想要一个线性模型,你的输入应该是A和B,模型是Ax+B
  • 我明白了。我写了GLS.set_method('residuals') 以获取func = dict(zip(meths, funcs))[self.method] 中的密钥以取回函数get_residuals(self)。在我的例子中,parameters[slope, y-intercept],最初的猜测是通过GLS.set_parameters('estimate') 获得的。
猜你喜欢
  • 1970-01-01
  • 2016-02-28
  • 1970-01-01
  • 2017-05-10
  • 2018-01-02
  • 1970-01-01
  • 2012-10-27
  • 2019-09-29
  • 2012-02-14
相关资源
最近更新 更多