暴露 C 字符串给 NumPy 的最快方法是什么?

3
我正在将一些旧的文本日志转换成Python可用的格式。由于文件非常大,所以我正在编写自己的C扩展程序,尽可能快地运行并使用正则表达式解析出相关字段。我的最终目标是将这些字段导出到NumPy数组中的strings。我知道可以在C中创建NumPy数组作为PyObject,然后对每个元素调用SetItem,但我想尽可能进行优化。 我能否像使用memcpyPyBuffer_FromMemory这样的东西直接将C字符串读入NumPystring数组中?我理解NumPy数组在内部类似于C数组,但是我是否必须确保NumPy数组将被连续分配?
我打算使用NumPy数组构建Pandas列进行统计分析。据我了解,Pandas使用NumPy数组将列存储在DataFrame中,因此从NumPyPandas不会有过多的开销。如果可能的话,我想避免使用cython

3
在Python中,正则表达式的速度不比C慢。 - Daniel
是的,我知道编译后的正则表达式在 C 和 Python 之间的速度是相当的。我的意思是通过在 C 中构建 NumPy 数组来避免其他方面的开销。 - Max
1个回答

3
为了让你更好地理解字符串数组是如何存储的,我将创建一个,并以多种方式查看它:
In [654]: np.array(['one','two','three','four'],dtype='S5')
Out[654]: 
array([b'one', b'two', b'three', b'four'], 
      dtype='|S5')
In [655]: x=np.array(['one','two','three','four'],dtype='S5')
In [656]: x.tostring()
Out[656]: b'one\x00\x00two\x00\x00threefour\x00'
In [657]: x.view(np.uint8)
Out[657]: 
array([111, 110, 101,   0,   0, 116, 119, 111,   0,   0, 116, 104, 114,
       101, 101, 102, 111, 117, 114,   0], dtype=uint8)

因此,它的数据缓冲区由20个字节(4*S5)组成。对于长度小于5的字符串,在字节中放置(或保留)0

是的,有用于创建给定大小和dtype的新数组以及将数据块复制到这些数组的C函数。查看numpy文档的C部分,或查看其在github存储库上的一些numpy代码。

关于pandas转移,请注意pandas会轻松更改其列的dtype。例如,如果您将Nonenan放入列中,则很可能会将其更改为对象dtype。

对象数组在数据缓冲区中存储指针。

In [658]: y=np.array(['one','two','three','four'],dtype=object)
In [659]: y
Out[659]: array(['one', 'two', 'three', 'four'], dtype=object)
In [660]: y.tostring()
Out[660]: b'\xe0\x0f\xc5\xb5\xa0\xfah\xb5\x80\x0b\x8c\xb4\xc09\x8b\xb4'

如果我理解正确,该数据缓冲区有16个字节——4个4字节指针。字符串存储在内存中的其他地方作为常规Python字符串(在这种情况下是unicode字符串(Py3))。

=================

fromstringfrombuffer让我从缓冲区重新创建数组。

In [696]: x=np.array(['one','two','three','four'],dtype='S5')
In [697]: xs=x.tostring()
In [698]: np.fromstring(xs,'S5')
Out[698]: 
array([b'one', b'two', b'three', b'four'], 
      dtype='|S5')
In [700]: np.frombuffer(xs,'S5')
Out[700]: 
array([b'one', b'two', b'three', b'four'], 
      dtype='|S5')

这个方法可以不用复制缓冲区就能运行。

但是,如果有多个位于内存不同位置的字符串,则需要将它们复制到一个连续的缓冲区中才能构建数组。


感谢您提供详细的评论。我想补充一点,将一个字符串的numpy数组转换为对象数组会基本上抵消掉numpy所带来的任何性能优势。阅读了您的回复后,我认为最好的做法是:(1)创建一个适当大小和数据类型的numpy数组;(2)使用PyArray_BYTES在C中获取numpy数组的字节,然后(3)使用memcpy将我的字符串复制到该内存中。 - Max

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