【问题标题】:Fast conversion of numeric data into fixed width format file in Python在 Python 中将数值数据快速转换为固定宽度的格式文件
【发布时间】:2010-12-23 16:57:24
【问题描述】:

在 Python 中将仅包含数字数据的记录转换为固定格式字符串并将它们写入文件的最快方法是什么?例如,假设record 是一个巨大的列表,由具有idxywt 属性的对象组成,我们经常需要将它们刷新到外部文件。可以使用以下 sn -p 完成刷新:

with open(serial_fname(), "w") as f: 
    for r in records:
        f.write("%07d %11.5e %11.5e %7.5f\n" % (r.id, r.x, r.y, r.wt))

但是我的代码花费了太多时间来生成外部文件,而在两次刷新之间做它应该做的事情的时间太少了。

修改原问题:

我在编写服务器软件时遇到这个问题实时预处理形式。许多消费者系统都是 Matlab 应用程序。

我在下面列出了到目前为止我收到的一些建议(感谢)一些 cmets:

  • 只转储更改,而不是整个数据集:我实际上已经在这样做了。由此产生的变更集仍然很大。
  • 使用二进制(或其他更有效的)文件格式:我非常受制于 Matlab 可以合理有效地读取的内容,此外格式应该与平台无关。
  • 使用数据库:我实际上是在尝试绕过当前的数据库解决方案,这种解决方案被认为既慢又麻烦,尤其是在 Matlab 方面。
  • 将任务划分为单独的进程: 目前转储代码正在其自己的线程中运行。然而,由于 GIL,它仍然使用相同的内核。我想我可以将其移至完全独立的进程。

【问题讨论】:

    标签: python performance large-data-volumes


    【解决方案1】:

    您可以尝试使用 ctypes 将循环推送到 C。

    【讨论】:

      【解决方案2】:

      我试图检查 numpy.savetxt 是否可以加快速度,所以我编写了以下模拟:

      import sys
      import numpy as np
      
      fmt = '%7.0f %11.5e %11.5e %7.5f'
      records = 10000
      
      np.random.seed(1234)
      aray = np.random.rand(records, 4)
      
      def writ(f, aray=aray, fmt=fmt):
        fw = f.write
        for row in aray:
          fw(fmt % tuple(row))
      
      def prin(f, aray=aray, fmt=fmt):
        for row in aray:
          print>>f, fmt % tuple(row)
      
      def stxt(f, aray=aray, fmt=fmt):
        np.savetxt(f, aray, fmt)
      
      nul = open('/dev/null', 'w')
      def tonul(func, nul=nul):
        func(nul)
      
      def main():
        print 'looping:'
        loop(sys.stdout, aray)
        print 'savetxt:'
        savetxt(sys.stdout, aray)
      

      我发现结果(在我的 2.4 GHz Core Duo Macbook Pro、Mac OS X 10.5.8、python.org 上 DMG 的 Python 2.5.4、从源代码构建的 numpy 1.4 rc1 上)有点令人惊讶,但它们re 相当可重复,所以我认为他们可能会感兴趣:

      $ py25 -mtimeit -s'import ft' 'ft.tonul(ft.writ)'
      10 loops, best of 3: 101 msec per loop
      $ py25 -mtimeit -s'import ft' 'ft.tonul(ft.prin)'
      10 loops, best of 3: 98.3 msec per loop
      $ py25 -mtimeit -s'import ft' 'ft.tonul(ft.stxt)'
      10 loops, best of 3: 104 msec per loop
      

      所以,savetxt 似乎比调用write 的循环慢了几个百分点...但是好旧的print(也在循环中)似乎是几个百分点write (我想它避免了某种调用开销)。我意识到 2.5% 左右的差异并不是很重要,但这并不是我直觉预期的方向,所以我想我会报告它。 (顺便说一句,使用真实文件而不是 /dev/null 只会增加 6 或 7 毫秒,因此无论哪种方式都不会改变太多)。

      【讨论】:

        【解决方案3】:

        现在您更新了您的问题,我对您所面临的问题有了更好的了解。

        我不知道“当前被认为既慢又麻烦的数据库解决方案”是什么,但我仍然认为如果使用得当,数据库会有所帮助。

        运行 Python 代码来收集数据,并使用 ORM 模块将数据插入/更新到数据库中。然后运行一个单独的进程来制作一个“报告”,这将是固定宽度的文本文件。数据库将完成所有生成文本文件的工作。如有必要,将数据库放在自己的服务器上,因为现在硬件非常便宜。

        【讨论】:

          【解决方案4】:

          我没有看到任何关于我可以真正优化的代码的 sn-p。所以,我认为我们需要做一些完全不同的事情来解决你的问题。

          您的问题似乎是您正在咀嚼大量数据,并且将数据格式化为字符串并将字符串写入文件很慢。您说的“刷新”意味着您需要定期保存数据。

          您是定期保存所有数据,还是只保存更改的数据?如果您正在处理一个非常大的数据集,只更改一些数据,然后写入所有数据……我们可以从这个角度来解决您的问题。

          如果您有一个大型数据集,并且想要不时更新它……您是数据库的候选人。一个真正的数据库,为了速度而用 C 语言编写,可以让你向它抛出大量数据更新,并将所有记录保持在一致的状态。然后,您可以每隔一段时间运行一个“报告”,它将提取记录并从中写入您的固定宽度文本文件。

          换句话说,我建议您将问题分为两部分:在计算或接收更多数据时逐步更新数据集,并将整个数据集转储为固定宽度的文本格式,以供您进一步处理.

          请注意,您实际上可以从数据库生成文本文件,而无需停止正在更新它的 Python 进程。你会得到一个不完整的快照,但如果记录是独立的,那应该没问题。

          如果您的进一步处理也在 Python 中,您可以将数据永远留在数据库中。不要费心通过固定宽度的文本文件来回传输数据。我假设您使用的是固定宽度的文本文件,因为很容易再次提取数据以供将来处理。

          如果你使用数据库的思想,尝试使用PostgreSQL。它是免费的,它是一个真正的数据库。要在 Python 中使用数据库,您应该使用 ORM。最好的之一是 SqlAlchemy。

          要考虑的另一件事:如果您将数据保存为固定宽度的文本文件格式以供将来在另一个应用程序中解析和使用数据,并且如果该应用程序可以读取 JSON 以及固定宽度,也许您可以使用编写 JSON 的 C 模块。它可能不会更快,但它可能;您可以对其进行基准测试并查看。

          除上述之外,我唯一的另一个想法是将您的程序拆分为“worker”部分和“updater”部分,其中worker 生成更新的记录,而updater 部分将记录保存到磁盘。或许通过让工作人员将更新的记录以文本格式输出到标准输出来让他们进行交流;并让更新程序从标准输入中读取并更新其数据记录。更新程序可以使用字典来存储文本记录,而不是 SQL 数据库;随着新的到来,它可以简单地更新字典。像这样的:

          for line in sys.stdin:
              id = line[:7]  # fixed width: id is 7 wide
              records[id] = line # will insert or update as needed
          

          您实际上可以让更新程序保留两个字典,并在另一个写入磁盘的同时不断更新一个。

          将工作程序和更新程序分开是确保工作程序不会花费所有时间进行更新的好方法,也是平衡多个 CPU 内核工作的好方法。

          我暂时没有想法。

          【讨论】:

            【解决方案5】:

            您可以尝试在内存中构建所有输出字符串,例如使用长字符串。 然后将这个长字符串写入文件中。

            更快: 您可能希望使用二进制文件而不是文本文件来记录信息。但是你需要编写另一个工具来查看二进制文件。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-10-24
              • 2012-02-11
              • 2015-09-11
              • 2015-04-18
              相关资源
              最近更新 更多