навіщо мені робити копію кадру даних у пандах


189

Вибираючи підкадровий кадр з батьківського фрейму даних, я помітив, що деякі програмісти роблять копію кадру даних за допомогою .copy()методу. Наприклад,

X = my_dataframe[features_list].copy()

... замість просто

X = my_dataframe[features_list]

Чому вони роблять копію кадру даних? Що буде, якщо я не зроблю копію?


6
Я здогадуюсь, що вони вживають особливої ​​обережності, щоб не змінювати рамки вихідних даних. Можливо, непотрібне, але коли ти інтерактивно кидаєш щось разом, краще безпечно, ніж шкода.
Пол H

8
Я припускаю, що це не дурне запитання щодо надання негативу.
Елізабет Сьюзен Джозеф

Відповіді:


207

Це розширюється на відповідь Павла. У Pandas індексація DataFrame повертає посилання на початковий DataFrame. Таким чином, зміна підмножини змінить початкову DataFrame. Таким чином, ви хочете використовувати копію, якщо ви хочете переконатися, що початковий DataFrame не повинен змінюватися. Розглянемо наступний код:

df = DataFrame({'x': [1,2]})
df_sub = df[0:1]
df_sub.x = -1
print(df)

Ви отримаєте:

x
0 -1
1  2

На відміну від цього, df залишається незмінним:

df_sub_copy = df[0:1].copy()
df_sub_copy.x = -1

6
це глибока копія?
бікашг

6
Так. Режим за замовчуванням - "глибока" копія! pandas.pydata.org/pandas-docs/stable/reference/api/…
Амбарееш

44

Тому що, якщо ви не зробите копію, то індексами все ще можна керувати в іншому місці, навіть якщо призначити DataFrame іншим іменем.

Наприклад:

df2 = df
func1(df2)
func2(df)

func1 може змінити df, змінивши df2, щоб уникнути цього:

df2 = df.copy()
func1(df2)
func2(df)

Зачекайте, почекайте, почекайте, чи можете ви пояснити, ЧОМУ це відбувається? Не має сенсу.
NoName

2
це тому, що в першому прикладі `df2 = df , both variables reference the same DataFrame instance. So any changes made to df` або df2буде зроблено в один і той же екземпляр об'єкта. В той час, як у df2 = df.copy()другому екземплярі об'єкта створюється, копія першого, але тепер dfі df2посилання на різні екземпляри об'єкта та будь-які зміни будуть внесені до відповідного екземпляра DataFrame.
Педро

17

Необхідно зазначити, що повернення копії чи перегляду залежить від виду індексації.

Документація щодо панди говорить:

Повернення перегляду проти копії

Правила про те, коли повернення даних повертаються, повністю залежать від NumPy. Щоразу, коли в операції індексації бере участь масив міток або булевого вектора, результатом буде копія. При одній мітці / скалярній індексації та нарізанні, наприклад, df.ix [3: 6] або df.ix [:, 'A'], буде повернено представлення.



12

Основна мета - уникнути ланцюгової індексації та усунути SettingWithCopyWarning.

Тут ланцюгова індексація - щось подібне dfc['A'][0] = 111

У документі зазначено, що слід уникати ланцюгової індексації при поверненні подання проти копії . Ось трохи змінений приклад цього документа:

In [1]: import pandas as pd

In [2]: dfc = pd.DataFrame({'A':['aaa','bbb','ccc'],'B':[1,2,3]})

In [3]: dfc
Out[3]:
    A   B
0   aaa 1
1   bbb 2
2   ccc 3

In [4]: aColumn = dfc['A']

In [5]: aColumn[0] = 111
SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

In [6]: dfc
Out[6]:
    A   B
0   111 1
1   bbb 2
2   ccc 3

Тут aColumnпредставлений вид, а не копія оригіналу DataFrame, тому зміна aColumnпризведе до зміни оригіналу dfc. Далі, якщо спочатку індексуємо рядок:

In [7]: zero_row = dfc.loc[0]

In [8]: zero_row['A'] = 222
SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

In [9]: dfc
Out[9]:
    A   B
0   111 1
1   bbb 2
2   ccc 3

Цей час zero_rowє копією, тому оригінал dfcне змінюється.

З цих двох прикладів, наведених вище, ми бачимо, що неоднозначно ви хочете змінити оригінальну DataFrame чи ні. Це особливо небезпечно, якщо ви пишете щось подібне:

In [10]: dfc.loc[0]['A'] = 333
SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

In [11]: dfc
Out[11]:
    A   B
0   111 1
1   bbb 2
2   ccc 3

Цього разу це зовсім не спрацювало. Тут ми хотіли змінити dfc, але ми фактично змінили проміжне значення, dfc.loc[0]яке є копією і негайно відкидається. Це дуже важко передбачити , буде чи проміжне значення , як dfc.loc[0]і dfc['A']вид або копію, так що це не гарантовано , буде чи не буде оновлюватися оригінал DataFrame. Ось чому слід уникати ланцюгової індексації, а панди створюють SettingWithCopyWarningдля цього виду ланцюгового оновлення індексації.

Зараз це використання .copy(). Щоб усунути попередження, зробіть копію, щоб виразно висловити свій намір:

In [12]: zero_row_copy = dfc.loc[0].copy()

In [13]: zero_row_copy['A'] = 444 # This time no warning

Оскільки ви dfcзмінюєте копію, ви знаєте, що оригінал ніколи не зміниться, і ви не очікуєте, що він зміниться. Ваше очікування відповідає поведінці, то SettingWithCopyWarningзникає.

Примітка. Якщо ви хочете змінити оригінальний DataFrame, документ пропонує використовувати loc:

In [14]: dfc.loc[0,'A'] = 555

In [15]: dfc
Out[15]:
    A   B
0   555 1
1   bbb 2
2   ccc 3

2

Взагалі безпечніше працювати з копіями, ніж на оригінальних кадрах даних, за винятком випадків, коли ви знаєте, що оригінал вам більше не знадобиться і хочете перейти до маніпульованої версії. Зазвичай, ви все одно матимете користь для оригінального кадру даних для порівняння з маніпульованою версією тощо. Тому більшість людей працюють над копіями та злиттями наприкінці.


0

Припустимо, що у вас є кадр даних, як показано нижче

df1
     A    B    C    D
4 -1.0 -1.0 -1.0 -1.0
5 -1.0 -1.0 -1.0 -1.0
6 -1.0 -1.0 -1.0 -1.0
6 -1.0 -1.0 -1.0 -1.0

Коли ви хочете створити інший df2, ідентичний df1, безcopy

df2=df1
df2
     A    B    C    D
4 -1.0 -1.0 -1.0 -1.0
5 -1.0 -1.0 -1.0 -1.0
6 -1.0 -1.0 -1.0 -1.0
6 -1.0 -1.0 -1.0 -1.0

І хотілося б змінити значення df2 лише як показано нижче

df2.iloc[0,0]='changed'

df2
         A    B    C    D
4  changed -1.0 -1.0 -1.0
5       -1 -1.0 -1.0 -1.0
6       -1 -1.0 -1.0 -1.0
6       -1 -1.0 -1.0 -1.0

Одночасно змінюється і df1

df1
         A    B    C    D
4  changed -1.0 -1.0 -1.0
5       -1 -1.0 -1.0 -1.0
6       -1 -1.0 -1.0 -1.0
6       -1 -1.0 -1.0 -1.0

Оскільки два df однакові object, ми можемо перевірити це за допомогоюid

id(df1)
140367679979600
id(df2)
140367679979600

Тож вони як один і той же об'єкт і одна зміна іншої також передадуть те саме значення.


Якщо ми додамо copy, і тепер, df1і df2розглянемо як різні object, якщо ми зробимо однакові зміни в одному з них, інший не зміниться.

df2=df1.copy()
id(df1)
140367679979600
id(df2)
140367674641232

df1.iloc[0,0]='changedback'
df2
         A    B    C    D
4  changed -1.0 -1.0 -1.0
5       -1 -1.0 -1.0 -1.0
6       -1 -1.0 -1.0 -1.0
6       -1 -1.0 -1.0 -1.0

Добре зазначити, що коли ви підміняєте оригінальний фрейм даних, можна також додати копію, щоб уникнути SettingWithCopyWarning

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.