Jak radzić sobie z SettingWithCopyWarning w Pandy

Tło

Właśnie uaktualniłem moje pandy z 0.11 do 0.13. 0rc1. Teraz aplikacja wyskakuje wiele nowych ostrzeżeń. Jeden z nich taki:

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
Chcę wiedzieć, co to dokładnie znaczy? Czy muszę coś zmienić?

Jak mam zawiesić ostrzeżenie, jeśli nalegam na użycie quote_df['TVol'] = quote_df['TVol']/TVOL_SCALE?

Funkcja, która daje błędy

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

Więcej komunikatów o błędach

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])
Author: Trenton McKinney, 2013-12-17

15 answers

SettingWithCopyWarning został stworzony w celu oznaczania potencjalnie mylących "przykutych" zadań, takich jak poniższe, które nie zawsze działają zgodnie z oczekiwaniami, szczególnie gdy pierwsza selekcja zwraca kopię. [zobacz GH5390 i GH5597 do dyskusji w tle.]

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

Ostrzeżenie proponuje przepisanie w następujący sposób:

df.loc[df['A'] > 2, 'B'] = new_val
Jednak to nie pasuje do twojego użycia, co jest równoważne:
df = df[df['A'] > 2]
df['B'] = new_val
/ Align = "left" / zapisuje wracając do oryginalnej ramki (ponieważ nadpisujesz odniesienie do niej), niestety tego wzorca nie można odróżnić od pierwszego przykładu przypisania łańcuchowego. Stąd Ostrzeżenie (fałszywie dodatnie). Możliwość wystąpienia fałszywych alarmów jest omówiona w docs on indexing , Jeśli chcesz przeczytać dalej. Możesz bezpiecznie wyłączyć to nowe ostrzeżenie za pomocą następującego przypisania.
import pandas as pd
pd.options.mode.chained_assignment = None  # default='warn'

Inne Zasoby

 984
Author: Garrett,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-09-29 20:10:58

Jak radzić sobie z SettingWithCopyWarning W pandach?

[[70]}ten post jest przeznaczony dla czytelników, którzy, [71]}
  1. chciałbym zrozumieć, co to Ostrzeżenie oznacza
  2. chciałbym zrozumieć różne sposoby tłumienia tego ostrzeżenia
  3. chcieliby zrozumieć, jak poprawić swój kod i przestrzegać dobrych praktyk, aby uniknąć tego ostrzeżenia w przyszłości.

Setup

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

Co to jest SettingWithCopyWarning?

Aby wiedzieć, jak radzić sobie z to Ostrzeżenie, ważne jest, aby zrozumieć, co to znaczy i dlaczego jest podniesiony w pierwszej kolejności.

Podczas filtrowania ramek danych możliwe jest zwrócenie widoku lub kopii , w zależności od wewnętrznego układu i różnych szczegółów implementacji. "Widok" jest, jak sugeruje termin, widokiem na oryginalne dane, więc modyfikacja widoku może zmodyfikować oryginalny obiekt. Z drugiej strony "kopia" jest replikacją danych z oryginału, a modyfikacja kopii nie ma wpływu na oryginał.

Jak wspomniano w innych odpowiedziach, SettingWithCopyWarning został stworzony do oznaczania operacji "przypisanie łańcuchowe". Rozważ df w konfiguracji powyżej. Załóżmy, że chcesz wybrać wszystkie wartości w kolumnie "B", gdzie wartości w kolumnie " A " wynoszą > 5. Pandy pozwalają robić to na różne sposoby, niektóre bardziej poprawne niż inne. Na przykład,

df[df.A > 5]['B']
 
1    3
2    6
Name: B, dtype: int64

I,

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

1    3
2    6
Name: B, dtype: int64

Zwracają ten sam wynik, więc jeśli czytasz tylko te wartości, to bez różnicy. Więc w czym problem? Problem z przypisaniem łańcuchowym polega na tym, że zazwyczaj trudno jest przewidzieć, czy zostanie zwrócony widok lub Kopia, , więc w dużej mierze staje się to problemem podczas próby przypisania wartości z powrotem. aby bazować na wcześniejszym przykładzie, rozważ, jak ten kod jest wykonywany przez interpreter:

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

Z pojedynczym __setitem__ wywołaniem do df. Otoh, rozważ ten kod:

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

Teraz, w zależności od tego, czy __getitem__ zwrócił Widok czy kopię, operacja __setitem__ może nie zadziałać .

Ogólnie rzecz biorąc, należy użyć loc do przypisywania na podstawie etykiet oraz iloc W przypadku przypisywania liczb całkowitych / pozycyjnych, ponieważ specyfikacja gwarantuje, że zawsze działają na oryginale. Dodatkowo do Ustawienia pojedynczej komórki należy użyć at oraz iat.

Więcej można znaleźć w dokumentacji.

Uwaga
Wszystkie indeksowanie logiczne operacje wykonywane za pomocą loc mogą być również wykonywane za pomocą iloc. Jedyną różnicą jest to, że iloc spodziewa się albo liczby całkowite/pozycje dla indeksu lub tablicy numpy wartości logicznych oraz indeksy liczb całkowitych / pozycyjnych dla kolumn.

Na przykład,

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

Można zapisać nas

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

I,

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

Można zapisać jako

df.iloc[1, 0] = 100

I tak dalej.


Powiedz mi tylko, jak stłumić Ostrzeżenie!

Rozważmy prostą operację na kolumnie "A" df. Wybranie " A " i podzielenie przez 2 podniesie Ostrzeżenie, ale operacja zadziała.

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
Jest kilka sposobów na bezpośrednie wyciszenie tego ostrzeżenia:]}
  1. (zalecane) użyj loc do cięcia podzbiorów :

     df2 = df.loc[:, ['A']]
     df2['A'] /= 2     # Does not raise 
    
  2. Zmień pd.options.mode.chained_assignment
    Można ustawić na None, "warn", lub "raise". "warn" jest wartością domyślną. None całkowicie stłumi Ostrzeżenie, a "raise" rzuci SettingWithCopyError, uniemożliwiając przeprowadzenie operacji przez.

     pd.options.mode.chained_assignment = None
     df2['A'] /= 2
    
  3. Make a deepcopy

     df2 = df[['A']].copy(deep=True)
     df2['A'] /= 2
    

@Peter Cotton w komentarzach wymyślił miły sposób nieinwazyjnej zmiany trybu (zmodyfikowany z ten gist) za pomocą Menedżera kontekstu, aby ustawić tryb Tylko tak długo, jak jest to wymagane, i przywrócić go do stanu pierwotnego po zakończeniu.

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

Użycie jest następujące:

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

Lub, aby podnieść exception

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

Problem XY: co robię źle?

Przez większość czasu użytkownicy próbują szukać sposobów na wyeliminowanie tego wyjątku bez pełnego zrozumienia, dlaczego został on podniesiony w pierwszej kolejności. Jest to dobry przykład problemu XY, gdzie użytkownicy próbują rozwiązać problem "Y", który jest faktycznie objawem głębiej zakorzenionego problemu "X". Pytania będą stawiane w oparciu o typowe problemy, które napotykają to Ostrzeżenie, a rozwiązania będą przedstawione.

Pytanie 1
Mam 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

Chcę przypisać wartości w kol" A " > 5 do 1000. Moje oczekiwane wyjście to

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

Niewłaściwy sposób:

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

Right way using loc:

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

Pytanie 21
Próbuję ustawić wartość w komórce (1, 'D') na 12345. Moje oczekiwane wyjście to

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

Próbowałem różnych sposobów dostępu do tej komórki, takich jako df['D'][1]. Jak najlepiej to zrobić?

1. To pytanie nie jest konkretnie związane z ostrzeżeniem, ale dobrze jest zrozumieć, jak prawidłowo wykonać tę konkretną operację aby uniknąć sytuacji, w których ostrzeżenie może potencjalnie powstać w przyszłość.

Możesz użyć jednej z następujących metod, aby to zrobić.

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

Pytanie 3
Staram się podzbiór wartości na podstawie jakiegoś warunku. Mam a DataFrame

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

Chciałbym przypisać wartości w " D "do 123 takich, że" C " = = 5. I wypróbowany

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

Co wydaje się w porządku, ale jestem nadal coraz SettingWithCopyWarning! Jak to naprawić?

To prawdopodobnie z powodu kodu wyżej w Twoim potoku. Czy stworzyłeś df2 z czegoś większego, jak

df2 = df[df.A > 5]

? W tym przypadku indeksowanie logiczne zwróci Widok, więc df2 odwoła się do oryginału. Musisz przypisać df2 do Kopia :

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

Pytanie 4
Staram się zrzucić kolumnę "C" na miejsce z

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

Ale używając

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

SettingWithCopyWarning. Dlaczego to się dzieje?

To dlatego, że df2 musiał zostać utworzony jako widok z innej operacji krojenia, takiej jak

df2 = df[df.A > 5]

Rozwiązaniem jest albo zrobić copy() Z df, albo użyć loc, jak wcześniej.

 343
Author: cs95,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-18 12:00:08

Ogólnie rzecz biorąc, celem SettingWithCopyWarning jest pokazanie użytkownikom (a zwłaszcza nowym użytkownikom), że mogądziałać na kopii, a nie na oryginale, jak myślą. Istnieją fałszywe alarmy (jeśli wiesz, co robisz, może to być ok ). Jedną z możliwości jest po prostu wyłączenie (domyślnie warn) ostrzeżenia, jak sugeruje @Garrett.

Oto inna opcja:

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

Możesz ustawić flagę is_copy na False, która skutecznie wyłączy sprawdzanie, dla tego obiektu :

In [5]: dfa.is_copy = False

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

Jeśli wyraźnie skopiujesz, nie nastąpi żadne dalsze Ostrzeżenie:

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

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

Kod pokazany powyżej, chociaż uzasadniony, i prawdopodobnie coś, co ja również robię, jest technicznie argumentem za tym ostrzeżeniem, a nie fałszywym wynikiem. Innym sposobem, aby Nie mieć Ostrzeżenie, byłoby wykonanie operacji wyboru przez reindex, np.

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

Lub

quote_df = quote_df.reindex(['STK', ...], axis=1)  # v.0.21
 158
Author: Jeff,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-11-09 19:23:22

Pandy dataframe copy warning

Kiedy pójdziesz i zrobisz coś takiego:

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

pandas.ix w tym przypadku zwraca nową, samodzielną ramkę danych.

Jakiekolwiek wartości, które zdecydujesz się zmienić w tej ramce danych, nie zmieni oryginalnej ramki danych.

To właśnie pandy próbują was ostrzec.

Dlaczego .ix to zły pomysł

Obiekt .ix próbuje zrobić więcej niż jedną rzecz, a dla każdego, kto przeczytał cokolwiek o czystym kodzie, to silny zapach.

Biorąc pod uwagę ten dataframe:

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

Dwa zachowania:

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

Zachowanie pierwsze: dfcopy jest teraz samodzielną ramką danych. Zmiana nie zmieni df

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

Zachowanie drugie: zmienia to oryginalną ramkę danych.


Użyj .loc zamiast

Twórcy pandas uznali, że obiekt .ix był dość śmierdzący [spekulatywnie] i w ten sposób stworzyli dwa nowe obiekty, które pomagają w przystąpieniu i przypisaniu danych. (Inne być .iloc)

.loc jest szybszy, ponieważ nie próbuje utworzyć kopii danych.

.loc ma na celu modyfikację istniejącej ramki danych w miejscu, co jest bardziej wydajne pamięci.

.loc jest przewidywalny, ma jedno zachowanie.


Rozwiązanie

To, co robisz w swoim przykładzie kodu, to Ładowanie dużego pliku z dużą ilością kolumn, a następnie modyfikowanie go, aby był mniejszy.

Funkcja pd.read_csv może Ci w tym pomóc, a także Przyspiesz ładowanie pliku.

Więc zamiast robić to

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]]

Zrób to

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

Spowoduje to odczytanie tylko interesujących Cię kolumn i poprawne ich nazwanie. Nie ma potrzeby używania złego obiektu .ix do robienia magicznych rzeczy.

 44
Author: firelynx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-20 09:12:55

Tutaj odpowiadam bezpośrednio na pytanie. Jak sobie z tym poradzić?

Zrób .copy(deep=False) po krojeniu. Zobacz pandy.Ramka danych.Kopia .

Czekaj, czy kawałek nie zwraca kopii? W końcu to właśnie próbuje powiedzieć komunikat ostrzegawczy? Przeczytaj długą odpowiedź:

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

To daje ostrzeżenie:

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

To nie:

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

Zarówno df0 jak i df1 są obiektami DataFrame, ale coś w nich jest innego, co umożliwia drukowanie Uwaga. Dowiedzmy się, co to jest.

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

Używając wybranego narzędzia diff, zobaczysz, że poza kilkoma adresami, jedyną istotną różnicą jest to:

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

Metodą, która decyduje o tym, czy ostrzegać, jest DataFrame._check_setitem_copy, która sprawdza _is_copy. Proszę bardzo. Stwórz copy, aby twoja ramka Danych nie była _is_copy.

Ostrzeżenie sugeruje użycie .loc, ale jeśli użyjesz .loc na ramce, która _is_copy, nadal otrzymasz to samo ostrzeżenie. Wprowadza w błąd? Tak. Irytujące? Jasne. Pomocny? Potencjalnie, gdy używane jest przypisanie łańcuchowe. Ale nie może poprawnie wykryć przypisania łańcucha i drukuje Ostrzeżenie bezkrytycznie.

 28
Author: user443854,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-05-24 18:36:09

Ten temat jest naprawdę mylący z pand. Na szczęście ma stosunkowo proste rozwiązanie.

Problem polega na tym, że nie zawsze jest jasne, czy operacje filtrowania danych (np. loc) zwracają kopię, czy Widok ramki danych. Dalsze korzystanie z takich przefiltrowanych ramek danych może być zatem mylące.

Proste rozwiązanie jest (chyba że trzeba pracować z bardzo dużymi zestawami danych):

Zawsze, gdy musisz zaktualizować jakiekolwiek wartości, zawsze upewnij się, że jawnie skopiujesz Ramka danych przed zadaniem.

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)
 19
Author: Mikulas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-09 17:57:58

Aby usunąć wszelkie wątpliwości, moim rozwiązaniem było zrobienie głębokiej kopii kawałka zamiast zwykłej kopii. Może to nie mieć zastosowania w zależności od kontekstu (ograniczenia pamięci / rozmiar plasterka, potencjalne pogorszenie wydajności - zwłaszcza jeśli kopia występuje w pętli, jak to miało miejsce dla mnie, itp...)

Aby było jasne, oto Ostrzeżenie, które otrzymałem:

/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

Ilustracja

Miałem wątpliwości, czy ostrzeżenie zostało rzucone z powodu kolumny, którą upuszczałem na kopię plaster. Chociaż technicznie nie próbowano ustawić wartości w kopii plasterka, nadal była to modyfikacja kopii plasterka. Poniżej znajdują się (uproszczone) kroki, które podjąłem, aby potwierdzić podejrzenie, mam nadzieję, że pomoże to tym z nas, którzy próbują zrozumieć Ostrzeżenie.

Przykład 1: upuszczenie kolumny na oryginał wpływa na kopię

[7]}już to wiedzieliśmy, ale to jest zdrowe przypomnienie. To jest nie o czym jest Ostrzeżenie.
>> 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

Jest możliwe aby uniknąć zmian wprowadzonych na df1 mających wpływ na df2. Uwaga: możesz uniknąć importowania copy.deepcopy, wykonując zamiast tego df.copy().

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

Przykład 2: upuszczenie kolumny na kopię może mieć wpływ na oryginał

To faktycznie ilustruje Ostrzeżenie.
>> 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

Możliwe jest uniknięcie zmian wprowadzonych na df2, aby wpłynąć na 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
Zdrówko!
 9
Author: Raphvanns,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2021-02-05 19:57:52

To powinno zadziałać:

quote_df.loc[:,'TVol'] = quote_df['TVol']/TVOL_SCALE
 7
Author: jrouquie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-09 09:48:49

Miałem ten problem z .apply() podczas przypisywania nowej ramki danych z wcześniej istniejącej ramki danych, na której użyłem metody .query(). Na przykład:

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

Zwróci ten błąd. Poprawka, która wydaje się rozwiązywać błąd w tym przypadku, polega na zmianie tego na:

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

Nie jest to jednak wydajne, szczególnie w przypadku dużych ramek danych, z powodu konieczności wykonania nowej kopii.

Jeśli używasz metody .apply() do generowania nowej kolumny i jej wartości, poprawka to rozwiązuje błąd i jest bardziej efektywne poprzez dodanie .reset_index(drop=True):

prop_df = df.query('column == "value"').reset_index(drop=True)
prop_df['new_column'] = prop_df.apply(function, axis=1)
 7
Author: Zilbert97,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-03-27 12:46:02

Niektórzy mogą chcieć po prostu stłumić Ostrzeżenie:

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
 6
Author: delica,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-05-17 09:47:34

Można by uniknąć tego całego problemu, jak sądzę:

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]),
    )
)

Użycie Assign. Z dokumentacji : Przypisz nowe kolumny do ramki danych, zwracając nowy obiekt (kopię) ze wszystkimi oryginalnymi kolumnami oprócz nowych.

Zobacz artykuł Toma Augspurgera na temat metody łańcuchowania w pandach: https://tomaugspurger.github.io/method-chaining

 4
Author: hughdbrown,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-16 21:27:38

Jeśli przypisałeś wycinek do zmiennej i chcesz ustawić za pomocą zmiennej tak jak w poniższym:

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

I nie chcesz używać Jeffs solution, ponieważ twój stan df2 jest długi lub z jakiegoś innego powodu, możesz użyć następującego:

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

df2.index.tolist() zwraca indeksy ze wszystkich wpisów w df2, które następnie zostaną użyte do Ustawienia kolumny B w oryginalnej ramce danych.

 3
Author: Steohan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-24 01:30:48

Ponieważ to pytanie jest już w pełni wyjaśnione i omówione w istniejących odpowiedziach, po prostu przedstawię schludne pandas podejście do menedżera kontekstu za pomocą pandas.option_context (Linki do docs i przykład) - nie ma absolutnie potrzeby tworzenia niestandardowej klasy z wszystkimi metodami dunder i innymi wodotryskami.

Najpierw sam kod menedżera kontekstu:

from contextlib import contextmanager

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

Następnie przykład:

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

Warto zauważyć, że oba podejścia nie modyfikują a, które jest to dla mnie trochę zaskakujące i nawet płytka Kopia df z .copy(deep=False) uniemożliwiłaby podniesienie tego ostrzeżenia (o ile rozumiem płytka kopia powinna przynajmniej zmodyfikować a, ale tak nie jest. pandas Magia.).

 3
Author: m-dz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-02-03 13:41:12

Dla mnie ten problem pojawił się w następującym >uproszczonym

Stary kod z ostrzeżeniem:

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

To wydrukowało ostrzeżenie dla linii old_row[field] = new_row[field]

Ponieważ wiersze w metodzie update_row w rzeczywistości są typu Series, zastąpiłem wiersz na:

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

Tj. metoda dostępu/wyszukiwania dla Series. Mimo, że oba działają dobrze, a wynik jest taki sam, w ten sposób nie muszę wyłączać Ostrzeżenia (=zachowaj je dla innych problemów indeksowania łańcucha gdzie indziej).

Mam nadzieję, że to komuś pomoże.
 2
Author: Petr Szturc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-27 09:39:57

Kontynuacja pytania / uwagi dla początkujących

Może wyjaśnienie dla innych początkujących jak ja (pochodzę z R, który wydaje się działać nieco inaczej pod maską). Poniższy, nieszkodliwy i funkcjonalny kod generował Ostrzeżenie SettingWithCopy i nie mogłem zrozumieć dlaczego. Przeczytałem i zrozumiałem wydane z "przykutym indeksowaniem", ale mój kod nie zawiera żadnego:

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

Ale później, o wiele za późno, spojrzałem na to, gdzie funkcja plot() nazywa się:

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

Więc "df" nie jest ramką danych, ale obiektem, który w jakiś sposób pamięta, że został utworzony przez indeksowanie ramki danych(więc jest to Widok?), co uczyniłoby linię w plot ()

 df['target'] = ...

Odpowiednik

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

Który jest indeksowaniem łańcuchowym. Dobrze zrozumiałem?

W każdym razie,

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

Naprawiłem to.

 2
Author: musbur,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-02-13 07:39:54