R语言时间序列处理介绍--以A股财报数据处理为案例

本文以处理A股财务报表为例,介绍了将数据转换成时间序列后在进行处理的一些方法和思路。将会用到xts,lapply,do.call等数据结构和函数。

1、 简介

我们从各个途径获得了个股的财务报表原始数据后,还需要对数据做一些处理,以便后续指标计算和使用。举个简单的例子,个股发布的利润表和现金流量表,在年内各个季度值都是累计值,不方便环比比较,所以我们现在想把它们全部都处理成当季实际发生额。对于这样的数据,无论是SQL还是R,Python里面传统的数据结构,实现起来都是要费一番功夫进行数据处理的。但是如果使用了时间序列的方法,再结合一些R语言自带的语法结构,只需要短短几行代码,就能完成复杂的数据清洗。

2、 原始数据

原始文件我已经整理好了,记录了万科,国农科技,世纪星源和深振业A这四只股票从2014年一季度到2017年三季度,利润表里“营业总收入”的数据(单位:万元)。每只个股有15条记录,合计60行数据。数据结构如下:

## 'data.frame':    60 obs. of  3 variables:
##  $ 季度      : chr  "2017-09-30" "2017-06-30" "2017-03-31" "2016-12-31" ...
##  $ 名称      : chr  "万科" "万科" "万科" "万科" ...
##  $ 营业总收入: int  11710050 6981048 1858923 24047724 11705480 7479529 1461131 19554913 7959621 5026680 ...

以万科为例,具体内容如下:data[data$名称==“万科”,]

##          季度 名称 营业总收入
## 1  2017-09-30 万科   11710050
## 2  2017-06-30 万科    6981048
## 3  2017-03-31 万科    1858923
## 4  2016-12-31 万科   24047724
## 5  2016-09-30 万科   11705480
## 6  2016-06-30 万科    7479529
## 7  2016-03-31 万科    1461131
## 8  2015-12-31 万科   19554913
## 9  2015-09-30 万科    7959621
## 10 2015-06-30 万科    5026680
## 11 2015-03-31 万科     889434
## 12 2014-12-31 万科   14638800
## 13 2014-09-30 万科    6313959
## 14 2014-06-30 万科    4096190
## 15 2014-03-31 万科     949722

我们看到,每只个股按照时间倒序排列,营业总收入是一个累计值。比如,表中显示万科在2017年3季度的营业收入为11710050(万元),2季度的营业收入为6981048(万元),那么万科2017年3季度的营业收入世纪发生额为11710050-6981048=4729002 万元。我们的目的是在原始数据的基础之上,再加一列,把单季度的发生额加在后面。

3、处理过程

  • 3.1、数据切分
    原始数据里有4只股票,他们的数据结构是一致的,处理方法也一致,为了方便处理,把原始数据从数据框切成列表。在dataframe上使用split,可以将dataframe按照指定的条件切成一个个列表。示例如下:

     data<-split(data,data$名称)   
    
    #数据类型
    class(data)
    ## [1] "list"
    
    #列表名称
    names(data)
    
    ## [1] "国农科技" "深振业A"  "世纪星源" "万科"
    
    #第一个列表内容
    data[[1]]
     
     ##          季度     名称 营业总收入
    ## 16 2017-09-30 国农科技       7100
    ## 17 2017-06-30 国农科技       2929
    ## 18 2017-03-31 国农科技       1087
    ## 19 2016-12-31 国农科技      28767
    ## 20 2016-09-30 国农科技      21757
    ## 21 2016-06-30 国农科技      10215
    ## 22 2016-03-31 国农科技       1348
    ## 23 2015-12-31 国农科技      12045
    ## 24 2015-09-30 国农科技       8889
    ## 25 2015-06-30 国农科技       5955
    ## 26 2015-03-31 国农科技       2094
    ## 27 2014-12-31 国农科技       8061
    ## 28 2014-09-30 国农科技       4842
    ## 29 2014-06-30 国农科技       2743
    ## 30 2014-03-31 国农科技       1130
    

    这样数据从dataframe切分成了4个列表,分别对应每一只个股。

  • 3.2、数据处理

    • 3.2.1: 将数据转换成时间序列
      df<-data[[1]]
      df$季度<-as.Date(df$季度)
      df<-as.xts(df[-c(1,2)],order.by=df$季度)
      class(df)
      ## [1] "xts" "zoo"
      df
      ##            营业总收入
      ## 2014-03-31       1130
      ## 2014-06-30       2743
      ## 2014-09-30       4842
      ## 2014-12-31       8061
      ## 2015-03-31       2094
      ## 2015-06-30       5955
      ## 2015-09-30       8889
      ## 2015-12-31      12045
      ## 2016-03-31       1348
      ## 2016-06-30      10215
      ## 2016-09-30      21757
      ## 2016-12-31      28767
      ## 2017-03-31       1087
      ## 2017-06-30       2929
      ## 2017-09-30       7100
      
      
      时间序列只能处理数值型的数据,数据转化成时间序列后,原来数据框中的日期和名称都消失了。要在后面在加上去。现在开始计算单季数据,只要拿当前的值减去上期的值即可。在时间序列了,可以使用DIFF差分函数来实现,diff(x,n),表示将当前值减去N个周期前的值。默认n=1.将处理后的数据合并会原来的数据。并把日期加上去。
      datadiff<-diff(df)
      datanew<-as.data.frame(merge(df,datadiff))
      datanew<-cbind(row.names(datanew),datanew)
      colnames(datanew)<-c("季度","营业总收入","营业总收入单季")
      datanew
      
      ##                  季度 营业总收入 营业总收入单季
      ## 2014-03-31 2014-03-31       1130             NA
      ## 2014-06-30 2014-06-30       2743           1613
      ## 2014-09-30 2014-09-30       4842           2099
      ## 2014-12-31 2014-12-31       8061           3219
      ## 2015-03-31 2015-03-31       2094          -5967
      ## 2015-06-30 2015-06-30       5955           3861
      ## 2015-09-30 2015-09-30       8889           2934
      ## 2015-12-31 2015-12-31      12045           3156
      ## 2016-03-31 2016-03-31       1348         -10697
      ## 2016-06-30 2016-06-30      10215           8867
      ## 2016-09-30 2016-09-30      21757          11542
      ## 2016-12-31 2016-12-31      28767           7010
      ## 2017-03-31 2017-03-31       1087         -27680
      ## 2017-06-30 2017-06-30       2929           1842
      ## 2017-09-30 2017-09-30       7100           4171
      
      

    注意到这个结果还有一个问题,一个是一季度的数据不需要减去上期值,一季度的单季数值等于累计值。所以数据还要处理一下。

        #quarter 方法来自lubridate包,可以传入文本判断季度
        datanew[quarter(datanew$季度)==1,3]=datanew[quarter(datanew$季度)==1,2]
    
  • 3.2.2:处理所有列表数据
    把以上代码包装一下,所有的个股都按照这个方法处理。不建议在R里使用循环,这里使用Lappy来轮询处理每一个列表数据。Lappy可以传入列表、数据框,返回一个列表。

        DataPrc<-function (x){
        
        #转成时间序列
        x$季度<-as.Date(x$季度)
        StkNam<-as.character(x$名称[1])
        x<-as.xts(x[-c(1,2)],order.by=x$季度)
        
        #利用差分计算单期值并合并
        x.diff<-diff(x)
        x.new<-as.data.frame(merge(x,x.diff))
        x.new<-cbind(row.names(x.new),StkNam,x.new)
        colnames(x.new)<-c("季度","名称","营业总收入","营业总收入单季")
        #处理特殊情况
        #quarter 方法来自lubridate包,可以传入文本判断季度
        x.new[quarter(x.new$季度)==1,4]=x.new[quarter(x.new$季度)==1,3]
        
        x.new
        
        }
        
        stkdata<-lapply(data,DataPrc)
        
        stkdata
        
        ## $国农科技
        ##                  季度     名称 营业总收入 营业总收入单季
        ## 2014-03-31 2014-03-31 国农科技       1130           1130
        ## 2014-06-30 2014-06-30 国农科技       2743           1613
        ## 2014-09-30 2014-09-30 国农科技       4842           2099
        ## 2014-12-31 2014-12-31 国农科技       8061           3219
        ## 2015-03-31 2015-03-31 国农科技       2094           2094
        ## 2015-06-30 2015-06-30 国农科技       5955           3861
        ## 2015-09-30 2015-09-30 国农科技       8889           2934
        ## 2015-12-31 2015-12-31 国农科技      12045           3156
        ## 2016-03-31 2016-03-31 国农科技       1348           1348
        ## 2016-06-30 2016-06-30 国农科技      10215           8867
        ## 2016-09-30 2016-09-30 国农科技      21757          11542
        ## 2016-12-31 2016-12-31 国农科技      28767           7010
        ## 2017-03-31 2017-03-31 国农科技       1087           1087
        ## 2017-06-30 2017-06-30 国农科技       2929           1842
        ## 2017-09-30 2017-09-30 国农科技       7100           4171
        
        ## $深振业A
        ##                  季度    名称 营业总收入 营业总收入单季
        ## 2014-03-31 2014-03-31 深振业A      26292          26292
        ## 2014-06-30 2014-06-30 深振业A      49149          22857
        ## 2014-09-30 2014-09-30 深振业A      64985          15836
        ## 2014-12-31 2014-12-31 深振业A     232873         167888
        ## 2015-03-31 2015-03-31 深振业A     138923         138923
        ## 2015-06-30 2015-06-30 深振业A     202261          63338
        ## 2015-09-30 2015-09-30 深振业A     230546          28285
        ## 2015-12-31 2015-12-31 深振业A     365431         134885
        ## 2016-03-31 2016-03-31 深振业A      38249          38249
        ## 2016-06-30 2016-06-30 深振业A      86869          48620
        ## 2016-09-30 2016-09-30 深振业A     114571          27702
        ## 2016-12-31 2016-12-31 深振业A     335883         221312
        ## 2017-03-31 2017-03-31 深振业A     116791         116791
        ## 2017-06-30 2017-06-30 深振业A     186960          70169
        ## 2017-09-30 2017-09-30 深振业A     231926          44966
        ## 
        ## $世纪星源
        ##                  季度     名称 营业总收入 营业总收入单季
        ## 2014-03-31 2014-03-31 世纪星源       1218           1218
        ## 2014-06-30 2014-06-30 世纪星源       2386           1168
        ## 2014-09-30 2014-09-30 世纪星源       3585           1199
        ## 2014-12-31 2014-12-31 世纪星源       5278           1693
        ## 2015-03-31 2015-03-31 世纪星源       1349           1349
        ## 2015-06-30 2015-06-30 世纪星源       3629           2280
        ## 2015-09-30 2015-09-30 世纪星源       4576            947
        ## 2015-12-31 2015-12-31 世纪星源       8413           3837
        ## 2016-03-31 2016-03-31 世纪星源       4342           4342
        ## 2016-06-30 2016-06-30 世纪星源      18995          14653
        ## 2016-09-30 2016-09-30 世纪星源      35050          16055
        ## 2016-12-31 2016-12-31 世纪星源      48186          13136
        ## 2017-03-31 2017-03-31 世纪星源       7145           7145
        ## 2017-06-30 2017-06-30 世纪星源      20360          13215
        ## 2017-09-30 2017-09-30 世纪星源      31423          11063
        
        ## $万科
        ##                  季度 名称 营业总收入 营业总收入单季
        ## 2014-03-31 2014-03-31 万科     949722         949722
        ## 2014-06-30 2014-06-30 万科    4096190        3146468
        ## 2014-09-30 2014-09-30 万科    6313959        2217769
        ## 2014-12-31 2014-12-31 万科   14638800        8324841
        ## 2015-03-31 2015-03-31 万科     889434         889434
        ## 2015-06-30 2015-06-30 万科    5026680        4137246
        ## 2015-09-30 2015-09-30 万科    7959621        2932941
        ## 2015-12-31 2015-12-31 万科   19554913       11595292
        ## 2016-03-31 2016-03-31 万科    1461131        1461131
        ## 2016-06-30 2016-06-30 万科    7479529        6018398
        ## 2016-09-30 2016-09-30 万科   11705480        4225951
        ## 2016-12-31 2016-12-31 万科   24047724       12342244
        ## 2017-03-31 2017-03-31 万科    1858923        1858923
        ## 2017-06-30 2017-06-30 万科    6981048        5122125
        ## 2017-09-30 2017-09-30 万科   11710050        4729002
        



    - **4、合并**
   
    以上结果显示数据都是列表,把它们合成一个数据框。方便后续处理。你当然可以选择使用循环将列表合并,但R里处理循环的效率实在无法恭维。这里有个更好的办法,代码如下:
    
    ```
    result<- do.call(rbind,stkdata)
    rownames(result) <- NULL
    head(result[result$名称=="世纪星源",],2)
    
    #          季度     名称 营业总收入 营业总收入单季
    ## 31 2014-03-31 世纪星源       1218           1218
    ## 32 2014-06-30 世纪星源       2386           1168
    

do.call() 是告诉列表一个函数,让列表里的所有元素来执行这个函数。将其用于列表合并,效果比循环好太多。

这样,我们就把数据整理完毕了。这种差分的数据处理方法,在很多场景都有应用。比如运营上拿到了一系列周期上的指标数值,都同时会看看同比、环比的增减情况。这些数据使用传统的数据结构,使用传统的数据处理方法,计算脚本都是很复杂的,而把数据转化成时间序列后,这些处理的过程都可以用简单的方法解决。另外,在使用R进行数据分析时,应该利用这种向量化语言的特点,用向量化的方法处理数据。

猜你喜欢

转载自blog.csdn.net/bluewhalelove/article/details/83582789