Làm thế nào để đối phó với SettingsWithCopyWarning trong Pandas?


629

Lý lịch

Tôi vừa nâng cấp Pandas của tôi từ 0.11 lên 0.13.0rc1. Bây giờ, ứng dụng đang xuất hiện nhiều cảnh báo mới. Một trong số họ thích điều này:

E:\FinReporter\FM_EXT.py:449: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
  quote_df['TVol']   = quote_df['TVol']/TVOL_SCALE

Tôi muốn biết chính xác nó có nghĩa là gì? Tôi có cần phải thay đổi một cái gì đó?

Tôi nên đình chỉ cảnh báo như thế nào nếu tôi khăng khăng sử dụng quote_df['TVol'] = quote_df['TVol']/TVOL_SCALE?

Hàm đưa ra lỗi

def _decode_stock_quote(list_of_150_stk_str):
    """decode the webpage and return dataframe"""

    from cStringIO import StringIO

    str_of_all = "".join(list_of_150_stk_str)

    quote_df = pd.read_csv(StringIO(str_of_all), sep=',', names=list('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefg')) #dtype={'A': object, 'B': object, 'C': np.float64}
    quote_df.rename(columns={'A':'STK', 'B':'TOpen', 'C':'TPCLOSE', 'D':'TPrice', 'E':'THigh', 'F':'TLow', 'I':'TVol', 'J':'TAmt', 'e':'TDate', 'f':'TTime'}, inplace=True)
    quote_df = quote_df.ix[:,[0,3,2,1,4,5,8,9,30,31]]
    quote_df['TClose'] = quote_df['TPrice']
    quote_df['RT']     = 100 * (quote_df['TPrice']/quote_df['TPCLOSE'] - 1)
    quote_df['TVol']   = quote_df['TVol']/TVOL_SCALE
    quote_df['TAmt']   = quote_df['TAmt']/TAMT_SCALE
    quote_df['STK_ID'] = quote_df['STK'].str.slice(13,19)
    quote_df['STK_Name'] = quote_df['STK'].str.slice(21,30)#.decode('gb2312')
    quote_df['TDate']  = quote_df.TDate.map(lambda x: x[0:4]+x[5:7]+x[8:10])

    return quote_df

Thêm thông báo lỗi

E:\FinReporter\FM_EXT.py:449: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
  quote_df['TVol']   = quote_df['TVol']/TVOL_SCALE
E:\FinReporter\FM_EXT.py:450: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
  quote_df['TAmt']   = quote_df['TAmt']/TAMT_SCALE
E:\FinReporter\FM_EXT.py:453: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
  quote_df['TDate']  = quote_df.TDate.map(lambda x: x[0:4]+x[5:7]+x[8:10])

2
Đây là trình quản lý bối cảnh để tạm thời đặt mức cảnh báo gist.github.com/notbanker/2be3ed34539c86e22ffdd88fd95ad8bc
Peter Cotton

2
bạn có thể sử dụng df.set_value, tài liệu ở đây - pandas.pydata.org/pandas-docs/urdy/generated/ mẹo
leonprou

1
pandas.pydata.org/pandas-docs/urdy/ Tài liệu chính thức giải thích chi tiết
wyx

3
@leonprou df.set_valueđã bị phản đối. Gấu trúc bây giờ khuyên bạn nên sử dụng .at[]hoặc .iat[]thay vào đó. tài liệu ở đây pandas.pydata.org/pandas-docs/urdy/generated/ mài
Kyle C

Tôi ngạc nhiên không ai nhắc đến gấu trúc option_contextở đây: pandas.pydata.org/pandas-docs/ sóng / user_guide / options.html , sử dụng nhưwith pd.option_context("mode.chained_assignment", None): [...]
m-dz

Câu trả lời:


795

Việc SettingWithCopyWarningđược tạo để gắn cờ các bài tập "xâu chuỗi" có khả năng gây nhầm lẫn, chẳng hạn như sau, không phải lúc nào cũng hoạt động như mong đợi, đặc biệt khi lựa chọn đầu tiên trả về một bản sao . [xem GH5390GH5597 để thảo luận về nền tảng.]

df[df['A'] > 2]['B'] = new_val  # new_val not set in df

Cảnh báo đưa ra đề nghị viết lại như sau:

df.loc[df['A'] > 2, 'B'] = new_val

Tuy nhiên, điều này không phù hợp với cách sử dụng của bạn, tương đương với:

df = df[df['A'] > 2]
df['B'] = new_val

Mặc dù rõ ràng rằng bạn không quan tâm đến việc ghi nó trở lại khung ban đầu (vì bạn đang ghi đè tham chiếu đến nó), thật không may, mẫu này không thể được phân biệt với ví dụ gán chuỗi đầu tiên. Do đó cảnh báo (dương tính giả). Khả năng dương tính giả được đề cập trong các tài liệu về lập chỉ mục , nếu bạn muốn đọc thêm. Bạn có thể vô hiệu hóa cảnh báo mới này một cách an toàn với bài tập sau.

import pandas as pd
pd.options.mode.chained_assignment = None  # default='warn'

34
Tôi nghĩ rằng tôi chủ yếu sẽ ủng hộ không cảnh báo về điều này cả. Nếu bạn làm việc với cú pháp gán chuỗi, bạn chắc chắn có thể tìm ra thứ tự lập chỉ mục cần xảy ra để nó hoạt động như mong đợi trong bất kỳ tình huống nào. Tôi nghĩ rằng nó quá hoang tưởng rằng có những biện pháp phòng ngừa toàn diện về nó. Với tinh thần giống như "để mọi người trưởng thành" về các phương pháp hoặc thuộc tính của lớp 'riêng tư', tôi nghĩ rằng tốt hơn cho gấu trúc là để người dùng trưởng thành về các bài tập bị xiềng xích. Chỉ sử dụng chúng nếu bạn biết những gì bạn đang làm.
ely

48
Thật là một chút không bình thường khi cố gắng cảnh báo mọi người khi họ đi hack để tìm giải pháp thay thế. Các phương thức Pandas kiểu mới hơn để truy cập (được cải thiện .ix, cải tiến .iloc, v.v.) chắc chắn có thể được xem là "cách chính" mà không cảnh báo mọi người liên tục về các cách khác. Thay vào đó hãy để họ trưởng thành và nếu họ muốn thực hiện nhiệm vụ xiềng xích, thì hãy làm điều đó. Dù sao hai xu của tôi. Người ta thường thấy những bình luận bất mãn từ các nhà phát triển Pandas ở đây khi các bài tập được xâu chuỗi sẽ hoạt động để giải quyết vấn đề, nhưng sẽ không được coi là cách "chính" để làm như vậy.
ely

8
@EMS vấn đề là không phải lúc nào cũng rõ ràng từ mã nơi một bản sao so với chế độ xem đang được thực hiện và một số lỗi / nhầm lẫn phát sinh từ vấn đề này. Chúng tôi đã xem xét đưa vào một tệp / tùy chọn RC để tự động thực hiện cấu hình, điều này có thể hữu ích hơn khi đưa ra cách cài đặt với cảnh báo sao chép hoạt động.
Jeff Tratner

3
Lý do để cảnh báo là cho những người nâng cấp mã cũ, tất nhiên. Và tôi chắc chắn cần một cảnh báo, bởi vì tôi đang xử lý một số mã cũ rất xấu xí.
Thomas Andrew

16
Bên cạnh đó, tôi thấy rằng việc vô hiệu hóa cảnh báo chained_assocation: pd.options.mode.chained_assignment = Noneđã khiến mã của tôi chạy nhanh hơn khoảng 6 lần. Bất cứ ai khác có kinh nghiệm kết quả tương tự?
Muon

209

Làm thế nào để đối phó với SettingWithCopyWarningPandas?

Bài viết này là dành cho độc giả, những người,

  1. Muốn hiểu cảnh báo này có nghĩa là gì
  2. Muốn hiểu các cách khác nhau để ngăn chặn cảnh báo này
  3. Muốn hiểu cách cải thiện mã của họ và tuân theo các thực tiễn tốt để tránh cảnh báo này trong tương lai.

Thiết lập

np.random.seed(0)
df = pd.DataFrame(np.random.choice(10, (3, 5)), columns=list('ABCDE'))
df
   A  B  C  D  E
0  5  0  3  3  7
1  9  3  5  2  4
2  7  6  8  8  1

Là gì SettingWithCopyWarning?

Để biết làm thế nào để đối phó với cảnh báo này, điều quan trọng là phải hiểu ý nghĩa của nó và tại sao nó được nêu ra ngay từ đầu.

Khi lọc DataFrames, có thể lát / lập chỉ mục một khung để trả về dạng xem hoặc bản sao , tùy thuộc vào bố cục bên trong và các chi tiết triển khai khác nhau. "Chế độ xem", như thuật ngữ gợi ý, chế độ xem vào dữ liệu gốc, do đó sửa đổi chế độ xem có thể sửa đổi đối tượng ban đầu. Mặt khác, "bản sao" là bản sao dữ liệu từ bản gốc và sửa đổi bản sao không có tác dụng đối với bản gốc.

Như được đề cập bởi các câu trả lời khác, SettingWithCopyWarningđã được tạo để gắn cờ các hoạt động "gán xích". Xem xét dftrong các thiết lập ở trên. Giả sử bạn muốn chọn tất cả các giá trị trong cột "B" trong đó các giá trị trong cột "A" là> 5. Pandas cho phép bạn thực hiện điều này theo nhiều cách khác nhau, một số chính xác hơn các giá trị khác. Ví dụ,

df[df.A > 5]['B']

1    3
2    6
Name: B, dtype: int64

Và,

df.loc[df.A > 5, 'B']

1    3
2    6
Name: B, dtype: int64

Chúng trả về cùng một kết quả, vì vậy nếu bạn chỉ đọc các giá trị này, điều đó không có gì khác biệt. Vậy, vấn đề là gì? Vấn đề với việc gán chuỗi, nói chung là rất khó để dự đoán xem một chế độ xem hoặc bản sao được trả lại, vì vậy điều này phần lớn trở thành vấn đề khi bạn đang cố gắng gán lại các giá trị. Để xây dựng trên ví dụ trước, hãy xem xét cách mã này được trình thông dịch thực thi:

df.loc[df.A > 5, 'B'] = 4
# becomes
df.__setitem__((df.A > 5, 'B'), 4)

Với một __setitem__cuộc gọi đến df. OTOH, xem xét mã này:

df[df.A > 5]['B'] = 4
# becomes
df.__getitem__(df.A > 5).__setitem__('B", 4)

Bây giờ, tùy thuộc vào việc __getitem__trả về một khung nhìn hay một bản sao, __setitem__thao tác có thể không hoạt động .

Nói chung, bạn nên sử dụng loccho gán dựa trên nhãn và iloccho gán dựa trên số nguyên / vị trí, vì thông số kỹ thuật đảm bảo rằng chúng luôn hoạt động trên bản gốc. Ngoài ra, để thiết lập một ô duy nhất, bạn nên sử dụng atiat.

Nhiều hơn có thể được tìm thấy trong các tài liệu .

Lưu ý
Tất cả các hoạt động lập chỉ mục boolean được thực hiện với loccũng có thể được thực hiện với iloc. Sự khác biệt duy nhất là ilocmong đợi số nguyên / vị trí cho chỉ mục hoặc một mảng numpy của các giá trị boolean và chỉ mục số nguyên / vị trí cho các cột.

Ví dụ,

df.loc[df.A > 5, 'B'] = 4

Có thể viết bằng mũi

df.iloc[(df.A > 5).values, 1] = 4

Và,

df.loc[1, 'A'] = 100

Có thể được viết như

df.iloc[1, 0] = 100

Và như thế.


Chỉ cần cho tôi biết làm thế nào để đàn áp cảnh báo!

Hãy xem xét một hoạt động đơn giản trên cột "A" của df. Chọn "A" và chia cho 2 sẽ đưa ra cảnh báo, nhưng thao tác sẽ hoạt động.

df2 = df[['A']]
df2['A'] /= 2
/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/IPython/__main__.py:1: 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

df2
     A
0  2.5
1  4.5
2  3.5

Có một số cách để im lặng cảnh báo trực tiếp này:

  1. Làm một deepcopy

    df2 = df[['A']].copy(deep=True)
    df2['A'] /= 2
  2. Thay đổipd.options.mode.chained_assignment
    có thể được thiết lập để None, "warn"hoặc "raise". "warn"là mặc định Nonesẽ loại bỏ hoàn toàn cảnh báo và "raise"sẽ ném SettingWithCopyError, ngăn không cho hoạt động đi qua.

    pd.options.mode.chained_assignment = None
    df2['A'] /= 2

@Peter Cotton trong các nhận xét, đã đưa ra một cách hay là không thay đổi chế độ (được sửa đổi từ ý chính này ) bằng trình quản lý bối cảnh, để đặt chế độ chỉ khi cần thiết và đặt lại chế độ đó thành trạng thái ban đầu khi hoàn thành.

class ChainedAssignent:
    def __init__(self, chained=None):
        acceptable = [None, 'warn', 'raise']
        assert chained in acceptable, "chained must be in " + str(acceptable)
        self.swcw = chained

    def __enter__(self):
        self.saved_swcw = pd.options.mode.chained_assignment
        pd.options.mode.chained_assignment = self.swcw
        return self

    def __exit__(self, *args):
        pd.options.mode.chained_assignment = self.saved_swcw

Cách sử dụng như sau:

# some code here
with ChainedAssignent():
    df2['A'] /= 2
# more code follows

Hoặc, để nâng cao ngoại lệ

with ChainedAssignent(chained='raise'):
    df2['A'] /= 2

SettingWithCopyError: 
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

"Vấn đề XY": Tôi đang làm gì sai?

Rất nhiều thời gian, người dùng cố gắng tìm cách để loại bỏ ngoại lệ này mà không hiểu đầy đủ lý do tại sao nó được nêu ra ngay từ đầu. Đây là một ví dụ điển hình cho vấn đề XY , trong đó người dùng cố gắng giải quyết vấn đề "Y" thực sự là một triệu chứng của vấn đề gốc rễ sâu hơn "X". Các câu hỏi sẽ được đưa ra dựa trên các vấn đề phổ biến gặp phải cảnh báo này và các giải pháp sau đó sẽ được trình bày.

Câu hỏi 1
Tôi có DataFrame

df
       A  B  C  D  E
    0  5  0  3  3  7
    1  9  3  5  2  4
    2  7  6  8  8  1

Tôi muốn gán giá trị trong col "A"> 5 đến 1000. Sản lượng dự kiến ​​của tôi là

      A  B  C  D  E
0     5  0  3  3  7
1  1000  3  5  2  4
2  1000  6  8  8  1

Cách sai để làm điều này:

df.A[df.A > 5] = 1000         # works, because df.A returns a view
df[df.A > 5]['A'] = 1000      # does not work
df.loc[df.A  5]['A'] = 1000   # does not work

Đúng cách sử dụng loc:

df.loc[df.A > 5, 'A'] = 1000


Câu hỏi 2 1
Tôi đang cố gắng đặt giá trị trong ô (1, 'D') thành 12345. Sản lượng dự kiến ​​của tôi là

   A  B  C      D  E
0  5  0  3      3  7
1  9  3  5  12345  4
2  7  6  8      8  1

Tôi đã thử các cách khác nhau để truy cập vào tế bào này, chẳng hạn như df['D'][1]. Cách tốt nhất để làm việc này là gì?

1. Câu hỏi này không liên quan cụ thể đến cảnh báo, nhưng thật tốt khi hiểu cách thực hiện thao tác cụ thể này một cách chính xác để tránh các tình huống có thể xảy ra cảnh báo trong tương lai.

Bạn có thể sử dụng bất kỳ phương pháp nào sau đây để làm điều này.

df.loc[1, 'D'] = 12345
df.iloc[1, 3] = 12345
df.at[1, 'D'] = 12345
df.iat[1, 3] = 12345


Câu hỏi 3
Tôi đang cố gắng tập hợp các giá trị dựa trên một số điều kiện. Tôi có một DataFrame

   A  B  C  D  E
1  9  3  5  2  4
2  7  6  8  8  1

Tôi muốn gán giá trị trong "D" cho 123 sao cho "C" == 5. Tôi đã thử

df2.loc[df2.C == 5, 'D'] = 123

Có vẻ tốt nhưng tôi vẫn nhận được SettingWithCopyWarning! Làm thế nào để tôi sửa lỗi này?

Điều này thực sự có thể là do mã cao hơn trong đường ống của bạn. Bạn đã tạo ra df2từ một cái gì đó lớn hơn, như

df2 = df[df.A > 5]

? Trong trường hợp này, lập chỉ mục boolean sẽ trả về một khung nhìn, do đó df2sẽ tham chiếu bản gốc. Những gì bạn cần làm là gán df2cho một bản sao :

df2 = df[df.A > 5].copy()
# Or,
# df2 = df.loc[df.A > 5, :]


Câu hỏi 4
Tôi đang cố gắng thả cột "C" tại chỗ từ

   A  B  C  D  E
1  9  3  5  2  4
2  7  6  8  8  1

Nhưng sử dụng

df2.drop('C', axis=1, inplace=True)

Ném SettingWithCopyWarning. Tại sao chuyện này đang xảy ra?

Điều này là do df2phải được tạo như một khung nhìn từ một số thao tác cắt khác, chẳng hạn như

df2 = df[df.A > 5]

Giải pháp ở đây là một trong hai làm cho một copy()số df, hoặc sử dụng loc, như trước đây.


7
Tái bút: Hãy cho tôi biết nếu tình huống của bạn không nằm trong danh sách câu hỏi của phần 3. Tôi sẽ sửa đổi bài viết của tôi.
cs95

150

Nói chung, quan điểm của việc SettingWithCopyWarninglà cho người dùng (và đặc biệt là người dùng mới) rằng họ thể đang hoạt động trên một bản sao chứ không phải bản gốc như họ nghĩ. Có những thông tin sai (IOW nếu bạn biết bạn đang làm gì thì có thể ổn ). Một khả năng đơn giản là tắt cảnh báo (theo mặc định cảnh báo ) như @Garrett đề xuất.

Đây là một lựa chọn khác:

In [1]: df = DataFrame(np.random.randn(5, 2), columns=list('AB'))

In [2]: dfa = df.ix[:, [1, 0]]

In [3]: dfa.is_copy
Out[3]: True

In [4]: dfa['A'] /= 2
/usr/local/bin/ipython:1: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
  #!/usr/local/bin/python

Bạn có thể đặt is_copycờ thành False, sẽ tắt kiểm tra một cách hiệu quả cho đối tượng đó :

In [5]: dfa.is_copy = False

In [6]: dfa['A'] /= 2

Nếu bạn sao chép rõ ràng thì sẽ không có cảnh báo nào nữa xảy ra:

In [7]: dfa = df.ix[:, [1, 0]].copy()

In [8]: dfa['A'] /= 2

Mã mà OP đang hiển thị ở trên, trong khi hợp pháp, và có lẽ là điều tôi cũng làm, về mặt kỹ thuật là trường hợp cho cảnh báo này, và không phải là dương tính giả. Một cách khác để không có cảnh báo sẽ là thực hiện thao tác lựa chọn thông qua reindex, ví dụ

quote_df = quote_df.reindex(columns=['STK', ...])

Hoặc là,

quote_df = quote_df.reindex(['STK', ...], axis=1)  # v.0.21

Cảm ơn thông tin và thảo luận, tôi chỉ tắt cảnh báo để giao diện điều khiển im lặng về điều này. Nghe có vẻ như khung nhìn & bảng trong cơ sở dữ liệu SQL. Tôi cần biết thêm về lợi ích của việc giới thiệu khái niệm 'bản sao', nhưng IMHO có phần là một gánh nặng để chăm sóc sự khác biệt về ngữ nghĩa ngữ nghĩa tinh vi ..
bigbird 18/12/13

19
Tôi đồng ý với bản sao (); nó rõ ràng và nó đã khắc phục vấn đề của tôi (đó là một dương tính giả).
1

5
Sau khi cập nhật lên 0.16tôi thấy nhiều điểm tích cực sai hơn, vấn đề với dương tính giả là người ta học cách bỏ qua nó, mặc dù đôi khi nó là hợp pháp.
bảnh bao

3
@dashesy bạn đang thiếu điểm. đôi khi thậm chí có thể hầu hết thời gian nó có thể làm việc. Nhưng nó có thể xảy ra nếu khung lớn hơn / nhỏ hơn hoặc bạn thêm một cột nói về một loại khác mà nó không hoạt động. Đó là điểm. Bạn đang làm một cái gì đó có thể làm việc nhưng không được bảo đảm. Điều này rất khác với cảnh báo khấu hao. Nếu bạn muốn tiếp tục sử dụng nó và nó hoạt động, thật tuyệt. Nhưng được cảnh báo trước.
Jeff

3
@Jeff có ý nghĩa bây giờ, vì vậy nó là một undefinedhành vi. Nếu bất cứ điều gì nó sẽ đưa ra một lỗi sau đó (để tránh những cạm bẫy nhìn thấy C), vì apibị đóng băng, hành vi cảnh báo hiện tại có ý nghĩa cho khả năng tương thích ngược. Và tôi sẽ làm cho họ ném để bắt chúng là lỗi trong mã sản xuất của tôi ( warnings.filterwarnings('error', r'SettingWithCopyWarning). Ngoài ra, đề xuất sử dụng .locđôi khi cũng không giúp được gì (nếu nó nằm trong một nhóm).
dashesy

41

Cảnh báo sao chép dữ liệu của Pandas

Khi bạn đi và làm một cái gì đó như thế này:

quote_df = quote_df.ix[:,[0,3,2,1,4,5,8,9,30,31]]

pandas.ix trong trường hợp này trả về một khung dữ liệu mới, độc lập.

Bất kỳ giá trị nào bạn quyết định thay đổi trong khung dữ liệu này, sẽ không thay đổi khung dữ liệu gốc.

Đây là những gì gấu trúc cố gắng cảnh báo bạn về.


Tại sao .ix là một ý tưởng tồi

Đối .ixtượng cố gắng làm nhiều hơn một điều, và đối với bất kỳ ai đã đọc bất cứ điều gì về mã sạch, đây là một mùi mạnh.

Đưa ra khung dữ liệu này:

df = pd.DataFrame({"a": [1,2,3,4], "b": [1,1,2,2]})

Hai hành vi:

dfcopy = df.ix[:,["a"]]
dfcopy.a.ix[0] = 2

Hành vi một: dfcopybây giờ là một khung dữ liệu độc lập. Thay đổi nó sẽ không thay đổidf

df.ix[0, "a"] = 3

Hành vi hai: Điều này thay đổi khung dữ liệu gốc.


Sử dụng .locthay thế

Các nhà phát triển gấu trúc nhận ra rằng .ixđối tượng này khá nặng mùi [theo suy đoán] và do đó đã tạo ra hai đối tượng mới giúp gia nhập và gán dữ liệu. (Người khác .iloc)

.loc là nhanh hơn, bởi vì nó không cố gắng tạo một bản sao của dữ liệu.

.loc có nghĩa là để sửa đổi khung dữ liệu hiện tại của bạn, giúp bộ nhớ hiệu quả hơn.

.loc là dự đoán, nó có một hành vi.


Giải pháp

Những gì bạn đang làm trong ví dụ mã của bạn là tải một tệp lớn với nhiều cột, sau đó sửa đổi nó nhỏ hơn.

Các pd.read_csvchức năng có thể giúp bạn ra rất nhiều điều này và cũng làm cho việc tải tệp nhanh hơn rất nhiều.

Vì vậy, thay vì làm điều này

quote_df = pd.read_csv(StringIO(str_of_all), sep=',', names=list('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefg')) #dtype={'A': object, 'B': object, 'C': np.float64}
quote_df.rename(columns={'A':'STK', 'B':'TOpen', 'C':'TPCLOSE', 'D':'TPrice', 'E':'THigh', 'F':'TLow', 'I':'TVol', 'J':'TAmt', 'e':'TDate', 'f':'TTime'}, inplace=True)
quote_df = quote_df.ix[:,[0,3,2,1,4,5,8,9,30,31]]

Làm cái này

columns = ['STK', 'TPrice', 'TPCLOSE', 'TOpen', 'THigh', 'TLow', 'TVol', 'TAmt', 'TDate', 'TTime']
df = pd.read_csv(StringIO(str_of_all), sep=',', usecols=[0,3,2,1,4,5,8,9,30,31])
df.columns = columns

Điều này sẽ chỉ đọc các cột bạn quan tâm và đặt tên cho chúng đúng. Không cần sử dụng các .ixđối tượng ác để làm công cụ ma thuật.


"Các nhà phát triển gấu trúc nhận ra rằng đối tượng .ix khá nặng mùi [theo suy đoán] và do đó tạo ra hai đối tượng mới" - đối tượng kia là gì?
jf328

3
@ jf328 .iloc Tôi nghĩ
Brian Biên

1
Vâng, nó là .iloc. Đây là hai phương pháp chính để lập chỉ mục cấu trúc dữ liệu gấu trúc. Đọc thêm trong tài liệu.
Ninjakannon

Làm thế nào để thay thế một cột DataFrame bằng dấu thời gian thành cột bằng đối tượng hoặc chuỗi thời gian ngày?
boldnik

@boldnik Kiểm tra câu trả lời này stackoverflow.com/a/37453925/3730397
Firelynx

20

Ở đây tôi trả lời câu hỏi trực tiếp. Làm thế nào để đối phó với nó?

Thực hiện .copy(deep=False)sau khi bạn cắt lát. Xem gấu trúc.DataFrame.copy .

Đợi đã, không phải là một lát trả lại một bản sao? Rốt cuộc, đây là những gì thông điệp cảnh báo đang cố gắng nói? Đọc câu trả lời dài:

import pandas as pd
df = pd.DataFrame({'x':[1,2,3]})

Điều này đưa ra một cảnh báo:

df0 = df[df.x>2]
df0['foo'] = 'bar'

Điều này không:

df1 = df[df.x>2].copy(deep=False)
df1['foo'] = 'bar'

Cả hai df0df1DataFrameđối tượng, nhưng một cái gì đó về chúng là khác nhau cho phép gấu trúc in cảnh báo. Hãy tìm hiểu nó là gì.

import inspect
slice= df[df.x>2]
slice_copy = df[df.x>2].copy(deep=False)
inspect.getmembers(slice)
inspect.getmembers(slice_copy)

Sử dụng công cụ tìm khác biệt của bạn, bạn sẽ thấy rằng ngoài một vài địa chỉ, sự khác biệt duy nhất về vật chất là:

|          | slice   | slice_copy |
| _is_copy | weakref | None       |

Phương pháp quyết định có cảnh báo hay không là DataFrame._check_setitem_copykiểm tra _is_copy. Vì vậy, ở đây bạn đi. Tạo một khung copysao cho DataFrame của bạn không_is_copy .

Cảnh báo đang đề xuất sử dụng .loc, nhưng nếu bạn sử dụng .loctrên một khung đó _is_copy, bạn vẫn sẽ nhận được cảnh báo tương tự. Gây hiểu lầm? Đúng. Làm phiền? Bạn đặt cược. Hữu ích? Có khả năng, khi phân công chuỗi được sử dụng. Nhưng nó không thể phát hiện chính xác việc gán chuỗi và in cảnh báo một cách bừa bãi.


11

Chủ đề này thực sự khó hiểu với Pandas. May mắn thay, nó có một giải pháp tương đối đơn giản.

Vấn đề là không phải lúc nào cũng rõ liệu các hoạt động lọc dữ liệu (ví dụ: loc) trả về một bản sao hoặc chế độ xem của DataFrame. Do đó, việc sử dụng thêm DataFrame được lọc như vậy có thể gây nhầm lẫn.

Giải pháp đơn giản là (trừ khi bạn cần làm việc với các bộ dữ liệu rất lớn):

Bất cứ khi nào bạn cần cập nhật bất kỳ giá trị nào, hãy luôn đảm bảo rằng bạn sao chép ngầm định DataFrame trước khi gán.

df  # Some DataFrame
df = df.loc[:, 0:2]  # Some filtering (unsure whether a view or copy is returned)
df = df.copy()  # Ensuring a copy is made
df[df["Name"] == "John"] = "Johny"  # Assignment can be done now (no warning)

Có một lỗi đánh máy: nên nói rõ ràng
s9527

7

Để xóa bỏ mọi nghi ngờ, giải pháp của tôi là tạo một bản sao sâu của lát cắt thay vì một bản sao thông thường. Điều này có thể không áp dụng được tùy thuộc vào ngữ cảnh của bạn (Hạn chế bộ nhớ / kích thước của lát cắt, khả năng suy giảm hiệu suất - đặc biệt là nếu bản sao xảy ra trong một vòng lặp giống như tôi đã làm, v.v ...)

Để rõ ràng, đây là cảnh báo tôi nhận được:

/opt/anaconda3/lib/python3.6/site-packages/ipykernel/__main__.py:54:
SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame
See the caveats in the documentation:
http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy

Hình minh họa

Tôi đã nghi ngờ rằng cảnh báo đã bị ném vì một cột tôi đang rơi trên một bản sao của lát cắt. Mặc dù về mặt kỹ thuật không cố gắng đặt giá trị trong bản sao của lát cắt, đó vẫn là một sửa đổi của bản sao của lát cắt. Dưới đây là các bước (đơn giản hóa) tôi đã thực hiện để xác nhận sự nghi ngờ, tôi hy vọng nó sẽ giúp những người trong chúng ta đang cố gắng hiểu cảnh báo.

Ví dụ 1: thả một cột trên bản gốc ảnh hưởng đến bản sao

Chúng tôi đã biết điều đó rồi nhưng đây là một lời nhắc nhở lành mạnh. Đây KHÔNG phải là những gì cảnh báo là về.

>> data1 = {'A': [111, 112, 113], 'B':[121, 122, 123]}
>> df1 = pd.DataFrame(data1)
>> df1

    A   B
0   111 121
1   112 122
2   113 123


>> df2 = df1
>> df2

A   B
0   111 121
1   112 122
2   113 123

# Dropping a column on df1 affects df2
>> df1.drop('A', axis=1, inplace=True)
>> df2
    B
0   121
1   122
2   123

Có thể tránh những thay đổi được thực hiện trên df1 để ảnh hưởng đến df2

>> data1 = {'A': [111, 112, 113], 'B':[121, 122, 123]}
>> df1 = pd.DataFrame(data1)
>> df1

A   B
0   111 121
1   112 122
2   113 123

>> import copy
>> df2 = copy.deepcopy(df1)
>> df2
A   B
0   111 121
1   112 122
2   113 123

# Dropping a column on df1 does not affect df2
>> df1.drop('A', axis=1, inplace=True)
>> df2
    A   B
0   111 121
1   112 122
2   113 123

Ví dụ 2: thả một cột trên bản sao có thể ảnh hưởng đến bản gốc

Điều này thực sự minh họa cảnh báo.

>> data1 = {'A': [111, 112, 113], 'B':[121, 122, 123]}
>> df1 = pd.DataFrame(data1)
>> df1

    A   B
0   111 121
1   112 122
2   113 123

>> df2 = df1
>> df2

    A   B
0   111 121
1   112 122
2   113 123

# Dropping a column on df2 can affect df1
# No slice involved here, but I believe the principle remains the same?
# Let me know if not
>> df2.drop('A', axis=1, inplace=True)
>> df1

B
0   121
1   122
2   123

Có thể tránh những thay đổi được thực hiện trên df2 để ảnh hưởng đến df1

>> data1 = {'A': [111, 112, 113], 'B':[121, 122, 123]}
>> df1 = pd.DataFrame(data1)
>> df1

    A   B
0   111 121
1   112 122
2   113 123

>> import copy
>> df2 = copy.deepcopy(df1)
>> df2

A   B
0   111 121
1   112 122
2   113 123

>> df2.drop('A', axis=1, inplace=True)
>> df1

A   B
0   111 121
1   112 122
2   113 123

Chúc mừng!


4

Điều này sẽ làm việc:

quote_df.loc[:,'TVol'] = quote_df['TVol']/TVOL_SCALE

4

Một số có thể muốn đơn giản để ngăn chặn cảnh báo:

class SupressSettingWithCopyWarning:
    def __enter__(self):
        pd.options.mode.chained_assignment = None

    def __exit__(self, *args):
        pd.options.mode.chained_assignment = 'warn'

with SupressSettingWithCopyWarning():
    #code that produces warning

3

Nếu bạn đã gán lát cho một biến và muốn đặt bằng biến như sau:

df2 = df[df['A'] > 2]
df2['B'] = value

Và bạn không muốn sử dụng giải pháp Jeffs vì tính toán điều kiện của bạn df2kéo dài hoặc vì một số lý do khác, sau đó bạn có thể sử dụng như sau:

df.loc[df2.index.tolist(), 'B'] = value

df2.index.tolist() trả về các chỉ mục từ tất cả các mục trong df2, sau đó sẽ được sử dụng để đặt cột B trong khung dữ liệu gốc.


cái này đắt hơn gấp 9 lần sau đó df ["B"] = value
Claudiu Creanga

Bạn có thể giải thích điều này sâu sắc hơn @ClaudiuCreanga?
gies0r

2

Đối với tôi vấn đề này xảy ra trong một ví dụ sau> đơn giản hóa. Và tôi cũng đã có thể giải quyết nó (hy vọng với một giải pháp chính xác):

mã cũ với cảnh báo:

def update_old_dataframe(old_dataframe, new_dataframe):
    for new_index, new_row in new_dataframe.iterrorws():
        old_dataframe.loc[new_index] = update_row(old_dataframe.loc[new_index], new_row)

def update_row(old_row, new_row):
    for field in [list_of_columns]:
        # line with warning because of chain indexing old_dataframe[new_index][field]
        old_row[field] = new_row[field]  
    return old_row

Điều này in cảnh báo cho dòng old_row[field] = new_row[field]

Vì các hàng trong phương thức update_row thực sự là loại Series, tôi đã thay thế dòng này bằng:

old_row.at[field] = new_row.at[field]

tức là phương pháp truy cập / tra cứu cho a Series. Mặc dù cả hai đều hoạt động tốt và kết quả là như nhau, theo cách này tôi không phải vô hiệu hóa các cảnh báo (= giữ chúng cho các vấn đề lập chỉ mục chuỗi khác ở một nơi khác).

Tôi hy vọng điều này có thể giúp một ai đó.


2

Bạn có thể tránh toàn bộ vấn đề như thế này, tôi tin:

return (
    pd.read_csv(StringIO(str_of_all), sep=',', names=list('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefg')) #dtype={'A': object, 'B': object, 'C': np.float64}
    .rename(columns={'A':'STK', 'B':'TOpen', 'C':'TPCLOSE', 'D':'TPrice', 'E':'THigh', 'F':'TLow', 'I':'TVol', 'J':'TAmt', 'e':'TDate', 'f':'TTime'}, inplace=True)
    .ix[:,[0,3,2,1,4,5,8,9,30,31]]
    .assign(
        TClose=lambda df: df['TPrice'],
        RT=lambda df: 100 * (df['TPrice']/quote_df['TPCLOSE'] - 1),
        TVol=lambda df: df['TVol']/TVOL_SCALE,
        TAmt=lambda df: df['TAmt']/TAMT_SCALE,
        STK_ID=lambda df: df['STK'].str.slice(13,19),
        STK_Name=lambda df: df['STK'].str.slice(21,30)#.decode('gb2312'),
        TDate=lambda df: df.TDate.map(lambda x: x[0:4]+x[5:7]+x[8:10]),
    )
)

Sử dụng Assign. Từ tài liệu : Gán các cột mới cho DataFrame, trả về một đối tượng mới (một bản sao) với tất cả các cột ban đầu bên cạnh các cột mới.

Xem bài viết của Tom Augspurger về phương pháp xâu chuỗi trong gấu trúc: https://tomaugspurger.github.io/method-chained


2

Câu hỏi / nhận xét cho người mới bắt đầu

Có lẽ một sự làm rõ cho những người mới bắt đầu khác như tôi (tôi đến từ R dường như hoạt động hơi khác một chút dưới mui xe). Mã chức năng và giao diện vô hại sau đây tiếp tục tạo ra cảnh báo Cài đặtWithCopy và tôi không thể hiểu tại sao. Tôi đã đọc và hiểu cả việc ban hành với "lập chỉ mục chuỗi", nhưng mã của tôi không chứa bất kỳ:

def plot(pdb, df, title, **kw):
    df['target'] = (df['ogg'] + df['ugg']) / 2
    # ...

Nhưng sau đó, sau đó, quá muộn, tôi đã xem xét hàm của hàm () được gọi là:

    df = data[data['anz_emw'] > 0]
    pixbuf = plot(pdb, df, title)

Vì vậy, "df" không phải là khung dữ liệu mà là một đối tượng bằng cách nào đó nhớ rằng nó được tạo bằng cách lập chỉ mục khung dữ liệu (vì vậy có phải là khung nhìn không?) Sẽ tạo ra dòng trong âm mưu ()

 df['target'] = ...

tương đương với

 data[data['anz_emw'] > 0]['target'] = ...

đó là một chỉ mục chuỗi. Tôi đã hiểu đúng chưa?

Dù sao,

def plot(pdb, df, title, **kw):
    df.loc[:,'target'] = (df['ogg'] + df['ugg']) / 2

Đã sửa nó.


1

Vì câu hỏi này đã được giải thích và thảo luận đầy đủ trong các câu trả lời hiện có, tôi sẽ chỉ cung cấp một pandascách tiếp cận gọn gàng cho trình quản lý bối cảnh bằng cách sử dụng pandas.option_context(liên kết đến tài liệuví dụ ) - hoàn toàn không cần phải tạo một lớp tùy chỉnh với tất cả các phương thức dunder và các chuông khác và còi.

Đầu tiên, chính trình quản lý bối cảnh:

from contextlib import contextmanager

@contextmanager
def SuppressPandasWarning():
    with pd.option_context("mode.chained_assignment", None):
        yield

Sau đó, một ví dụ:

import pandas as pd
from string import ascii_letters

a = pd.DataFrame({"A": list(ascii_letters[0:4]), "B": range(0,4)})

mask = a["A"].isin(["c", "d"])
# Even shallow copy below is enough to not raise the warning, but why is a mystery to me.
b = a.loc[mask]  # .copy(deep=False)

# Raises the `SettingWithCopyWarning`
b["B"] = b["B"] * 2

# Does not!
with SuppressPandasWarning():
    b["B"] = b["B"] * 2

Đáng chú ý là cả hai cách tiếp cận đều không sửa đổi a, điều này hơi gây ngạc nhiên cho tôi và ngay cả một bản sao df nông .copy(deep=False)sẽ ngăn cảnh báo này được nêu ra (theo như tôi hiểu thì sao chép cũng nên sửa đổi a, nhưng nó không 't. pandasma thuật.).


hmmm, tôi hiểu điều đó nếu cảnh báo đưa ra một cái gì đó là sai rõ ràng, vì vậy tốt hơn là tránh cảnh báo như đàn áp nó, bạn nghĩ sao?
jezrael

Không, cảnh báo chỉ là một cảnh báo. Giống như ở đây, nó cảnh báo bạn điều gì đó thể sai, điều này thật tuyệt khi biết, nhưng nếu bạn biết điều gì và tại sao bạn đang làm thì hoàn toàn ổn để loại bỏ một số trong số họ. Xem phần giải thích trong stackoverflow.com/a/20627316/4272484 về việc gán lại tài liệu tham khảo.
m-dz

1

Tôi đã gặp vấn đề này .apply()khi gán một khung dữ liệu mới từ khung dữ liệu có sẵn mà tôi đã sử dụng .query()phương pháp này. Ví dụ:

prop_df = df.query('column == "value"')
prop_df['new_column'] = prop_df.apply(function, axis=1)

Sẽ trả lại lỗi này. Cách khắc phục có vẻ như giải quyết lỗi trong trường hợp này là bằng cách thay đổi điều này thành:

prop_df = df.copy(deep=True)
prop_df = prop_df.query('column == "value"')
prop_df['new_column'] = prop_df.apply(function, axis=1)

Tuy nhiên, điều này KHÔNG hiệu quả đặc biệt là khi sử dụng các tệp dữ liệu lớn, do phải tạo một bản sao mới.

Nếu bạn đang sử dụng .apply()phương thức tạo cột mới và các giá trị của nó, cách khắc phục sẽ khắc phục lỗi và hiệu quả hơn là bằng cách thêm .reset_index(drop=True):

prop_df = df.query('column == "value"').reset_index(drop=True)
prop_df['new_column'] = prop_df.apply(function, axis=1)
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.