• Posts tagged "R"
  • (Page 2)

Blog Archives

用R语言把数据玩出花样

R的极客理想系列文章,涵盖了R的思想,使用,工具,创新等的一系列要点,以我个人的学习和体验去诠释R的强大。

R语言作为统计学一门语言,一直在小众领域闪耀着光芒。直到大数据的爆发,R语言变成了一门炙手可热的数据分析的利器。随着越来越多的工程背景的人的加入,R语言的社区在迅速扩大成长。现在已不仅仅是统计领域,教育,银行,电商,互联网….都在使用R语言。

要成为有理想的极客,我们不能停留在语法上,要掌握牢固的数学,概率,统计知识,同时还要有创新精神,把R语言发挥到各个领域。让我们一起动起来吧,开始R的极客理想。

关于作者:

  • 张丹, 程序员R,Nodejs,Java
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/r-transform/

前言

作为数据分析师,每天都有大量的数据需要处理,我们会根据业务的要求做各种复杂的报表,包括了分组、排序、过滤、转置、差分、填充、移动、合并、分裂、分布、去重、找重、填充 等等的操作。

有时为了计算一个业务指标,你的SQL怎么写都不会少于10行时,另外你可能也会抱怨Excel功能不够强大,这个时候R语言绝对是不二的选择了。用R语言可以高效地、优雅地解决数据处理的问题,让R来帮你打开面向数据的思维模式。

目录

  1. 为什么要用R语言做数据处理?
  2. 数据处理基础
  3. 个性化的数据变换需求

1. 为什么要用R语言做数据处理?

R语言是非常适合做数据处理的编程语言,因为R语言的设计理念,就是面向数据的,为了解决数据问题。读完本文,相信你就能明白,什么是面向数据的设计了。

一个BI工程师每天的任务,都是非常繁琐的数据处理,如果用Java来做简直就是折磨,但是换成R语言来做,你会找到乐趣的。

当接到一个数据处理的任务后,我们可以把任务拆解为很多小的操作,包括了分组、排序、过滤、转置、差分、填充、移动、合并、分裂、分布、去重、找重等等的操作。对于实际应用的复杂的操作来说,就是把这些小的零碎的操作,拼装起来就好了。

在开始之前,我们要先了解一下R语言支持的数据类型,以及这些常用类型的特点。对于BI的数据处理的工作来说,可能有4种类型是最常用的,分别是向量、矩阵、数据框、时间序列。

  • 向量 Vector : c()
  • 矩阵 Matrix: matrix()
  • 数据框 DataFrame: data.frame()
  • 时间序列 XTS: xts()

我主要是用R语言来做量化投资,很多的时候,都是和时间序列类型数据打交道,所以我把时间序列,也定义为R语言最常用的数据处理的类型。时间序列类型,使用的是第三方包xts中定义的类型。

2. 数据处理基础

本机的系统环境:

  • Win10 64bit
  • R: version 3.2.3 64bit

2.1 创建一个数据集

创建一个向量数据集。


> x<-1:20;x
 [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20

创建一个矩阵数据集。


> m<-matrix(1:40,ncol=5);m
     [,1] [,2] [,3] [,4] [,5]
[1,]    1    9   17   25   33
[2,]    2   10   18   26   34
[3,]    3   11   19   27   35
[4,]    4   12   20   28   36
[5,]    5   13   21   29   37
[6,]    6   14   22   30   38
[7,]    7   15   23   31   39
[8,]    8   16   24   32   40

创建一个数据框数据集。


> df<-data.frame(a=1:5,b=c('A','A','B','B','A'),c=rnorm(5));df
  a b          c
1 1 A  1.1519118
2 2 A  0.9921604
3 3 B -0.4295131
4 4 B  1.2383041
5 5 A -0.2793463

创建一个时间序列数据集,时间序列使用的第三方的xts类型。关于xts类型的详细介绍,请参考文章 可扩展的时间序列xts。


> library(xts)
> xts(1:10,order.by=as.Date('2017-01-01')+1:10)
           [,1]
2017-01-02    1
2017-01-03    2
2017-01-04    3
2017-01-05    4
2017-01-06    5
2017-01-07    6
2017-01-08    7
2017-01-09    8
2017-01-10    9
2017-01-11   10

2.2 查看数据概况

通常进行数据分析的第一步是,查看一下数据的概况信息,在R语言里可以使用summary()函数来完成。


# 查看矩阵数据集的概况
> m<-matrix(1:40,ncol=5)
> summary(m)
       V1             V2              V3              V4              V5       
 Min.   :1.00   Min.   : 9.00   Min.   :17.00   Min.   :25.00   Min.   :33.00  
 1st Qu.:2.75   1st Qu.:10.75   1st Qu.:18.75   1st Qu.:26.75   1st Qu.:34.75  
 Median :4.50   Median :12.50   Median :20.50   Median :28.50   Median :36.50  
 Mean   :4.50   Mean   :12.50   Mean   :20.50   Mean   :28.50   Mean   :36.50  
 3rd Qu.:6.25   3rd Qu.:14.25   3rd Qu.:22.25   3rd Qu.:30.25   3rd Qu.:38.25  
 Max.   :8.00   Max.   :16.00   Max.   :24.00   Max.   :32.00   Max.   :40.00  

# 查看数据框数据集的概况信息
> df<-data.frame(a=1:5,b=c('A','A','B','B','A'),c=rnorm(5))
> summary(df)
       a     b           c          
 Min.   :1   A:3   Min.   :-1.5638  
 1st Qu.:2   B:2   1st Qu.:-1.0656  
 Median :3         Median :-0.2273  
 Mean   :3         Mean   :-0.1736  
 3rd Qu.:4         3rd Qu.: 0.8320  
 Max.   :5         Max.   : 1.1565  

通过查看概况,可以帮助我们简单了解数据的一些统计特征。

2.3 数据合并

我们经常需要对于数据集,进行合并操作,让数据集满足处理的需求。对于不同类型的数据集,有不同的处理方法。

向量类型


> x<-1:5
> y<-11:15
> c(x,y)
 [1]  1  2  3  4  5 11 12 13 14 15

数据框类型的合并操作。


> df<-data.frame(a=1:5,b=c('A','A','B','B','A'),c=rnorm(5));df
  a b          c
1 1 A  1.1519118
2 2 A  0.9921604
3 3 B -0.4295131
4 4 B  1.2383041
5 5 A -0.2793463

# 合并新行
> rbind(df,c(11,'A',222))
   a b                  c
1  1 A    1.1519117540872
2  2 A  0.992160365445798
3  3 B -0.429513109491881
4  4 B   1.23830410085338
5  5 A -0.279346281854269
6 11 A                222

# 合并新列
> cbind(df,x=LETTERS[1:5])
  a b          c x
1 1 A  1.1519118 A
2 2 A  0.9921604 B
3 3 B -0.4295131 C
4 4 B  1.2383041 D
5 5 A -0.2793463 E

# 合并新列
> merge(df,LETTERS[3:5])
   a b          c y
1  1 A  1.1519118 C
2  2 A  0.9921604 C
3  3 B -0.4295131 C
4  4 B  1.2383041 C
5  5 A -0.2793463 C
6  1 A  1.1519118 D
7  2 A  0.9921604 D
8  3 B -0.4295131 D
9  4 B  1.2383041 D
10 5 A -0.2793463 D
11 1 A  1.1519118 E
12 2 A  0.9921604 E
13 3 B -0.4295131 E
14 4 B  1.2383041 E
15 5 A -0.2793463 E

2.4 累计计算

累计计算,是很常用的一种计算方法,就是把每个数值型的数据,累计求和或累计求积,从而反应数据的增长的一种特征。


# 向量x
> x<-1:10;x
 [1]  1  2  3  4  5  6  7  8  9 10

# 累计求和
> cum_sum<-cumsum(x)

# 累计求积
> cum_prod<-cumprod(x)

# 拼接成data.frame
> data.frame(x,cum_sum,cum_prod)
    x cum_sum cum_prod
1   1       1        1
2   2       3        2
3   3       6        6
4   4      10       24
5   5      15      120
6   6      21      720
7   7      28     5040
8   8      36    40320
9   9      45   362880
10 10      55  3628800

我们通常用累计计算,记录中间每一步的过程,看到的数据处理过程的特征。

2.5 差分计算

差分计算,是用向量的后一项减去前一项,所获得的差值,差分的结果反映了离散量之间的一种变化。


> x<-1:10;x
 [1]  1  2  3  4  5  6  7  8  9 10

# 计算1阶差分
> diff(x)
[1] 1 1 1 1 1 1 1 1 1

# 计算2阶差分
> diff(x,2)
[1] 2 2 2 2 2 2 2 2

# 计算2阶差分,迭代2次
> diff(x,2,2)
[1] 0 0 0 0 0 0

下面做一个稍微复杂一点的例子,通过差分来发现数据的规律。


# 对向量2次累积求和
> x <- cumsum(cumsum(1:10));x
 [1]   1   4  10  20  35  56  84 120 165 220

# 计算2阶差分
> diff(x, lag = 2)
[1]   9  16  25  36  49  64  81 100

# 计算1阶差分,迭代2次
> diff(x, differences = 2)
[1]  3  4  5  6  7  8  9 10

# 同上
> diff(diff(x))
[1]  3  4  5  6  7  8  9 10

差分其实是很常见数据的操作,但这种操作是SQL很难表达的,所以可能会被大家所忽视。

2.6 分组计算

分组是SQL中,支持的一种数据变换的操作,对应于group by的语法。

比如,我们写一个例子。创建一个数据框有a,b,c的3列,其中a,c列为数值型,b列为字符串,我们以b列分组,求出a列与c的均值。


# 创建数据框
> df<-data.frame(a=1:5,b=c('A','A','B','B','A'),c=rnorm(5));df
  a b           c
1 1 A  1.28505418
2 2 A -0.04687263
3 3 B  0.25383533
4 4 B  0.70145787
5 5 A -0.11470372

# 执行分组操作
> aggregate(. ~ b, data = df, mean)
  b        a         c
1 A 2.666667 0.3744926
2 B 3.500000 0.4776466

同样的数据集,以b列分组,对a列求和,对c列求均值。当对不同列,进行不同的操作时,我们同时也需要换其他函数来处理。


# 加载plyr库
> library(plyr)

# 执行分组操作
> ddply(df,.(b),summarise,
+       sum_a=sum(a),
+       mean_c=mean(c))
  b sum_a      mean_c
1 A     8 -0.05514761
2 B     7  0.82301276

生成的结果,就是按b列进行分组后,a列求和,c列求均值。

2.7 分裂计算

分裂计算,是把一个向量按照一列规则,拆分成多个向量的操作。

如果你想把1:10的向量,按照单双数,拆分成2个向量。


> split(1:10, 1:2)
$`1`
[1] 1 3 5 7 9

$`2`
[1]  2  4  6  8 10

另外,可以用因子类型来控制分裂。分成2步操作,第一步先分成与数据集同样长度的因子,第二步进行分裂,可以把一个大的向量拆分成多个小的向量。


# 生成因子规则
> n <- 3; size <- 5
> fat <- factor(round(n * runif(n * size)));fat
 [1] 2 3 2 1 1 0 0 2 0 1 2 3 1 1 1
Levels: 0 1 2 3

# 生成数据向量
> x <- rnorm(n * size);x
 [1]  0.68973936  0.02800216 -0.74327321  0.18879230 -1.80495863  1.46555486  0.15325334  2.17261167  0.47550953
[10] -0.70994643  0.61072635 -0.93409763 -1.25363340  0.29144624 -0.44329187

# 对向量以因子的规则进行拆分
> split(x, fat)
$`0`
[1] 1.4655549 0.1532533 0.4755095

$`1`
[1]  0.1887923 -1.8049586 -0.7099464 -1.2536334  0.2914462 -0.4432919

$`2`
[1]  0.6897394 -0.7432732  2.1726117  0.6107264

$`3`
[1]  0.02800216 -0.93409763

这种操作可以非常有效地,对数据集进行分类整理,比if..else的操作,有本质上的提升。

2.8 排序

排序是所有数据操作中,最常见一种需求了。在R语言中,你可以很方便的使用排序的功能,并不用考虑时间复杂度与空间复杂度的问题,除非你自己非要用for循环来实现。

对向量进行排序。


# 生成一个乱序的向量
> x<-sample(1:10);x
 [1]  6  2  5  1  9 10  8  3  7  4

# 对向量排序 
> x[order(x)]
 [1]  1  2  3  4  5  6  7  8  9 10

以数据框某一列进行排序。


> df<-data.frame(a=1:5,b=c('A','A','B','B','A'),c=rnorm(5));df
  a b          c
1 1 A  1.1780870
2 2 A -1.5235668
3 3 B  0.5939462
4 4 B  0.3329504
5 5 A  1.0630998

# 自定义排序函数 
> order_df<-function(df,col,decreasing=FALSE){
+     df[order(df[,c(col)],decreasing=decreasing),]
+ }

# 以c列倒序排序
> order_df(df,'c',decreasing=TRUE)
  a b          c
1 1 A  1.1780870
5 5 A  1.0630998
3 3 B  0.5939462
4 4 B  0.3329504
2 2 A -1.5235668

排序的操作,大多都是基于索引来完成的,用order()函数来生成索引,再匹配的数据的数值上面。

2.9 去重与找重

去重,是把向量中重复的元素过滤掉。找重,是把向量中重复的元素找出来。


> x<-c(3:6,5:8);x
[1] 3 4 5 6 5 6 7 8

# 去掉重复元素
> unique(x)
[1] 3 4 5 6 7 8

# 找到重复元素,索引位置
> duplicated(x)
[1] FALSE FALSE FALSE FALSE  TRUE  TRUE FALSE FALSE

# 找到重复元素
> x[duplicated(x)]
[1] 5 6

2.10 转置

转置是一个数学名词,把行和列进行互换,一般用于对矩阵的操作。


# 创建一个3行5列的矩阵
> m<-matrix(1:15,ncol=5);m
     [,1] [,2] [,3] [,4] [,5]
[1,]    1    4    7   10   13
[2,]    2    5    8   11   14
[3,]    3    6    9   12   15

# 转置后,变成5行3列的矩阵
> t(m)
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    4    5    6
[3,]    7    8    9
[4,]   10   11   12
[5,]   13   14   15

2.11 过滤

过滤,是对数据集按照某种规则进行筛选,去掉不符合条件的数据,保留符合条件的数据。对于NA值的操作,主要都集中在了过滤操作和填充操作中,因此就不在单独介绍NA值的处理了。


# 生成数据框
> df<-data.frame(a=c(1,NA,NA,2,NA),
+     b=c('B','A','B','B',NA),
+     c=c(rnorm(2),NA,NA,NA));df
   a    b          c
1  1    B -0.3041839
2 NA    A  0.3700188
3 NA    B         NA
4  2    B         NA
5 NA <NA>         NA

# 过滤有NA行的数据
> na.omit(df)
  a b          c
1 1 B -0.3041839

# 过滤,保留b列值为B的数据
> df[which(df$b=='B'),]
   a b          c
1  1 B -0.3041839
3 NA B         NA
4  2 B         NA

过滤,类似与SQL语句中的 WHERE 条件语句,如果你用100个以上的过滤条件,那么你的程序就会比较复杂了,最好想办法用一些巧妙的函数或者设计模式,来替换这些过滤条件。

2.12 填充

填充,是一个比较有意思的操作,你的原始数据有可能会有缺失值NA,在做各种计算时,就会出现有问题。一种方法是,你把NA值都去掉;另外一种方法是,你把NA值进行填充后再计算。那么在填充值时,就有一些讲究了。

把NA值进行填充。


# 生成数据框
> df<-data.frame(a=c(1,NA,NA,2,NA),
+      b=c('B','A','B','B',NA),
+      c=c(rnorm(2),NA,NA,NA));df
   a    b          c
1  1    B  0.2670988
2 NA    A -0.5425200
3 NA    B         NA
4  2    B         NA
5 NA <NA>         NA

# 把数据框a列的NA,用9进行填充
> na.fill(df$a,9)
[1] 1 9 9 2 9

# 把数据框中的NA,用1进行填充
> na.fill(df,1)
     a      b      c           
[1,] " 1"   "B"    " 0.2670988"
[2,] "TRUE" "A"    "-0.5425200"
[3,] "TRUE" "B"    "TRUE"      
[4,] " 2"   "B"    "TRUE"      
[5,] "TRUE" "TRUE" "TRUE"     

填充时,有时并不是用某个固定的值,而是需要基于某种规则去填充。


# 生成一个zoo类型的数据
> z <- zoo(c(2, NA, 1, 4, 5, 2), c(1, 3, 4, 6, 7, 8));z
 1  3  4  6  7  8 
 2 NA  1  4  5  2 

# 对NA进行线性插值
> na.approx(z) 
       1        3        4        6        7        8 
2.000000 1.333333 1.000000 4.000000 5.000000 2.000000 

# 对NA进行线性插值
> na.approx(z, 1:6)
  1   3   4   6   7   8 
2.0 1.5 1.0 4.0 5.0 2.0 

# 对NA进行样条插值
> na.spline(z)
        1         3         4         6         7         8 
2.0000000 0.1535948 1.0000000 4.0000000 5.0000000 2.0000000 

另外,我们可以针对NA的位置进行填充,比如用前值来填充或后值来填充。


> df
   a    b          c
1  1    B  0.2670988
2 NA    A -0.5425200
3 NA    B         NA
4  2    B         NA
5 NA <NA>         NA

# 用当前列中,NA的前值来填充
> na.locf(df)
   a b          c
1  1 B  0.2670988
2  1 A -0.5425200
3  1 B -0.5425200
4  2 B -0.5425200
5  2 B -0.5425200

# 用当前列中,NA的后值来填充
> na.locf(df,fromLast=TRUE)
   a b          c
1  1 B  0.2670988
2  2 A -0.5425200
3  2 B       <NA>
4  2 B       <NA>

2.13 计数

计数,是统计同一个值出现的次数。


# 生成30个随机数的向量
> set.seed(0)
> x<-round(rnorm(30)*5);x
 [1]  6 -2  7  6  2 -8 -5 -1  0 12  4 -4 -6 -1 -1 -2  1 -4  2 -6 -1  2  1  4  0  3  5 -3 -6  0

# 统计每个值出现的次数
> table(x)
x
-8 -6 -5 -4 -3 -2 -1  0  1  2  3  4  5  6  7 12 
 1  3  1  2  1  2  4  3  2  3  1  2  1  2  1  1 

用直方图画出。


> hist(x,xlim = c(-10,13),breaks=20)

2.14 统计分布

统计分布,是用来判断数据是否是满足某种统计学分布,如果能够验证了,那么我们就可以用到这种分布的特性来理解我们的数据集的情况了。常见的连续型的统计分布有9种,其中最常用的就是正态分布的假设。关于统计分布的详细介绍,请参考文章 常用连续型分布介绍及R语言实现

  • runif() :均匀分布
  • rnorm() :正态分布
  • rexp() :指数分布
  • rgamma() :伽马分布
  • rweibull() :韦伯分布
  • rchisq() :卡方分布
  • rf() :F分布
  • rt() :T分布
  • rbeta() :贝塔分布

统计模型定义的回归模型,就是基于正态分布的做的数据假设,如果残差满足正态分布,模型的指标再漂亮都是假的。如果你想进一步了解回归模型,请参考文章R语言解读一元线性回归模型

下面用正态分布,来举例说明一下。假设我们有一组数据,是人的身高信息,我们知道平均身高是170cm,然后我们算一下,这组身高数据是否满足正态分布。


# 生成身高数据
> set.seed(1)
> x<-round(rnorm(100,170,10))
> head(x,20)
 [1] 164 172 162 186 173 162 175 177 176 167 185 174 164 148 181 170 170 179 178 176

# 画出散点图 
> plot(x)

通过散点图来观察,发现数据是没有任何规律。接下来,我们进行正态分布的检验,Shapiro-Wilk进行正态分布检验。


> shapiro.test(x)
	Shapiro-Wilk normality test
data:  x
W = 0.99409, p-value = 0.9444

该检验原假设为H0:数据集符合正态分布,统计量W为。统计量W的最大值是1,越接近1,表示样本与正态分布越匹配。p值,如果p-value小于显著性水平α(0.05),则拒绝H0。检验结论: W接近1,p-value>0.05,不能拒绝原假设,所以数据集S符合正态分布!

同时,我们也可以用QQ图,来做正态分布的检验。


> qqnorm(x)
> qqline(x,col='red')

图中,散点均匀的分布在对角线,则说明这组数据符合正态分布。

为了,更直观地对正态分布的数据进行观察,我们可以用上文中计数操作时,使用的直方图进行观察。


> hist(x,breaks=10)

通过计数的方法,发现数据形状如钟型,中间高两边低,中间部分的数量占了95%,这就是正态的特征。当判断出,数据是符合正态分布后,那么才具备了可以使用一些的模型的基础。

2.15 数值分段

数值分段,就是把一个连续型的数值型数据,按区间分割为因子类型的离散型数据。


> x<-1:10;x
 [1]  1  2  3  4  5  6  7  8  9 10

# 把向量转换为3段因子,分别列出每个值对应因子
> cut(x, 3)
 [1] (0.991,4] (0.991,4] (0.991,4] (0.991,4] (4,7]     (4,7]     (4,7]     (7,10]    (7,10]    (7,10]   
Levels: (0.991,4] (4,7] (7,10]

# 对因子保留2位精度,并支持排序
> cut(x, 3, dig.lab = 2, ordered = TRUE)
 [1] (0.99,4] (0.99,4] (0.99,4] (0.99,4] (4,7]    (4,7]    (4,7]    (7,10]   (7,10]   (7,10]  
Levels: (0.99,4] < (4,7] < (7,10]

2.16 集合操作

集合操作,是对2个向量的操作,处理2个向量之间的数值的关系,找到包含关系、取交集、并集、差集等。


# 定义2个向量x,y
> x<-c(3:8,NA);x
[1]  3  4  5  6  7  8 NA
> y<-c(NA,6:10,NA);y
[1] NA  6  7  8  9 10 NA

# 判断x与y重复的元素的位置
> is.element(x, y)
[1] FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE

# 判断y与x重复的元素的位置
> is.element(y, x)
[1]  TRUE  TRUE  TRUE  TRUE FALSE FALSE  TRUE

# 取并集
> union(x, y)
[1]  3  4  5  6  7  8 NA  9 10

# 取交集
> intersect(x, y)
[1]  6  7  8 NA

# 取x有,y没有元素
> setdiff(x, y)
[1] 3 4 5

# 取y有,x没有元素
> setdiff(y, x)
[1]  9 10

# 判断2个向量是否相等
> setequal(x, y)
[1] FALSE

2.17 移动窗口

移动窗口,是用来按时间周期观察数据的一种方法。移动平均,就是一种移动窗口的最常见的应用了。

在R语言的的TTR包中,支持多种的移动窗口的计算。

  • runMean(x) :移动均值
  • runSum(x) :移动求和
  • runSD(x) :移动标准差
  • runVar(x) :移动方差
  • runCor(x,y) :移动相关系数
  • runCov(x,y) :移动协方差
  • runMax(x) :移动最大值
  • runMin(x) :移动最小值
  • runMedian(x):移动中位数

下面我们用移动平均来举例说明一下,移动平均在股票交易使用的非常普遍,是最基础的趋势判断的根踪指标了。


# 生成50个随机数
> set.seed(0)
> x<-round(rnorm(50)*10);head(x,10)
 [1]  13  -3  13  13   4 -15  -9  -3   0  24

# 加载TTR包
> library(TTR)

# 计算周期为3的移动平均值
> m3<-SMA(x,3);head(m3,10)
 [1]         NA         NA  7.6666667  7.6666667 10.0000000  0.6666667 -6.6666667 -9.0000000 -4.0000000
[10]  7.0000000

# 计算周期为5的移动平均值
> m5<-SMA(x,5);head(m5,10)
 [1]   NA   NA   NA   NA  8.0  2.4  1.2 -2.0 -4.6 -0.6

当计算周期为3的移动平均值时,结果的前2个值是NA,计算的算法是


(第一个值 + 第二个值 + 第三个值)  /3 = 第三个值的移动平均值
(13      +    -3   +     13)    /3 = 7.6666667

画出图形


> plot(x,type='l')
> lines(m3,col='blue')
> lines(m5,col='red')

图中黑色线是原始数据,蓝色线是周期为3的移动平均值,红色线是周期为5的移动平均值。这3个线中,周期越大的越平滑,红色线波动是最小的,趋势性是越明显的。如果你想更深入的了解移动平均线在股票中的使用情况,请参考文章二条均线打天下

2.18 时间对齐

时间对齐,是在处理时间序列类型时常用到的操作。我们在做金融量化分析时,经常遇到时间不齐的情况,比如某支股票交易很活跃,每一秒都有交易,而其他不太活跃的股票,可能1分钟才有一笔交易,当我们要同时分析这2只股票的时候,就需要把他们的交易时间进行对齐。


# 生成数据,每秒一个值
> a<-as.POSIXct("2017-01-01 10:00:00")+0:300

# 生成数据,每59秒一个值
> b<-as.POSIXct("2017-01-01 10:00")+seq(1,300,59)

# 打印a
> head(a,10)
 [1] "2017-01-01 10:00:00 CST" "2017-01-01 10:00:01 CST" "2017-01-01 10:00:02 CST" "2017-01-01 10:00:03 CST"
 [5] "2017-01-01 10:00:04 CST" "2017-01-01 10:00:05 CST" "2017-01-01 10:00:06 CST" "2017-01-01 10:00:07 CST"
 [9] "2017-01-01 10:00:08 CST" "2017-01-01 10:00:09 CST"

# 打印b 
> head(b,10)
[1] "2017-01-01 10:00:01 CST" "2017-01-01 10:01:00 CST" "2017-01-01 10:01:59 CST" "2017-01-01 10:02:58 CST"
[5] "2017-01-01 10:03:57 CST" "2017-01-01 10:04:56 CST"

按分钟进行对齐,把时间都对齐到分钟线上。


# 按分钟对齐
> a1<-align.time(a, 1*60)
> b1<-align.time(b, 1*60)

# 查看对齐后的结果
> head(a1,10)
 [1] "2017-01-01 10:01:00 CST" "2017-01-01 10:01:00 CST" "2017-01-01 10:01:00 CST" "2017-01-01 10:01:00 CST"
 [5] "2017-01-01 10:01:00 CST" "2017-01-01 10:01:00 CST" "2017-01-01 10:01:00 CST" "2017-01-01 10:01:00 CST"
 [9] "2017-01-01 10:01:00 CST" "2017-01-01 10:01:00 CST"

> head(b1,10)
[1] "2017-01-01 10:01:00 CST" "2017-01-01 10:02:00 CST" "2017-01-01 10:02:00 CST" "2017-01-01 10:03:00 CST"
[5] "2017-01-01 10:04:00 CST" "2017-01-01 10:05:00 CST"

由于a1数据集,每分钟有多条数据,取每分钟的最后一条代表这分钟就行。


> a1[endpoints(a1,'minutes')]
[1] "2017-01-01 10:01:00 CST" "2017-01-01 10:02:00 CST" "2017-01-01 10:03:00 CST" "2017-01-01 10:04:00 CST"
[5] "2017-01-01 10:05:00 CST" "2017-01-01 10:06:00 CST"

这样子就完成了时间对齐,把不同时间的数据放到都一个维度中了。

3. 个性化的数据变换需求

我们上面已经介绍了,很多种的R语言数据处理的方法,大多都是基于R语言内置的函数或第三方包来完成的。在实际的工作中,实际还有再多的操作,完全是各性化的。

3.1 过滤数据框中,列数据全部为空的列

空值,通常都会给我们做数值计算,带来很多麻烦。有时候一列的数据都是空时,我们需要先把这一个过滤掉,再进行数据处理。

用R语言程序进行实现


# 判断哪列的值都是NA
na_col_del_df<-function(df){
  df[,which(!apply(df,2,function(x) all(is.na(x))))]  
} 

# 生成一个数据集
> df<-data.frame(a=c(1,NA,2,4),b=rep(NA,4),c=1:4);df
   a  b c
1  1 NA 1
2 NA NA 2
3  2 NA 3
4  4 NA 4

# 保留非NA的列
> na_col_del_df(df)
   a c
1  1 1
2 NA 2
3  2 3
4  4 4

3.2 替换数据框中某个区域的数据

我们想替换数据框中某个区域的数据,那么应该怎么做呢?

找到第一个数据框中,与第二个数据框中匹配的行的值作为条件,然后替换这一行的其他指定列的值。


> replace_df<-function(df1,df2,keys,vals){
+     row1<-which(apply(mapply(match,df1[,keys],df2[,keys])>0,1,all))
+     row2<-which(apply(mapply(match,df2[,keys],df1[,keys])>0,1,all))
+     df1[row1,vals]<-df2[row2,vals]
+     return(df1)
+ }

# 第一个数据框 
> df1<-data.frame(A=c(1,2,3,4),B=c('a','b','c','d'),C=c(0,4,0,4),D=1:4);df1
  A B C D
1 1 a 0 1
2 2 b 4 2
3 3 c 0 3
4 4 d 4 4

# 第二个数据框 
> df2<-data.frame(A=c(1,3),B=c('a','c'),C=c(9,9),D=rep(8,2));df2
  A B C D
1 1 a 9 8
2 3 c 9 8

# 定义匹配条件列 
> keys=c("A","B")

# 定义替换的列
> vals=c("C","D")

# 数据替换
> replace_df(df1,df2,keys,vals)
  A B C D
1 1 a 9 8
2 2 b 4 2
3 3 c 9 8
4 4 d 4 4

其实不管R语言中,各种内置的功能函数有多少,自己做在数据处理的时候,都要自己构建很多DIY的函数。

3.3 长表和宽表变换

长宽其实是一种类对于标准表格形状的描述,长表变宽表,是把一个行数很多的表,让其行数减少,列数增加,宽表变长表,是把一个表格列数减少行数增加。

长表变宽表,指定program列不动,用fun列的每一行,生成新的列,再用time列的每个值进行填充。


# 创建数据框
> df<-data.frame(
+     program=rep(c('R','Java','PHP','Python'),3),
+     fun=rep(c('fun1','fun2','fun3'),each = 4),
+     time=round(rnorm(12,10,3),2)
+ );df
   program  fun  time
1        R fun1 10.91
2     Java fun1  6.59
3      PHP fun1  9.26
4   Python fun1 11.17
5        R fun2 12.27
6     Java fun2  6.61
7      PHP fun2  7.28
8   Python fun2  9.39
9        R fun3  9.22
10    Java fun3 11.20
11     PHP fun3 13.40
12  Python fun3 10.67

# 加载reshape2库
> library(reshape2)

# 长表变宽表
> wide <- reshape(df,v.names="time",idvar="program",timevar="fun",direction = "wide");wide
  program time.fun1 time.fun2 time.fun3
1       R     10.91     12.27      9.22
2    Java      6.59      6.61     11.20
3     PHP      9.26      7.28     13.40
4  Python     11.17      9.39     10.67

接下来,进行反正操作,把宽表再转换为长表,还是使用reshape()函数。


# 宽表变为长表
> reshape(wide, direction = "long")
            program  fun  time
R.fun1            R fun1  8.31
Java.fun1      Java fun1  8.45
PHP.fun1        PHP fun1 10.49
Python.fun1  Python fun1 10.45
R.fun2            R fun2  8.72
Java.fun2      Java fun2  4.15
PHP.fun2        PHP fun2 11.47
Python.fun2  Python fun2 13.25
R.fun3            R fun3 10.10
Java.fun3      Java fun3 13.86
PHP.fun3        PHP fun3  9.96
Python.fun3  Python fun3 14.64

我们在宽表转换为长表时,可以指定想转换部分列,而不是所有列,这样就需要增加一个参数进行控制。比如,只变换time.fun2,time.fun3列到长表,而不变换time.fun1列。


> reshape(wide, direction = "long", varying =3:4)
       program time.fun1  time id
1.fun2       R      8.31  8.72  1
2.fun2    Java      8.45  4.15  2
3.fun2     PHP     10.49 11.47  3
4.fun2  Python     10.45 13.25  4
1.fun3       R      8.31 10.10  1
2.fun3    Java      8.45 13.86  2
3.fun3     PHP     10.49  9.96  3
4.fun3  Python     10.45 14.64  4

这样子的转换变形,是非常有利于我们从多角度来看数据的。

3.4 融化

融化,用于把以列进行分组的数据,转型为按行存储,对应数据表设计的概念为,属性表设计。

我们设计一下标准的二维表结构,然后按属性表的方式进行转换。


# 构建数据集
> df<-data.frame(
+   id=1:10,
+   x1=rnorm(10),
+   x2=runif(10,0,1)
+ );df
   id          x1          x2
1   1  1.78375335 0.639933473
2   2  0.26424700 0.250290845
3   3 -1.83138689 0.963861236
4   4 -1.77029220 0.451004465
5   5 -0.92149552 0.322621217
6   6  0.88499153 0.697954226
7   7  0.68905343 0.002045145
8   8  1.35269693 0.765777220
9   9  0.03673819 0.908817646
10 10  0.49682503 0.413977373

# 融合,以id列为固定列
> melt(df, id="id")
   id variable        value
1   1       x1  1.783753346
2   2       x1  0.264247003
3   3       x1 -1.831386887
4   4       x1 -1.770292202
5   5       x1 -0.921495517
6   6       x1  0.884991529
7   7       x1  0.689053430
8   8       x1  1.352696934
9   9       x1  0.036738187
10 10       x1  0.496825031
11  1       x2  0.639933473
12  2       x2  0.250290845
13  3       x2  0.963861236
14  4       x2  0.451004465
15  5       x2  0.322621217
16  6       x2  0.697954226
17  7       x2  0.002045145
18  8       x2  0.765777220
19  9       x2  0.908817646
20 10       x2  0.413977373

这个操作其实在使用ggplot2包画图时,会被经常用到。因为ggplot2做可视化时画多条曲线时,要求的输入的数据格式必须时属性表的格式。

3.5 周期分割

周期分割,是基于时间序列类型数据的处理。比如黄金的交易,你可以用1天为周期来观察,也可以用的1小时为周期来观察,也可以用1分钟为周期来看。

下面我们尝试先生成交易数据,再对交易数据进行周期的分割。本例仅为周期分割操作的示范,数据为随机生成的,请不要对数据的真实性较真。


# 加载xts包
> library(xts)

# 定义生成每日交易数据函数
> newTick<-function(date='2017-01-01',n=30){
+   newDate<-paste(date,'10:00:00')
+   xts(round(rnorm(n,10,2),2),order.by=as.POSIXct(newDate)+seq(0,(n-1)*60,60))
+ }

假设我们要生成1年的交易数据,先产生1年的日期向量,然后循环生成每日的数据。


# 设置交易日期
> dates<-as.Date("2017-01-01")+seq(0,360,1)
> head(dates)
[1] "2017-01-01" "2017-01-02" "2017-01-03" "2017-01-04" "2017-01-05" "2017-01-06"

# 生成交易数据
> xs<-lapply(dates,function(date){
+   newTick(date)
+ })

# 查看数据静态结构
> str(head(xs,2))
List of 2
 $ :An ‘xts’ object on 2017-01-01 10:00:00/2017-01-01 10:29:00 containing:
  Data: num [1:30, 1] 9.98 9.2 10.21 9.08 7.82 ...
  Indexed by objects of class: [POSIXct,POSIXt] TZ: 
  xts Attributes:  
 NULL
 $ :An ‘xts’ object on 2017-01-02 10:00:00/2017-01-02 10:29:00 containing:
  Data: num [1:30, 1] 9.41 13.15 6.07 10.12 10.37 ...
  Indexed by objects of class: [POSIXct,POSIXt] TZ: 
  xts Attributes:  
 NULL

# 转型为xts类型 
> df<-do.call(rbind.data.frame, xs)
> xdf<-as.xts(df)
> head(xdf)
                       V1
2017-01-01 10:00:00  9.98
2017-01-01 10:01:00  9.20
2017-01-01 10:02:00 10.21
2017-01-01 10:03:00  9.08
2017-01-01 10:04:00  7.82
2017-01-01 10:05:00 10.47

现在有了数据,那么我们可以对数据日期,按周期的分割了,从而生成开盘价、最高价、最低价、收盘价。这里一样会用到xts包的函数。关于xts类型的详细介绍,请参考文章 可扩展的时间序列xts


# 按日进行分割,对应高开低收的价格
> d1<-to.period(xdf,period='days');head(d1)
                    xdf.Open xdf.High xdf.Low xdf.Close
2017-01-01 10:29:00     9.98    13.74    5.35     13.34
2017-01-02 10:29:00     9.41    13.54    6.07      9.76
2017-01-03 10:29:00    12.11    13.91    7.16     10.75
2017-01-04 10:29:00    10.43    14.02    6.31     12.10
2017-01-05 10:29:00    11.51    13.97    6.67     13.97
2017-01-06 10:29:00    10.57    12.81    4.30      5.16

# 按月进行分割
> m1<-to.period(xdf,period='months');m1
                    xdf.Open xdf.High xdf.Low xdf.Close
2017-01-31 10:29:00     9.98    16.40    3.85     10.14
2017-02-28 10:29:00     8.25    16.82    4.17     11.76
2017-03-31 10:29:00    10.55    15.54    2.77      9.61
2017-04-30 10:29:00     9.40    16.13    3.84     11.77
2017-05-31 10:29:00    13.79    16.74    3.97     10.25
2017-06-30 10:29:00     9.29    16.15    4.38      7.92
2017-07-31 10:29:00     5.39    16.09    4.55      9.88
2017-08-31 10:29:00     5.76    16.34    3.27     10.86
2017-09-30 10:29:00     9.56    16.40    3.58     10.09
2017-10-31 10:29:00     8.64    15.50    3.23     10.26
2017-11-30 10:29:00     9.20    15.38    3.00     10.92
2017-12-27 10:29:00     6.99    16.22    3.87      8.87

# 按7日进行分割
> d7<-to.period(xdf,period='days',k=7);head(d7)
                    xdf.Open xdf.High xdf.Low xdf.Close
2017-01-07 10:29:00     9.98    15.54    4.30     10.42
2017-01-14 10:29:00    11.38    14.76    5.74      9.17
2017-01-21 10:29:00     9.57    16.40    3.85     11.91
2017-01-28 10:29:00    10.51    14.08    4.66     10.97
2017-02-04 10:29:00    10.43    16.69    4.53      6.09
2017-02-11 10:29:00    11.98    15.23    5.04     11.57

最后,通过可视化把不同周期的收盘价,画到一个图中。


> plot(d1$xdf.Close)
> lines(d7$xdf.Close,col='red',lwd=2)
> lines(m1$xdf.Close,col='blue',lwd=2)

从图中,可以看出切换为不同的周期,看到的形状是完全不一样的。黑色线表示以日为周期的,红色线表示以7日为周期的,蓝色线表示以月为周期的。

从本文的介绍来看,要做好数据处理是相当不容易的。你要知道数据是什么样的,业务逻辑是什么,怎么写程序以及数据变形,最后怎么进行BI展示,表达出正确的分析维度。试试R语言,忘掉程序员的思维,换成数据的思维,也许繁琐的数据处理工作会让你开心起来。

本文所介绍的数据处理的方法,及个性化的功能函数,我已经发布为一个github的开源项目,项目地址为:https://github.com/bsspirit/RTransform 欢迎大家试用,共同完善。

转载请注明出处:
http://blog.fens.me/r-transform/

打赏作者

2017CDAS中国数据分析师行业峰会:用R语言解读股利贴现模型

跨界知识聚会系列文章,“知识是用来分享和传承的”,各种会议、论坛、沙龙都是分享知识的绝佳场所。我也有幸作为演讲嘉宾参加了一些国内的大型会议,向大家展示我所做的一些成果。从听众到演讲感觉是不一样的,把知识分享出来,你才能收获更多。

关于作者:

  • 张丹(Conan), 程序员R,Nodejs,Java
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/meeting-cdas-20170729

前言

今年的数据分析师大会,国贸,中国大饭店,高规格,上档次!虽然只有1天,却吸引了3000+人的报名参会。11个分会场都是从数据角度来切入,包括了 大数据与生物医疗,大数据与云计算,互联网大数据,电商大数据,大数据与金融,大数据与人工智能,数据可视化与商业BI,大数据与交通旅游,大数据与智慧投资,数据库技术与实战,CDA数据分析师专场。

我的分享在 大数据与金融专场,见到了很多熟悉的朋友,同时也认识不少的新朋友。希望大家能够学到知识,并真正地落地到实际的工作中来。

我已经连续参加了3年的CDAS中国数据分析师行业峰会,祝这个数据的大会越办越好。前2年数据分析师大会会议纪要:2016数据分析师大会2015数据分析师大会

目录

  1. 我的演讲主题:用R语言解读股利贴现模型
  2. 会议体验和照片分享

1. 我的演讲主题:用R语言解读股利贴现模型

用R语言解读股利贴现模型,PPT下载,主要内容来自我的一篇博文:用R语言解读股利贴现模型(未发布)

本次分享我详细讲述了,股利贴现模型的原理和方法,并用这个模型分析招商银行(600036.SH)股票,最后用程序来实现。如果你按照我的思路去操作,相信也能很快找到被低估的股票,从而赚到靠能力可以赚到的钱。

本次分享的目录:

  1. 发现错误的定价
  2. 股利贴现模型
  3. 投资机会
  4. A股市场案例分析
  5. 用R语言实现

为了本次的分享,我花了2周的时间进行准备。希望能够给大家分享一个,实用的模型,这样听完了就可以回去动手实验了。由于分享时间比较短,而且又有不少的金融专业知识,要在30分内给大家讲一个新东西,确实很难啊,我也是挑战了一下自己。

我一直延续了一贯的演讲风格,有内容,有图片,有代码,有互动。从方法理论的思路开始,到市场特征检验,再到数学公式,R语言建模,把知识和市场操作联系起来,听完我的分享,你回去把上就可以动手实践。利用IT人的技术优势,可以真正地与实际操作结合起来,实现从IT技术到价值的转变。

2. 会议体验和照片分享

这次的大会虽然只有1天,也能看出来主办方准备充分。不得不说一句,所有的工作人员辛苦了!

“跨界互联,数聚未来”是本次会议的主题,会议主页:http://cdas.cda.cn/。以数据为题,研习技术,比拼创意,交流思想,探寻未来,打造一场大数据与大思维的盛筵。

2.1 大数据与金融场,我是第4位分享嘉宾。

  • 李峰,IBM Analytics LBS首席数据科学家,主题:人工智能助力银行审计管理
  • 于晓松,诸葛io产品VP,主题:深入金融场景的数据驱动与应用
  • 郑志勇,集思录副总裁,主题:资产配置与数据分析
  • 张丹,《R的极客理想》系列图书作者,主题:用R语言实现量化交易策略
  • 雷涛,天云大数据CEO,主题:Fintech实践:从BI到AI的演进路径
  • 赵刚,北京赛智时代信息技术咨询有限公司CEO,主题:“双创”大数据金融分析服务

我在分享的照片

其他嘉宾的照片

李峰

于晓松

郑志勇

金融会场照片

2.2 会议相关照片

大会开幕式

精彩瞬间

工作人员

最后,感谢CDAS工作人员的辛苦劳动,希望保持高水平会议越办越好!

转载请注明出处:
http://blog.fens.me/meeting-cdas-20170729

打赏作者

2017微软MVP:在AWS上部署免费的Shiny应用

跨界知识聚会系列文章,“知识是用来分享和传承的”,各种会议、论坛、沙龙都是分享知识的绝佳场所。我也有幸作为演讲嘉宾参加了一些国内的大型会议,向大家展示我所做的一些成果。从听众到演讲感觉是不一样的,把知识分享出来,你才能收获更多。

关于作者

  • 张丹, 程序员R,Nodejs,Java
  • weibo:@Conan_Z
  • blog:http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/meeting-mvp-20170723

前言

第一次参加微软的技术日活动,感觉微软已经变得开放了,已不是10年前我所印象的对开源很敌对的微软了。在微软的会场,我主要介绍的是开源技术R语言。

本次的我的主题是AWS和Shiny,Shiny是R语言中一个“神级”的,绝对是好东西;而AWS为全球的开发者提供了免费的服务器,值得赞赏。把这两个好的东西结合在一起,就是一个很好的业务模式,可以极大的帮助个人开发者,来发布自己的作品或产品。

本次所分享的内容为一个实战案例,你可以跟着我的操作,重现我所讲的总有内容。错过沙龙的同学,也可以在天善社区的网站上找到分享的内容。

目录

  1. 我的演讲主题:在AWS上部署免费的Shiny应用
  2. 会议体验和照片分享

1. 我的演讲主题:在AWS上部署免费的Shiny应用

在AWS上部署免费的Shiny应用,PPT下载,主要内容来自我的一篇博文:在AWS上部署免费的Shiny应用

分享主题的目录大纲如下:

  1. Shiny是什么?
  2. 本地开发一个Shiny小应用
  3. 申请AWS免费服务器
  4. 在AWS上安装R语言环境
  5. 在AWS上安装Shiny Server
  6. 在AWS上部署自己的Shiny应用
  7. 番外篇

前6个目录都是正常的技术,番外篇,其实是比较有意思的一个内容。原本我是准备把一个基于赌场原型的Shiny应用放到互联网,考虑服务器位置和选型的问题,无意中发现了AWS的免费资源,这样就有了这样的一篇Shiny与AWS结合的文章。

提问环节时,同学们很热列问了不少的R和Shiny的问题。真的希望大家,动手试一下,R语言一定会给你很棒的体验的。

分享结束后,和几位现场的朋友,聊了中国市场环境与发展机会。感觉到各行各业的人,都体会到了大数据所带来的变化,准备进行大数据的市场,获得更多的商业机会。我觉得中国是有大数据土壤的,而且环境在变好,有越来越多的机会,给留我们去创新和发展。

同时,我也在致力于推动R语言在中国金融领域的发展,让R可以给更多的用户使用,培养出更多的数据分析师。也希望让我们中国人的技术能够走出去到世界的舞台。希望多能认识志同道合的朋友,一起做一些事情。

2. 会议体验和照片分享

本次由5个主题组成,主要是微软的产品技术介绍,毕竟是在人家的主场。

本次微软MVP参加分享的5位嘉宾,主持人 和 微软MVP项目负责人介绍:

吕品,活动介绍 & 现场主持
天善智能联合创始人,微软MVP

Christina,主题:微软项目介绍
China MVP Community Manager

张丹,主题:在AWS上部署免费的Shiny应用,PPT下载
《R的极客理想》系列图书作者,前况客创始人兼CTO。12年IT编程背景,精通R ,Java, Nodejs 编程,获得10项SUN及IBM技术认证。著有《R的极客理想-工具篇》、《R的极客理想-高级开发篇》,合著《数据实践之美》,新书《R的极客理想-量化投资篇》。

刘凯,主题:PowerBI高阶分析:高阶预测分析;DAX万能组合函数的高级应用
IMA中国教育委员会认证讲师,擅长业财融合背景下的数据整合和运营财务分析。曾任职于4A的奥美互动咨询和四大中的KPMG毕博管理咨询,服务于海尔金控等多家客户。

宋卫东,主题:数据仓库运维那些事
从事商业智能数据仓库方向11年,就职某汽车公司,负责数据仓库和大数据等项目。

李奇,主题:Excel BI:无所不能的业务数据分析利器
中国电子表格应用大会主席、曾任职于IBM及德勤会计师事务所,从事业务分析及数据分析咨询工作,Excel BI培训讲师、擅长用Excel创建商业智能报表。

宋沄剑,主题:亿级SQL Server运维的n个最佳实践,PPT下载
微软DataPlatform MVP(since 2012),目前就职于易车网,负责易车数据库与基于Apache生态圈的大数据平台的运维。曾任数据库高级顾问,帮助国内多家客户设计高可用/灾备方案,监控方案、运维自动化方案,并处理SQL Server企业应用实施、日常管理中常见的棘手问题。曾翻译包括SQLServer、PowerShell、Python在内的多本技术书籍,个人技术博客在国内最大的微软社区排名考前,曾多次在数据库大会、Teched、Ignite上分享经验。

嘉宾在分享的照片。

张丹

刘凯

李奇

Christina

宋卫东

宋沄剑

2.2 会议相关照片

本次的场地在微软的北京研发中心,忍不住要说会场很棒,不差钱就是好,干净、整齐、大方。

现场照片

大合照

沙龙很成功,感谢天善社区的工作人员,微软主办方的工作人员,IT大咖说直播的工作人员,大家都辛苦了!祝高质的沙龙,在北京越办越好,让无数有理想的年轻人,能开阔眼界,了解行业动态,推动知识的进步。

转载请注明出处:
http://blog.fens.me/meeting-mvp-20170723

打赏作者

在AWS上部署免费的Shiny应用

R的极客理想系列文章,涵盖了R的思想,使用,工具,创新等的一系列要点,以我个人的学习和体验去诠释R的强大。

R语言作为统计学一门语言,一直在小众领域闪耀着光芒。直到大数据的爆发,R语言变成了一门炙手可热的数据分析的利器。随着越来越多的工程背景的人的加入,R语言的社区在迅速扩大成长。现在已不仅仅是统计领域,教育,银行,电商,互联网….都在使用R语言。

要成为有理想的极客,我们不能停留在语法上,要掌握牢固的数学,概率,统计知识,同时还要有创新精神,把R语言发挥到各个领域。让我们一起动起来吧,开始R的极客理想。

关于作者:

  • 张丹(Conan), 程序员Java,R,PHP,Javascript
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/r-shiny-aws/

前言

无意中发现了AWS的提供免费的服务器资源,必须要大赞一下,写篇文章大大的推广。

Shiny是R语言中一个神级的应用,唯一的缺点就是不支持并发。所以,我们很多时候都是做本地Shiny应用,用于展示各种报表的效果。但有时候也需要把报表上传到互联网上,其他人也能看到。这样就需要一个互联网的解决方案,刚好AWS有了免费的服务器支持。简直是完美!!

目录

  1. Shiny是什么?
  2. 本地开发一个Shiny小应用
  3. 申请AWS免费服务器
  4. 在AWS上安装R语言环境
  5. 在AWS上安装Shiny Server
  6. 在AWS上部署自己的Shiny应用

1. Shiny是什么?

Shiny是RStudio公司开发的,一个用于R语言的Web应用程序框架,可以轻松开发交互式web应用,不需要了解HTML, CSS, JS等前端知识。

Shiny构建出应用的惊艳程度,远远超过了说明的文字。一定要学学,下面是一个Shiny小程序的截图。

Shiny的主页:http://shiny.rstudio.com/

我们安装Shiny可以直接从CRAN获取,通过一行R程序就可以安装了。


~ R
> install.packages("shiny")

2. 本地开发一个Shiny小应用

下面我们用Shiny开发一个小应用的实例,主要是为介绍Shiny的用法,包括网页的界面UI和后端程序,数据源使用R语言自带的一个数据集。

数据集是faithful,统计的是美国黄石国家公园的泉水(Old Faithful geyser) 喷发的持续时间和喷发等待时间 。

包括2列,eruptions为喷发持续时间,waiting为喷发的等待时间。


> head(faithful,20)
   eruptions waiting
1      3.600      79
2      1.800      54
3      3.333      74
4      2.283      62
5      4.533      85
6      2.883      55
7      4.700      88
8      3.600      85
9      1.950      51
10     4.350      85
11     1.833      54
12     3.917      84
13     4.200      78
14     1.750      47
15     4.700      83
16     2.167      52
17     1.750      62
18     4.800      84
19     1.600      52
20     4.250      79

开发环境所使用的系统环境

  • Win10 64bit
  • R: 3.2.3 x86_64-w64-mingw32/x64 b4bit

Shiny应用,分为定义了客户端程序ui.R,和服务器端程序server.R,这2个文件默认要求放同一个目录中。另外,我们还需要一个启动文件run.R,用于启动Shiny的应用。当然,如果在RStudio中开发,就不需要run.R的文件,直接点Shiny应用的启动按钮就行了。

开始创建项目目录和文件。


~ cd D:\workspace\dash
~ mkdir demo1
~ notepad run.R
~ cd demo1
~ notepad server.R
~ notepad ui.R

目录结构如下:


D:\workspace\dash
|--run.R
|--demo1
   |--server.R
   |--ui.R

编辑文件:run.R


library("shiny")
runApp("./demo1",host='127.0.0.1',port=3840)

编辑文件:server.R


library(shiny)

shinyServer(function(input, output) {

  # 输出到UI的main_plot
  output$main_plot <- renderPlot({
    
    # 直方图
    hist(faithful$eruptions,
         probability = TRUE,
         breaks = as.numeric(input$n_breaks),
         xlab = "持续时间",
         main = "喷发持续时间")
    
    # 是否显示individual_obs
    if (input$individual_obs) {
      rug(faithful$eruptions)
    }
    
    # 是否显示conditionalPanel
    if (input$density) {
      dens <- density(faithful$eruptions, adjust = input$bw_adjust)
      lines(dens, col = "blue")
    }
    
  })
})

编辑文件:ui.R


library(shiny)
shinyUI(bootstrapPage(
  
  headerPanel("第一个Shiny应用"),
  
  # 左侧布局
  sidebarPanel(
    
    # 下拉框
    selectInput(inputId = "n_breaks",label = "直方图中的分隔数",choices = c(10, 20, 35, 50),selected = 20),
    
    # 单选框
    checkboxInput(inputId = "individual_obs",label = strong("实际观察点"),value = FALSE),
    
    # 单选框
    checkboxInput(inputId = "density",label = strong("密度估计曲线"),value = FALSE)
  ),
  
  # 主布局
  mainPanel(
    plotOutput(outputId = "main_plot", height = "300px"),
    
    conditionalPanel(condition = "input.density == true",
                     sliderInput(inputId = "bw_adjust",label = "带宽调整", min = 0.2, max = 2, value = 1, step = 0.2)
    )
  )
))

启动Shiny应用时,本地的3840端口,就被打开了。


~ D:\workspace\dash>R -f run.r
R version 3.2.3 (2015-12-10) -- "Wooden Christmas-Tree"
Copyright (C) 2015 The R Foundation for Statistical Computing
Platform: x86_64-w64-mingw32/x64 (64-bit)
R
'license()''licence()'
R.
'contributors()'
'citation()'RR
'demo()''help()'
'help.start()'HTML
'q()'R.

> library("shiny")
> runApp("./demo1",host='127.0.0.1',port=3840)

Listening on http://127.0.0.1:3840

我们可以用浏览器,来访问本地的服务 http://127.0.0.1:3840 。

这是一个标准的Web网页,如果我们操作网页上的表单元素,对应该的数据也会发生变化了。这样我们就完成了一个本的Shiny的小应用的开发,接下来就是把这个程序部署到AWS上面了。

3. 申请AWS免费服务器

AWS是Amazon提供的一个云服务平台,利用亚马逊 AWS,软件开发人员可以轻松购买计算、存储、数据库和其他基于 Internet 的服务来支持其应用程序。开发人员能够灵活选择任何开发平台或编程环境,以便于其尝试解决问题。由于开发人员只需按使用量付费,无需前期资本支出,亚马逊 AWS 是向最终用户交付计算资源、保存的数据和其他应用程序的一种最经济划算的方式。

AWS有一个免费的套餐,让互联网用户可以免费的使用他的资源,包括了服务器,数据库,CDN,负载均衡等服务。我们为了部署自己的Shiny应用,可以申请免费的服务器资源,先跑一下,看看效果。

首先,你需要注册一个AWS账号,然后登录进去,选择地区,申请免费的服务器。目前免费开放的区域不包括中国区,我选择了一个日本东京的服务器,Ubuntu Linux 64bit。

免费的资源,有一些限制,只能1核心CPU,1G内存,最大30G存储等。当然了,有免费的资源,已经是很棒的了,而且是AWS的服务。

接下来,就是做资源配置,然后就可以启动服务器了。

大概等3分钟,服务器启动完成,然后就可以通过SSH进行访问了。

AWS为了保证安全性,建议使用秘钥访问,而不是直接的用户名和密码的方式,所以你需要创建一个秘钥对,下载一个xx.pem的私钥,然后配到Putty或XShell等用于远程登录的客户端里。

然后,就可以用XShell连接,免费创建的EC2的服务器了。在这里,如果是Ubuntu的Linux服务器,用户名需要使用ubuntu,不能直接设置root。

4. 安装R语言环境

登录后,我们就可以安装R语言的环境了。安装过程比较简单,如果你需要装指定版本的R软件,那么你需要参考文章,R的历史版本安装。如果是安装默认版本的R语言环境,直接使用是apt-get命令就是最方便的。

服务器所使用的系统环境

  • Linux Ubuntu 16.04.2 LTS 64-bit
  • R: 3.2.3 x86_64-pc-linux-gnu (64-bit)

我们先更新apt的软件源,安装必备的系统软件,包括r-base和git,以及的Shiny应用的依赖库libcurl4-openssl-dev,libxml2-dev。如果你忘了装了,后面再装也都不影响。


~ sudo apt-get update
~ sudo apt-get install r-base
~ sudo apt-get install git
~ sudo apt-get install libcurl4-openssl-dev
~ sudo apt-get install libxml2-dev

接下来,让我们安装R语言的依赖包。这里有一个小技巧,就是在R语言的环境中安装第三方R包,并切换成root用户。


~ sudo -i  # 切换成root用户
~ R        # 启动R语言环境

我们需要预装的包,主要就是shiny,当然如果你还有依赖其他的包,都可以一块安装。安装时,R会让我们选择软件源,如果用https协议的镜像列表,你需要配置一下curl进行下载。你依然可以选择用http协议的镜像列表,选61之后,会出现http的镜像列表。

我们的服务器在Tokyo,所以也选择Tokyo的镜像列表,然后开始下载R的第三方软件包。


# 安装包
> install.packages("shiny")

Installing package into ‘/usr/local/lib/R/site-library’
(as ‘lib’ is unspecified)
--- Please select a CRAN mirror for use in this session ---
HTTPS CRAN mirror 

 1: 0-Cloud [https]                 2: Algeria [https]              
 3: Australia (Canberra) [https]    4: Australia (Melbourne) [https]
 5: Australia (Perth) [https]       6: Austria [https]              
25: France (Montpellier) [https]   26: France (Paris 2) [https]     
27: Germany (Göttingen) [https]    28: Germany (Münster) [https]    
29: Greece [https]                 30: Iceland [https]              
31: India [https]                  32: Indonesia (Jakarta) [https]  
33: Ireland [https]                34: Italy (Padua) [https]        
35: Japan (Tokyo) [https]          36: Malaysia [https]             
61: (HTTP mirrors)                 

Selection: 61

HTTP CRAN mirror 

 1: 0-Cloud                       2: Algeria                    
 3: Argentina (La Plata)          4: Australia (Canberra)       
 5: Austria                       6: Belgium (Antwerp)          
 7: Belgium (Ghent)               8: Brazil (BA)                
 9: Brazil (PR)                  10: Brazil (RJ)                
11: Brazil (SP 1)                12: Brazil (SP 2)              
13: Bulgaria                     14: Canada (BC)                
37: Hungary                      38: Iceland                    
39: India                        40: Iran                       
41: Ireland                      42: Italy (Milano)             
43: Italy (Padua)                44: Italy (Palermo)            
45: Japan (Tokyo)                46: Korea (Seoul 1)            
47: Korea (Seoul 2)              48: Korea (Ulsan)                          
89: USA (TX 1)                   90: Venezuela   

Selection: 45

顺利安装完R的依赖包,接下来就是要安装Shiny Server了。Shiny Server是一个单独的软件,目前还不支持通过apt-get或R本身进行安装,需要下载安装。

5. 安装Shiny Server

在开发时,我们其实只是用到了shiny的R语言第三方包,可以在本地的开发环境,运行Shiny的程序。那么,如果把一个Shiny放到公司内网或外网给其他人用呢?这时就是需要Shiny Server了。

Shiny Server提供一个稳定的Shiny应用在线的运行环境,Shiny Server分成开源版本和企业版本。开源版本,提供了基本的Shiny功能,数据、可视化、运行环境,对于个人来说已经足够用了,而且非常友好。企业版本,提供安全和管理功能添加到基本的开源版本中。RStudio公司出品,必属精品!!

Shiny Server是一个单独的软件,我们需要下载进行安装,下载地址:https://www.rstudio.com/products/shiny/shiny-server/

在Ubuntu的环境中,我们可以通过下面的命令,进行下载和安装。


~ sudo apt-get install gdebi-core
~ wget https://download3.rstudio.org/ubuntu-12.04/x86_64/shiny-server-1.5.3.838-amd64.deb
~ sudo gdebi shiny-server-1.5.3.838-amd64.deb

Reading package lists... Done
Building dependency tree        
Reading state information... Done
Reading state information... Done

Shiny Server
 Shiny Server is a server program from RStudio, Inc. that makes Shiny applications available over the web. Shiny is a web application framework for the R statistical computation language.
Do you want to install the software package? [y/N]:y
Selecting previously unselected package shiny-server.
(Reading database ... 63176 files and directories currently installed.)
Preparing to unpack shiny-server-1.5.3.838-amd64.deb ...
Unpacking shiny-server (1.5.3.838) ...
Setting up shiny-server (1.5.3.838) ...
Creating user shiny
Adding LANG to /etc/systemd/system/shiny-server.service, setting to en_US.UTF-8
Created symlink from /etc/systemd/system/multi-user.target.wants/shiny-server.service to /etc/systemd/system/shiny-server.service.
● shiny-server.service - ShinyServer
   Loaded: loaded (/etc/systemd/system/shiny-server.service; enabled; vendor preset: enabled)
   Active: active (running) since Thu 2017-07-06 07:40:24 UTC; 6ms ago
  Process: 25322 ExecStartPost=/bin/sleep 3 (code=exited, status=0/SUCCESS)
 Main PID: 25325 (shiny-server)
    Tasks: 12
   Memory: 35.4M
      CPU: 411ms
   CGroup: /system.slice/shiny-server.service
           ├─25321 /bin/bash -c /opt/shiny-server/bin/shiny-server --pidfile=/var/run/shiny-server.pid >> /var/log...
           └─25325 /opt/shiny-server/ext/node/bin/shiny-server /opt/shiny-server/lib/main.js --pidfile=/var/run/sh...

Jul 06 07:40:21 ip-172-31-31-236 systemd[1]: Starting ShinyServer...
Jul 06 07:40:24 ip-172-31-31-236 systemd[1]: shiny-server.service: Supervising process 25325 which is not our...xits.
Jul 06 07:40:24 ip-172-31-31-236 systemd[1]: Started ShinyServer.
Hint: Some lines were ellipsized, use -l to show in full.

运行完安装的命令,默认情况Shiny Server会被直接启动起来,其中3838的端口被打开。

检查启动端口


~ netstat -nlpt
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -               
tcp        0      0 0.0.0.0:3838            0.0.0.0:*               LISTEN      -               
tcp6       0      0 :::22                   :::*                    LISTEN      -               

我们可以通过浏览器,直接基于IP和端口进行Shiny Server的访问了。

打开的页面是默认的Shiny Server的网页,如果和上面的截图一样,说明你的Shiny Server安装成功了。

提醒一下,AWS的EC2的主机,一定要配置网络访问策略,打开3838端口,允许外部访问,不然一直都是无法访问此网站的错误。

6. 部署自己的Shiny应用

还差最后一步,就是把我们自己开发的Shiny应用,部署到AWS的EC2上面。代码上传的过程,我们可以基于github来完成。

操作过程如下:

  1. 在github上面,新建一个项目,名为shiny-demo。
  2. 把本地开发的代码,上传到github的shiny-demo项目中。
  3. 在AWS的EC2上,从github的shiny-demo项目中,下载代码。
  4. 在AWS的EC2上,修改Shiny Server的配置,加载项目代码。
  5. 在AWS的EC2上,重启Shiny Server,发现错误。
  6. 在AWS的EC2上,查看日志修复错误。
  7. 在浏览器上访问,自己的Shiny应用。

6.1. 在github上面创建项目,名为shiny-demo。

github操作过程省略。项目地址 https://github.com/bsspirit/shiny-demo

6.2 把本地开发的代码,上传到github的shiny-demo项目中。

切换到本地开发的环境。


~ cd d:\workspace\dash
~ git init
~ git add .
~ git commit -m 'init'
~ git remote add origin https://github.com/bsspirit/shiny-demo.git
~ git push -u origin master  

6.3 在AWS的EC2上,从github的shiny-demo项目中,下载代码。

切换到服务器环境。


# 查看当前目录
~ pwd
/home/ubuntu

# 下载github项目
~ git clone https://github.com/bsspirit/shiny-demo
Cloning into 'shiny-demo'...
remote: Counting objects: 12, done.
remote: Compressing objects: 100% (11/11), done.
remote: Total 12 (delta 0), reused 12 (delta 0), pack-reused 0
Unpacking objects: 100% (12/12), done.
Checking connectivity... done.

# 查看项目文件
~ cd shiny-demo
~ tree
.
├── demo1
│   ├── server.R
│   └── ui.R
├── README.md
└── run.r

1 directory, 4 files

6.4 在AWS的EC2上,修改Shiny Server的配置,加载项目代码。

编辑shiny-server的配置文件shiny-server.conf。


~ sudo vi /etc/shiny-server/shiny-server.conf

# Instruct Shiny Server to run applications as the user "shiny"
run_as shiny;

# Define a server that listens on port 3838
server {
  listen 3838;

  # Define a location at the base URL
  location / {

    # Host the directory of Shiny Apps stored in this directory
    site_dir /srv/shiny-server;

    # Log all Shiny output to files in this directory
    log_dir /var/log/shiny-server;

    # When a user visits the base URL rather than a particular application,
    # an index of the applications available in this directory will be shown.
    directory_index on;
  }

  # 新增加指向github的代码位置
  location /demo1 {
    app_dir /home/ubuntu/shiny-demo/demo1;
    log_dir /var/log/shiny-server/demo1;
  }
}

增加 location /demo1 { } 的配置部分,用来把自己的Shiny的应用,在Shiny Server中进行注册。当然,对于不熟悉Shiny Server的配置的人,可以参考Shiny Server的管理员使用手册 http://docs.rstudio.com/shiny-server/

6.5 在AWS的EC2上,重启Shiny Server,发现错误。

重启Shiny Server,虽然只是重启,但经常出现错误。


~ sudo service shiny-server restart

# 对于Ubuntu 15.04+的系统,推荐用下面的命令。
~ sudo systemctl restart shiny-server

重启后,就可以在浏览器上,访问自己的Shiny应用了。

打开以后,出现了一个错误页面,这种情况太正常了。会出现各种各样的异常的情况,然后就需要我们反复进行调试了。

6.6 在AWS的EC2上,查看日志,修复错误。

主要的调试的方法,就是检查Shiny Server的日志。日志在设置上,有一个很大的坑,我也是在挠头搞了3个小时后才发现的。

由于Shiny Server为了保证性能,所以非敏感性的错误日志被设置了自动清除,每当你出现了错误,要去看日志定位问题的时候,这个日志就刚好被自动清除了。坑很大!!都开始怀疑人生了。

所以,你在调试时需要修改一个参数,保证日志不会被自动清除。

修改文件shiny-server.conf


~ sudo vi /etc/shiny-server/shiny-server.conf

run_as shiny;

access_log /var/log/shiny-server/access.log default;  # 增加记录访问
preserve_logs true;                                   # 禁止自动清除日志

# Define a server that listens on port 3838
server {
  listen 3838;

# 省略
}

再次重启Shiny Server。


~ sudo systemctl restart shiny-server

发现问题,检查日志,然后,我们对应日志的解决问题。


~ sudo cat /var/log/shiny-server/demo1/demo1-shiny-20170706-081120-42749.log 

Listening on http://127.0.0.1:42749
Warning: Error in if: argument is of length zero
Stack trace (innermost first):
    101: density.default
    100: density
     99: renderPlot [/home/ubuntu/shiny-demo/demo1/server.R#22]
     89: 
     78: plotObj
     77: origRenderFunc
     76: output$main_plot
      1: runApp

很多情况下,诡异的错误都是缺少第三方包造成的,当你程序中使用了第三包的时候,一直要记得在Shiny的服务器上面安装好。记得用root用户!!

总结一下,我们利用免费的AWS的EC2服务器资源,发布了自己的Shiny应用,是多么的开心啊!这样以后就可以大胆地去开发自己喜欢的Shiny应用了,当然不只是Shiny应用,你可以利用AWS的免费资源,做更多的事情。老司机,都明白的!!

原本是准备把一个基于赌场原型的Shiny应用放到互联网,考虑服务器位置和选型的问题,无意中发现了AWS的免费资源,这样就有了这样的一篇Shiny与AWS结合的文章。下一篇,要不要分析一下赌场的模型呢。

转载请注明出处:
http://blog.fens.me/r-shiny-aws/

打赏作者

2017猎奇金融大数据:用R语言开始量化投资

跨界知识聚会系列文章,“知识是用来分享和传承的”,各种会议、论坛、沙龙都是分享知识的绝佳场所。我也有幸作为演讲嘉宾参加了一些国内的大型会议,向大家展示我所做的一些成果。从听众到演讲感觉是不一样的,把知识分享出来,你才能收获更多。

关于作者

  • 张丹, 程序员R,Nodejs,Java
  • weibo:@Conan_Z
  • blog:http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/meeting-cda-20170521

前言

很高兴能够参加由CDA主办的数据分析师线下沙龙的活动,虽然活动规模不大,但是场子很棒,氛围很好。刚好这次沙龙是金融大数据的主题,也是为了给我的新书《R的极客理想-量化投资篇》预热,给大家分享一下R语言在量化投资领域的应用。

本次所分享的内容为我的新书开篇内容,干货多多,错过沙龙的同学,可以在CDA的网站上找到分享的内容。

目录

  1. 我的演讲主题:用R语言开始量化投资
  2. 会议体验和照片分享

1. 我的演讲主题:用R语言开始量化投资

用R语言开始量化投资,PPT下载,主要内容来自我的一篇博文:用R语言开始量化投资

分享主题的目录大纲如下:

  1. 为什么用R语言?
  2. 跨界结合的思维模式
  3. R语言量化工具包
  4. 量化策略实战应用
  5. 有理想的极客

分享结束后,和几个现场的朋友,在聊R语言的大方向,大家都觉得R语言有很大的机会,只是R语言应该如何落地呢?我觉得有很多的点和方向可以做。比如:培训教育,用R来替换SAS,R的企业服务,R的SAAS的个人服务等。

我就在致力于推动R语言在中国金融领域的发展,让R可以给更多的用户使用,培养出更多的数据分析师。也希望让我们中国人的技术能够走出去到世界的舞台。希望多能认识志同道合的朋友,一起做一些事情。

2. 会议体验和照片分享

本次由4个主题组成,主要介绍了金融大数据在国内的应用情况。本次的场地是由科技寺提供的,忍不住还要再说一够场面很棒,如果再能配上咖啡就更好了。

本次猎奇金融大数据专场4位嘉宾:

鲁四海,主题:大数据风控。
中国新一代IT产业推进联盟技术分委会秘书长、首席数据官联盟发起人。主要研究方向为大数据,参与编写了《影响中国大数据产业进程100人》,在中国新IT联盟、北达软讲授大数据技术应用课程。

张丹,主题:如何用R语言开始量化投资。
《R的极客理想》系列图书作者,前况客创始人兼CTO。10年IT编程背景,精通R ,Java, Nodejs 编程,获得10项SUN及IBM技术认证。

于洋,主题:金融大数据运营增长创新案例。
TalkingData增长部门总监,曾加入金山从事游戏数据分析及运营,2012年加入TalkingData,历任游戏业务咨询总监,金融业务咨询总监,现从事在零售,金融,航旅等方向的数据营销,运营及增长创新业务。

李金霞,主题:实战案例:数据驱动下的互联网营销。
神策数据数据分析师,曾就职于民生银行、百度人才、飞信,负责数据处理相关工作。2016年加入神策数据,主导客户包括纷享销客、网易七鱼、ofo、拉钩云人事、多盟等。

嘉宾在分享的照片。

曹鑫,主持人。

张丹,《R的极客理想》系列图书作者

鲁四海,

于洋,

李金霞,美女分析师

2.2 会议相关照片

本次分享的场地,很有特色,宽敞、明亮、很有创意、很舒服,像是咖啡厅,开放式的办公环境,很棒!

很意外地获得了一个CDA给的奖杯,收藏一下。

沙龙很成功,感谢工作人员的辛苦劳动组织。在北京以数据为主题的沙龙,并不是太多,祝CDA的活动能一直坚持品质,越办越好!

转载请注明出处:
http://blog.fens.me/meeting-cda-20170521

打赏作者