zoukankan      html  css  js  c++  java
  • 回归模型效果评估系列1-QQ图

    (erbqi)导语 QQ图全称 Quantile-Quantile图,也就是分位数-分位数图,简单理解就是把两个分布相同分位数的值,构成点(x,y)绘图;如果两个分布很接近,那个点(x,y)会分布在y=x直线附近;反之则不;可以通过QQ图从整体评估回归模型的预测效果
     

    QQ图一般有两种,正态QQ图和普通QQ图,区别在于正态QQ图中其中有一个分布是正态分布,下面来看下这两种分布

    正态QQ图

     下图来自这里                                                  使用Filliben's estimate来确定n分点

    下面我们尝试绘制正态QQ图

    使用开源库自带函数,很简单,但是可能一些细节看不到

    import numpy as np 
    from matplotlib import pyplot as plt
    import matplotlib
    matplotlib.style.use('ggplot')
    # 用正态分布随机生死100个数据
    x = np.round(np.random.normal(loc=0.0, scale=1.0, size=100),2)
    from scipy.stats import probplot
    f = plt.figure(figsize=(8, 6))
    ax = f.add_subplot(111)
    probplot(x, plot=ax)
    plt.show()

     下面展开一些细节,为下面我们的普通QQ做点铺垫

    import sys,os
    import pandas as pd 
    import numpy as np 
    from scipy.stats import norm,linregress
    from matplotlib import pyplot as plt
    # 返回长度为len(x)的order_statistic_medians
    def calc_uniform_order_statistic_medians(x):
        N = len(x)
        osm_uniform = np.zeros(N, dtype=np.float64)
        osm_uniform[-1] = 0.5**(1.0 / N)
        osm_uniform[0] = 1 - osm_uniform[-1]
        i = np.arange(2, N)
        osm_uniform[1:-1] = (i - 0.3175) / (N + 0.365)
        return osm_uniform
    # 用正态分布随机生死100个数据
    x = np.round(np.random.normal(loc=0.0, scale=1.0, size=100),2)
    osm_uniform = calc_uniform_order_statistic_medians(x)
    # ppf(Percent point function) 是 cdf(Cumulative distribution function) 的逆函数,就是取对应分位数对应的值
    osm = norm.ppf(osm_uniform)
    osr = np.sort(x)
    # 计算osm和osr组合的样本的线性回归的 斜率 截距 等信息
    slope, intercept, rvalue, pvalue, stderr = linregress(osm, osr)
    
    plt.figure(figsize=(10,8))
    plt.plot(osm, osr, 'bo', osm, slope*osm + intercept, 'r-')
    plt.legend()
    plt.show()

    左图是100个采样点,右图是1000个采样点,对比可以发现 ,1000个采样点的分布更接近直线y=x,也就是更拟合正态分布

    普通QQ图和正态不同的地方在于参考系不是正态分布而可能是任意分布的数据集,这正是我们要用的

    下图来自这里       

    下图是一个场景,虚线是真实的网络变化,实线是简单的平滑预测的结果,我希望通过普通QQ图看下简单的平滑预测的拟合效果

    先看下两个曲线的cdf图( Fx(x)=P(X≤x) ),

    这个图的累计分布点是np.linspace(min(X), max(X), len(X))计算来的,看起来有点怪

    我们重新计算以原始数据为累计分布点的cdf图,发现有趣的地方了吗?

    在两个曲线的数量一致的情况下,我们把两组数据从小到大排序之后,相同位置对应的cdf的值是一样的,

    所以两个曲线的数量一致的情况下,QQ图只需要从小到大排序即可

    可以看到,正式的网络曲线和平滑预测曲线的QQ图的斜率只有0.79,说明平滑预测的分布和源数据的分布差别还是挺大的。

    最后是代码

    httpspeedavg = np.array([1821000, 2264000, 2209000, 2203000, 2306000, 2005000, 2428000,
           2246000, 1642000,  721000, 1125000, 1335000, 1367000, 1760000,
           1807000, 1761000, 1767000, 1723000, 1883000, 1645000, 1548000,
           1608000, 1372000, 1532000, 1485000, 1527000, 1618000, 1640000,
           1199000, 1627000, 1620000, 1770000, 1741000, 1744000, 1986000,
           1931000, 2410000, 2293000, 2199000, 1982000, 2036000, 2462000,
           2246000, 2071000, 2220000, 2062000, 1741000, 1624000, 1872000,
           1621000, 1426000, 1723000, 1735000, 1443000, 1735000, 2053000,
           1811000, 1958000, 1828000, 1763000, 2185000, 2267000, 2134000,
           2253000, 1719000, 1669000, 1973000, 1615000, 1839000, 1957000,
           1809000, 1799000, 1706000, 1549000, 1546000, 1692000, 2335000,
           2611000, 1855000, 2092000, 2029000, 1695000, 1379000, 2400000,
           2522000, 2140000, 2614000, 2399000, 2376000])
    
    def smooth_(squences,period=5):
        res = []
        gap = period/2
        right = len(squences)
        for i in range(right):
            res.append(np.mean(squences[i-gap if i-gap > 0 else 0:i+gap if i+gap < right else right]))
        return res 
    
    httpavg = np.round((1.0*httpspeedavg/1024/1024).tolist(),2)
    smooth = np.round(smooth_((1.0*httpspeedavg/1024/1024).tolist(),5),2)
    
    f = plt.figure(figsize=(8, 6))
    ax = f.add_subplot(111)
    probplot(smooth, plot=ax)
    # plt.show()
    
    f = plt.figure(figsize=(8, 6))
    ax = f.add_subplot(111)
    probplot(httpavg, plot=ax)
    # plt.show()
    
    import statsmodels.api as sm
    plt.figure(figsize=(15,8))
    ecdf = sm.distributions.ECDF(httpavg)
    x = np.linspace(min(httpavg), max(httpavg), len(httpavg))
    y = ecdf(x)
    plt.plot(x, y, label='httpavg',color='blue',marker='.')
    ecdf1 = sm.distributions.ECDF(smooth)
    x1 = np.linspace(min(smooth), max(smooth), len(smooth))
    y1 = ecdf1(x1)
    plt.plot(x1, y1, label='smooth',color='red',marker='.')
    plt.legend(loc='best')
    # plt.show()
    def cdf(l):
        res = []
        length = len(l)
        for i in range(length):
            res.append(1.0*(i+1)/length)
        return res
    plt.figure(figsize=(15,8))
    x = np.sort(httpavg)
    y = cdf(x)
    plt.plot(x, y, label='httpavg',color='blue',marker='.')
    x1 = np.sort(smooth)
    y1 = cdf(x1)
    plt.plot(x1, y1, label='smooth',color='red',marker='.')
    plt.legend(loc='best')
    # plt.show()
    from scipy.stats import norm,linregress
    plt.figure(figsize=(10,8))
    httpavg = np.sort(httpavg)
    smooth  = np.sort(smooth)
    slope, intercept, rvalue, pvalue, stderr = linregress(httpavg, smooth)
    plt.plot(httpavg, smooth, 'bo', httpavg, slope*httpavg + intercept, 'r-')
    xmin = np.amin(httpavg)
    xmax = np.amax(httpavg)
    ymin = np.amin(smooth)
    ymax = np.amax(smooth)
    posx = xmin + 0.50 * (xmax - xmin)
    posy = ymin + 0.01 * (ymax - ymin)
    plt.text(posx, posy, "$R^2=%1.4f$ y = %.2f *x + %.2f"  % (rvalue,slope,intercept))
    plt.plot(httpavg,httpavg,color='green',label='y=x')
    plt.legend(loc='best')
    # plt.show()

     

  • 相关阅读:
    HTML元素事件说明
    JQuery基本方法介绍和使用
    Eclipse设置注释模板
    AJAX回调(调用后台方法返回数据)
    Hibernate常用增删改查方法
    C memset
    PAT-Top1002. Business (35)
    PAT-Top1001. Battle Over Cities
    聂老师的考验(反向bfs)
    CSUST选拔赛题解
  • 原文地址:https://www.cnblogs.com/qwj-sysu/p/8484924.html
Copyright © 2011-2022 走看看