Панды: Получить SettingWithCopyWarning при использовании set_categories
У меня есть два кадра данных. Оба имеют одинаковый набор столбцов, но некоторые столбцы имеют категориальную типизацию (на основе фактических содержащихся значений). Чтобы объединить их, я обновляю категориальный тип категориальных столбцов с объединением обоих значений.
def appendDFsWithCat(df1, df2):
columns = df1.select_dtypes(include=['category']).columns
for c in columns:
catValues1 = list(df1[c].cat.categories)
catValues2 = list(df2[c].cat.categories)
catValues = list(set(catValues1 + catValues2))
df1[c] = df1[c].cat.set_categories(catValues)
df2[c] = df2[c].cat.set_categories(catValues)
return df1.append(df2, ignore_index=True).reset_index(drop=True)
Все работает, как и ожидалось, но я хотел бы понять, почему SettingWithCopyWarning повышается при выполнении этого кода:
df1[c] = df1[c].cat.set_categories(catValues)
Utility.py:149: SettingWithCopyWarning:
Я не нашел другой возможности обновить данные категории, чем использованные.
1 ответ
Скорее всего, это происходит из-за объектов, которые вы передаете своей функции.
Если я настроил следующий пример:
cats1 = pd.Series(['a', 'a', 'b', 'b'], name='cat', dtype="category")
data1 = pd.Series([1, 2, 3, 4], name='val', dtype=np.int64)
df1 = pd.concat([cats1, data1], axis=1)
и запустите свою функцию:
print appendDFsWithCat(df1, df1)
Я не получаю ошибку и этот вывод:
cat val
0 a 1
1 a 2
2 b 3
3 b 4
4 a 1
5 a 2
6 b 3
7 b 4
Однако, если я запускаю это:
print appendDFsWithCat(df1.iloc[:-1], df1)
Я получаю следующее предупреждение:
C:\Anaconda2\lib\site-packages\ipykernel\__main__.py:7: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead
See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
И этот вывод:
cat val
0 a 1
1 a 2
2 b 3
3 a 1
4 a 2
5 b 3
6 b 4
Если вы прочитаете предупреждение, оно сообщит вам, что вы пытаетесь установить значения для объекта, который является срезом или видом другого объекта. Это означает, что фрейм данных, которому вы назначаете значения в определенных местоположениях, является только ссылкой на другой объект. Я создал эту ситуацию, передавая фрейм данных функции, которая, как я знал, была срезом или представлением.
Вы можете обойти это, заставляя объекты быть их собственными вещами, как это:
Решение
def appendDFsWithCat(df1, df2):
# I added this line to ensure they are their own dataframes
df1, df2 = df1.copy(), df2.copy()
columns = df1.select_dtypes(include=['category']).columns
for c in columns:
catValues1 = list(df1[c].cat.categories)
catValues2 = list(df2[c].cat.categories)
catValues = list(set(catValues1 + catValues2))
df1[c] = df1[c].cat.set_categories(catValues)
df2[c] = df2[c].cat.set_categories(catValues)
return df1.append(df2, ignore_index=True).reset_index(drop=True)
Теперь, когда я бегу:
print appendDFsWithCat(df1.iloc[:-1], df1)
Я получил:
cat val
0 a 1
1 a 2
2 b 3
3 a 1
4 a 2
5 b 3
6 b 4
С сейчас предупреждениями.