我目前正在使用以下代码导入 6,000 个 csv 文件(带标题)并将它们导出到单个 csv 文件(带单个标题行)。
#import csv files from folder
path =r'data/US/market/merged_data'
allFiles = glob.glob(path + "/*.csv")
stockstats_data = pd.DataFrame()
list_ = []
for file_ in allFiles:
df = pd.read_csv(file_,index_col=None,)
list_.append(df)
stockstats_data = pd.concat(list_)
print(file_ + " has been imported.")
这段代码工作正常,但速度很慢。最多可能需要 2 天的时间来处理。
我得到了一个用于终端命令行的单行脚本,它执行相同的操作(但没有标题)。此脚本需要 20 秒。
for f in *.csv; do cat "`pwd`/$f" | tail -n +2 >> merged.csv; done
有谁知道如何加速第一个 Python 脚本?为了缩短时间,我考虑过不将其导入 DataFrame 并只是连接 CSV,但我无法弄清楚。
谢谢。
原文由 mattblack 发布,翻译遵循 CC BY-SA 4.0 许可协议
如果您不需要内存中的 CSV,只需从输入复制到输出,那么完全避免解析和复制而不在内存中构建会便宜得多:
就是这样;
shutil.copyfileobj
有效地处理数据复制,大大减少了解析和重新序列化的 Python 级别工作。不要省略 `allFiles.sort()!†这假设所有 CSV 文件都具有相同的格式、编码、行尾等,编码编码使得换行符显示为等效于 ASCII
\n
的单个字节并且它是字符中的最后一个字节(所以ASCII 和所有 ASCII 超集编码都有效,UTF-16-BE 和 UTF-32-BE 也有效,但 UTF-16-LE 和 UTF-32-LE 无效)并且标头不包含嵌入的换行符,但如果那是案例,它比替代品快得多。对于换行符的编码版本看起来不够像 ASCII 换行符,或者输入文件采用一种编码而输出文件应采用不同编码的情况,您可以添加编码和解码的工作不添加 CSV 解析/序列化工作,使用(添加
from io import open
如果在 Python 2 上,以获得类似 Python 3 的高效编码感知文件对象,并定义known_input_encoding
到一些字符串表示输入文件的已知编码,例如known_input_encoding = 'utf-16-le'
,以及可选的输出文件的不同编码):这仍然比涉及
csv
模块 _快得多_,尤其是在现代 Python 中(其中io
模块已经进行了越来越大的优化,以至于解码和重新编码的成本是非常小,尤其是首先执行 I/O 的成本)。即使编码不应该更改,它也是自检查编码(例如 UTF 系列)的良好有效性检查;如果数据与假定的自检编码不匹配,则极不可能有效解码,因此您将得到一个异常而不是无声的不当行为。因为这里链接 的一些重复项 正在寻找比
copyfileobj
更快的解决方案,一些选项:The only succinct, reasonably portable option is to continue using
copyfileobj
and explicitly pass a non-defaultlength
parameter, egshutil.copyfileobj(infile, outfile, 1 << 20)
(1 << 20
是 1 MiB,这个数字shutil
已经切换到普通shutil.copyfile
由于卓越的性能而在 Windows 上调用)。仍然可移植,但仅适用于二进制文件而不简洁,将复制底层代码
copyfile
在 Windows 上使用,它使用可重用的bytearray
缓冲区,其大小大于copyfileobj
的默认值(1 MiB,而不是 64 KiB),消除一些分配开销copyfileobj
不能完全避免大缓冲区。 You’d replaceshutil.copyfileobj(infile, outfile)
with (3.8+’s walrus operator,:=
, used for brevity) the following code adapted from CPython 3.10’s implementation ofshutil._copyfileobj_readinto
(如果您不介意使用非公共 API,您可以直接使用它):shutil.copyfile
在后台使用复制每个文件的非标头部分,使用特定于操作系统的 API 可以通过单个系统调用完成工作(无论文件大小)并避免额外的数据副本(通过将所有工作推送到内核甚至文件系统操作中,从用户空间删除副本)例如:一种。在 Linux 内核 2.6.33 及更高版本(以及允许
sendfile(2)
系统调用在打开的文件之间工作的任何其他操作系统)上,您可以替换.readline()
和copyfileobj
调用:为了使其具有弹性信号,可能需要检查
sendfile
的返回值,并跟踪发送的字节数+跳过的字节数和剩余的字节数,循环直到你将它们全部复制(这些很低级系统调用,它们可以被中断)。关于检查复制的字节数是否少于预期并重试的类似警告。
它假设
fcopyfile
注意搜索位置(文档不是 100% 的),并且如前所述,它不仅是特定于 macOS 的,而且使用未记录的 CPython 内部结构,这些内部结构可能会在任何版本中发生变化。† 关于对
glob
的结果进行排序的旁白:不应省略allFiles.sort()
调用;glob
不对结果施加任何排序,并且为了可重现的结果,您需要施加 一些 排序(如果具有相同名称和数据的相同文件生成输出文件,那就不好了顺序不同只是因为在运行之间,文件被移出目录,然后又移回目录,并更改了本机迭代顺序)。如果没有sort
调用,此代码(以及所有其他 Python+glob 模块答案)将无法可靠地从包含a.csv
和b.csv
(或字母表)的目录中读取任何其他有用的)命令;它会因操作系统、文件系统以及相关目录中文件创建/删除的整个历史而异。这在现实世界中已经破坏了一些东西,请参阅 代码故障可能导致超过 100 项已发表研究中的错误的 详细信息。