2015-06-17 6 views
3

Учитывая У меня есть этот мультииндексированных dataframe:Полностью удалить одну метку индекса из мультииндекса, в dataframe

>>> import pandas as p 
>>> import numpy as np 
... 
>>> arrays = [np.array(['bar', 'bar', 'baz', 'baz', 'foo', 'foo']), 
...   np.array(['one', 'two', 'one', 'two', 'one', 'two'])] 
... 
>>> s = p.Series(np.random.randn(6), index=arrays) 
>>> s 
bar one -1.046752 
    two 2.035839 
baz one 1.192775 
    two 1.774266 
foo one -1.716643 
    two 1.158605 
dtype: float64 

Как я должен сделать, чтобы устранить указательный бар?
Я попытался с drop

>>> s1 = s.drop('bar') 
>>> s1 
baz one 1.192775 
    two 1.774266 
foo one -1.716643 
    two 1.158605 
dtype: float64 

Кажется ОК, но бар еще там каким-то причудливым образом:

>>> s1.index 
MultiIndex(levels=[[u'bar', u'baz', u'foo'], [u'one', u'two']], 
      labels=[[1, 1, 2, 2], [0, 1, 0, 1]]) 
>>> s1['bar'] 
Series([], dtype: float64) 
>>> 

Как я мог получить поездку любого остатка от этого индекса этикетки?

+0

Это звучит как ошибка, нет? –

+0

@AmiTavory Может быть. Во всяком случае, вопрос все еще стоит. :-) – joaquin

+0

совершенно! точка, возможно, возможно, помимо поиска обходного пути - вы можете записать ее как ошибку? кредит для его поиска принадлежит вам. –

ответ

3

Определенно выглядит как ошибка.

s1.index.tolist() возвращается к ожидаемому значению без «бара».

>>> s1.index.tolist() 
[('baz', 'one'), ('baz', 'two'), ('foo', 'one'), ('foo', 'two')] 

s1 ["bar"] возвращает нулевую серию.

>>> s1["bar"] 
Series([], dtype: float64) 

Стандартные методы отменяют это, кажется, не работает, либо:

>>> del s1["bar"] 
>>> s1["bar"] 
Series([], dtype: float64) 
>>> s1.__delitem__("bar") 
>>> s1["bar"] 
Series([], dtype: float64) 

Однако, как и ожидалось, пытаясь захватить новый ключ вызывает исключение KeyError:

>>> s1["booz"] 
... KeyError: 'booz' 

Основное отличие заключается в том, что вы действительно смотрите исходный код между ними в pandas.core.index.py

class MultiIndex(Index): 
    ... 

    def _get_levels(self): 
     return self._levels 

    ... 

    def _get_labels(self): 
     return self._labels 

    # ops compat 
    def tolist(self): 
     """ 
     return a list of the Index values 
     """ 
     return list(self.values) 

Таким образом, index.tolist() и _labels не имеют доступа к одному и тому же фрагменту общей информации, на самом деле они даже не близки.

Итак, мы можем использовать это, чтобы вручную обновить результирующий индексатор.

>>> s1.index.labels 
FrozenList([[1, 1, 2, 2], [0, 1, 0, 1]]) 
>>> s1.index._levels 
FrozenList([[u'bar', u'baz', u'foo'], [u'one', u'two']]) 
>>> s1.index.values 
array([('baz', 'one'), ('baz', 'two'), ('foo', 'one'), ('foo', 'two')], dtype=object) 

Если мы сравним это с начальным multindexed индексом, мы получим

>>> s.index.labels 
FrozenList([[0, 0, 1, 1, 2, 2], [0, 1, 0, 1, 0, 1]]) 
>>> s.index._levels 
FrozenList([[u'bar', u'baz', u'foo'], [u'one', u'two']]) 

Так _levels атрибуты не обновляются, а значения есть.

EDIT: Переопределение было не так просто, как я думал.

EDIT: написал пользовательскую функцию, чтобы исправить это поведение

from pandas.core.base import FrozenList, FrozenNDArray 

def drop(series, level, index_name): 
    # make new tmp series 
    new_series = series.drop(index_name) 
    # grab all indexing labels, levels, attributes 
    levels = new_series.index.levels 
    labels = new_series.index.labels 
    index_pos = levels[level].tolist().index(index_name) 
    # now need to reset the actual levels 
    level_names = levels[level] 
    # has no __delitem__, so... need to remake 
    tmp_names = FrozenList([i for i in level_names if i != index_name]) 
    levels = FrozenList([j if i != level else tmp_names 
         for i, j in enumerate(levels)]) 
    # need to turn off validation 
    new_series.index.set_levels(levels, verify_integrity=False, inplace=True) 
    # reset the labels 
    level_labels = labels[level].tolist() 
    tmp_labels = FrozenNDArray([i-1 if i > index_pos else i 
           for i in level_labels]) 
    labels = FrozenList([j if i != level else tmp_labels 
         for i, j in enumerate(labels)]) 
    new_series.index.set_labels(labels, verify_integrity=False, inplace=True) 
    return new_series 

Пример пользователя:

>>> s1 = drop(s, 0, "bar") 
>>> s1.index 
MultiIndex(levels=[[u'baz', u'foo'], [u'one', u'two']], 
      labels=[[0, 0, 1, 1], [0, 1, 0, 1]]) 
>>> s1.index.tolist() 
[('baz', 'one'), ('baz', 'two'), ('foo', 'one'), ('foo', 'two')] 
>>> s1["bar"] 
... 
KeyError: 'bar' 

EDIT: Это, кажется, специфичные для dataframes/серии с multiindexing, как стандартные панд. Класс core.index.Index не имеет одинаковых ограничений. Я бы рекомендовал подать отчет об ошибке.

Рассмотрим ту же серию со стандартным индексом:

>>> s = p.Series(np.random.randn(6)) 
>>> s.index 
Int64Index([0, 1, 2, 3, 4, 5], dtype='int64') 
>>> s.drop(0, inplace=True) 
>>> s.index 
Int64Index([1, 2, 3, 4, 5], dtype='int64') 

То же самое верно и для dataframe

>>> df = p.DataFrame([np.random.randn(6), np.random.randn(6)]) 
>>> df.index 
Int64Index([0, 1], dtype='int64') 
>>> df.drop(0, inplace=True) 
>>> df.index 
Int64Index([1], dtype='int64') 
+1

+1 Хорошие очки. Даже если я не могу решить поставленный вопрос, я думаю, вы дадите здесь некоторые подсказки, которые могли бы помочь решить мою практическую проблему с кодированием. – joaquin

+0

@joaquin, я добавил в пользовательскую функцию, которая делает все для вас. Он отбрасывает индекс и использует стандартную индексацию Pandas, чтобы предотвратить переделку фрейма данных и только сброс индексов. –

1

Смотрите долгое обсуждение here.

Нижняя линия, ее неясно , когда для пересчета уровней, поскольку операция, выполняемая пользователем, неизвестна (подумайте с точки зрения индекса). Например, скажем, что вы отбрасываете, а затем добавляете значение к уровню (например, посредством индексации). Это было бы очень расточительным и несколько вычислительным.

In [11]: s1.index 
Out[11]: 
MultiIndex(levels=[[u'bar', u'baz', u'foo'], [u'one', u'two']], 
      labels=[[1, 1, 2, 2], [0, 1, 0, 1]]) 

Вот сам фактический индекс.

In [12]: s1.index.values 
Out[12]: array([('baz', 'one'), ('baz', 'two'), ('foo', 'one'), ('foo', 'two')], dtype=object) 

In [13]: s1.index.get_level_values(0) 
Out[13]: Index([u'baz', u'baz', u'foo', u'foo'], dtype='object') 

In [14]: s1.index.get_level_values(1) 
Out[14]: Index([u'one', u'two', u'one', u'two'], dtype='object') 

Если вы действительно чувствуете, что необходимо «избавиться» от удалённого уровня, просто обновите индекс. Однако это вовсе не вредно. Эти факторизации (например, ярлыки) скрыты от пользователя (да, они отображаются, но это должно быть честно больше из-за путаницы, поэтому этот вопрос).

In [15]: pd.MultiIndex.from_tuples(s1.index.values) 
Out[15]: 
MultiIndex(levels=[[u'baz', u'foo'], [u'one', u'two']], 
      labels=[[0, 0, 1, 1], [0, 1, 0, 1]]) 
Смежные вопросы