让我们从一个随机的(可再现的)数据数组开始-
# Setup
In [11]: np.random.seed(0)
...: a = np.random.randint(0,9,(7,2))
...: a[2] = a[0]
...: a[4] = a[1]
...: a[6] = a[1]
# Check values
In [12]: a
Out[12]:
array([[5, 0],
[3, 3],
[5, 0],
[5, 2],
[3, 3],
[6, 8],
[3, 3]])
# Check its itemsize
In [13]: a.dtype.itemsize
Out[13]: 8
让我们使用覆盖两个元素的自定义数据类型将每行视为标量。为此,我们将使用void-dtype
。如文档中所述-
https://docs.scipy.org/doc/numpy-1.13.0/reference/arrays.dtypes.html#specifying-and-constructing-data-types,https://docs.scipy.org/doc/numpy-1.13.0/reference/arrays.interface.html#arrays-interface)和stackoverflow Q&A中,似乎是-
In [23]: np.dtype((np.void, 16)) # 8 is the itemsize, so 8x2=16
Out[23]: dtype('V16')
# Create new view of the input
In [14]: b = a.view('V16').ravel()
# Check new view array
In [15]: b
Out[15]:
array([b'\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x03\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00',
b'\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x05\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00',
b'\x03\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00',
b'\x06\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00',
b'\x03\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00'],
dtype='|V16')
# Use pandas.factorize on the new view
In [16]: pd.factorize(b)
Out[16]:
(array([0, 1, 0, 0, 1, 2, 1]),
array(['\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
'\x03\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00',
'\x06\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00'],
dtype=object))
我无法理解的两个因数分解了输出,因此产生了后续问题-
第一个输出(= 0)的第四个元素看起来不正确,因为它的ID与第三个元素相同,但是在b
中,第四个和第三个元素是不同的。为什么这样?
为什么第二个输出具有对象dtype,而b
的dtype是V16
。这是否还会导致1.
中提到的错误值?
一个更大的问题可能是-pandas.factorize
是否涵盖自定义数据类型?从文档中,我看到了:
values:序列一维序列。不是大熊猫对象的序列 在分解之前被强制为ndarrays。
在提供的示例案例中,我们有一个NumPy数组,因此除非文档未明确说明自定义数据类型部分,否则不会假设输入有问题?
系统设置:Ubuntu 16.04,Python:2.7.12,NumPy:1.16.2,Pandas: 0.24.2。
在Python-3.x上
系统设置:Ubuntu 16.04,Python:3.5.2,NumPy:1.16.2,Pandas: 0.24.2。
运行相同的设置,我得到-
In [18]: b
Out[18]:
array([b'\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x03\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00',
b'\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x05\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00',
b'\x03\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00',
b'\x06\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00',
b'\x03\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00'],
dtype='|V16')
In [19]: pd.factorize(b)
Out[19]:
(array([0, 1, 0, 2, 1, 3, 1]),
array([b'\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x03\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00',
b'\x05\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00',
b'\x06\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00'],
dtype=object))
因此,factorize
的第一个输出看起来不错。但是,第二个输出再次具有对象dtype,与输入不同。因此,同样的问题-为什么此dtype会更改?
具有这样的自定义数据类型:
为什么在Python2.x上错误使用labels
,uniques
和不同的uniques
dtype?
为什么在Python3.x上使用不同的uniques
dtype?
答案 0 :(得分:9)
关于将V16
强制转换为object
的原因,pandas
中的许多函数将数据转换为内部函数可以处理的数据类型之一,here。如果数据类型不在列表中,则它将成为对象–并且pandas不会将结果转换回原始dtype,它会出现。
关于Python 2和Python 3之间的差异,两者都只有一个熊猫代码库,那么为什么它们给出不同的结果?
事实证明,Python 2使用字符串类型(只是字节数组)来表示数据¹,而Python 3使用字节类型。这样的结果是,Python 2使用StringHashTable
进行因式分解,Python 3使用PyObjectHashTable
,而StringHashTable
在您的情况下给出了错误的结果。我认为这是因为StringHashTable
中的字符串被假定为以0结尾的字符串,而对于您的字符串则不是这种情况–实际上,如果仅比较直到第一个零字节的行,则第一个和第四行看起来相同。
结论:这是一个错误,我们可能应该为此提出一个问题。
¹更多详细信息:对ensure_object
的此调用在Python 2中返回一个字符串数组,但在Python 3中返回一个字节数组(如b
前缀所示)。相应地,选择的here哈希表也不同。