在Python中存储大量数据

6
也许我可以先介绍一下我的问题。我正在编写一个Python程序,用于后处理不同的物理模拟。每个模拟最多可以创建100 GB的输出。我需要处理不同时间步长的不同信息(如位置、场和密度等)。我希望一次性访问所有这些数据,但由于我的系统内存不足,这是不可能的。通常我使用读取文件,然后进行一些操作并清除内存。然后我读取其他数据,再进行一些操作并清除内存。
现在我的问题是,如果我这样做,那么我将花费大量的时间来重复读取数据,这需要很长时间。我希望只读取一次并将其存储以便轻松访问。你知道一种快速存储大量数据的方法或者一种不需要很多空间的方法吗?
我刚刚创建了一种比普通的打开-读取方法快十倍左右的方法。但我使用了cat( Linux命令)。这是一种非常粗糙的方法,我希望将其从我的脚本中删除。
是否可以使用数据库来存储这些数据,并比正常读取更快地获取数据?(对不起,我不是计算机科学家,我对数据库没有很多了解)。
out = string.split(os.popen("cat "+base+"phs/phs01_00023_"+time).read())
# and if I want to have this data as arrays then I normally use and reshape (if I
# need it)
out = array(out)
out = reshape(out)

通常我会使用numpy方法numpy.loadtxt,需要与普通读取相同的时间。

f = open('filename')
f.read()
...

我认为loadtxt只是使用了正常方法,并添加了一些代码行。

我知道有一些更好的读取数据的方法。但我发现它们都非常慢。现在我将尝试使用mmap,希望能获得更好的性能。


你能否编辑你的问题,以包含更具体的要求?需要进行哪种操作?一次需要操作多少数据集?这些操作是流式的还是需要在内存中进行完整子集?你的 cat 方法和 open-read 方法是什么?(有时候小细节会真正拖慢事情的进展) - payne
“这是一个非常不规范的方法”?这是什么意思?它能正常工作,对吗?为什么它被称为“不规范”?也许你应该把代码包含进来。 - S.Lott
在100GB的情况下,你可能需要一些其他的数据存储/数据库。可以是从tokyocabinet到mongodb再到redis、sqlite和postgresql等任何东西。这都是权衡的问题,你可能是唯一能确定你真正需要什么的人。 - chmullig
脏话是使用Linux命令。我认为它在Windows上不起作用。我从未测试过,因为我身边没有任何Windows机器。 - ahelm
3个回答

7

我建议使用HDF5。有两种常用的Python接口,h5pyPyTables。虽然后者似乎更为广泛,但我更喜欢前者。


感谢您的解决方案。对于这种情况,我需要将我的数据保存在HDF5格式中,是吗?我的模拟结果采用标准的FORTRAN格式。如果我理解正确的话,我可以使用FORTRAN API。但我不想改变FORTRAN代码。我没有编写模拟代码,也不是我的代码。也许我可以在读取后将普通数据转换为HDF5格式。 - ahelm
如果您的数据结构不太复杂,编写一个读取数据并将其以HDF5格式写入的转换器应该相当容易 -- 如果使用Python代码,可能只需要几行。但是,也许您可以先尝试使用mmap,这是Greg的建议 -- 如果这对您有效,那么更容易些。 - Sven Marnach

7
如果您使用的是64位操作系统,您可以使用mmap模块将整个文件映射到内存空间中。然后,由于操作系统负责管理您的访问模式,因此可以更快地读取数据的随机位。请注意,实际上您并不需要100 GB的RAM才能使其工作,因为操作系统将在虚拟内存中管理所有内容。
我曾在64位FreeBSD 8上使用一个30 GB的文件(维基百科XML文章转储)进行过这样的操作,并且效果非常好。

0

如果你在处理大型数据集,Python 可能不是最佳选择。如果想使用像 MySQL 或 Postgres 这样的数据库,应该尝试使用 SQLAlchemy。它可以通过小型 Python 对象轻松地处理可能很大的数据集。例如,如果你使用这样的定义:

from datetime import datetime
from sqlalchemy import Column, DateTime, Enum, ForeignKey, Integer, \
    MetaData, PickleType, String, Text, Table, LargeBinary
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import column_property, deferred, object_session, \
    relation, backref

SqlaBaseClass = declarative_base()

class MyDataObject(SqlaBaseClass):
  __tablename__ = 'datarows'
  eltid         = Column(Integer, primary_key=True)
  name          = Column(String(50, convert_unicode=True), nullable=False, unique=True, index=True)
  created       = Column(DateTime)
  updated       = Column(DateTime, default=datetime.today)

  mylargecontent = deferred(Column(LargeBinary))

  def __init__(self, name):
      self.name    = name
      self.created = datetime.today()

  def __repr__(self):
      return "<MyDataObject name='%s'>" %(self.name,)

然后,您可以使用小数据对象轻松访问所有行:

# set up database connection; open dbsession; ... 

for elt in dbsession.query(MyDataObject).all():
    print elt.eltid # does not access mylargecontent

    if (something(elt)):
        process(elt.mylargecontent) # now large binary is pulled from db server
                                    # on demand

我想重点是:您可以添加任意数量的字段到您的数据中,根据需要添加索引以加快搜索速度。而且,最重要的是,当您使用一个MyDataObject时,您可以将潜在的大字段设置为deferred,这样它们只有在需要时才会被加载。

对于Python的评论点赞,但我认为它不适合高负载软件的选项。 - Terseus
-1 对于 Python 的注释,科学 Python 用户经常处理大型数据集。 - olokki
“Python可能不是你最佳的选择”我认为这并不是一个有争议的说法,而我回答中的其余部分确实解决了如何在Python中实现这一问题。 - phooji

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