书名:R语言金融分析与建模
ISBN:978-7-115-57225-7
本书由人民邮电出版社发行数字版。版权所有,侵权必究。
您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。
我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。
如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。
著 严玉星
审 校 张敬信
责任编辑 胡俊英
人民邮电出版社出版发行 北京市丰台区成寿寺路11号
邮编 100164 电子邮件 315@ptpress.com.cn
网址 http://www.ptpress.com.cn
读者服务热线:(010)81055410
反盗版热线:(010)81055315
R既是统计、挖掘、计算、分析、制图等方面的工具,也是一个强大的开发与应用平台。几乎任何与数据相关的难题,都可以借助R语言来解决。而金融领域正是与数据密切相关的行业,可以通过R实现量化金融分析与建模。
本书系统地介绍了R的包与编程方法,并通过丰富的金融案例展示了R在金融分析和金融建模方面的应用。本书分为5篇,共30章,从R语言基础、金融模型及基础知识、数据及相关操作、R在金融建模中的应用和R高级技能几个主题出发,全面讲解了R在金融量化中的应用和技巧。
本书适合从事金融数据分析、金融量化建模的读者学习。通过阅读本书,读者将了解全球化的金融市场数据,学习多样的金融建模思想和解决方案。
严玉星,毕业于麦吉尔大学,获金融学博士学位。他有丰富的教学经验,教授过各类金融课程,如金融建模、期权和期货、投资组合理论、定量财务分析、企业融资和金融数据库。此外,他精通R、Python、SAS、MATLAB和C语言,是金融数据分析方面的专家。
张敬信,哈尔滨工业大学基础数学博士,哈尔滨商业大学数学与应用数学系主任,副教授。奉行终生学习理念,热爱编程,是一名R语言爱好者。在知乎开设“R&Python数据科学”“数学建模与数学实验”“高等数学学习”等专栏,所创作的内容深入浅出、通俗易懂,受到广泛好评。
大数据时代已经到来,如何基于数据完成科学、有效的决策是多数人应该学习和掌握的新技能。开源软件R是世界上流行的数据分析、统计计算及制图语言,几乎能够完成任何数据处理工作。近来R在编程语言排行榜的排名也逐渐上升,可见R的影响力和受欢迎程度都有提升。R可安装并运行于所有主流平台,为我们提供了成千上万的专业模块和实用工具,是数据挖掘、数据分析领域的好工具。
本书注重基础知识和实用技能,系统地介绍了R的特性和它的强大功能,引导读者由浅入深地学习R语言并掌握R在金融领域的应用。本书的作者不仅有丰富的教学经验,而且对R的金融应用有着深入的见解。
本书的读者对象如下:
本书的结构是基于作者丰富的教学经验和学生反馈来搭建的。全书分为5篇,共30章。
第1篇(第1~5章)是R语言基础,由浅入深地介绍了R语言的基础知识,包括如何下载和安装R、如何启动和退出R、一些基本的R命令、日期变量、循环语句、条件语句、用R作图以及一些常用的R包等内容。
第2篇(第6~10章)是金融模型及基础知识,介绍了金融财务相关的公式和知识点,并通过编写R程序完成基本的计算,同时结合R完成财务报表分析、资本资产定价模型,并讲解了多因子线性模型和夏普比率等知识点。
第3篇(第11~19章)是数据及相关操作,介绍了各种开源数据接口、如何获取数据、数据的处理及相关操作、矩阵、数据框和数据列、R和Excel的互动、读写二进制数据、字符串变量的操作等内容。
第4篇(第20~28章)是R在金融建模中的应用,介绍了各类检验及事件研究、期权定价模型、蒙特卡罗随机模拟法、投资组合理论、在险价值、信用风险、买卖价差、交易成本、流动性、文本处理、金融学相关的R包等内容。
第5篇(第29、30章)是R高级技能,介绍了如何用R对内容加密,如何用R读取压缩文件。
每章还提供了练习题,以便读者巩固当前章节所学的知识点,夯实基础。
虽然作者花了很多时间和精力去核对书中的文字、代码和图片,但因为时间仓促和水平有限,书中仍难免会有一些错误和纰漏。如果大家发现什么问题,恳请反馈给作者,他的邮箱是pyan@geneseo.edu。
本书由异步社区出品,社区(https://www.epubit.com/)为您提供相关资源和后续服务。
本书提供配套资源,请在异步社区本书页面中点击“配套资源”,跳转到下载界面,按提示进行操作即可。注意:为保证购书读者的权益,该操作会给出相关提示,要求输入提取码进行验证。
作者和编辑尽最大努力来确保书中内容的准确性,但难免会存在疏漏。欢迎您将发现的问题反馈给我们,帮助我们提升图书的质量。
当您发现错误时,请登录异步社区,按书名搜索,进入本书页面,点击“提交勘误”,输入错误信息,点击“提交”按钮即可。本书的作者和编辑会对您提交的错误信息进行审核,确认并接受后,您将获赠异步社区的100积分。积分可用于在异步社区兑换优惠券、样书或奖品。
我们的联系邮箱是contact@epubit.com.cn。
如果您对本书有任何疑问或建议,请您发邮件给我们,并请在邮件标题中注明本书书名,以便我们更高效地做出反馈。
如果您有兴趣出版图书、录制教学视频,或者参与图书翻译、技术审校等工作,可以发邮件给我们;有意出版图书的作者也可以到异步社区在线投稿(直接访问www.epubit.com/ selfpublish/submission即可)。
如果您所在的学校、培训机构或企业,想批量购买本书或异步社区出版的其他图书,也可以发邮件给我们。
如果您在网上发现有针对异步社区出品图书的各种形式的盗版行为,包括对图书全部或部分内容的非授权传播,请您将怀疑有侵权行为的链接发邮件给我们。您的这一举动是对作者权益的保护,也是我们持续为您提供有价值的内容的动力之源。
“异步社区”是人民邮电出版社旗下IT专业图书社区,致力于出版精品IT技术图书和相关学习产品,为作译者提供优质出版服务。异步社区创办于2015年8月,提供大量精品IT技术图书和电子书,以及高品质技术文章和视频课程。更多详情请访问异步社区官网https://www.epubit.com。
“异步图书”是由异步社区编辑团队策划出版的精品IT专业图书的品牌,依托于人民邮电出版社近40年的计算机图书出版积累和专业编辑团队,相关图书在封面上印有异步图书的LOGO。异步图书的出版领域包括软件开发、大数据、人工智能、测试、前端、网络技术等。
异步社区
微信服务号
在本章中,我们简单介绍一下如何下载及安装R,如何启动和退出R,R是否区分大小写,以及如何为变量赋值。简言之,我们假定读者是R初学者,如金融专业的学生,对R一无所知。对于有一定R基础的学员,他们可以快速地浏览本章的内容,或直接跳到下一章。在本章中,我们将讨论为变量赋值的不同方法。在本章末尾,我们还讨论了将R应用到金融学上的优势及阻碍。实际上,最大的阻碍是金融相关专业的学生对学习计算机编程的内在恐惧。为此,在本书中,我们从最简单的方法入手,主要的目的是消除这些学生内在的恐惧,以增强他们的信心。我坚信在不远的将来,用一门计算机语言编程是每个金融系的学生所必备的能力之一。
为安装R,我们有以下5个步骤。
第1步:访问R官网。
第2步:单击“Download R”。
第3步:选择一个靠近你的地址。
第4步:选择适当的软件(PC、Mac)。
第5步:单击“base”。
安装完毕后,R图标将出现在你的计算机桌面上,如图1-1所示。
图1-1 R图标
若要启动R,请双击计算机桌面上R图标(见图1-1)。若要退出R,只需键入q()即可。
> q() # 第一种方法退出R
以上语句中,#标志是指注释句的开始。
# 这是一个注释行
# > 这左边的大于号是 R 的命令提示符
当退出时,R软件将询问你“是否保存工作空间映像”(见图1-2)。这实际上是问你是否“要保持所生成的变量及自编函数以供将来使用”。此阶段对新手而言,只需回答否(No),即选择图1-2中的第2个选项。
图1-2 询问用户是否保存工作空间映像
还有另一种方式可以退出R,即通过单击R软件菜单上的“File”,再单击“Exit”即可。在退出R且不想保存我们的一些已赋值的变量和自写函数时,可以使用q("no")的命令。
> q("no") # 退出R不保存生成的变量和函数
> q("yes") # 退出R保持生成的变量和函数
为变量赋值,可以使用<−、=或−>。
> x <-10 # 为 x 赋值10
> y = 20 # 为 y 赋值20
> 30 -> z # 为 z 赋值30
> word <-"Hello " # word 是一字符变量(字符串)
若要显示变量的值,只需键入它的变量名即可。
> x<-10
> x
[1] 10
对R软件而言,我们不需要单独定义一个变量而可以直接为它赋值。
> fv <-100
R是区分大小写的,这意味着大写的和小写的
代表不同的变量。
> x <-10
> X
Error: object 'X' not found
以上的出错信息是指(大写的)'X'没能找到,原因是我们没有为其赋值。若要把几个R命令放在一起(同一行),可以使用分号(;)将它们隔开。
> fv <-10;pv <-100;n <-10;rate <-0.05
将一系列的值分配给一个向量,可以使用c()指令,这里c可理解为一列(英文为column)。
> x<-c(1,2.5,3.4,6.2)
如果向量内各个值之间的增量为1,起始值和终止值为和
,我们可以使用
:
。
> y <-1:50
此时,意味着给y赋值为“1,2,3…,49,50”。
我们也可以颠倒其顺序。
> z <-10:1
此时,意味着给z赋值为“10,9,8…,2,1”。
请尝试使用以下代码和打印以查看其值。
> x <-1.5: 10
有时我们需要列出所有变量。此时,可以使用ls()函数。
> ls()
当不再需要一个变量时,我们可以从内存中删除它。
> rm(x) # 删除x变量
若要同时删除多个变量时,我们用逗号来分隔它们。
> rm(x,y,pv) # 同时删除 x、 y 及 pv
若要删除所有变量,我们有以下代码。
> rm(list=ls()) # 删除所有变量
还有另一方法来删除所有对象(变量),即[单击]R 菜单上的"Misc",然后选择 "Remove all objects…"。
若要在屏幕上打印字符变量(字符串),我们可以使用cat()或print()函数。记住要把句子用双引号或单引号包括起来。在下面的语句中,\n为换行符号。
> cat("Hello World!\n\n\n")
Hello World!
>
然而,对print()函数而言,另起一行的符号"\n"是无效的,见下例。
> print("Hello R\n")
[1] "Hello R\n"
>
我们也可以打印已赋值的变量。
> x<-'this is great'
> print(x)
[1] "this is great"
>
当一个命令占用多行时,我们会看到+符号。假设我们打算将1~10赋值给x。
> x <-1:10 # 将1,2,......到 10赋值给 x
出于某些原因,在完成整个命令之前,我们不小心按下了回车键。这时+符号将显示。换句话说,我们使用了两行来完成该语句。
> x <-1: # 我们不小心按下回车键
+ 10 # 继续键入其余的命令
>
在编程时,往往会按错键,如双引号或单引号不匹配等。对初学者而言,尤其如此。很多时候,我们并不想弄清楚问题在哪里,因为找错可能比重新键入语句更为费时。我们只想回到R提示符并重新打入该命令。在这种情况下,按Esc键即可返回到R的提示符(<)。Esc键在键盘的左上角。
> x <-9"(999asdfklj
+
# 使用 Esc 重新回到 R 的提示符
>
我们有几种方法来找到有关特定函数的信息。如果我们想查找均值函数(mean)的信息,我们可用“?mean”“help(mean)”或“example(mean)”。
>?mean
help(mean)的命令可达到和“?mean”命令相同的目的。
> help(mean)
若要获取一个特定函数的有关实例,我们可使用example()函数。
> example(mean)
为了寻求帮助,我们也可以使用R菜单上的Help功能,即单击“Help”,然后选择“FAQ on R”。
FAQ的英文全拼为Frequently Asked Questions,意思为“常问的问题”。图1-3显示了单击R菜单上的“Help”后所显示的所有条目。
图1-3 Help菜单
当不确定函数的拼法时,可以使用apropos()函数。
> apropos("sd")
[1] ".F_dqrrsd" ".isMethodsDispatchOn"
[3] "assignClassDef" "completeClassDefinition"
[5] "getClassDef" "isClassDef"
[7] "isdebugged" "makePrototypeFromClassDef"
[9] "sd" "SSD"
[11] "superClassDepth" "tsdiag"
[13] "TukeyHSD" "UScitiesD"
>
若想我们的关键字(字母)在一词的开始时出现,我们使用“^关键字”,如apropos("^col")。若希望其在词尾出现用“关键字$”,如apropos("col$")。
> apropos("col$")
[1] "col" "max.col" "ncol" "NCOL"
> apropos("^col")
[1] "col" "col2rgb" "colMeans"
[4] "colnames" "colnames<-" "colorConverter"
[7] "colorRamp" "colorRampPalette" "colors"
[10] "colorspaces" "colours" "colSums"
> apropos("col$")
[1] "col" "max.col" "ncol" "NCOL"
>
此外,为显示长度为3的所有函数,我们有以下指令:apropos("^.{3}$")。
> apropos("^.{3}$")
[1] "$<-" "%*%" "%/%" "%o%" "%x%" ".gt" ":::" "@<-" "[<-" "<<-"
[11] "abs" "acf" "AIC" "all" "any" "aov" "Arg" "ave" "BIC" "bmp"
[21] "BOD" "box" "bxp" "cat" "ccf" "co2" "CO2" "col" "cor" "cos"
[31] "cov" "cut" "det" "dim" "dir" "end" "exp" "fft" "fix" "for"
[41] "get" "glm" "hat" "hcl" "hsv" "IQR" "lag" "lcm" "log" "mad"
[51] "Map" "max" "min" "Mod" "new" "nlm" "nls" "npk" "Ops" "par"
[61] "pdf" "pie" "png" "ppr" "raw" "rep" "rev" "rgb" "rle" "row"
[71] "rug" "seq" "sin" "SSD" "stl" "str" "sub" "sum" "svd" "svg"
[81] "tan" "tar" "try" "tsp" "unz" "url" "var" "x11" "X11" "xor"
[91] "zip"
>
在此阶段对于新手而言,无须理解("^.{3}$")的确切含义。我们会在以后章节中对该语句作详细的讲解。
R可以用作一个简单的计算器。原因是通过R,我们可以调用许多内存函数。例如,mean()是求平均值的函数。
> x <-1:50
> mean(x)
[1] 25.5
大家可以尝试其他的函数:max()、min()、median()、sd()和var()等。
> x <-1:50
> max(x)
[1] 50
> min(x)
[1] 1
> median(x)
[1] 25.5
> sd(x)
[1] 14.57738
当我们要重新显示已执行的命令并修改它时,可以按键盘上的向上键。
> x <-1:5
> x <-1:500
发出一组命令行后,我们可以反复使用向上键和向下键来“召回”并纠正“老”的命令。这对检查和修改我们的代码非常方便,原因是你总能找回前一命令并对其做少许的修改。
我们不应混淆dir()和ls()的区别。dir()可以列出我们当前的工作目录或指定目录(如使用绝对路径法)下的文件,如程序、输入数据文件、输出数据文件、输入数据集和输出数据集等。ls()可以列出在我们当前的工作空间所包含的R对象(objects)。
> ls() # 列出当前工作空间包含的所有对象
R对象包括我们定义的变量、数据表、数据框、向量、数组和函数。在此阶段,如果你不了解数据表、数据框、向量和数组等的定义,不必担心。因为在后面的章节中,我们将仔细讨论这些概念。
> ls(pat='test') # 显示包含 'text' 的所有对象
另一种方式来显示所有的目标是使用objects()函数
> objects() # 第二种方式来显示所有的对象
我们用rm()函数从内存中删除不必要的变量、函数及数据。
> rm(x) # 删除 x
> rm(x,y) # 删除 x 和 y
有几种方法可以删除所有对象,如下所示。
# rm(list=ls((all=TRUE))) # 删除所有对象 (方法 1)
# rm(list=ls()) # 简单一点的方法来删除所有对象
通过R软件的菜单,也可以删除所有对象,即单击R菜单上的“Misc”,然后选择“Remove all objects…”
另外,如果想要从我们当前的工作目录或指定目录下删除一个文件,可以使用file.remove()函数。
> file.remove('test.R') # 相对路径法
> file.remove ('datas/test2.R') # 绝对路径法
在大多数情况下,大多数研究人员并不在意R软件的精度。然而,知道如何找到它对将来可能有益,因为我们可能会遇到有关的问题。
> .Machine $ double.eps
[1] 2.220446e-16
dir()函数用来列出所有的程序、数据集和其他存在于当前的工作目录下的文件。
> dir() # 显示当前工作目录中下的所有程序等
当我们想要显示某一类文件时,可以使用以下代码。
> dir(pattern="ratio") # 显示文件名中含有"ratio" 的文件
如果我们打算检查非当前工作目录下的文件,则需添加path="our_path_here",这就是所谓的绝对途径法。
> dir(path="datas/",pattern="t.R")
为显示当前的工作目录,我们使用getwd()函数。
> getwd()
[1] "C:/Users/yyan/Documents"
在做项目时,我们往往会生成专门的文件目录(档案),以包含所有的数据、程序和其他相关的文件。在启动R后,我们通常希望那个与项目相关联的目录成为当前工作目录。第1种改变当前工作目录的方式是使用R菜单栏,即通过单击“File”,然后选择“Change dir…”。
第2种方式是使用setwd()函数来改变当前的工作目录。
> setwd("datas") # 改变当前的工作目录
> getwd() # 查看当前的工作目录
[1]"datas"
在上述语句中,getwd()用于显示当前目录。上面我们用该函数来确认已经改变了的工作目录。
如我们在下面3个月将用R完成一个项目。在启动R时,我们希望R直接将有关的目录设置为当前工作目录。为此,我们将按下面的步骤设置。第1步,在R软件的图标上单击鼠标右键;第2步,选择“属性”;第3步,在弹出窗口的“起始位置”选项处,填入我们指定的工作目录。
例如,如果你打算在启动R时,直接将c:\test_R\作为工作目录,那就可以按照上面的方法去做。
本书的主要目的是将R应用在金融学上面将R应用在金融领域方面的优势,这里就不再赘述了。我们只是将金融学的学生学习R的优点用表给出。表1.1列出在财务入门课程中引入R软件的优势。
表1.1 R软件在金融学上使用的优势
1.零成本,用户可以免费下载 |
---|
2.没有黑箱(透明的函数、公式和逻辑) |
3.比金融计算器或Excel更为灵活(如用户可以生成自己的函数) |
4.用户可以查看使用各函数的实例 |
5.可以估计市场风险、总风险、流动性度量和资本资产定价模型等 |
6.可以从雅虎财经等互联网平台下载数据 |
7.可扩展并包括其他的功能 |
8.对有关金融学方面的研究特别有用 |
9.使个人的简历更加抢眼 |
10.R在金融业已得到广泛的使用 |
11.现有30多个有关金融的R包(软件包) |
12.因为世界各地的许多用户的继续研发,会出现更多有用的R包 |
灵活性意味着,用户可以选取他们自己最喜欢的函数。例如,我们知道可以写很简单的一行的R程序来计算未来值的现值。有关公式为:
(1-1)
在公式(1-1)中,是现值,
是未来值,
是资本的期贴现率(折现率),
是周期数。
pv_f<-function(fv,r,n) fv/(1+r)^n
在后面我们将仔细讲解该函数的构造。对上述的pv_f的函数而言,用户可以为其重命名为pv_function或my_PV_function等。如果你仍想保持原有的函数,可以添加另一个函数。请参阅下面的示例。
> my_PV_function(fv,r,n) pv_f(fv,r,n)
在上面的程序中,my_PF_function()的功能和我们原有的pv_f()函数一样。当一名本科学生申请硕士学位时,R的知识会给她/他许多优势。使用R的知识和技能会为想去华尔街工作的毕业生增加一定的分量。因为很多金融机构都是使用S+。S+可以认为是R的表姐妹。表1.2给出了R在金融学上使用的障碍。
表1.2 R在金融学上使用的障碍
1.大多数金融学教师不知道R软件 |
---|
2.使用金融计算器可以设计闭卷考试 |
3.几乎没有金融教科书包括R的使用 |
4.金融教科书的作者不愿意改变他们教科书 |
5.很多出版商不愿意打破当前的模式 |
6.金融计算器的厂家 |
7.守旧的心态 |
(1)使用R软件的优点和阻碍有哪些?
(2)就R而言,如何将一个值分配给一个新的变量?
(3)R区分大小写吗?如何获取有关特定函数的帮助?
(4)使用R时,如何添加注释?R编译器编译它吗?
(5)空格在R中有无作用?
(6)如何下载与R相关的手册?
(7)我们需要先定义一个变量,例如整数或字符,然后再使用它吗?
(8)函数ls()和rm()之间的区别有哪些?
(9)为某个向量赋值,起始值为−2.45,终止值为10,相同的增值为2.25。共有多少值?
(10)x的输入值范围1~100和202~300。
(11)反转上题中的输入值。
(12)如何列出所有文件?
(13)函数ls()和dir()有何区别?
(14)如何发现有关sd()函数的有用信息?
(15)如何发现所有5个字母的内在函数?
(16)对非计算机系的学生而言,如何克服学习编程的恐惧?
(17)生成一系列数字:1、2、3、5、8及10并计算他们的均值及方差。
(18)R中的零是如何定义的?
大多数金融数据是按时间排列的。而处理好时间序列中最重要的要素之一就是生成一有效的日期变量。在这一章中,我们会说明为什么它很重要,如何正确地定义它,以及如何使用它。对于各种金融数据库和数据操作,日期变量扮演着独特的角色,因为我们在很多场景下,例如每个月末选择特定的工作日或最后一天以及合并的个别股票收益与市场指数收益数据集时,需要这样的一个变量。一个比较典型的例子是将包含个股收益率数据集和包含市场收益率的时间序列相合并,提取从1960年到1980年所有的数据,将月数据分成两组:一组是有1月份的收益率,另一组是其他月份的收益率。
有很多方法可以生成这样的日期变量。其中,最好的是使用as.Date()函数。如果x定义为1月的最后一天,那么x+1应该是2月的第一天。在第一行代码中,"%Y-%m-%d"是依赖于输入值结构的格式。
> x<-as.Date("2011-01-31","%Y-%m-%d")
> x
[1] "2011-01-31"
> x+40
[1] "2011-03-12"
以下是使用as.Date()函数的另一例子。
> x <- "03/31/2017"
> date <-as.Date(x,"%m/%d/%Y")
> date+ 1
[1] "2017-04-01"
对第一行语句而言,因为输入的是"03/31/2017",所以相应的格式为"%m/%d/%Y"。如果日期无效,那么as.Date()函数会给出一个出错的结果——NA。例如我们用as.Date()函数定义"2/31/2012"时,将产生NA,即一个无效的结果。
> x <- "02/31/2012"
> date <-as.Date(x,"%m/%d/%Y")
> date
[1] NA
> x <- "02/3/2012"
> date <-as.Date(x,"%m/%d/%Y")
> date
[1] "2012-02-03"
由于as.date()函数的输入值必须是字符变量,因此我们必须将整数转换为字符变量。不然,就会有出错信息。
> as.Date(20171212,"%Y%m%d")
Error in charToDate(x) :
character string is not in a standard unambiguous format
>
函数as.date()的输入值是字符串,而不是整数。因此我们可以用as.character()函数将其转换成字符串变量。
> a<-20110131
> typeof(a)
[1] "double"
> b<-as.character(a)
> typeof(b)
[1] "character"
> d<-as.Date(b,format="%Y%m%d")
> d
[1] "2011-01-31"
> d+50
[1] "2011-03-22"
>
正如我们之前所讨论的,这不是定义日期变量的好方法。在以后的章节中我们会对该方法加以明确的叙述。
> date<-19900101
> year<-as.integer(date/10000)
> year
[1] 1990
有时与日期相关的输入变量是一个字符变量。因此,我们需要其他方式来转换那些字符使其变成有用的日期变量。
> x<-"1990-01-02"
> year<-substr(x,1,4) # from 1 to 4
> month<-substr(x,6,7) # from 6 to 7
> day<-substr(x,9,10)
> date<-paste(year,month,day,sep="") #
> date
[1] "19900102"
在上述代码中,paste()函数用来将两个字符串联起来,我们还可以进一步处理数据(例如获取日期和计算回报)。
x<-read.csv('ibm.csv',header=T)
y<-substr(x[,1],1,4) # year
m<-substr(x[,1],6,7) # month
d<-substr(x[,1],9,10) # day
date<-as.integer(as.character(paste(y,m,d,sep="")))
data<-cbind(date,x[,7]) # combine date and price
函数gsub()用于全局替换(即,用新的字符替换所有的字符)。
>x<-"1990-01-02"
>date<-gsub("-","",x) # replace all – with nothing
> date # i.e., remove all –
[1] "19900102"
> as.integer(date)
[1] 19900102
在下面的例子中,我们要检索年份变量。你应该记住,输出是一个字符串而不是一个整数。
> ddate<-as.Date("2011-01-31","%Y-%m-%d")
> format(ddate, "%Y")
[1] "2011"
我们用%mand%d来输出月和日的字符串变量,如下所示。
> ddate<-as.Date("2011-01-31","%Y-%m-%d")
> format(ddate, "%m")
[1] "01"
> format(ddate, "%d")
[1] "31"
>
为了把“2010”这样的字符串转换成整数,我们可以使用as.integer()函数。
> x<-"2010"
> as.integer(x)+1
[1] 2011
为了将“201.1”这样的字符串转换成实数,可以使用as.numeric()函数。
> x<-"201.1"
> as.numeric(x)
[1] 201.1
如果我们想把一个字符串日期变量转换成一个整数,我们使用as.integer()函数。
> x<-as.Date("2011-01-31","%Y-%m-%d")
> as.integer(format(x,"%Y")) # convert string to integer
[1] 2011
> as.integer(format(x,"%m"))
[1] 1
> as.integer(format(x,"%d"))
[1] 31
对于字符串操作,paste()函数非常重要。如果我们打算将两个字符串变量x="hello"和y="world!"放在一起。
> x<-"hello"
> y<-"world!"
> paste(x,y,sep=" ") # sep is separator
[1] "hello world!"
如果两个字符串之间不需要任何空间,我们使用sep="".
> x<-"hello"
> y<-"world!"
> paste(x,y,sep="") # sep is separator
[1] "helloworld!"
如果我们把sep=""省略,就会有下面的情况出现,这说明sep的缺省值是一个空格。
> x<-"hello"
> y<-"world!"
> paste(x,y)
[1] "hello world!"
在下面的代码中,我们实现了将字符串拆分,再合并,然后转换成整数形式的日期变量。
> x<-"2011-01-31"
> y<-substr(x,1,4)
> m<-substr(x,6,7)
> d<-substr(x,9,10)
> date<-as.integer(paste(y,m,d,sep=""))
> date
[1] 20110131
我们也可以使用sub()函数(单个替换)和gsub()函数(全局替换)把字符串转换成整数形式的日期变量,请参阅下面的例子。
> x<-"2011-02-25"
> sub("-","",x) # replace "-" with nothing
[1] "201102-25"
> x<-"2011-02-25"
> gsub("-","",x) # replace all "-" with nothing
[1] "20110225"
由于我们通常需要一个整数形式的输出。如在后面的章节中,我们需要计算股票的10年风险。为此,我们可以使用as.integer()函数。
> x<-"2011-01-31"
> as.integer(gsub("-","",x))
我们可以指定一个时间范围,并计算起始日期到结束日期之间的天数。
> d1<-as.Date("19900101",format="%Y%m%d")
> d2<-as.Date("19901231",format="%Y%m%d")
> days<-seq(d1,d2,by=1)
> length(days)
[1] 365
在将每日价格转换为估计月度回报时,我们可能需要每月股市的结束日期。
x<-seq(as.Date("2005/1/1"),as.Date("2005/10/30"), "days")
> n<-length(x) # note: days is the keyword
> m<-format(x,"%m") # generate a month variable
> y<-data.frame(x,m)
> y2<-subset(y,y[1:(n-1),2]!=y[2:n,2])
> y2[1:5,]
x m
31 2005-01-31 01
59 2005-02-28 02
90 2005-03-31 03
120 2005-04-30 04
151 2005-05-31 05
从理论上而言上述的程序给出了每月的最后一个日期。可惜的是,上诉方法不能保证每月的最后一天是不是交易日,因为有假期的存在。为此我们需要再生成两个有关的R数据集,详见下面小节。
在一些情况下,我们想知道哪一天股票市场是有交易的,这里我们是指北美的股票市场。为此我们生成了两个R数据集:一个是日频率,另一个为月频率,如图2-1所示。
图2-1 两个R数据集
我们可以写一个R程序直接下载:
> con<-url("http://datayyy.com/data_R/tradingDaysMonthly.RData")
> load(con)
> close(con)
> tradingDaysM=read.csv("datas/tradingDaysMonthly.csv")
> nrow(tradingDaysM)
[1] 1090
> head(.tradingDaysM)
[1] "1925-12-31" "1926-01-30" "1926-02-27" "1926-03-31" "1926-04-30"
[6] "1926-05-28"
> tail(.tradingDaysM)
[1] "2014-07-31" "2014-08-29" "2014-09-30" "2014-10-31" "2014-11-28"
[6] "2014-12-31"
同理,我们可下载tradingDaysDaily.RData。
> tradingDaysDaily=read.csv("datas/tradingDaysDaily.csv")
> nrow(tradingDaysDaily)
[1] 23986
> head(tradingDaysDaily)
[1] "1925-12-31" "1926-01-02" "1926-01-04" "1926-01-05" "1926-01-06"
[6] "1926-01-07"
> tail(tradingDaysDaily)
[1] "2017-12-12" "2017-12-13" "2017-12-14" "2017-12-15" "2017-12-18"
[6] "2017-12-19"
在后面的章节中我们将解释周日效应:即星期一的收益率不同于星期二的收益率,等等。在测试所谓的周日效应时,我们需要按不同周的周日将日股票收益率分成不同的小组。
> x<-"2011-2-13"
> weekdays(as.Date(x))
[1] "Sunday"
以下代码显示如何获取星期一的观察值。
> x<-read.csv("http://canisius.edu/~yany/ibmDaily.csv",header=T)
> n<-nrow(x)
> d<-data.frame(as.Date(x[1:(n-1),1]),(x[1:(n-1),7]-x[2:n,7])/x[2:n,7])
> colnames(d)<-c("date","ret")
> y<-subset(d,weekdays(d[,1])=="Monday")
> head(y)
date ret
8 2011-02-14 -0.003844980
13 2011-02-07 0.005019896
18 2011-01-31 0.017530584
23 2011-01-24 0.026534960
32 2011-01-10 -0.001968103
37 2011-01-03 0.004925434
在以后的章节中我们将对上述程序中一些相关的关键词加以描述。
对cbind()函数而言,要求所有的变量有相同的类型。因此,当使用cbind()函数来连接不同类型的列时,我们可能会有意想不到的结果。
> x<-as.Date("1990-01-02",format="%Y-%m-%d")
> y<-as.integer(format(x, "%Y"))
> data<-cbind(x,y)
> data
x y
[1,] 7306 1990
> x<-as.Date("1990-01-02",format="%Y-%m-%d")
> y<-as.integer(format(x,"%Y"))
> data<-data.frame(x,y)
> data
x y
1 1990-01-02 1990
cbind()函数产生意想不到的结果的主要原因是它产生了一个矩阵这要求所有列具有相同的类型(即,全部是数字或全部是字符)。
> x<-2001
> y<-2009
> cbind(x,y)
x y
[1,] 2001 2009
> x<-"2001"
> y<-2009
> cbind(x,y)
x y
[1,] "2001" "2009"
因此,使用data.frame()用于组合具有不同数据类型的列是一个好主意。
> x<-"2001"
> y<-2009
> z<-data.frame(x,y,stringsAsFactors=FALSE)
> z
x y
1 2001 2009
> z[1,1]
[1] "2001"
> z[1,2]
[1] 2009
要在date1和date2之间生成日期,请参阅下面的代码:
> seq(as.Date("2010/1/1"), as.Date("2010/12/31"), "days")
要生成每个月的第一天,请使用以下程序:
> x<-seq(as.Date("2010/1/1"), as.Date("2010/12/31"), "months")
> head(x)
[1] "2010-01-01" "2010-02-01" "2010-03-01" "2010-04-01" "2010-05-01" "2010-06-01"
> tail(x)
[1] "2010-07-01" "2010-08-01" "2010-09-01" "2010-10-01" "2010-11-01" "2010-12-01"
>
同理,如要生成每年的第一天,可以使用以下程序:
> y<-seq(as.Date("1926/1/1"), as.Date("2018/1/1"), "years")
> head(y)
[1] "1926-01-01" "1927-01-01" "1928-01-01" "1929-01-01" "1930-01-01"
[6] "1931-01-01"
> tail(y)
[1] "2013-01-01" "2014-01-01" "2015-01-01" "2016-01-01" "2017-01-01"
[6] "2018-01-01"
>
我们可以用Sys.Date()函数来显示今日的日期。
> today<-Sys.Date()
> today
[1] "2017-12-22"
该软件包用于更高级的数据操作。如果我们想知道今天的日期和时间,可以使用下面的命令。
> library(timeDate)
> Sys.timeDate()
GMT
[1] [2017-12-19 20:59:56]
>
如果你想知道有关这个更多的信息,可以用help()函数。
如果你想发现有关假日的信息,可以用holiday()函数。
> holiday(2000:2017,Holiday="ChristmasEve")
GMT
[1] [2000-12-24] [2001-12-24] [2002-12-24] [2003-12-24] [2004-12-24]
[6] [2005-12-24] [2006-12-24] [2007-12-24] [2008-12-24] [2009-12-24]
[11] [2010-12-24] [2011-12-24] [2012-12-24] [2013-12-24] [2014-12-24]
[16] [2015-12-24] [2016-12-24] [2017-12-24]
下面语句生成基于纽约股票交易所的节假日。NYSE是(New York Stock Exchange,纽约股票交易)的第一个字母的缩写。
> holidayNYSE(2017)
NewYork
[1] [2017-01-02] [2017-01-16] [2017-02-20] [2017-04-14] [2017-05-29]
[6] [2017-07-04] [2017-09-04] [2017-11-23] [2017-12-25]
>
(1)指出将日期变量定义为整数的不足之处。
> ddate<-20110131
> year<-as.integer(ddate/10000)
> month<-as.integer(ddate/100)-year*100
> day<-ddate-year*10000-month*100
(2)对于1990/02/03,生成相应的日期变量作为整数。
(3)对于(1)题中的同一日期,使用as.Date()函数将其定义为具有真正意义的日期变量。
(4)使用as.Date()函数在2000年1月1日~2007年12月31日之间生成所有日期。
(5)对(4)题中为时间序列生成相应的年份、月份和日期。
(6)从1975年7月1日~2010年12月31日之间有多少天?
(7)从1975年7月1日至2010年12月31日之间有多少个交易日?
提示
使用tradingDaysDaily.RData数据集
(8)2000年是闰年吗?
(9)先尝试运行下面的代码然后解释功能。
>x <- as.Date(paste(1999:2009, "-12-31",sep=""))
(10)下面的命令有什么问题?
> date<-as.Date("01/31/2011","%m-%d-%Y")
> z<-as.Date("01/31/2011","%d/%m/%Y")
> z
[1] NA
(11)解释以下代码和结果:
> days<- seq(as.Date("2005/1/1"),as.Date("2005/1/10"), "days")
> days
[1] "2005-01-01" "2005-01-02" "2005-01-03" "2005-01-04" "2005-01-05"
[6] "2005-01-06" "2005-01-07" "2005-01-08" "2005-01-09" "2005-01-10"
>w<-format(days, "%W")
> w
[1] "00" "00" "01" "01" "01" "01" "01" "01" "01" "02"
(12)解释下列语句。
> x<-as.Date("2017/12/25")
> isWeekday(x)
2017-12-25
TRUE
> isWeekend(x)
2017-12-25
FALSE
> x<-Sys.Date()
> isHoliday(x)
Error in isBizday(x, holidays, wday = wday) :
trying to get slot "Data" from an object (class "Date") that is not an S4 object
> dd<-as.timeDate(x)
> dd
GMT
[1] [2017-12-25]
> isHoliday(dd)
2017-12-25
TRUE
(13)如何从日期函数中读取年、月、日?
(14)如何从20170201读取年、月、日?
(15)如何从3/31/2017读取年、月、日?
(16)cbind()data.frame()函数的主要区别是什么?
(17)如果为整数,
为字符串,如用cbind()函数将它们放在一起。结果如何?
(18)如果为整数,
为字符串,如用data.frame()函数将它们放在一起。结果如何?
(19)写一个R程序列出所有从1900年至2018年之间的闰年。
(20)从timeDate包中,找出所有有关不同地区节假日的函数。
> library(timeDate)
> help(package=timeDate)