Python:拆分文件让百万级数据运行速度提高135倍

发布时间:2020-10-07 阅读 2451

Stata 连享会   主页 || 视频 || 推文

温馨提示: 定期 清理浏览器缓存,可以获得最佳浏览体验。

课程详情 https://gitee.com/arlionn/Course   |   lianxh.cn

课程主页 https://gitee.com/arlionn/Course

作者:许梦洁 (中山大学)

Stata 连享会: 知乎 | 简书 | 码云


目录


语言:Python 方法:拆分文件 目的:提高运行速度

一、任务描述

对2010年后49083条上市公司股权变更数据(Firm-Event 观测)分别统计每个事件发生前后15天公司:

  • 发布的临时公告数
  • 累计超额收益(CAR)

二、数据描述

数据集 总样本数 2010年后的样本数
上市公司股权变更记录 57584 49083
上市公司公告记录 2787026 2758934
上市公司日超额收益 9749464 5534947

三、解决思路

Python 构造一个类似于 Excel 中的 countif 函数即可。具体见我上一篇博文 [百万级大样本中的 countif 实现]

四、潜在问题

虽然按照上一篇文章的思路也能基本完成任务,但是程序运行非常慢,光跑一次统计窗口期公告数据的程序就要27个小时,所以必须优化程序以提高运行速度。

五、优化思路

由于全样本非常大,上篇博文的程序相当于是对每一个公司股东股权变动事件在全部的公告池(2758934条记录)中进行搜索,显然这样做是无效率的。

因此,此次程序优化的主要思路是分别拆分 49083 条公司股东股权变动事件和 2758934 条上市公司公告记录,并将两类拆分的文件对应起来。举个例子:

  • 第一步: 将股票代码在 000001 到 000049 的公司股权变动事件拆出来
  • 第二步: 将股票代码在000001 到 000049 的公司公告拆出来形成一个小的公告搜索池,然后对这部分公司的股权事件在这个小搜索池里统计公告记录。

通过拆分文件得到的精确匹配大大减小了每一个事件的搜索范围,可以大幅提高程序运行效率。优化后跑一次同样的统计窗口期公告数据程序仅需 12 分钟,是原来运行速度的 135 倍。

六、核心代码(以统计窗口期CAR为例)

1. 初步拆分

为了保证运行效率最高,首先平均分拆 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

2. 根据分拆文件记录拆分股票节点

遍历拆出来的 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()

连享会计量方法专题……

3. 根据拆分节点拆分事件列表并再拆分 CAR 列表

由于初步拆分是根据样本数拆分,因此出现了同一只股票不同日期的 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

4. 基于拆分后的事件列表和日期序列统计数据

一一对应地拆完大文件之后就可以在缩小的搜索范围里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

免费公开课:


课程一览

支持回看,所有课程可以随时购买观看。

专题 嘉宾 直播/回看视频
最新专题 DSGE, 因果推断, 空间计量等
Stata数据清洗 游万海 直播, 2 小时,已上线
研究设计 连玉君 我的特斯拉-实证研究设计-幻灯片-
面板模型 连玉君 动态面板模型-幻灯片-
面板模型 连玉君 直击面板数据模型 [免费公开课,2小时]

Note: 部分课程的资料,PPT 等可以前往 连享会-直播课 主页查看,下载。


关于我们

  • Stata连享会 由中山大学连玉君老师团队创办,定期分享实证分析经验。直播间 有很多视频课程,可以随时观看。
  • 连享会-主页知乎专栏,300+ 推文,实证分析不再抓狂。
  • 公众号推文分类: 计量专题 | 分类推文 | 资源工具。推文分成 内生性 | 空间计量 | 时序面板 | 结果输出 | 交乘调节 五类,主流方法介绍一目了然:DID, RDD, IV, GMM, FE, Probit 等。
  • 公众号关键词搜索/回复 功能已经上线。大家可以在公众号左下角点击键盘图标,输入简要关键词,以便快速呈现历史推文,获取工具软件和数据下载。常见关键词:课程, 直播, 视频, 客服, 模型设定, 研究设计, stata, plus, 绘图, 编程, 面板, 论文重现, 可视化, RDD, DID, PSM, 合成控制法

连享会主页  lianxh.cn
连享会主页 lianxh.cn

连享会小程序:扫一扫,看推文,看视频……

扫码加入连享会微信群,提问交流更方便

✏ 连享会学习群-常见问题解答汇总:
https://gitee.com/arlionn/WD