【发布时间】:2016-12-19 10:48:08
【问题描述】:
我有一个大的 CSV 文件,其中包含格式如下的股票相关数据:
股票代码、日期、[一些变量...]
因此,每一行都以符号(如“AMZN”)开头,然后是日期,然后是与所选日期的价格或交易量相关的 12 个变量。该文件中代表了大约 10,000 种不同的证券,我每天都有一条线表示股票已公开交易。该文件首先按股票代码的字母顺序排序,然后按时间顺序按日期排序。整个文件大约 3.3 GB。
我想要解决的任务是能够提取给定股票代码相对于当前日期的最新 n 行数据。我有执行此操作的代码,但根据我的观察,每次检索似乎平均需要大约 8-10 秒(所有测试都提取了 100 行)。
我有一些我想运行的功能需要我为成百上千个符号抓取这样的块,我真的很想减少时间。我的代码效率低下,但我不知道如何让它运行得更快。
首先,我有一个名为 getData 的函数:
def getData(symbol, filename):
out = ["Symbol","Date","Open","High","Low","Close","Volume","Dividend",
"Split","Adj_Open","Adj_High","Adj_Low","Adj_Close","Adj_Volume"]
l = len(symbol)
beforeMatch = True
with open(filename, 'r') as f:
for line in f:
match = checkMatch(symbol, l, line)
if beforeMatch and match:
beforeMatch = False
out.append(formatLineData(line[:-1].split(",")))
elif not beforeMatch and match:
out.append(formatLineData(line[:-1].split(",")))
elif not beforeMatch and not match:
break
return out
(这段代码有几个辅助函数,checkMatch 和 formatLineData,我将在下面展示。)然后,还有另一个名为 getDataColumn 的函数可以获取我想要的列,并表示正确的天数:
def getDataColumn(symbol, col=12, numDays=100, changeRateTransform=False):
dataset = getData(symbol)
if not changeRateTransform:
column = [day[col] for day in dataset[-numDays:]]
else:
n = len(dataset)
column = [(dataset[i][col] - dataset[i-1][col])/dataset[i-1][col] for i in range(n - numDays, n)]
return column
(如果为真,changeRateTransform 将原始数字转换为每日变化率数字。)辅助函数:
def checkMatch(symbol, symbolLength, line):
out = False
if line[:symbolLength+1] == symbol + ",":
out = True
return out
def formatLineData(lineData):
out = [lineData[0]]
out.append(datetime.strptime(lineData[1], '%Y-%m-%d').date())
out += [float(d) for d in lineData[2:6]]
out += [int(float(d)) for d in lineData[6:9]]
out += [float(d) for d in lineData[9:13]]
out.append(int(float(lineData[13])))
return out
是否有人对我的代码的哪些部分运行缓慢以及如何使其表现更好有任何见解?如果不加快速度,我就无法进行我想做的那种分析。
编辑: 为了响应 cmets,我对代码进行了一些更改,以便利用 csv 模块中的现有方法:
def getData(symbol, database):
out = ["Symbol","Date","Open","High","Low","Close","Volume","Dividend",
"Split","Adj_Open","Adj_High","Adj_Low","Adj_Close","Adj_Volume"]
l = len(symbol)
beforeMatch = True
with open(database, 'r') as f:
databaseReader = csv.reader(f, delimiter=",")
for row in databaseReader:
match = (row[0] == symbol)
if beforeMatch and match:
beforeMatch = False
out.append(formatLineData(row))
elif not beforeMatch and match:
out.append(formatLineData(row))
elif not beforeMatch and not match:
break
return out
def getDataColumn(dataset, col=12, numDays=100, changeRateTransform=False):
if not changeRateTransform:
out = [day[col] for day in dataset[-numDays:]]
else:
n = len(dataset)
out = [(dataset[i][col] - dataset[i-1][col])/dataset[i-1][col] for i in range(n - numDays, n)]
return out
使用 csv.reader 类的性能更差。我测试了两只股票,AMZN(靠近文件顶部)和 ZNGA(靠近文件底部)。使用原始方法,运行时间分别为 0.99 秒和 18.37 秒。使用利用 csv 模块的新方法,运行时间分别为 3.04 秒和 64.94 秒。两者都返回正确的结果。
我的想法是,查找库存比解析所占用的时间更多。如果我在文件 A 中的第一只股票上尝试这些方法,这些方法都在大约 0.12 秒内运行。
【问题讨论】:
-
你应该使用 csv 模块而不是在这里滚动你自己的。 pandas csv 阅读器还可以大大加快摄取速度。
-
对于重复查询,另一个选项是将整个内容放入 sqlite 并添加一些索引。
-
您可以考虑使用生成器,将所有
out.append(...)替换为yield ...,以便函数可以延迟执行(为不断增长的列表重新分配空间更少) -
我会将其全部写入数据库,然后查询所需的输出。
-
@JeffDavis 是的,这使得缩小特定性能问题的范围有点棘手。但我认为我们已经确定了几个不同的领域。一是摄取速度——我认为 pandas 很好地涵盖了这一点。另一个是处理速度,如果您的数据集是内存中的数据集,pandas 可能会再次覆盖该处理速度。最后,这是关于静态数据集还是持续更新的数据集。
标签: python performance csv