pandas.factorize with custom array datatype

Div*_*kar 8 python arrays numpy python-3.x pandas

Let's start off with a random (reproducible) data array -

# 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
Run Code Online (Sandbox Code Playgroud)

Let's view each row as a scalar using custom datatype that covers two elements. We will use void-dtype for this purpose. As mentioned in the docs -

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) and in stackoverflow Q&A, it seems that would be -

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))
Run Code Online (Sandbox Code Playgroud)

Two things off factorize's output that I could not understand and hence the follow-up questions -

  1. The fourth element of the first output (=0) looks wrong, because it has same ID as the third element, but in b, the fourth and third elements are different. Why so?

  2. Why does the second output has an object dtype, while the dtype of b was V16. Is this also causing the wrong value mentioned in 1.?

A bigger question could be - Does pandas.factorize cover custom datatypes? From docs, I see :

values : sequence A 1-D sequence. Sequences that aren’t pandas objects are coerced to ndarrays before factorization.

In the provided sample case, we have a NumPy array, so one would assume no issues with the input, unless the docs didn't clarify about the custom datatype part?

System setup : Ubuntu 16.04, Python : 2.7.12, NumPy : 1.16.2, Pandas : 0.24.2.

On Python-3.x

System setup : Ubuntu 16.04, Python : 3.5.2, NumPy : 1.16.2, Pandas : 0.24.2.

Running the same setup, I get -

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))
Run Code Online (Sandbox Code Playgroud)

So, the first output off factorize looks alright here. But, the second output has object dtype again, different from the input. So, the same question - Why this dtype change?

Compiling the questions/tl;dr

With such a custom datatype :

  1. 为什么出错labelsuniques并且uniquesPython2.x上的dtype 不同?

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

nnn*_*mmm 9

至于为什么V16被胁迫object,很多功能在pandas数据转换为数据类型的内部功能可以处理,一个在这里。如果数据类型不在列表中,它将成为一个对象,并且pandas不会将结果转换回原始dtype,而是出现。

关于Python 2和Python 3之间的差异,两者都只有一个熊猫代码库,那么为什么它们给出不同的结果呢?

事实证明,Python 2使用字符串类型(仅是字节数组)来表示数据¹,而Python 3使用字节类型。这样的结果是Python 2使用a StringHashTable进行因式分解,Python 3使用a PyObjectHashTableStringHashTable在您的情况下给出不正确的结果。我认为这是因为StringHashTable假设字符串中的字符串以零结尾,而您的字符串却不是这种情况–实际上,如果您仅比较行至第一个零字节,则第一行和第四行看起来是相同的。

结论:这是一个错误,我们可能应该为此提出一个问题。

¹更详细的信息:此调用ensure_object在Python 2中返回一个字符串数组,但在Python 3中返回一个字节数组(如b前缀所示)。相应地,此处选择的哈希表是不同的。