温馨提示: 定期 清理浏览器缓存,可以获得最佳浏览体验。
作者:许梦洁 (中山大学)
目录
语言:Python 方法:拆分文件 目的:提高运行速度
对2010年后49083条上市公司股权变更数据(Firm-Event 观测)分别统计每个事件发生前后15天公司:
数据集 | 总样本数 | 2010年后的样本数 |
---|---|---|
上市公司股权变更记录 | 57584 | 49083 |
上市公司公告记录 | 2787026 | 2758934 |
上市公司日超额收益 | 9749464 | 5534947 |
在Python
构造一个类似于 Excel
中的 countif 函数即可。具体见我上一篇博文 [百万级大样本中的 countif 实现]。
虽然按照上一篇文章的思路也能基本完成任务,但是程序运行非常慢,光跑一次统计窗口期公告数据的程序就要27个小时,所以必须优化程序以提高运行速度。
由于全样本非常大,上篇博文的程序相当于是对每一个公司股东股权变动事件在全部的公告池(2758934条记录)中进行搜索,显然这样做是无效率的。
因此,此次程序优化的主要思路是分别拆分 49083 条公司股东股权变动事件和 2758934 条上市公司公告记录,并将两类拆分的文件对应起来。举个例子:
通过拆分文件得到的精确匹配大大减小了每一个事件的搜索范围,可以大幅提高程序运行效率。优化后跑一次同样的统计窗口期公告数据程序仅需 12 分钟,是原来运行速度的 135 倍。
为了保证运行效率最高,首先平均分拆 CAR 序列,遍历股票日超额收益序列数据,设定阈值为 50000,每 50000 条数据拆出一个文件,以 "CAR+编号" 为文件名 (eg: CAR109.txt
)。最后共拆出了 110 个文件,Python 代码如下:
LIMIT = 50000
file_count = 0
url_list = []
with open("股票日超额收益序列.txt") as f:
for line in f:
url_list.append(line)
if len(url_list)<LIMIT:
continue
file_name = str(file_count)+".txt"
with open(file_name,'w') as file:
for url in url_list[:-1]:
file.write(url)
file.write(url_list[-1].strip())
url_list = []
file_count += 1
遍历拆出来的 110 个 CAR 文件,分别记录每个文件最后一个观测的股票代码,并逐条写入 “拆分节点.txt” 中。Python 代码如下:
import pandas as pd
for i in range(111):
file = "CAR"+repr(i)+".txt"
data = pd.read_table(file, header=None, encoding='utf-8', delim_whitespace=True)
data.columns = ['stkcd', '日期序列', '日超额收益']
stkcdlist = data.loc[:,'stkcd']
end = str(stkcdlist[len(stkcdlist)-1])
with open("拆分节点.txt",'a') as f:
f.write(end+"\n")
print(end)
f.close()
连享会计量方法专题……
由于初步拆分是根据样本数拆分,因此出现了同一只股票不同日期的 CAR 会被拆到两个不同文件的情况,十分不利于后面股权变更文件与 CAR 文件拆分后实现完美匹配。
因此,需要根据第二步得到拆分节点处的股票代码对 CAR 列表再拆分,并同时拆分股权变更列表。经过这一步后拆分后的两种文件就可以实现精确匹配。
此外,由于事先已经对股权变更文件以及 CAR 文件根据股票代码以及日期进行排过序,因此接下来的拆分只需逐行遍历,判断遍历到的观测股票代码与拆分节点处的股票代码的关系,如果遍历处股票代码大于当前拆分节点,则保存一个分拆文件,清空相关变量并开启下一个分拆文件。Python 代码如下:
import pandas as pd
dataf = pd.read_table("拆分节点.txt",header=None,encoding='utf-8',delim_whitespace=True)
dataf.columns = ['节点']
LIMIT = dataf.loc[:,'节点']
file_count = 0
url_list = []
line_count = 0
#这里的分拆文件也可以是公司股权变更事件
with open("股票日超额收益序列.txt") as f:
for line in f:
url_list.append(line)
line_count += 1
currentstkcd = int(line.split("\t")[0])
print(currentstkcd)
try:
print(LIMIT[file_count])
if (currentstkcd < LIMIT[file_count]):
continue
except:
print("已经是最后一个观测")
file_name = "CAR"+str(file_count)+".txt"
print(file_name)
with open(file_name,'w') as file:
try:
file.write(left)
except:
pass
for url in url_list[:-1]:
file.write(url)
left = url_list[-1]
url_list = []
file_count += 1
一一对应地拆完大文件之后就可以在缩小的搜索范围里countif啦,这部分思路见上一篇博文百万级大样本中的countif实现。跑完49083条数据的结果只需要12分钟,简直是飞一般的感觉(`・ω・´)。
import pandas as pd
from datetime import *
timespan = timedelta(days=1)
def getlist(add):
data = pd.read_table(add,encoding='utf-8',delim_whitespace=True)
data.columns=['stkcd','日期序列','日超额收益']
return(data)
def 区间计数(股票代码,减持日期,前置窗口长度,后置窗口长度):
减持时间戳 = datetime.strptime(减持日期,"%Y-%m-%d")
开始日期 = (减持时间戳-timespan*前置窗口长度).strftime("%Y-%m-%d")
print(开始日期)
结束日期 = (减持时间戳+timespan*后置窗口长度).strftime("%Y-%m-%d")
print(结束日期)
clist = data.loc[(data['stkcd'] == 股票代码) & (data['日期序列'] <= 结束日期) & (data['日期序列'] >= 开始日期), '日超额收益']
区间CAR = sum(clist)
return(区间CAR)
for i in range(111):
with open("15天CAR统计结果.txt",'a') as g:
try:
f = open("事件" + repr(i)+".txt",'r')
data = getlist("CAR" +repr(i)+".txt")
lines = f.readlines()
for line in lines:
stkcd = int(line.split(',')[0].split("\n")[0])
print(stkcd)
eventdate = line.split(',')[1].split("\n")[0]
事件前15天CAR = 区间计数(stkcd, eventdate, 15, 0)
事件后15天CAR = 区间计数(stkcd, eventdate, 0, 15)
print([stkcd, eventdate, 事件前15天CAR, 事件后15天CAR])
g.write(','.join([repr(stkcd), eventdate, repr(事件前15天CAR), repr(事件后15天CAR)])+'\n')
f.close()
except:
print("skip "+repr(i))
连享会计量方法专题……
股票代码 | 事件日期 | 事件前15天CAR | 事件后15天CAR | 事件前15天公告数 | 事件后15天公告数 |
---|---|---|---|---|---|
2 | 2014-03-21 | 0.192826 | 0.06398 | 18 | 6 |
2 | 2014-08-29 | -0.057021 | -0.033097 | 19 | 6 |
2 | 2014-09-16 | -0.031721 | -0.031635 | 6 | 16 |
2 | 2015-01-24 | -0.010155 | -0.107722 | 3 | 13 |
2 | 2015-01-28 | -0.069575 | -0.045201 | 6 | 10 |
2 | 2015-07-11 | 0.356788 | -0.126676 | 15 | 16 |
2 | 2015-07-25 | -0.192525 | 0.0095 | 17 | 12 |
2 | 2015-08-04 | 0.019329 | -0.120508 | 7 | 29 |
2 | 2015-08-27 | 0.142061 | -0.048584 | 22 | 11 |
2 | 2015-12-07 | 0.242967 | 0.221147 | 11 | 16 |
2 | 2015-12-09 | 0.353391 | 0.276527 | 12 | 15 |
2 | 2015-12-16 | 0.268726 | 0.124451 | 17 | 26 |
2 | 2016-07-07 | -0.27522 | -0.133624 | 46 | 18 |
2 | 2016-08-05 | 0.294057 | 0.190569 | 9 | 8 |
2 | 2016-08-09 | 0.295028 | 0.076121 | 7 | 15 |
连享会-直播课 上线了!
http://lianxh.duanshu.com
免费公开课:
直击面板数据模型 - 连玉君,时长:1小时40分钟 Stata 33 讲 - 连玉君, 每讲 15 分钟. 部分直播课 课程资料下载 (PPT,dofiles等)
支持回看,所有课程可以随时购买观看。
专题 | 嘉宾 | 直播/回看视频 |
---|---|---|
⭐ 最新专题 ⭐ | DSGE, 因果推断, 空间计量等 | |
⭕ Stata数据清洗 | 游万海 | 直播, 2 小时,已上线 |
研究设计 | 连玉君 | 我的特斯拉-实证研究设计,-幻灯片- |
面板模型 | 连玉君 | 动态面板模型,-幻灯片- |
面板模型 | 连玉君 | 直击面板数据模型 [免费公开课,2小时] |
Note: 部分课程的资料,PPT 等可以前往 连享会-直播课 主页查看,下载。
关于我们
课程, 直播, 视频, 客服, 模型设定, 研究设计, stata, plus, 绘图, 编程, 面板, 论文重现, 可视化, RDD, DID, PSM, 合成控制法
等
连享会小程序:扫一扫,看推文,看视频……
扫码加入连享会微信群,提问交流更方便
✏ 连享会学习群-常见问题解答汇总:
✨ https://gitee.com/arlionn/WD