用時間序列的方法處理數據

作者:藍京,商業銀行數據分析師 R語言中文社區專欄作者

1、 本文簡介

本文以處理A股財務報表為例,介紹了將數據轉換成時間序列後在進行處理的一些方法和思路。將會用到xts,lapply,do.call等數據結構和函數。

我們從各個途徑獲得了個股的財務報表原始數據後,還需要對數據做一些處理,以便後續指標計算和使用。舉個簡單的例子,個股發布的利潤表和現金流量表,在年內各個季度值都是累計值,不方便環比比較,所以我們現在想把它們全部都處理成當季實際發生額。對於這樣的數據,無論是SQL還是R,Python裡面傳統的數據結構,實現起來都是要費一番功夫進行數據處理的。但是如果使用了時間序列的方法,再結合一些R語言自帶的語法結構,只需要短短几行代碼,就能完成複雜的數據清洗。

2、 原始數據

原始文件我已經整理好了,記錄了*萬科*,*國農科技*,*世紀星源*和*深振業A*這四隻股票從2014年一季度到2017年三季度,利潤表裡「營業總收入」的數據(單位:萬元)。每隻個股有15條記錄,合計60行數據。數據結構如下:

## data.frame: 60 obs. of 3 variables:## $ 季度 : chr "2017-09-30" "2017-06-30" ...## $ 名稱 : chr "萬科" "萬科" "萬科" "萬科" ...## $ 營業總收入: int 11710050 6981048 1858923 ...

以萬科為例,具體內容如下:

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、數據處理

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 1130 NA## 2014-06-30 2743 1613## 2014-09-30 4842 2099## 2014-12-31 8061 3219## 2015-03-31 2094 -5967## 2015-06-30 5955 3861## 2015-09-30 8889 2934## 2015-12-31 12045 3156## 2016-03-31 1348 -10697## 2016-06-30 10215 8867## 2016-09-30 21757 11542## 2016-12-31 28767 7010## 2017-03-31 1087 -27680## 2017-06-30 2929 1842## 2017-09-30 7100 4171

注意到這個結果還有一個問題,一個是一季度的數據不需要減去上期值,一季度的單季數值等於累計值。所以數據還要處理一下。

#quarter方法來自lubridate包,可以傳入文本判斷季度datanew[quarter(datanew$季度)==1,3]=datanew[quarter(datanew$季度)==1,2]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) <- NULLhead(result[result$名稱=="世紀星源",],2)# 季度 名稱 營業總收入 營業總收入單季## 2014-03-31 世紀星源 1218 1218## 2014-06-30 世紀星源 2386 1168

do.call() 是告訴列表一個函數,讓列表裡的所有元素來執行這個函數。將其用於列表合併,效果比循環好太多。

這樣,我們就把數據整理完畢了。這種差分的數據處理方法,在很多場景都有應用。比如運營上拿到了一系列周期上的指標數值,都同時會看看同比、環比的增減情況。這些數據使用傳統的數據結構,使用傳統的數據處理方法,計算腳本都是很複雜的,而把數據轉化成時間序列後,這些處理的過程都可以用簡單的方法解決。另外,在使用R進行數據分析時,應該利用這種向量化語言的特點,用向量化的方法處理數據。

推薦閱讀:

TAG:時間序列分析 | R編程語言 | 數據挖掘 |