类对于没有编程基础的人真的是很头疼。。。到底self是啥?到底为什么要加_init__?那么今天我争取讲明白。。
1. 函数
假设我要生成一个1000*4的随机自变量x,设定回归系数coef是[1,2,3,4],然后计算出带有扰动项的y,然后再把模拟的y和x利用公式求出回归系数β,看看和coef的差异。
from numpy import matrix
import numpy as np
# 生成随机数x,然后传入真实的参数coef,返回x和模拟(含有扰动的)y。
def generate_random_number(size, coef):
x = matrix(np.random.random(size))
coef = matrix(coef).T
y = np.dot(x, coef) + matrix(np.random.random(size[0]) / 100).T
return x, y
# 将计算β的过程拆开,这里计算X^T*X
def x_transp_x(x):
xtx = np.dot(x.T, x)
return xtx
# 这里计算 X^T*y
def x_transp_y(x, y):
xty = np.dot(x.T, y)
return xty
# 这里计算 (X^T*X)^(-1)
def inv_xtx(x):
xtx = x_transp_x(x)
Ixtx = xtx.I
return Ixtx
# 这里计算β=(X^T*X)^(-1)*(X^T*y)
def cal_beta(x, y):
xty = x_transp_y(x, y)
Ixtx = inv_xtx(x)
beta = np.dot(Ixtx, xty)
return beta
# 将上面的过程融合在schedule中。
def schedule():
x, y = generate_random_number(size, coef)
beta = cal_beta(x, y)
return beta
if __name__ == "__main__":
# x的形状是1000*4的矩阵,系数是[1,2,3,4],拟合beta通过最后的schedule函数得到。
size = (1000, 4)
coef = [1,2,3,4]
beta = schedule()
-_-||。。请谅解,以上故意把回归拆得这么繁琐。
可以看到,主要参数是x的形状size和真实的系数coef,中间的过程就是把这两个参数传来传去;其实所有的过程可以放在class中,简化整体的流程。
2. 类
class Ols:
# 定义class的时候,类名首字母大写,没括号。
# 同时注意:但凡在类中def函数时,哪怕不传入参数,函数的括号中第一个位置也要写上self。
def __init__(self, size, coef):
# init进行初始化参数,把我们需要从外部传入的参数,作为类的属性,这里传入了size和coef。
self.size = size
self.coef = matrix(coef).T
# 这里顺便把generate_random_number函数拆开放到了初始化里面,因为真正在类中流通的是模拟的x和y。
# 注意:在调用size(以及coef)的时候,此处要加上self.,因为size已经成为了整体的属性,调用“自己”的属性加上self。
# 同时,x 和 y经过初始化,也变成了自己的属性,加上self。
self.x = matrix(np.random.random(self.size))
self.y = np.dot(self.x, self.coef) + matrix(np.random.random(self.size[0]) / 100).T
def x_transp_x(self):
# 定义函数(在类中又称为是方法),这里的x来自初始化的x,同样“自己”的属性加上self.。
xtx = np.dot(self.x.T, self.x)
return xtx
def x_transp_y(self):
xty = np.dot(self.x.T, self.y)
return xty
def inv_xtx(self):
# 特殊之处:调用类自己的函数的时候,要在函数之前加上self.函数名;
# 但凡调用类自己的属性(init部分)或者方法(类中定义的函数),都需要加上self.。
xtx = self.x_transp_x()
Ixtx = xtx.I
return Ixtx
def print_something(self, strings):
# 这个函数除了用到全局的属性变量,还需要用到外部的变量strings。
Ixtx = self.inv_xtx()
print("{strings}{Ixtx}.".format(strings = strings, Ixtx = Ixtx))
def cal_beta(self):
# 两个函数均调用的类自己的函数,因此要加上self;但是函数内没有传参,因此函数的括号中没有参数。
xty = self.x_transp_y()
Ixtx = self.inv_xtx()
beta = np.dot(Ixtx, xty)
return beta
接下来,是从类生成一个实例。
# 传入两个参数size和coef
ols1 = Ols((1000,4), [1,2,3,4])
# 计算beta。
beta_hat = ols1.cal_beta()
print(beta_hat)
因此,这样大大简化了计算的过程。
除了可以计算beta,还可以看看(X^T*X)^(-1)是什么的话,
Ixtx = ols1.inv_xtx()
print(Ixtx)
除此之外,如果还需要调用到外部的变量,那么调用的时候将参数传入,注意self在类的外面不可以写。
ols1.print_something("This is ")
3. 总结
类的模板如下:
class Something:
# 类名大写首字母,且没括号。
# 但凡def的时候,括号内第一个写self。
def __init__(self, coef1, coef2, coef3):
self.coef1 = coef1
self.coef2 = coef2
self.coef3 = coef3
#如果还想利用coef1~coef3生成新的变量coef4,而且全局很多部分用到这个coef4话:
# self.coef4 = some_func(self.coef1, self.coef2, self.coef3)
# 只要下面def的函数中调用到了coef1~coef4,都需要加上“self.”+ “变量”才行。
def func1(self):
# 把需要用到的类的属性参数传入,记得加上self.。
result = do_something1(self.coef1, self.coef2, self.coef3, self.coef4)
return result
def func2(self):
# 如果需要调用类中自己定义的函数,那么同样调用的时候,加上self.。
# func1来自类自己的方法,因此加上self.。
result1 = self.func1()
# 这里不必写成self.result1,因为result1不是属性。
# 属性都在__init__中,不在__init__那里定义的变量就不是属性。
result2 = do_something2(result1)
def func3(self, var1):
# 如果需要调用到外部的变量,那么在self之后补上,同样var1不必加上self。
result = do_something3(var1)
return result
接着,从类中生成实例。
# 从类生成实例
sth1 = Something(coef1, coef2, coef3)
# 需要调用方法:
# func1没有外部参数,因此括号中不用填写。
result1 = sth1.func1()
# func3调用到了外部的参数,因此括号中填上外部参数。
result3 = sth1.func3(var1)
以上就是简单的类函数定义。是我看了B站的教学视频总结。大家如有疑惑,在bilibili中搜索“python class”。。。