使用Pandas Multiindex和数量时失败,显示“类别级别必须唯一”

2 投票
1 回答
1145 浏览
提问于 2025-04-17 15:43

在使用pandas的多重索引时,如果索引中包含单位,有时候会出现问题。让我给你举个例子:

import quantities as pq
import pandas as pd

i = np.arange(10) * pq.J
j = np.array([1 for _ in xrange(10)]) * pq.K

pd.MultiIndex.from_tuples(zip(i, j), names=['Energy', 'Temperature'])

这个例子会报错,错误信息如下:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-86-c2d09517b80e> in <module>()
      5 j = np.array([1 for _ in xrange(10)]) * pq.K
      6 
----> 7 pd.MultiIndex.from_tuples(zip(i, j), names=['Energy', 'Temperature'])

C:\Python27\lib\site-packages\pandas\core\index.pyc in from_tuples(cls, tuples, sortorder, names)
   1685 
   1686         return MultiIndex.from_arrays(arrays, sortorder=sortorder,
-> 1687                                       names=names)
   1688 
   1689     @property

C:\Python27\lib\site-packages\pandas\core\index.pyc in from_arrays(cls, arrays, sortorder, names)
   1646             return Index(arrays[0], name=name)
   1647 
-> 1648         cats = [Categorical.from_array(arr) for arr in arrays]
   1649         levels = [c.levels for c in cats]
   1650         labels = [c.labels for c in cats]

C:\Python27\lib\site-packages\pandas\core\categorical.pyc in from_array(cls, data)
     59 
     60         return Categorical(labels, levels,
---> 61                            name=getattr(data, 'name', None))
     62 
     63     _levels = None

C:\Python27\lib\site-packages\pandas\core\categorical.pyc in __init__(self, labels, levels, name)
     45     def __init__(self, labels, levels, name=None):
     46         self.labels = labels
---> 47         self.levels = levels
     48         self.name = name
     49 

C:\Python27\lib\site-packages\pandas\core\categorical.pyc in _set_levels(self, levels)
     68         levels = _ensure_index(levels)
     69         if not levels.is_unique:
---> 70             raise ValueError('Categorical levels must be unique')
     71         self._levels = levels
     72 

ValueError: Categorical levels must be unique

如果我去掉单位,代码就能正常运行。

i = np.arange(10)
j = np.array([1 for _ in xrange(10)])

pd.MultiIndex.from_tuples(zip(i, j), names=['Energy', 'Temperature'])

如果我保留单位,但为j使用一个独特的项目,代码也能正常运行。

i = np.arange(10) * pq.J
j = np.arange(10) * pq.K

pd.MultiIndex.from_tuples(zip(i, j), names=['Energy', 'Temperature'])

不过,这样做并不可行,因为这些索引是来自测量数据的。我真的很想保留单位,但由于我对pandas的内部机制不太了解,所以不知道该怎么解决这个问题。

版本信息

我使用的是pandas版本0.10.1和quantities版本0.10.1,运行在python 2.7上。

1 个回答

2

我能够重现这个错误,但它在Linux上是间歇性的,每隔几次调用pd.MultiIndex.from_tuples(...)就会失败。

我认为这个错误是因为quantity对象违反了Python的一个规则:如果a==b成立,那么hash(a)==hash(b)也应该成立。这个规则的详细信息可以参考这里:http://bugs.python.org/issue13707#msg150596https://groups.google.com/forum/#!msg/sympy/pJ2jg2csKgU/0nn21xqZEmwJ

这里有一个不好的哈希行为的例子。

In [5]: (1 * pq.K) == (1 * pq.K)
Out[5]: True

In [6]: hash(1 * pq.K) == hash(1 * pq.K)
Out[6]: False

根据这种行为,我认为这是一个关于数量对象的问题,这导致了pandas内部状态的不合法。

在我看来,最好的解决办法是让数量对象返回一个基于当前值的一致哈希,就像这个(被拒绝的)请求,想要在数量对象上添加一个__hash__()函数:https://github.com/python-quantities/python-quantities/pull/29
要么这样,要么在尝试哈希时抛出一个错误,如果它想表现得像一个可变对象。

撰写回答