0%

数据预测与曲线拟合

什么是曲线拟合

所谓的曲线拟合,就是使用某一个模型(或者称为方程式),将一系列的数据拟成平滑的曲线,以便观察两组数据之间的内在联系,了解数据之间的变化趋势。

曲线拟合的作用

在数据分析时,我们有时需要通过已有数据来预测未来数据。在一些复杂的数据模型中,数据维度很多,数据之间的关系很复杂,我们可能会用到深度学习的算法。但是在一些简单的数据模型中,数据之间有很明显的相关性,那我们就可以使用简单的曲线拟合来预测未来的数据。

曲线拟合的方法

Excel曲线工具

假设我们有一组用户生命周期价值(LTV)和天数的对应数据

Day LTV
1 $0.20
2 $0.35
3 $0.45
4 $0.52
5 $0.57
6 $0.60
7 $0.62
8 $0.63

将数据放进Excel中,插入折线图

右击蓝色曲线,选择“添加趋势线”,并选择趋势线为“对数”,并勾选“显示公式”

可以看到,曲线图中出现了一条虚线的曲线,并显示了对应的公式为

y=0.2148ln(x)+0.2077y=0.2148\ln{(x)}+0.2077

Excel的趋势线工具提供了几个常用的函数,包括指数函数、对数函数等,可以满足一般需求。但是如果数据曲线相对复杂的话,就需要用到下面的工具了。

Excel Solver

要使用Solver,首先需要进入Excel的选项,启用“规划求解”工具

启用之后,在工具栏“数据”标签页下,会多出一个“规划求解”的工具

我们先看一下这个工具的界面,就可以大概了解它的功能。简单来说,Solver可以通过改变一些单元格的值,来使一个目标单元格的值最接近理想值。

我们还是用之前的表格来举例如何使用Solver。现在已经知道了LTV对于Day来说是一条类似对数函数的曲线,可以使用对数函数来模拟。我们假设这个函数是

y=aln(x)+by=a\ln{(x)}+b

其中a和b为参数,是可变的。我们调整一下表格,添加一列Estimate LTV,为根据a、b和Day列计算出来的值。再增加一列Diff,为Estimate LTV和Real LTV的差值的平方(平方是为了防止不同行的正负差值会互相抵消),最后加总这些差值。

我们希望通过改变a、b的值,使通过公式得出的LTV与真实LTV的偏差最小,即Total Diff(F18)的值最小。打开规划求解工具,设置如下

点击求解,片刻后即可计算出最接近目标的a、b值

可以看到,a、b的值和使用趋势线得到的值是一样的。

在线工具

介绍一个非常好用的在线曲线拟合工具:

http://www.qinms.com/webapp/curvefit/cf.aspx

Python matplotlib库

Python的matplotlib库有一个自定义公式来拟合曲线的功能。下面代码演示了通过它来拟合上述例子的过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# -*- coding: UTF-8 -*-
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
import scipy.optimize as optimization

data_day = [1,2,3,4,5,6,7,8] #x坐标值x coord
data_ltv = [0.2,0.35,0.45,0.52,0.57,0.6,0.62,0.63] #y坐标值y coord

xdata = np.array(data_day)
ydata = np.array(data_ltv)

#定义使用的公式customize equation
def lnFunction(x, A, B):
return A*np.log(x)+B

guess = [1, 1] #定义初始A、Binitialize a and b
try:
params, params_covariance = optimization.curve_fit(lnFunction, xdata, ydata, guess) #拟合,A、B结果存入paramscurve fitting and store a, b values to params
print params
result = '' #输出结果to store result
for i in range(1, 15):
result += str(round(lnFunction(i, params[0], params[1]), 2)) #将i带入公式中的x,使用拟合出的A、B值计算y值,并保留两位小数calculate result for each i as x using the a, b values, and round the result to 2 points
if i != 14:
result += ',' #每个结果用逗号隔开,并省略最后一个逗号separate each result with comma, and omit the last comma
print result
except:
print ''

输出结果为:

1
2
[0.21482987 0.20772681]
0.21,0.36,0.44,0.51,0.55,0.59,0.63,0.65,0.68,0.7,0.72,0.74,0.76,0.77

曲线拟合公式

在前面的例子中,我们使用了对数函数来进行拟合。在上文提到的在线曲线拟合工具网站中,也列出了一些常见的拟合方程,包括直线、多项式、对数、指数等。其中有一个方程对于拟合自然曲线非常好用,就是四参数方程

四参数方程的格式为:

y=ad1+(xc)b+dy=\frac{a-d}{1+(\frac{x}{c})^{b}}+d

我们还是用最开始的Day/LTV数据来举例,比较一下它和对数函数拟合的结果。

四参数方程 - Solver比较

修改一下之前的Excel表,将Estimate LTV列使用的公式修改成四参数方程

在规划求解设置中,依然是期望Total Diff达到最小值,可变单元格增加了c、d两个参数

点击求解,获得a、b、c、d四个参数的最优解。我们可以看到,Total Diff比使用对数函数时减少了将近一半

四参数方程 - matplotlib比较

同样,我们修改一下python脚本,改为使用四参数方程。请注意,我们这次添加了一个param_bounds值,将C这个参数限定在了0.001到正无穷。这是因为C在公式中充当了分母,不能等于0,而我还没找到一个有效的限制C不为0的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# -*- coding: UTF-8 -*-
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
import scipy.optimize as optimization

data_day = [1,2,3,4,5,6,7,8] #x坐标值x coord
data_ltv = [0.2,0.35,0.45,0.52,0.57,0.6,0.62,0.63] #y坐标值y coord

xdata = np.array(data_day)
ydata = np.array(data_ltv)

#定义使用的公式customize equation
def fourPL(x, A, B, C, D):
return ((A-D)/(1.0+((x/C)**(B))) + D)

guess = [1, 1, 1, 1] #定义初始A、B值initialize a and b
param_bounds = ([-np.inf,-np.inf,0.001,-np.inf],[np.inf,np.inf,np.inf,np.inf])
try:
params, params_covariance = optimization.curve_fit(fourPL, xdata, ydata, guess, bounds = param_bounds) #拟合,A、B、C、D结果存入paramscurve fitting and store a, b, c, d values to params
print params
result = '' #输出结果to store result
for i in range(1, 15):
result += str(round(fourPL(i, params[0], params[1], params[2], params[3]), 2)) #将i带入公式中的x,使用拟合出的A、B、C、D值计算y值,并保留两位小数calculate result for each i as x using the a, b, c, d values, and round the result to 2 points
if i != 14:
result += ',' #每个结果用逗号隔开,并省略最后一个逗号separate each result with comma, and omit the last comma
print result
except:
print ''

输出结果为

1
2
[0.0923509  1.73652154 2.41689489 0.70144044]
0.2,0.35,0.45,0.52,0.57,0.6,0.62,0.63,0.65,0.65,0.66,0.67,0.67,0.67

可以看到,相对于Excel Solver来说,matplotlib库获得的结果更精准,预测值和真实值几乎没有偏差。