转换为共享字符串数组的字符串的Numpy矩阵会导致类型不匹配

时间:2018-06-13 18:50:39

标签: python numpy multiprocessing ctypes shared-memory

我正在尝试使用multiprocessing in Python,但是,我在创建一些shared memory时遇到了麻烦。以下示例说明我的问题:

在引用the following时(稍微不同,因为他使用了一个充满浮点数的矩阵,但原理相同),我想将一个numpy字符串矩阵转换为shared memory空间以供进程使用。我有以下内容:

from ctypes import c_wchar_p
import numpy as np
from multiprocessing.sharedctypes import Array

input_array = np.array([['Red', 'Green', 'Blue', 'Yellow'],
                        ['Purple', 'Orange', 'Cyan', 'Pink']]).T

shared_memory = Array(c_wchar_p, input_array.size, lock=False) # Equivalent to just using a RawArray
np_wrapper = np.frombuffer(shared_memory, dtype='<U1').reshape(input_array.shape)
np.copyto(np_wrapper, input_array)
print(np_wrapper)

但是,np_wrapper只有每个字符串的第一个字符:

[['R' 'P']
 ['G' 'O']
 ['B' 'C']
 ['Y' 'P']]

我试图纠正这个问题:

  1. 我尝试将dtype函数的<U1<U6更改为dtype,这是input_array的{​​{1}}。但是,它会抛出以下异常:
  2.   

    ValueError:缓冲区大小必须是元素大小的倍数

    1. 我尝试使用dtype frombufferint64函数,因为我的shared_memory数组类型为frombuffer(即字符串指针)而我是在64位Windows 10系统上。但是,它会抛出以下异常:
    2.   

      ValueError:无法将大小为4的数组转换为形状(4,2)

      我非常困惑为什么我的打字错了。 是否有人对如何解决此问题有任何见解?

2 个答案:

答案 0 :(得分:1)

了解这个字符串数组包含的内容可能会有所帮助:

In [643]: input_array = np.array([['Red', 'Green', 'Blue', 'Yellow'],
     ...:                         ['Purple', 'Orange', 'Cyan', 'Pink']]).T
     ...: 
     ...:                         
In [644]: input_array.size
Out[644]: 8
In [645]: input_array.itemsize
Out[645]: 24
In [646]: input_array.nbytes
Out[646]: 192

因为它是转置,所以形状和步幅与输入数组不同,但字符串按原始顺序排列。

In [647]: input_array.__array_interface__
Out[647]: 
{'data': (139792902236880, False),
 'strides': (24, 96),
 'descr': [('', '<U6')],
 'typestr': '<U6',
 'shape': (4, 2),
 'version': 3}

我的猜测是Array应该使用nbytes而不是size进行定义。

答案 1 :(得分:1)

前言

在我详细说明我的解决方案之前,我想在一些有用的信息前面给我答案。事实证明,python中的函数memoryview()对于全面了解非常有用。例如,在将input_array的dtype指定为dtype='S6'(b / c减去要检查的字节数)后运行以下命令:

print(bytes(memoryview(input_array)))

然后得到以下结果:

b'Red\x00\x00\x00PurpleGreen\x00OrangeBlue\x00\x00Cyan\x00\x00YellowPink\x00\x00'

我们可以通过以下输出看到input_array中的每个条目都有6个字节的长度,并且在一个连续的内存块中布局。这告诉我们我们的Numpy数组不只是指向内存中字符串的8个指针。

回到指定dtype 的时候,@ hpaulj还提供了更有帮助的见解。阅读dtype documentation后,我们的数组的类型为<U6,其转换如下:

<  -- Little-Endian (b/c I am on an Intel-based system)
U  -- Unicode String (Remember with 4 bytes per Unicode String)
6  -- 24 bytes per entry in the array.

解决方案

TLDR;这是解决方案:

from ctypes import c_char
import numpy as np
from multiprocessing.sharedctypes import Array

input_array = np.array([['Red', 'Green', 'Blue', 'Yellow'],
                        ['Purple', 'Orange', 'Cyan', 'Pink']]).T

shared_memory = Array(c_char, input_array.size * input_array.itemsize, lock=False)
np_wrapper = np.frombuffer(shared_memory, dtype=input_array.dtype).reshape(input_array.shape)
np.copyto(np_wrapper, input_array)

print(shared_memory[:])
print(np_wrapper)

解决方案说明:

初始代码的第一个不正确的方面是初始shared_memory数组的输入信息。我们的Numpy数组不是一个指针数组,而是8个字符串紧密相邻地压缩(一些填充由最长的元素决定)。因此,使用类型c_wchar_p(即字符串指针)正确。我选择c_char而不是c_wchar因为c_char保证是一个字节,而c_wchar不是see documentation for further details

接下来,需要指定共享内存的整个大小。因为我选择了c_char作为我的类型,所以我将指定字节数。长度由以下给出:

  

有8个元素(input_array.size),每个元素包含24个字节(input_array.itemsize)。因此,共享内存总共有8 * 24 = 192个字节。

最后,在Numpy中使用frombuffer函数时,请务必指定正确 dtype,因为这是Numpy将如何分割并解释任意字节的来源只需使用dtype input_array完成翻译即可。

最后,copyto开始后,shared_memory将成功配置!