Иногда возникает ситуация, когда нужно отфильтровать с помощью некоторого логического выражения, записи в Pandas DataFrame и затем изменить их. Казалось бы, нет ничего проще:
#nc - Pandas DataFrame with DateTime as index #GBPUSD and Data - is a column in the nc nc[((nc.Date >= start_date) & (nc.Date <= end_date))]['GBPUSD'] = 10 print(nc)
Но print(nc) показывает, что значения не изменились, а Python выдает предупреждение:
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
Почему возникла ошибка? При выполнении первой части выражения:
nc[((nc.Date >= start_date) & (nc.Date <= end_date))]
создалась копия, а вторая операция, устанавливающая значение выражения, меняла значение уже в копии, соответственно, на исходном DataFrame изменения не отразились.
Нужно избавиться от двух последовательных операций, и делается это атомарной операцией:
nc.loc[((nc.Date >= start_date) & (nc.Date <= end_date)), 'GBPUSD'] = 10 print(nc)
В этом случае гарантированно меняется значение в исходном view, а не на копии, и предупреждение пропадает. Можно использовать другой вариант записи, но он менее надежен:
nc.GBPUSD[((nc.Date >= start_date) & (nc.Date <= end_date))] = 10 print(nc)
В данном случае результат такой же, но снова появляется предупреждение, поскольку такой код не гарантирует, что изменения выполнены на оригинале, а не на копии.
Можно сделать изменения в оригинале, используя индексы отфильтрованных записей:
#nc.index = np.arange(nc.shape[0]) indexes = nc[(nc.Date >= start_date) & (nc.Date <= end_date)].index nc.at[indexes, 'GBPUSD'] = 10 print(nc)
Сначала получаем индексы отфильтрованных значений, а затем используем
nc.at[indexes, 'GBPUSD'] = 10
для изменения значения в колонке ‘GBPUSD’ по списку индексов.