【问题标题】:Fastest way to generate delimited string from 1d numpy array从 1d numpy 数组生成分隔字符串的最快方法
【发布时间】:2010-04-27 13:20:43
【问题描述】:

我有一个程序需要将许多大型一维 numpy 浮点数组转换为分隔字符串。我发现这个操作相对于我程序中的数学运算非常慢,我想知道是否有办法加快它。例如,考虑以下循环,它在一个 numpy 数组中获取 100,000 个随机数,并将每个数组连接成一个逗号分隔的字符串。

import numpy as np
x = np.random.randn(100000)
for i in range(100):
    ",".join(map(str, x))

此循环大约需要 20 秒才能完成(总计,不是每个循环)。相比之下,考虑像元素乘法 (x*x) 这样的 100 个循环将花费不到 1/10 秒的时间来完成。显然,字符串连接操作会造成很大的性能瓶颈;在我的实际应用程序中,它将主导总运行时间。这让我想知道,有没有比 ",".join(map(str, x)) 更快的方法?由于 map() 是几乎所有处理时间发生的地方,这归结为是否有更快的方法将大量数字转换为字符串。

【问题讨论】:

  • 是数字到字符串的转换需要时间吗?
  • 乘以两个整数和转换/连接 100,000 个数字是完全不同的事情 - 你怎么能期望这两个操作在性能方面甚至在同一个球场?
  • 马克 - 是的。蒂姆——没错。我的观点很简单,字符串操作会造成真正的瓶颈,如果有办法加快速度就好了。
  • float.hexstr 快 25%。它可以使用"%a"格式以其他语言读回。

标签: python numpy


【解决方案1】:

有点晚了,但这对我来说更快:

#generate an array with strings
x_arrstr = np.char.mod('%f', x)
#combine to a string
x_str = ",".join(x_arrstr)

在我的机器上加速大约 1.5 倍

【讨论】:

    【解决方案2】:

    关于 Python 中各种字符串连接技术性能的非常好的文章:http://www.skymind.com/~ocrow/python_string/

    我有点惊讶于后一种方法的表现和它们一样好,但看起来你肯定可以在那里找到比你在那里做的更适合你的东西。

    网站上提到的最快方法

    方法 6:列表推导

    def method6():
      return ''.join([`num` for num in xrange(loop_count)])
    

    这种方法是最短的。我会破坏惊喜并告诉你这是 也是最快的。它非常紧凑,也很漂亮 可以理解的。使用列表推导创建数字列表 然后将它们加入到一起。再简单不过了。这 实际上只是方法 4 的简化版本,它消耗 几乎相同数量的内存。但它更快,因为我们 不必每次都调用 list.append() 函数 循环。

    【讨论】:

    • 谢谢 sblom。不幸的是,我的代码已经与提到的最快的解决方案基本相同。也许没有办法让它跑得更快。
    • @Abiel 如果你真的想要更快,那么你应该考虑使用 Cython。
    • 我认为通常人们喜欢答案解释要做什么,而不是仅仅链接到将来可能会或可能不会被删除的页面。
    【解决方案3】:

    首先将numpy数组转换为列表。 map 操作在列表上似乎比在 numpy 数组上运行得更快。

    例如

    import numpy as np
    x = np.random.randn(100000).tolist()
    for i in range(100):
        ",".join(map(str, x))
    

    在计时测试中,我发现此示例的速度始终保持 15% 的速度

    我会留下其他人来解释为什么这可能会更快,因为我不知道!

    【讨论】:

      【解决方案4】:

      我认为您可以尝试 numpy.savetxtcStringIO.StringIO 对象作为假文件传递...

      或者也许使用str(x) 并用逗号替换空格(编辑:这不会很好,因为str 会省略大型数组:-s)。

      由于这样做的目的是通过网络发送阵列,也许有更好的选择(在 cpu 和带宽方面更有效)。我在对其他答案的评论中指出的是将数组的二进制表示编码为 Base64 文本块。优化这一点的主要不便之处在于读取数据块的客户端应该能够做一些讨厌的事情,比如将字节数组重新解释为浮点数组,而这在类型安全语言中通常是不允许的;但它可以通过 C 库调用快速完成(并且大多数语言都提供了执行此操作的方法)。

      如果你不能弄乱比特,总是有可能一个接一个地处理数字,将解码后的字节转换为浮点数。

      哦,通过网络发送数据时要注意机器的字节序:转换为网络顺序 -> base64encode -> 发送 |接收 -> base64decode -> 转换为主机顺序

      【讨论】:

      • 感谢 fortran。不幸的是,我仍然无法使用 savetxt 或 str(x) 来提高速度。 str(x) 起初似乎要快得多,但是一旦 np.set_printoptions(threshold=100000) 就会消失(请参阅我对 unutbu 答案的评论)。
      【解决方案5】:

      numpy.savetxt 甚至比 string.join 还要慢。 ndarray.tofile() 似乎不适用于 StringIO。

      但我确实找到了一种更快的方法(至少适用于 OP 在 python2.5 上使用较低版本的 numpy 的示例):

      import numpy as np
      x = np.random.randn(100000)
      for i in range(100):
          (",%f"*100000)[1:] % tuple(x)
      

      如果您有一个明确定义的格式,例如在这种特殊情况下,字符串格式似乎比字符串连接更快。但我想知道为什么 OP 在内存中需要这么长的一串浮点数。

      较新版本的 numpy 没有显示速度提升。

      【讨论】:

      • Dingle - 无论出于何种原因,我都没有发现这比我原来的 join 和 str 示例更快。至于为什么需要这些长字符串,我有一个服务器应用程序,它对 numpy 数组进行操作,然后以纯文本字符串的形式分发结果,以便各种客户端(包括非 Python 客户端)可以使用数据(这包括发送通过 HTTP 向远程客户端发送数据)。如果有更好的方法来分发数据,我很乐意使用它,但请记住,使用任何编程语言并在任何操作系统上运行的客户端都需要能够使用它。
      • 在这种情况下,压缩的二进制数据比纯文本更好! :-) 我的 HTTP 知识现在有点生疏了,但您至少可以在 Base64 中编码原始浮点数以获得比十进制更好的位密度。确保所有平台的编组方案都相同(检查网络和主机字节顺序以及 IEEE 754 兼容表示)。如果没有 numpy 方法可以做到这一点,您可以用 C 编写自己的例程并使用 ctypes 调用它。
      • 感谢 fortran,这看起来可能是答案。当然,在 numpy 中执行 x.tostring() 非常快。我对跨不同环境读写二进制数据不是很熟悉,但我会深入研究。
      • @Abiel,timeit 显示快 20~30%。如果这里的数据大小不是问题,不确定fortran的建议是否会提高速度。 JSON 或 XML 呢?我认为网络上的二进制数据解压是不安全的。
      • fortran - 在看了你的建议之后,我很困惑在实践中你将如何在客户端解码数据,因为客户端不一定是用 Python 编写的。例如,客户端可能是用 Visual Basic 编写的,旨在将数字数组放入电子表格中。在这种情况下,我需要知道如何获取 numpy 数组的二进制表示并将其转换为类似 VB Variant 的东西。想法?
      【解决方案6】:

      在 OP 的代码中使用 itertools 中的 imap 而不是 map 给我带来了大约 2-3% 的改进,这并不算多,但可以与其他想法结合以提供更多改进。

      就个人而言,我认为如果你想要比这更好,你将不得不使用 Cython 之类的东西。

      【讨论】:

        【解决方案7】:
        ','.join(x.astype(str))
        

        比 as 慢 10% 左右

        x_arrstr = np.char.mod('%f', x)
        x_str = ",".join(x_arrstr)
        

        但更具可读性。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-04-21
          • 2017-04-25
          • 2016-06-06
          • 2019-01-27
          • 1970-01-01
          • 1970-01-01
          • 2021-11-06
          相关资源
          最近更新 更多