Почему одно использование iloc () дает SettingWithCopyWarning, а другое - нет? - PullRequest
0 голосов
/ 17 декабря 2018

Внутри метода из класса я использую этот оператор:

self.__datacontainer.iloc[-1]['c'] = value

Делая это, я получаю «SettingWithCopyWarning: значение пытается быть установлено на копию слайса из DataFrame"

Теперь я попытался воспроизвести эту ошибку и написать следующий простой код:

import pandas, numpy
df = pandas.DataFrame(numpy.random.randn(5,3),columns=list('ABC'))
df.iloc[-1]['C'] = 3

Там я не получаю ошибки.Почему я получаю ошибку в первом утверждении, а не во втором?

Ответы [ 3 ]

0 голосов
/ 17 декабря 2018

Цепная индексация

В качестве документации и нескольких других ответов на этом сайте ( [1] , [2] ) предполагают, что цепная индексация считается плохой практикой, и ее следует избегать.

Поскольку, как представляется, не существует изящного способа назначения назначений с использованием целочисленной индексации на основе позиций (т. е. .iloc) без нарушения правила индексации цепочки (по состоянию на pandas v0.23.4), рекомендуется вместо этого использовать индексирование на основе меток (т. е. .loc) для целей присвоения, когда это возможно.*

Однако, если вам абсолютно необходим доступ к данным по номеру строки, вы можете

df.iloc[-1, df.columns.get_loc('c')] = 42

или

df.iloc[[-1, 1], df.columns.get_indexer(['a', 'c'])] = 42

Панды ведут себя странно

Насколько я понимаю, вы абсолютно правы ожидать предупреждения при попытке искусственно воспроизвести ошибку.

Я обнаружил, что это зависит от того, как построен фрейм данных

df = pd.DataFrame({'a': [4, 5, 6], 'c': [3, 2, 1]})
df.iloc[-1]['c'] = 42 # no warning

df = pd.DataFrame({'a': ['x', 'y', 'z'], 'c': ['t', 'u', 'v']})
df.iloc[-1]['c'] = 'f' # no warning

df = pd.DataFrame({'a': ['x', 'y', 'z'], 'c': [3, 2, 1]})
df.iloc[-1]['c'] = 42 # SettingWithCopyWarning: ...

Кажетсяs pandas (не менее v0.23.4) по-разному обрабатывает кадры данных смешанного и однотипного типов, когда речь идет о цепочечных присвоениях [3]

def _check_is_chained_assignment_possible(self):
    """
    Check if we are a view, have a cacher, and are of mixed type.
    If so, then force a setitem_copy check.
    Should be called just near setting a value
    Will return a boolean if it we are a view and are cached, but a
    single-dtype meaning that the cacher should be updated following
    setting.
    """
    if self._is_view and self._is_cached:
        ref = self._get_cacher()
        if ref is not None and ref._is_mixed_type:
            self._check_setitem_copy(stacklevel=4, t='referant',
                                     force=True)
        return True
    elif self._is_copy:
        self._check_setitem_copy(stacklevel=4, t='referant')
    return False

это кажется мне странным, хотя я не уверен, что это не так.

Однако есть старая ошибка с похожим поведением.


ОБНОВЛЕНИЕ

Согласно разработчикам ожидается вышеупомянутое поведение.

0 голосов
/ 17 декабря 2018

Не зацикливайтесь на предупреждении.Предупреждение является лишь указанием, иногда оно даже не появляется , когда вы ожидаете, что оно должно .Иногда вы заметите, что происходит непоследовательно .Вместо этого просто избегайте цепочечного индексирования или вообще работайте с тем, что может быть копией.

Вы хотите индексировать по целочисленное расположение строки и метка столбца .Это неестественный микс, учитывая, что Pandas имеет функциональность для индексации по целым позициям или меткам, , но не одновременно одновременно .

В этом случае вы можете использовать целочисленное позиционное индексирование для строк и столбцов с помощью одного iat вызова:

df.iat[-1, df.columns.get_loc('C')] = 3

Или, если ваши индексные метки гарантированно уникальны, вы можете использоватьat:

df.at[df.index[-1], 'C'] = 3
0 голосов
/ 17 декабря 2018

Так что довольно сложно ответить без контекста вокруг вашей проблемной операции, но документация панд охватывает это довольно хорошо.

>>> df[['C']].iloc[0] = 2 # This is a problem
SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

В основном это сводится к - не надообъедините операции индексации, если вы можете просто использовать одну операцию для этого.

>>> df.loc[0, 'C'] = 2 # This is ok

Предупреждение, которое вы получаете, заключается в том, что вы не смогли установить значение в исходном кадре данных, которое, по-видимому, вы считаетепытаясь изменить - вместо этого вы скопировали его и установили что-то в копию (обычно, когда это происходит со мной, у меня даже нет ссылки на копию, и она просто собирает мусор, поэтому предупреждение довольно полезно)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...