1. 程式人生 > >R語言時間序列處理介紹--以A股財報資料處理為案例

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進行資料分析時,應該利用這種向量化語言的特點,用向量化的方法處理資料。