写在前面
从这期开始,大猫课堂将会推出一个新的系列:R练习50题,目的是使用50道练习题让大家掌握常用的数据操作,例如寻找每组最大的N个观测等。本练习题来源于Renkun (github.com/renkun-ken/r-data-practice) 在Github上的共享,我们认为它包括了绝大多数实践中会遇到的问题,特别具有代表性。只可惜Renkun并没有提供答案,所以我们在这里提供我们的版本。
我们的所有答案都将使用data.table这个包。我们认为data.table是最优秀的数据处理工具,没有之一。关于data.table的神奇之处以及它和其他工具(例如pandas)的比较,欢迎大家戳它的官网:github.com/Rdatatable/data.table.
拥有data.table的基础会有助于你更快看懂答案,但并非必要。我们会在讲解答案的时候穿插data.table的教学。关于data.table的入门,我们认为最好的教材是它的Github Wiki( github.com/Rdatatable/data.table/wiki)。
我们已经把所有50题的答案都上传到了我们的Github主页,等不及想先睹为快的同学,请猛戳这里:
“ github.com/Ravin515/r-data-practice”
数据集预览
所有50道练习题都基于同一个股票价格数据集而设计。虽然具有明显的金融背景,但是它和其他学科所遇到的数据集是相通的:在我们的数据集中,每个股票代码symbol和日期date的组合都决定了唯一的一个观测,相当于数据集的key,这种由“横截面”与“时间序列”共同组成的“面板数据”在工作中几乎随处可见。
我们首先使用以下代码导入数据集:
代码语言:javascript复制# We only need two packages here
library(data.table)
library(stringr)
# set `data_path` to your dir
data_path <- "C:/Users/rossz/OneDrive/App/R/stock-datatable-key/data"
setwd(data_path)
# read into data
data <- readRDS("stock-market-data.rds") data[1:5] # show top 5 obs
我们给出数据集前五行的预览:
值得说明的有一下几点:
- 数据集为“面板数据”:包含多个股票(横截面),而每个股票则有多个按照日期排序的变量(时间序列)
- 股票代码symbol 和日期date共同组成了数据集的key,也即每个唯一的symbol 和date组合决定了一个唯一的观测。
- 整个数据集首先按照代码symbol排列,其次按照日期date排列。
- 若干主要变量说明:
- symbol:股票代码。.SH 结尾的是沪股,.SZ 结尾的是深股
- date:日期
- pre_close:昨收盘
- open:开盘价
- high:最高价(日内)
- low:最低价(日内)
- close:收盘价
- volume:成交量
- amount:成交金额
- industry:行业
练
习1:哪些股票的代码中包含"8"这个数字?
问题分析
首先,我们需要把股票代码symbol中包含8的那些观测找出来。我们可以借助与stringr这个字符串处理包。这一步不难,稍微有些挑战的是去重。如果我们不去重,那么我们会得到非常多的重复观测。例如股票600128,如果它一共有100天的观测,那么我们会出现100个重复结果。为了去重,我们需要借助于data.table中的unique函数。
我们希望最终的输出是一个字符串向量:
代码
data[str_detect(symbol, "8"),
unique(symbol)]
- str_detect函数来自stringr包,它的输入是一个char vector,输出则是boolean vector,长度与原向量相同。str_detect(symbol, "8")含义为:对于symbol向量,判断其是否含有字符8,如果有,则为True,否则Faulse。
- unique:找出symbol中不重复的值。
- 在data.table的语法中,先进行列选择操作,再对列进行处理。所以上述语句会先执行str_detect,再执行unique。
练习2:每天上涨和下跌的股票各有多少?
问题分析
这一题需要引入分组的概念,并且按照“先分组,后统计”两步走。首先按照题意,我们需要为每个交易日date建立一个“组”。其次,对于每个组,我们需要生成两个统计数字:一个统计上涨的个数,一个统计下跌的个数。最终结果如下:
可以看到,对于每个date,它都对应了两个观测,一个是“UP”,一个是“DOWN”。
代码分析
代码语言:javascript复制data[,
.(num = uniqueN(symbol)),
keyby = .(date, updown = ifelse(close - pre_close > 0, "UP", "DOWN"))]
- 代码第一行只有一个逗号。这是因为data.table的第一个语句用来对列进行选择,由于我们这里需要对所有列进行统计,所以不需要进行任何操作。
- keyby用来进行分组,是整个代码的核心。先来看keyby = .(date, updown)这个结构,他的意思是,把整个数据集按照date和updown两个变量进行分组,并依次排序。其中,updown是我们新建的字符变量,用来表示分组,它只取两个值:UP, DOWN。这其中的难点是建立updown这个变量。我们使用了ifelse这个函数。ifelse(close - pre_close > 0, "UP", "DOWN")的意思是,如果今天的收盘价高于昨天的收盘价,那么取值UP,反之取值DOWN。
- 代码第二行生成了一个新变量num。由于在keyby语句中我们已经按照日期与涨跌进行了分组,所以这一步我们只需要统计每个组有多少个股票就可以了。我们在这里使用了uniqueN这个函数。它是data.table内置函数之一,和unique几乎执行相同的操作,唯一不同的是,unique返回的是不重复的item(是一个向量),而uniqueN返回的是不重复的数量(是一个数字)。
- 整个代码的执行顺序是:先选择行(逗号空白行),再分组(keyby语句),最后进行组间统计(num语句)。
- 我们的答案中,行、列以及分组三条语句各占一行,实际上这仅仅是为了让代码更直观。如果你愿意,data.table允许你把所有的代码都写在同一行,就像这样:
下期预告
在下一期,我们会继续带来剩余题目的解答~
大猫的R语言课堂
我是大猫,一个高中读文科但却在代码、数学的路上狂奔不止的Finance Ph. D Candidate。
我是村长,一个玩了9年指弹吉他,却被代码深深吸引的博士候选人。
大猫的微信号是:
iRoss2007
村长的B站主页是:http://space.bilibili.com/40771572
大猫的R语言课堂关注R语言、数据挖掘以及经济金融学。
我们与大家分享我们的知识和节操,我相信独乐乐不如众乐乐。