如何使用Python快速将数据加载到内存中?

3
我有一个大的csv文件(5 GB),我可以使用读取它。这个操作需要很多时间,需要10-20分钟。
如何加快速度?
将数据转换为格式是否有用?如果是的话,我该怎么做?
编辑:更多信息:
数据包含1852列和350000行。大多数列都是float65类型,并包含数字。一些其他列包含字符串或日期(我认为被视为字符串)。
我正在使用一台带有16 GB RAM和SSD硬盘的笔记本电脑。数据应该可以很好地适应内存(但我知道Python倾向于增加数据大小)。
编辑2:
在加载过程中,我收到此消息:
/usr/local/lib/python3.4/dist-packages/pandas/io/parsers.py:1164: DtypeWarning: Columns (1841,1842,1844) have mixed types. Specify dtype option on import or set low_memory=False.
  data = self._reader.read(nrows)

编辑:解决方案

读取一次csv文件并将其保存为 data.to_hdf('data.h5', 'table') 这种格式非常高效。


使用一个含混的问题告诉我们有关格式除了它是“CSV”和读取速度较慢之外什么都不清楚,很难给你一个不那么含混的答案。例如,如果SQLite是答案,我无法在不知道列是什么以及您需要进行什么样的搜索的情况下向您展示要生成的SQL代码。 - abarnert
这并不是一个含糊的问题。他们必须一遍又一遍地加载文件,那么如何更快地完成呢? - Dan
从EDIT 2:警告实际上为您提供了一些有用的信息。 您让它猜测列的数据类型;如果您确实知道它们,您可能会获得更好的性能和更正确或有用的结果。而且我不知道low_memory=False标志是什么意思,但它肯定听起来像是在文档中查找有帮助的东西,因为它可能对此处有所帮助。 - abarnert
@Dan:这个问题并不模糊,不能仅仅通过评论和回答来关闭它。但是如果他提供更多信息,我(或其他人)可能会写出更好的答案,这就是为什么我进行了评论的原因。 - abarnert
3个回答

4
这实际上取决于读取的哪个部分需要花费10分钟。
  • 如果实际上是从磁盘读取,那么任何更紧凑的数据形式都会更好。
  • 如果处理CSV格式(您可以通过CPU在一个核心上接近100%进行读取来判断;对于其他两个核心,它将非常低),则需要预处理的形式。
  • 如果正在交换内存,例如,因为您只有2GB的物理RAM,则除了拆分数据外,没有任何帮助。

重要的是要知道您的问题是哪个。例如,流压缩数据(例如使用gzip)将大大改善第一个问题,但会使第二个问题变得更糟。

听起来您可能遇到了第二个问题,这是个好消息。(但是,无论问题是什么,您都可以做一些更好的事情。)


您将其存储在SQLite数据库中的想法很好,因为它至少可以潜在地解决所有三个问题;您只需要按需从磁盘读取数据,并以相对紧凑且易于处理的形式存储它。 但它不是前两个问题的最佳解决方案,只是一个“相当不错”的解决方案。
特别是,如果您实际上确实需要在所有350000行上进行数组范围内的工作,并且无法将该工作转换为SQL查询,则不会从sqlite中获得太多好处。 最终,您将执行一个巨大的SELECT,将所有数据全部拉入,然后将其全部处理成一个大框架。
将形状和结构信息写出,然后以NumPy二进制形式编写底层数组。然后,对于读取,您需要进行反向操作。NumPy的二进制格式只是尽可能紧凑地存储原始数据的格式,它是一种可以快速写入的格式(基本上只是将原始内存存储转储到磁盘上)。这将改善第一个和第二个问题。
同样地,使用HDF5(无论是使用Pandas IO还是外部库,如PyTables或h5py)存储数据将改善第一个和第二个问题。HDF5旨在成为一种相对紧凑且简单的格式,用于存储通常在Pandas中存储的相同类型的数据。(它包括可选压缩作为内置功能,因此如果您知道其中哪一个,您可以进行调整。)它不会像最后一种选择那样完全解决第二个问题,但可能足够好,并且更简单(一旦您设置好HDF5库)。
最后,将数据进行pickling有时可能会更快。pickle是Python的本地序列化格式,并且可以被第三方模块挂钩 - NumPy和Pandas都已经挂钩,以便对其数据进行良好的pickling处理。
(虽然这不适用于这个问题,但它可能会帮助以后搜索此内容的人:如果您正在使用Python 2.x,请确保明确使用pickle format 2;我记得,NumPy在默认pickle格式0上非常糟糕。在Python 3.0+中,这不相关,因为默认格式至少为3。)

我认为延迟是由于处理造成的,因为只有一个CPU工作在100%。在这种情况下我该怎么办? - Donbeo
好的,那么您只需要一种需要更少处理的格式。正如答案所解释的那样,NumPy的二进制数组格式(加上存储数组形状和Pandas结构的包装器信息)和HDF5都符合要求。 - abarnert

0

Python有两个内置库 picklecPickle,可以存储任何Python数据结构。 cPicklepickle相同,只是cPickle在处理Unicode方面存在问题,并且速度快1000倍。 这两个库对于保存将以某种形式重新加载到Python中的内容非常方便,因为您不必担心文件I/O中出现某种错误。

通过使用一些XML文件,我发现从加载pickle而不是原始XML中获得了一些性能提升。我不完全确定性能如何与CSV相比,但值得一试,特别是如果您不必担心Unicode问题并且可以使用cPickle。它也很简单,因此,如果它不足够好,您可以转向其他方法而不会浪费太多时间。

一个简单的用法示例:

>>> import pickle
>>> stuff = ["Here's", "a", "list", "of", "tokens"]
>>> fstream = open("test.pkl", "wb")
>>> pickle.dump(stuff,fstream)
>>> fstream.close()
>>> 
>>> fstream2 = open("test.pkl", "rb")
>>> old_stuff = pickle.load(fstream2)
>>> fstream2.close()
>>> old_stuff
["Here's", 'a', 'list', 'of', 'tokens']
>>> 

注意文件流打开器中的“b”。这很重要——它保留了pickle的跨平台兼容性。我以前曾经没有做到这一点,结果让我后悔不已。

对于你的东西,我建议先编写一个解析CSV并将其保存为pickle的脚本;当你进行分析时,与之相关联的脚本会像上面第二个代码块中那样加载pickle。

我已经尝试过使用XML;我很好奇你使用CSV会得到多少提升。


2
首先,cPickle 是 Python 2 特有的,而 OP 使用的是 3.4 版本,它只有一个 pickle 库,并且会自动使用 C 加速器(如果适用)。实际上,这对于 NumPy(因此也包括 Pandas)数据通常没有太大帮助,因为 NumPy 基本上可以自己完成大部分工作。使用格式 2 和 0 相比更重要,但这与 3.4 版本无关,因为默认格式是 3,而不是 0。 - abarnert
1
其次,解析XML实际上是很耗费资源的;而CSV则不然。但你肯定是对的,这值得一试;最坏的情况就是他浪费了30分钟来测试它(或者,如果他聪明的话,他会花5分钟准备一个较小的数据集并测试它),而且如果它有帮助并且足够好,那么这个潜在的长期节省肯定是值得的。 - abarnert
另外,pickle存在安全风险,因为它从二进制形式加载。正如文档中所述 - https://docs.python.org/3/library/pickle.html,“您必须小心允许什么被反序列化。因此,如果安全是一个问题,”。 - Muhammad Khuzaima Umair

0
如果问题在于处理开销过大,那么您可以将文件分成较小的文件,并在不同的CPU核心或线程中处理它们。对于某些算法,Python时间会呈非线性增长,而分割方法将有助于解决这些情况。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接