Tại sao tôi nên tạo một bản sao của khung dữ liệu trong gấu trúc


188

Khi chọn một khung dữ liệu phụ từ khung dữ liệu mẹ, tôi nhận thấy rằng một số lập trình viên tạo một bản sao của khung dữ liệu bằng .copy()phương thức này. Ví dụ,

X = my_dataframe[features_list].copy()

...Thay vì chỉ

X = my_dataframe[features_list]

Tại sao họ tạo một bản sao của khung dữ liệu? Điều gì sẽ xảy ra nếu tôi không tạo một bản sao?


6
Tôi đoán là họ đang đề phòng thêm để không sửa đổi khung dữ liệu nguồn. Có lẽ không cần thiết, nhưng khi bạn ném một cái gì đó tương tác với nhau, an toàn tốt hơn là xin lỗi.
Paul H

8
Tôi cho rằng đây không phải là một câu hỏi ngu ngốc khi đưa ra một câu hỏi tiêu cực.
Elizabeth Susan Joseph

Câu trả lời:


206

Điều này mở rộng câu trả lời của Paul. Trong Pandas, lập chỉ mục một DataFrame trả về một tham chiếu đến DataFrame ban đầu. Do đó, việc thay đổi tập hợp con sẽ thay đổi DataFrame ban đầu. Do đó, bạn muốn sử dụng bản sao nếu bạn muốn đảm bảo DataFrame ban đầu không nên thay đổi. Hãy xem xét các mã sau đây:

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

Bạn sẽ nhận được:

x
0 -1
1  2

Ngược lại, các lá sau df không thay đổi:

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

6
Đây có phải là một bản sao sâu?
bikashg

6
Đúng. Chế độ mặc định là bản sao "sâu"! pandas.pydata.org/pandas-docs/urdy/reference/api/iêng
Ambareesh

44

Bởi vì nếu bạn không tạo một bản sao thì các chỉ mục vẫn có thể bị thao túng ở nơi khác ngay cả khi bạn gán DataFrame cho một tên khác.

Ví dụ:

df2 = df
func1(df2)
func2(df)

func1 có thể sửa đổi df bằng cách sửa đổi df2, vì vậy để tránh điều đó:

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

Chờ đợi chờ đợi, bạn có thể giải thích TẠI SAO điều này xảy ra? Không có ý nghĩa.
NoName

2
đó là bởi vì trong ví dụ đầu tiên, `df2 = df , both variables reference the same DataFrame instance. So any changes made to df` hoặc df2sẽ được tạo thành cùng thể hiện đối tượng. Trong khi trong df2 = df.copy()trường hợp đối tượng thứ hai được tạo, một bản sao của đối tượng thứ nhất, nhưng bây giờ dfdf2tham chiếu đến các thể hiện đối tượng khác nhau và mọi thay đổi sẽ được thực hiện đối với đối tượng DataFrame tương ứng của chúng.
Pedro

17

Cần phải đề cập rằng việc trả lại bản sao hoặc chế độ xem tùy thuộc vào loại lập chỉ mục.

Các tài liệu về gấu trúc nói:

Trả lại một cái nhìn so với một bản sao

Các quy tắc về thời điểm chế độ xem dữ liệu được trả về hoàn toàn phụ thuộc vào NumPy. Bất cứ khi nào một mảng các nhãn hoặc một vectơ boolean tham gia vào hoạt động lập chỉ mục, kết quả sẽ là một bản sao. Với việc lập chỉ mục và cắt vô hướng nhãn đơn, ví dụ df.ix [3: 6] hoặc df.ix [:, 'A'], một chế độ xem sẽ được trả về.



12

Mục đích chính là để tránh lập chỉ mục chuỗi và loại bỏ SettingWithCopyWarning.

Ở đây lập chỉ mục chuỗi là một cái gì đó như dfc['A'][0] = 111

Tài liệu cho biết nên tránh lập chỉ mục chuỗi trong Trả lại chế độ xem so với bản sao . Đây là một ví dụ sửa đổi một chút từ tài liệu đó:

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

Đây aColumnlà chế độ xem và không phải là bản sao từ Khung dữ liệu gốc, do đó sửa đổi aColumncũng sẽ khiến bản gốc dfcbị sửa đổi. Tiếp theo, nếu chúng ta lập chỉ mục hàng trước:

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

Lần này zero_rowlà một bản sao, vì vậy bản gốcdfc không được sửa đổi.

Từ hai ví dụ trên, chúng tôi thấy không rõ liệu bạn có muốn thay đổi DataFrame gốc hay không. Điều này đặc biệt nguy hiểm nếu bạn viết một cái gì đó như sau:

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

Lần này nó không hoạt động. Ở đây chúng tôi muốn thay đổi dfc, nhưng chúng tôi thực sự đã sửa đổi một giá trị trung gian dfc.loc[0]là bản sao và bị loại bỏ ngay lập tức. Rất khó để dự đoán liệu giá trị trung gian như dfc.loc[0]hay dfc['A']là một chế độ xem hay bản sao, do đó, không đảm bảo liệu DataFrame gốc có được cập nhật hay không. Đó là lý do tại sao nên tránh lập chỉ mục chuỗi và gấu trúc tạo ra SettingWithCopyWarningbản cập nhật lập chỉ mục chuỗi này.

Bây giờ là việc sử dụng .copy(). Để loại bỏ cảnh báo, hãy tạo một bản sao để thể hiện rõ ràng ý định của bạn:

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

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

Vì bạn đang sửa đổi một bản sao, bạn biết bản gốc dfcsẽ không bao giờ thay đổi và bạn không mong đợi nó sẽ thay đổi. Kỳ vọng của bạn phù hợp với hành vi, sau đó SettingWithCopyWarningbiến mất.

Lưu ý, nếu bạn muốn sửa đổi DataFrame gốc, tài liệu đề nghị bạn sử dụng 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

Nói chung, an toàn hơn khi làm việc trên các bản sao so với các khung dữ liệu gốc, ngoại trừ khi bạn biết rằng bạn sẽ không cần bản gốc nữa và muốn tiến hành phiên bản thao tác. Thông thường, bạn vẫn sẽ có một số sử dụng cho khung dữ liệu gốc để so sánh với phiên bản bị thao túng, v.v. Do đó, hầu hết mọi người đều làm việc trên các bản sao và hợp nhất vào cuối.


0

Giả sử bạn có khung dữ liệu như bên dưới

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

Khi bạn muốn tạo một df2cái khác giống hệt df1, mà khôngcopy

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

Và muốn sửa đổi giá trị df2 như dưới đây

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

Đồng thời df1 cũng được thay đổi

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

Vì hai df giống nhau object, chúng ta có thể kiểm tra nó bằng cách sử dụngid

id(df1)
140367679979600
id(df2)
140367679979600

Vì vậy, chúng là cùng một đối tượng và một thay đổi khác cũng sẽ vượt qua cùng một giá trị.


Nếu chúng ta thêm copy, và bây giờ df1df2được coi là khác nhau object, nếu chúng ta thực hiện cùng một thay đổi với một trong số chúng thì cái kia sẽ không thay đổi.

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

Rất tốt để đề cập, khi bạn đặt lại khung dữ liệu gốc, việc thêm bản sao cũng là an toàn để tránh SettingWithCopyWarning

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.