# -*- coding: utf-8 -*-
"""
Created on Sat Aug 18 11:08:38 2018

@author: acadsoc
"""

import pandas as pd
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from pyecharts import Bar, Line, Page, Overlap
import statsmodels.api as sm
from sklearn.preprocessing import StandardScaler
# import pymssql
from dateutil import parser
import copy
import os
import sys
from featureSelection import featureSelection

plt.style.use('ggplot') # 设置ggplot2画图风格
# 根据不同平台设置其中文字体路径
if sys.platform == 'linux':
    zh_font = matplotlib.font_manager.FontProperties(
        fname='path/anaconda3/lib/python3.6/site-packages/matplotlib/mpl-data/fonts/ttf/STZHONGS.TTF')
else:
    zh_font = matplotlib.font_manager.FontProperties(fname='C:\Windows\Fonts\STZHONGS.ttf')  # 设置中文字体

# 根据不同平台设定工作目录
if sys.platform == 'linux':
    os.chdir(path) # Linux path
else:
    os.chdir(path) # Windows path

class rollingRegression():
    '''
    滚动多元回归分析类。
    
    参数
    ----
    response : str
        回归因变量。
    date_begin : datetime
        起始日期。
    date_end : datetime
        截止日期。
    rolling_days : int
        滚动天数。
    intercept : bool
        回归方程是否带常数项。
    p_value_threshold : float
        回归系数按p值显示阈值。
    normalize : bool
        数据是否标准化。
        
    属性
    ----
    coef_ : dataframe
        回归系数。
    coef_pvalue_ : dataframe
        回归系数pvalue。
    r2_ : dataframe
        回归模型Rsquared 和 Rsquared_adj。
    echarts_page_ : echarts_page
        echarts_page 文件。       
    '''
    def __init__(self, response='新单数', date_begin='2018-01-01', date_end='2018-07-31', rolling_days=30,
                 intercept=False, p_value_threshold=.1, normalize=False):
        self.response = response  # 回归因变量
        self.date_begin = date_begin  # 起始日期
        self.date_end = date_end  # 终止日期
        self.rolling_days = rolling_days  # 滚动天数
        self.intercept = intercept  # 回归方程是否带常数项
        self.p_value_threshold = p_value_threshold  # p值显示阈值
        self.normalize = normalize  # 是否将数据标准化后再进行回归分析
        if self.normalize:  # 如果数据标准化,常数强制设置为0
            self.intercept = False
        # 起始日期间隔必须大于等于滚动天数
        if (parser.parse(self.date_end) - parser.parse(self.date_begin)).days < self.rolling_days:
            raise IOError('起始日期间隔必须大于等于滚动天数,请重新选择起始日期或者调整滚动日期。')    
            
    def getData(self, file='业绩相关数据2018-8-1.xlsx', variabls_in=None, variables_out=None):
        '''
        读取数据。
        注:该方法只适合读取特定方式的数据,第一列应是datetime类型,第二列是因变量,其余是自变量。
        
        参数
        ----
        file : str
            文件路径及文件名。
        variabls_in : list, 默认是None
            需要用到的自变量。
        variables_out : list, 默认是None
            不用的自变量。
            
        return
        ------
        df_ : dataframe
            分析用数据框,response在第一列。
        '''
        if variabls_in:   # 如果有选入模型的自变量,强制将variable_out设置为None
            variables_out = None
        
        if file.split('.')[-1] == 'xlsx':  # 根据不同文件格式读取数据        
            df = pd.read_excel(file)  
        elif file.split('.')[-1] == 'txt':
            df = pd.read_table(file)
        else:
            df = eval('pd.read_' + file.split('.')[-1] + '(file)')

df.index = df.iloc[:, 0]  # 将日期变为索引
        df = df.iloc[:, 1:]             
        df[df.isnull()] = 0  # 缺失值填充        
        df = df.astype(float)  # 将数据框object格式转换为float
        # dateTransfer = np.vectorize(self._dateTransfer)   # 向量化日期转换函数                
        # df.index = dateTransfer(df.index) # 转换索引日期格式          
        df.index = pd.DatetimeIndex(df.index)   # 将索引转换为datetime格式
        
        if variabls_in:
            df = pd.concat([df[df.columns[0]], df[variabls_in]], axis=1)
        
        if variables_out:
            for var in variables_out:
                df.pop(var)            
       
        if self.normalize:   # 数据标准化     
            df_std = StandardScaler().fit_transform(df)
            self.df_ = pd.DataFrame(df_std, index=df.index, columns=df.columns)    
        else:
            self.df_ = df
        
        return self
    
    def rollingOLS(self, df):
        '''
        滚动日期多元线性模型。
        
        参数
        ----
        df : dataframe
            回归分析用数据框,response在第一列。
            
        return
        ------
        coef : dataframe
            回归系数。
        coef_pvalue : dataframe
            回归系数pvalue。
        r2 : dataframe
            回归模型Rsquared 和 Rsquared_adj。
        '''
        df = df.loc[(df.index>=self.date_begin) & (df.index<=self.date_end), :]   # 按照参数给定起始、截止时间选择数据
        df = df.sort_index(ascending=True)  # 按日期升序排序
        coef = {}
        coef_pvalue = {}
        r2 = {}

# 从起始日开始做回归
        for i in range(df.shape[0] - self.rolling_days):
            date = df.index[i+self.rolling_days]   
            data = df.iloc[i:i+self.rolling_days, :]
            X = data.iloc[:, 1:]
            y = data.iloc[:, 0]        
            # 线性回归模型拟合    
            model = sm.OLS(y, X, hasconst=self.intercept)
            lr = model.fit()

# 按字典格式保存系数、pvalue、R2
            coef[date] = lr.params            
            coef_pvalue[date] = lr.pvalues
            r2[date] = []
            r2[date].append(lr.rsquared)
            r2[date].append(lr.rsquared_adj)

# 系数字典转化为数据框,并按日期升序排序
        coef = pd.DataFrame.from_dict(coef, orient='index')
        coef = coef.sort_index(ascending=True)

# 系数pvalue转化为数据框,并按日期升序排序
        coef_pvalue = pd.DataFrame.from_dict(coef_pvalue, orient='index')
        coef_pvalue = coef_pvalue.sort_index(ascending=True)

# R2转化为数据框,并按日期升序排序
        r2 = pd.DataFrame.from_dict(r2, orient='index')
        r2.columns = ['R_squred','R_squred_adj']
        r2 = r2.sort_index(ascending=True)
        return coef, coef_pvalue, r2
    
    def _dateTransfer(self, date):
        '''
        定义日期转换函数。
        
        参数
        ----
        date : str
            需要转换的日期数据。
        
        return
        ------
        date : datetime
            日期。
        '''
        return parser.parse(date).strftime('%Y-%m-%d')    
    
    def fit(self, feat_selected=None):
        '''
        多元回归分析并保存数据。
        
        参数
        ----
        feat_selected : list, 默认是None
            分析用的特征列表。
            
        return
        ------
        coef_ : dataframe
            回归系数。
        coef_pvalue_ : dataframe
            回归系数pvalue。
        r2_ : dataframe
            回归模型Rsquared 和 Rsquared_adj。
        '''
        if feat_selected is not None:
            df = pd.concat([self.df_.iloc[:, 0], self.df_[feat_selected]], axis=1)
        else:
            df = self.df_
        # 滚动回归分析        
        self.coef_, self.coef_pvalue_, self.r2_ = self.rollingOLS(df)  
        # 存储分析数据表
        self.coef_.to_excel('coef.xlsx')
        self.coef_pvalue_.to_excel('coef_pvalue.xlsx')
        self.r2_.to_excel('r2.xlsx')        
        return self    
    
    def coefPlots(self, width_subplot=12, height_subplot=5, columns_subplots=3):
        '''
        画滚动回归系数及pvalue图。
        
        参数
        ----
        width_subplot : int
            子图宽度。
        height_subplot : int
            子图高度。
        columns_subplots : int
            子图列数。
        '''
        num_subplots = self.coef_.shape[1] + 1  # 确定子图个数
        # 确定子图行数
        if num_subplots % columns_subplots == 0: # 余数为0
            rows_subplots = num_subplots // columns_subplots  # 取整
        else:
            rows_subplots = num_subplots // columns_subplots + 1
        # 确定画布宽、高
        width_figure = columns_subplots * width_subplot
        height_figure = rows_subplots * height_subplot
        
        # 绘制滚动回归R2图
        plt.figure(figsize=(width_figure, height_figure))
        plt.subplot(rows_subplots, columns_subplots, 1)
        plt.plot(self.r2_['R_squred'], color='r', lw=3, label='R_squred')
        plt.plot(self.r2_['R_squred_adj'], color='g', lw=3, label='R_squred_adj')
        plt.title('R2')
        plt.legend()
        # 在子图中画系滚动回归系数及p值图
        for i, feature in enumerate(self.coef_.columns):  # 系数图
            plt.subplot(rows_subplots, columns_subplots, i+2)
            plt.plot(self.coef_[feature], color='red', lw=3, label='Beta')

for t, pvalue in zip(self.coef_pvalue_.index, self.coef_pvalue_[feature]):  # p值图
                if pvalue <= self.p_value_threshold:
                    plt.vlines(t, ymin=np.min(self.coef_[feature]), ymax=np.max(self.coef_[feature]),
                               color='green', alpha=.3, lw=5, label='p_value')

#plt.xlabel('日期')
            if ((i + columns_subplots + 1) % columns_subplots) & (i > 0) == 0:
                plt.ylabel('coef')
            plt.title(feature, fontproperties=zh_font)
        # plt.savefig('rollingRegression.jpeg') # 保存图片
        plt.show()
        return self    
   
    def coefEcharts(self):
        '''
        利用Echarts画图。
        注:因为没有vline方法,故用echarts画出的图文件过大,在浏览器中打开很慢。
        
        参数
        ----
        
        return
        ------
        echarts_page_ : echarts_page
            echarts_page 文件。
        '''
        self.echarts_page_ = Page(self.response + '回归分析')
        charts = []
        zeros = np.zeros(self.coef_.shape[0])

line = Line('R2')  # R2图
        bar = Bar()
        line.add('R_squred', self.r2_.index, self.r2_['R_squred'], is_more_utils=True)
        line.add('R_squred_adj', self.r2_.index, self.r2_['R_squred_adj'], is_more_utils=True)
        charts.append(line)

for i, feature in enumerate(self.coef_.columns):  
            min_num = np.min(self.coef_[feature])
            max_num = np.max(self.coef_[feature])
            line = Line(feature)
            bar = Bar()
            ol = Overlap()
            line.add('coef', self.coef_.index, self.coef_[feature], is_more_utils=True) # 系数图
            #line.on()
            for t, pvalue in zip(self.coef_pvalue_.index, self.coef_pvalue_[feature]):  # p值图
                if pvalue <= self.p_value_threshold:
                    min_array, max_array = copy.deepcopy(zeros), copy.deepcopy(zeros)
                    min_array[self.coef_.index==t] = min_num
                    max_array[self.coef_.index==t] = max_num
                    bar.add('p-value', self.coef_.index, min_array)
                    bar.add('p-value', self.coef_.index, max_array)

ol.add(line)
            ol.add(bar)
            charts.append(ol)

self.echarts_page_.add(charts)
        self.echarts_page_.render()  # 保存格式为HTML, 保存地址为设定的全局path
        return self

RollingRegression(滚动回归分析)之Python实现的更多相关文章

  1. python书籍推荐:量化投资:以Python为工具

    所属网站分类: 资源下载 > python电子书 作者:mimi 链接:http://www.pythonheidong.com/blog/article/451/ 来源:python黑洞网 内 ...

  2. Ubuntu 16.04: How to install OpenCV

    参考:https://www.pyimagesearch.com/2016/10/24/ubuntu-16-04-how-to-install-opencv/ 步骤# 1:安装opencv的依赖项 本 ...

  3. 回归分析特征选择(包括Stepwise算法) python 实现

    # -*- coding: utf-8 -*-"""Created on Sat Aug 18 16:23:17 2018 @author: acadsoc"& ...

  4. python回归分析五部曲

    Python回归分析五部曲(一)—简单线性回归 https://blog.csdn.net/jacky_zhuyuanlu/article/details/78878405?ref=myread Py ...

  5. 个股与指数的回归分析(自带python ols 参数解读)

    sklearn实战-乳腺癌细胞数据挖掘(博客主亲自录制视频教程) https://study.163.com/course/introduction.htm?courseId=1005269003&a ...

  6. 利用Spark-mllab进行聚类,分类,回归分析的代码实现(python)

    Spark作为一种开源集群计算环境,具有分布式的快速数据处理能力.而Spark中的Mllib定义了各种各样用于机器学习的数据结构以及算法.Python具有Spark的API.需要注意的是,Spark中 ...

  7. $用python玩点有趣的数据分析——一元线性回归分析实例

    Refer:http://python.jobbole.com/81215/ 本文参考了博乐在线的这篇文章,在其基础上加了一些自己的理解.其原文是一篇英文的博客,讲的通俗易懂. 本文通过一个简单的例子 ...

  8. Python selenium chrome打包exe后禁用控制台输出滚动日志

    Python selenium chrome打包exe后,在运行的过程中,如果遇到需要input()输入时,会发现被不断滚动刷新的日志把命令行输入快速顶掉了,通过查阅资料不断实践,发现以下方法有效: ...

  9. (转)python中用logging实现日志滚动和过期日志删除

    转自:https://blog.csdn.net/ashi198866/article/details/46725813 logging库提供了两个可以用于日志滚动的class(可以参考https:/ ...

随机推荐

  1. Android StickyListHeadersListView头部悬停 分组列表实现

    最近在做分组列表,头部悬停的效果,在github上就搜到了StickyListHeaders(https://github.com/emilsjolander/StickyListHeaders)这个 ...

  2. hdoj4099(字典树+高精度)

    题目链接:https://vjudge.net/problem/HDU-4099 题意:给T组询问,每个询问为一个字符串(长度<=40),求以该字符串为开始的fibonacci数列的第一个元素的 ...

  3. [Asp.Net Core] - 设置 Syncfusion / RichTextEditor 移除超链接及高度自适应 的方法

    背景 使用 Syncfusion / RichTextEditor 对录入后的信息进行展示:1. 希望内容高度自适应.2. 希望禁用原文中的超链接.   实现 <div class=" ...

  4. Git使用总结(二):分支管理

    1.创建分支 a.直接创建 git branch dev(分支名) b.基于某个历史版本创建分支 git branch dev HEAD 2.查看分支 git branch -av 3.删除分支 gi ...

  5. 安装python的pip库setup.py出现报错的解决过程

    错误起因: 第一次安python3.72的时候,直接去官网下了压缩包,解压后也没有exe文件.环境也是手动配置,在之后安装Pycharm的时候,系统找不到解释器,手动加上. 错误经过: 等写程序用到i ...

  6. Python re模块学习

    这是re模块与正则的结合 re模块提供的函数 1.match  尝试在字符串的开头应用该模式,返回匹配对象,如果没有找到匹配,则为None. import re str1 = "Why ar ...

  7. 11 模块、模块的搜索顺序、__file__内置属性、__name__属性

    模块的概念 一个python文件就是一个模块. 模块名同时也是一个标识符,需要符合标识符的命名规则. 在模块中定义的全局变量.函数.类 都是个外界提供的直接使用的工具. 模块就好比工具包,要想使用一个 ...

  8. 使用Docker搭建svn服务器教程

    svn简介 SVN是Subversion的简称,是一个开放源代码的版本控制系统,相较于RCS.CVS,它采用了分支管理系统,它的设计目标就是取代CVS.互联网上很多版本控制服务已从CVS迁移到Subv ...

  9. dotnet Core学习之旅(序)

    .NET Core 新玩具,新工具,新生产力 我将在博客园我从0.1开始探索.NET Core 的过程. 为什么是从0.1开始而不是从0开始 我在微软刚宣布开源的时候便接触到了.NET Core 1. ...

  10. Vue使用指南(三)

    组件 '''1.根组件:new Vue()创建的组件,一般不明确自身的模板,模板就采用挂载点2.局部组件: local_component = {}2.全局组件: Vue.component({})' ...