单行观测值过长?filefilter帮你自动分行
本文作者:杨慧琳
本文编辑:李朋冲
技术总编:薛 原
有问题,不要怕!访问
http://www.wuhanstring.com/uploads/5_aboutus/爬虫俱乐部-用户问题登记表.docx (复制到浏览器中)下载爬虫俱乐部用户问题登记表并按要求填写后发送至邮箱statatraining@163.com,我们会及时为您解答哟~
爬虫俱乐部的github主站正式上线了!我们的网站地址是:https://stata-club.github.io,粉丝们可以通过该网站访问过去的推文哟~
爬虫俱乐部隆重推出数据定制及处理业务,您有任何网页数据获取及处理方面的难题,请发邮件至我们邮箱statatraining@163.com,届时会有俱乐部高级会员为您排忧解难!
在数据处理中,我们有时会遇到这种情形:当某个变量过长时,使用split命令拆分后,单行观测值长度超出最大存储范围(1048576个字节,即1M)。例如,在爬取搜狐财经网站上(http://q.stock.sohu.com/cn/000001/lshq.shtml)代码为000001的股票1990-2018年的历史交易数据时,我们看到了如下网页界面:
对于这个网页,我们直接在页面空白部分单击右键,查看网络源代码,无法找到网页表格对应信息。参照往期推文《一起来揪出网页真实链接!》的方法,我们使用浏览器的抓包功能,找到表格内容的真实链接为:
http://q.stock.sohu.com/hisHq?code=cn_000001&start=19900101&end=20181231&stat=1&order=D&period=d&callback=historySearchHandler&rt=jsonp。
在新标签页中打开链接,我们可以看到该网页的源代码,是由列表组成的列表,仅占一行;网页表格的每一行为一个列表中的内容(即一对方括号中间的部分),而整个表格对应了最外层的列表。我们将其保存到文件temp.txt中,打开后,可查看到整个源代码长度约占59.4万个字符。如下:
同时,我们发现源代码中的股票历史交易数据刚好在一对“[[...]]”符号之间,且该对符号仅出现一次。另外,表格中各行数据均以符号“],[”分隔。因此,我们在读入源代码后,可首先提取出“[[...]]”之间的内容,其次以“],[”为分割符,利用split命令将整体数据分割开,使每日股票交易信息位于一个单元格中。
由于该行源代码文件长度为594341个字符,而通过help limits可知,infix命令可读入的最大长度为524275个字符,直接采用这一命令读入将出现错误提示。如下:
还有一个潜在问题:在Stata15.0或更低版本中,infix将无法读入文本文件的最后一行内容。因此当文本文件只有一行时,infix无法将文件读入。我们转变方法,再次采用fileread()函数完整读入这一数据。我们令单行最大变量数为120000,并将单行数据大小设置为Stata最大可存储量,即1M。执行程序:
clear
cap mkdir F:/股票交易数据/
cd F:/股票交易数据/
set maxvar 120000 //设置最大观测值
set max_memory . //设置最大存储值
copy "http://q.stock.sohu.com/hisHq?code=cn_000001&start=19900101&end=20181231&stat=1&order=D&period=d&callback=historySearchHandler&rt=jsonp" ///
"temp.txt", replace
set obs 1
gen v = fileread("temp.txt")
replace v = ustrregexs(1) if ustrregexm(v, `"\[\[(.*?)\]\]"')
split v, p(`"],["')
然而,在执行split命令后,Stata提示错误:no room to add more variables because of width,即拆分后的变量超出单行最大存储范围,无法加入新的变量。我们查看已经拆分好的数据集,也发现该行数据大小达到1.02M,超出Stata最大可存储量。
可见,采用以上两种方法处理这一源代码均失败,我们需另寻他法。一个可行的思路便是先将一行源代码分成多行后再读入。文本文件的分行可通过一些外部编辑器实现,如Notepad++、Sublime Text等软件。以Notepad++为例,用该编辑器打开源代码文件temp.txt后,选择搜索→替换功能,在弹出的操作窗口中选择正则表达式查找模式,将符号“],[”替换为回车换行符“\r\n”,便可将股票每日交易数据显示为一列多行。如下:
但是,这种手动分行方式仅适用于处理少数源代码文件,在批量处理中并不可取。我们又想到,是否可以先用fileread()函数读入temp.txt,将“],[”替换为换行符后另存为temp1.txt,再次读入Stata呢?即执行如下程序:
clear
set obs 1
gen v = fileread("temp.txt")
replace v = subinstr(v,"],[", char(10),.) //在ASCII编码下,换行符表示为char(10)
outfile using temp1.txt, replace //将文件另存为temp1.txt
infix strL v 1-10000 using temp1.txt, clear
infix命令读入数据如下:
这也达到了分行的目的。然而,这种两阶段处理方法同样增加了Stata的工作量。那么,有什么方法能不先读入即可实现文件temp.txt中的符号替换呢?我们介绍一个强大的文本文件处理命令——filefilter。
爬虫俱乐部是您身边的科研助手,能够为您在数据处理、实证研究中提供帮助。承蒙近四万粉丝的支持与厚爱,我们在腾讯课堂推出了网络视频课程,专注于数据整理、网络爬虫、循环命令编制和结果输出…李老师及团队精彩地讲解,深入浅出,注重案例与实战,让您更加快速高效地掌握Stata技巧及数据处理的精髓,而且可以无限次重复观看,在原有课程基础上已上传了全新的内容!百分百好评,简单易学,一个月让您从入门到精通。绝对物超所值!观看学习网址:
https://ke.qq.com/course/286526?tuin=1b60b462,
敬请关注!
filefilter命令
filefilter可以替换文本文件中的某个特定字符或字符串,尤其可以对回车符、换行符一类的控制字符进行操作,也可用于转换文本编码、替换特定符号、更改文本格式等。其基本语法如下:
filefilter oldfile newfile , { from(oldpattern) to(newpattern) | ascii2ebcdic | ebcdic2ascii } [options] ( replace)
其中,oldfile和newfile分别代表转换之前及转换之后的文件名;from(oldpattern) to(newpattern) 表示将oldpattern替换为newpattern,可以是字符串或特定字符;ascii2ebcdic表示将ASCII编码文件转换为EBCDIC编码文件,ebcdic2ascii则是将EBCDIC编码文件转换为ASCII编码文件。
EBCDIC码和ASCII码是计算机中常用的字符编码。其中,IBM系列大型机采用EBCDIC码(扩展二进制编码的十进制交换码),微型机采用ASCII码(美国标准信息交换码)。EBCDIC为8位码, 每个字母和或数字被表示成一个8位的二进制数,有256个编码状态,用于定义标准字符和控制代码。但其英文字母为非连续排列,也不支持非字母组合起来的语言,如汉语、日语等。对于这类数据,可借助程序先将其编码转换为ASCII码,再进行处理。
在filefilter命令下,可使用如下字符:
regchar | 常规字符,如32-91、93-127位ASCII编码及除“\”外的128、161-255位ASCII拓展码。 |
\BS | 反斜杠 |
\r | 回车符 |
\n | 换行符 |
\t | 水平制表符 |
\M | Mac系统下用于分行的回车符,也可写作“\r” |
\W | Windows系统下用于分行的回车换行符,也可写作“\r\n” |
\U | Unix系统下用于分行的换行符,也可写作“\n” |
\LQ | 位于标准键盘左上角的英文重音符“ ` ” |
\RQ | 英文格式下单引号“ '” |
\Q | 英文格式下双引号“ "” |
\$ | 美元符号 |
\###d | 三位数十进制ASCII编码 |
\##h | 两位数十六进制ASCII编码 |
在此基础上,我们举例分析filefilter命令的常见用法。
(1)将文件string1.txt中的英文重音符“ ` ”替换为字符串“left quote”,另存为string2.txt。程序及结果如下:
filefilter string1.txt string2.txt, from(\LQ) to("left quote") replace
(2)在ASCII编码下,将文件data1.csv中以十六进制代码60,即十进制代码96定义的字符“ ` ”替换为字符串“left quote”,另存为data2.csv(读者可自行查阅十进制和十六进制的ASCII编码表)。程序及结果如下:
filefilter data1.csv data2.csv, from(\60h) to("left quote") replace
(3)删除文件file1.txt中的字符串“爬虫”,另存为file2.txt。程序及结果如下:
filefilter file1.txt file2.txt, from("爬虫") to("") replace
在本推文的股票交易数据爬虫案例中,我们则需将源代码中的符号“],[”替换为回车换行符“\r\n”或“\W”,执行程序:
filefilter temp.txt temp2.txt, from("],[") to(\W) replace
此时,替换后的源代码将保存在文件temp2.txt中,如下:
在此基础上,便可将该源代码文件读入Stata中进行后续处理,且无需担心单行变量大小超过最大存储范围的情形。完整程序及最终成果如下:
clear
copy "http://q.stock.sohu.com/hisHq?code=cn_000001&start=19900101&end=20181231&stat=1&order=D&period=d&callback=historySearchHandler&rt=jsonp" ///
"temp.txt", replace
filefilter temp.txt temp2.txt, from("],[") to(\W) replace
infix strL v 1-10000 using temp2.txt, clear
replace v = ustrregexra(v, `".*?\[\[""', "")
replace v = ustrregexra(v, "\]\].*", "")
split v, p(`"""')
keep v1 v3 v5 v7 v9 v11 v13 v15 v17 v19
destring _all, percent replace
rename _all (日期 开盘 收盘 涨跌额 涨跌幅 最低 最高 成交量 成交金额 换手率)
save 000001.dta, replace
这样,我们便轻松爬取了股票000001(平安银行)自上市以来的所有历史交易数据,filefilter命令的使用也大大提升了网页源代码的处理速度及效率。当然,这只是filefilter命令应用的冰山一角,更多功能还有待我们在实践中勤加探索!
对爬虫俱乐部的推文累计打赏超过1000元我们即可给您开具发票,发票类别为“咨询费”。用心做事,只为做您更贴心的小爬虫!
往期推文推荐
关于我们
微信公众号“爬虫俱乐部”分享实用的stata命令,欢迎转载、打赏。爬虫俱乐部是由李春涛教授领导下的研究生及本科生组成的大数据分析和数据挖掘团队。
此外,欢迎大家踊跃投稿,介绍一些关于stata的数据处理和分析技巧。
投稿邮箱:statatraining@163.com
投稿要求:
1)必须原创,禁止抄袭;
2)必须准确,详细,有例子,有截图;
注意事项:
1)所有投稿都会经过本公众号运营团队成员的审核,审核通过才可录用,一经录用,会在该推文里为作者署名,并有赏金分成。
2)邮件请注明投稿,邮件名称为“投稿+推文名称”。
3)应广大读者要求,现开通有偿问答服务,如果大家遇到关于stata分析数据的问题,可以在公众号中提出,只需支付少量赏金,我们会在后期的推文里给予解答。