pandas.factorize 与自定义数组数据类型
Posted
技术标签:
【中文标题】pandas.factorize 与自定义数组数据类型【英文标题】:pandas.factorize with custom array datatype 【发布时间】:2019-09-09 13:10:55 【问题描述】:让我们从一个随机(可重现的)数据数组开始 -
# 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) 和*** 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))
factorize 的输出中有两件事我无法理解,因此还有后续问题 -
第一个输出的第四个元素 (=0) 看起来不对,因为它与第三个元素具有相同的 ID,但在 b
中,第四个和第三个元素不同。为什么会这样?
为什么第二个输出有一个对象dtype,而b
的dtype是V16
。这是否也会导致1.
中提到的错误值?
一个更大的问题可能是 - pandas.factorize
是否涵盖自定义数据类型?从文档中,我看到:
values : 序列一维序列。不是 pandas 对象的序列 在因式分解之前被强制转换为 ndarray。
在提供的示例案例中,我们有一个 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
的第一个输出在这里看起来没问题。但是,第二个输出再次具有 object dtype,与输入不同。所以,同样的问题 - 为什么这个 dtype 会改变?
编译问题/tl;dr
使用这种自定义数据类型:
为什么 Python2.x 上的 labels
、uniques
和不同的 uniques
dtype 错误?
为什么 Python3.x 上的 uniques
dtype 不同?
【问题讨论】:
【参考方案1】:至于为什么V16
被强制转换为object
,pandas
中的很多函数将数据转换为内部函数可以处理的数据类型之一,here。如果数据类型不在列表中,它会变成一个对象——pandas 不会将结果转换回原来的 dtype,它会出现。
关于 Python 2 和 Python 3 之间的差异:两者只有一个 pandas 代码库,为什么它们会给出不同的结果?
事实证明,Python 2 使用字符串类型(只是字节数组)来表示您的数据¹,而 Python 3 使用字节类型。这样做的效果是 Python 2 使用 StringHashTable
进行分解,而 Python 3 使用 PyObjectHashTable
,而 StringHashTable
在您的情况下给出了不正确的结果。我相信这是因为StringHashTable
中的字符串被假定为零终止,而您的字符串并非如此——事实上,如果您只比较第一个零字节之前的行,则第一个和第四个行看起来相同。
结论:这是一个错误,我们可能应该为此提出问题。
¹ 更多细节:对ensure_object
的调用在 Python 2 中返回一个字符串数组,但在 Python 3 中返回一个字节数组(如 b
前缀所示)。相应地,here选择的hashtable也不一样。
【讨论】:
感谢您的回答!那么,似乎没有办法保留给定样本的数据类型“Vx”?当然,对于 Python-2x 上的错误结果,我会在 pandas github 上提交一个错误。以上是关于pandas.factorize 与自定义数组数据类型的主要内容,如果未能解决你的问题,请参考以下文章