web-dev-qa-db-fra.com

Après avoir renommé la colonne, obtenez l'erreur de clé

J'ai df:

df = pd.DataFrame({'a':[7,8,9],
                   'b':[1,3,5],
                   'c':[5,3,6]})

print (df)
   a  b  c
0  7  1  5
1  8  3  3
2  9  5  6

Renommez ensuite la première valeur par this :

df.columns.values[0] = 'f'

Tout semble très sympa:

print (df)
   f  b  c
0  7  1  5
1  8  3  3
2  9  5  6

print (df.columns)
Index(['f', 'b', 'c'], dtype='object')

print (df.columns.values)
['f' 'b' 'c']

Si sélectionnez b cela fonctionne bien:

print (df['b'])
0    1
1    3
2    5
Name: b, dtype: int64

Mais si vous sélectionnez a il retourne la colonne f:

print (df['a'])
0    7
1    8
2    9
Name: f, dtype: int64

Et si vous sélectionnez f obtenez une erreur de clé.

print (df['f'])
#KeyError: 'f'

print (df.info())
#KeyError: 'f'

Quel est le problème? Quelqu'un peut-il l'expliquer? Ou un bug?

13
jezrael

Vous n'êtes pas censé modifier l'attribut values.

Essayez df.columns.values = ['a', 'b', 'c'] et vous obtenez:

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-61-e7e440adc404> in <module>()
----> 1 df.columns.values = ['a', 'b', 'c']

AttributeError: can't set attribute

C'est parce que pandas détecte que vous essayez de définir l'attribut et vous arrête.

Cependant, cela ne peut pas vous empêcher de modifier l'objet values sous-jacent lui-même.

Lorsque vous utilisez rename, pandas enchaîne avec un tas de trucs de nettoyage. J'ai collé la source ci-dessous.

En fin de compte, ce que vous avez fait est de modifier les valeurs sans lancer le nettoyage. Vous pouvez l'initier vous-même avec un appel de suivi à _data.rename_axis (l'exemple peut être vu dans la source ci-dessous). Cela forcera l'exécution du nettoyage et vous pourrez alors accéder à ['f']

df._data = df._data.rename_axis(lambda x: x, 0, True)
df['f']

0    7
1    8
2    9
Name: f, dtype: int64

Morale de l'histoire: probablement pas une bonne idée de renommer une colonne de cette façon.


mais cette histoire devient plus étrange

C'est bon

df = pd.DataFrame({'a':[7,8,9],
                   'b':[1,3,5],
                   'c':[5,3,6]})

df.columns.values[0] = 'f'

df['f']

0    7
1    8
2    9
Name: f, dtype: int64

C'est pas bien

df = pd.DataFrame({'a':[7,8,9],
                   'b':[1,3,5],
                   'c':[5,3,6]})

print(df)

df.columns.values[0] = 'f'

df['f']
KeyError:

Il s'avère que nous pouvons modifier l'attribut values avant d'afficher df et il exécutera apparemment toute l'initialisation lors de la première display. Si vous l'affichez avant de modifier l'attribut values, il affichera une erreur.

encore plus étrange

df = pd.DataFrame({'a':[7,8,9],
                   'b':[1,3,5],
                   'c':[5,3,6]})

print(df)

df.columns.values[0] = 'f'

df['f'] = 1

df['f']

   f  f
0  7  1
1  8  1
2  9  1

Comme si nous ne savions pas déjà que c'était une mauvaise idée ...


source pour rename

def rename(self, *args, **kwargs):

    axes, kwargs = self._construct_axes_from_arguments(args, kwargs)
    copy = kwargs.pop('copy', True)
    inplace = kwargs.pop('inplace', False)

    if kwargs:
        raise TypeError('rename() got an unexpected keyword '
                        'argument "{0}"'.format(list(kwargs.keys())[0]))

    if com._count_not_none(*axes.values()) == 0:
        raise TypeError('must pass an index to rename')

    # renamer function if passed a dict
    def _get_rename_function(mapper):
        if isinstance(mapper, (dict, ABCSeries)):

            def f(x):
                if x in mapper:
                    return mapper[x]
                else:
                    return x
        else:
            f = mapper

        return f

    self._consolidate_inplace()
    result = self if inplace else self.copy(deep=copy)

    # start in the axis order to eliminate too many copies
    for axis in lrange(self._AXIS_LEN):
        v = axes.get(self._AXIS_NAMES[axis])
        if v is None:
            continue
        f = _get_rename_function(v)

        baxis = self._get_block_manager_axis(axis)
        result._data = result._data.rename_axis(f, axis=baxis, copy=copy)
        result._clear_item_cache()

    if inplace:
        self._update_inplace(result._data)
    else:
        return result.__finalize__(self)
18
piRSquared