pandas.factorize使用自定义数组数据类型

时间:2019-04-19 08:30:06

标签: python arrays python-3.x pandas numpy

让我们从一个随机的(可再现的)数据数组开始-

# 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-typeshttps://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))

我无法理解的两个因数分解了输出,因此产生了后续问题-

  1. 第一个输出(= 0)的第四个元素看起来不正确,因为它的ID与第三个元素相同,但是在b中,第四个和第三个元素是不同的。为什么这样?

  2. 为什么第二个输出具有对象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会更改?

编译问题/ tl; dr

具有这样的自定义数据类型:

  1. 为什么在Python2.x上错误​​使用labelsuniques和不同的uniques dtype?

  2. 为什么在Python3.x上使用不同的uniques dtype?

1 个答案:

答案 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哈希表也不同。