tło
Właśnie zaktualizowałem moje Pandy z 0.11 do 0.13.0rc1. Teraz aplikacja wyświetla 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 zawiesić ostrzeżenie, jeśli nalegam na użycie quote_df['TVol'] = quote_df['TVol']/TVOL_SCALE
?
Funkcja powodująca 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])
python
pandas
dataframe
chained-assignment
wielki robak
źródło
źródło
df.set_value
, dokumenty tutaj - pandas.pydata.org/pandas-docs/stable/generated/…df.set_value
został uznany za przestarzały. Pandy teraz zalecają użycie.at[]
lub.iat[]
zamiast tego. Dokumentyoption_context
tutaj o pandach : pandas.pydata.org/pandas-docs/stable/user_guide/options.html , użyj jakowith pd.option_context("mode.chained_assignment", None): [...]
Odpowiedzi:
SettingWithCopyWarning
Został stworzony, aby flaga potencjalnie mylące „przykuty” zadania, takie jak następujące, które nie zawsze działa zgodnie z oczekiwaniami, szczególnie gdy pierwszy wybór zwraca kopię . [patrz GH5390 i GH5597 w celu omówienia w tle.]Ostrzeżenie oferuje propozycję przepisania w następujący sposób:
Nie pasuje to jednak do twojego zastosowania, co jest równoważne z:
Chociaż jasne jest, że nie obchodzi Cię zapisywanie go z powrotem do oryginalnej ramki (ponieważ zastępujesz odniesienie do niego), niestety ten wzorzec nie może być odróżniony od pierwszego przykładu przypisania łańcuchowego. Stąd ostrzeżenie (fałszywie pozytywne). Potencjalne wyniki fałszywie dodatnie zostały omówione w dokumentach dotyczących indeksowania , jeśli chcesz przeczytać więcej. Możesz bezpiecznie wyłączyć to nowe ostrzeżenie za pomocą następującego zadania.
źródło
.ix
, ulepszone.iloc
itp.) Można zdecydowanie postrzegać jako „podstawowy sposób” bez ciągłego ostrzegania wszystkich o innych sposobach. Zamiast tego pozwól im być dorosłymi, a jeśli chcą wykonywać powiązane zadania, niech tak będzie. W każdym razie moje dwa centy. Widzimy tu niezadowolone komentarze deweloperów Pandas, gdy powiązane zadania będą działać na rzecz rozwiązania problemu, ale nie będą uważane za „podstawowy” sposób na zrobienie tego.pd.options.mode.chained_assignment = None
spowodowało, że mój kod działał około 6 razy szybciej. Czy ktoś jeszcze doświadczył podobnych wyników?Ten post jest przeznaczony dla czytelników, którzy:
Ustawiać
Co to jest
SettingWithCopyWarning
?Aby wiedzieć, jak sobie poradzić z tym ostrzeżeniem, ważne jest, aby zrozumieć, co to znaczy i dlaczego jest ono podnoszone.
Podczas filtrowania ramek danych istnieje możliwość wycinania / indeksowania ramki w celu zwrócenia widoku lub kopii , w zależności od układu wewnętrznego i różnych szczegółów implementacji. „Widok” to, jak sugeruje to termin, widok oryginalnych danych, więc modyfikacja widoku może modyfikować 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
utworzono go w celu oznaczenia operacji „przypisania łańcuchowego”. Rozważdf
powyższą konfigurację. Załóżmy, że chcesz wybrać wszystkie wartości w kolumnie „B”, gdzie wartości w kolumnie „A” to> 5. Panda pozwala to robić na różne sposoby, niektóre bardziej poprawne niż inne. Na przykład,I,
Zwracają ten sam wynik, więc jeśli czytasz tylko te wartości, nie ma to znaczenia. Więc o co chodzi? Problem z połączeniem łańcuchowym polega na tym, że zazwyczaj trudno jest przewidzieć, czy widok lub kopia zostaną zwrócone, więc w dużej mierze staje się to problemem, gdy próbujesz przypisać wartości z powrotem. Aby skorzystać z wcześniejszego przykładu, zastanów się, w jaki sposób ten kod jest wykonywany przez interpretera:
Za pomocą jednego
__setitem__
połączenia zdf
. OTOH, rozważ ten kod:Teraz, w zależności od tego, czy
__getitem__
zwrócił widok, czy kopię,__setitem__
operacja może nie działać .Zasadniczo należy używać
loc
do przypisywania opartego na etykietach i przypisywaniailoc
liczb całkowitych / pozycyjnych, ponieważ specyfikacja gwarantuje, że zawsze działają one na oryginale. Dodatkowo do ustawienia pojedynczej komórki należy użyćat
iiat
.Więcej można znaleźć w dokumentacji .
Po prostu powiedz mi, jak ukryć ostrzeżenie!
Rozważ prostą operację na kolumnie „A” w
df
. Wybranie „A” i podzielenie przez 2 spowoduje wyświetlenie ostrzeżenia, ale operacja będzie działać.Istnieje kilka sposobów bezpośredniego wyciszenia tego ostrzeżenia:
Zrobić
deepcopy
Zmiana
pd.options.mode.chained_assignment
może być ustawiony
None
,"warn"
albo"raise"
."warn"
jest wartością domyślną.None
całkowicie wyciszy to ostrzeżenie i"raise"
rzuciSettingWithCopyError
, uniemożliwiając przejście operacji.@Peter Cotton w komentarzach wymyślił fajny sposób nieinwazyjnej zmiany trybu (zmodyfikowany z tej treści ) za pomocą menedżera kontekstu, aby ustawić tryb tylko tak długo, jak jest to wymagane, i zresetować go z powrotem do stan oryginalny po zakończeniu.
Wykorzystanie jest następujące:
Lub, aby podnieść wyjątek
„Problem XY”: Co robię źle?
Przez większość czasu użytkownicy próbują znaleźć sposoby na wyeliminowanie tego wyjątku, nie w pełni rozumiejąc, dlaczego został zgłoszony. To dobry przykład problemu XY , w którym użytkownicy próbują rozwiązać problem „Y”, który w rzeczywistości jest objawem głębiej zakorzenionego problemu „X”. Pytania będą zadawane w oparciu o typowe problemy, które napotykają to ostrzeżenie, a następnie zostaną przedstawione rozwiązania.
Niewłaściwy sposób to zrobić:
Właściwy sposób za pomocą
loc
:Aby to zrobić, możesz użyć dowolnej z następujących metod.
Prawdopodobnie jest to prawdopodobnie spowodowane kodem znajdującym się wyżej w potoku. Czy stworzyłeś
df2
z czegoś większego, takiego jak? W takim przypadku indeksowanie logiczne zwróci widok, więc
df2
odniesie się do oryginału. To, co musisz zrobić, to przypisaćdf2
do kopii :Wynika to z faktu, że
df2
musiał zostać utworzony jako widok z innej operacji krojenia, takiej jakRozwiązaniem tego problemu jest albo zrobić
copy()
zdf
lub wykorzystanialoc
, jak poprzednio.źródło
Ogólnie rzecz biorąc, chodzi o
SettingWithCopyWarning
to, aby pokazać użytkownikom (a zwłaszcza nowym użytkownikom), że mogą działać na kopii, a nie na oryginale, jak myślą. Nie są fałszywe pozytywy (IOW, jeśli wiesz, co robisz, to może być ok ). Jedną z możliwości jest po prostu wyłączenie ostrzeżenia (domyślnie ostrzegaj ), jak sugeruje @Garrett.Oto kolejna opcja:
Możesz ustawić
is_copy
flagę naFalse
, która skutecznie wyłączy sprawdzanie dla tego obiektu :Jeśli wyraźnie skopiujesz, nie pojawi się żadne dodatkowe ostrzeżenie:
Kod, który pokazuje powyższy OP, chociaż jest zgodny z prawdą i prawdopodobnie coś, co również robię, jest technicznie uzasadnieniem tego ostrzeżenia, a nie fałszywym pozytywem. Innym sposobem na brak ostrzeżenia byłoby wykonanie operacji wyboru za pomocą
reindex
, npLub,
źródło
0.16
widzę o wiele więcej fałszywych alarmów, problem z fałszywymi alarmami polega na tym, że uczy się go ignorować, nawet jeśli czasem jest to uzasadnione.undefined
zachowanie. Jeśli cokolwiek, powinno to wygenerować błąd (aby uniknąć pułapekC
), ponieważapi
jest zamrożone, obecne zachowanie ostrzeżenia ma sens dla kompatybilności wstecznej. I sprawię, że rzucą, aby złapać je jako błędy w moim kodzie produkcyjnym (warnings.filterwarnings('error', r'SettingWithCopyWarning
). Również sugestia użycia.loc
czasami nie pomaga (jeśli jest w grupie).Ostrzeżenie przed kopiowaniem ramki danych Pandy
Kiedy idziesz i robisz coś takiego:
pandas.ix
w takim przypadku zwraca nową, autonomiczną ramkę danych.Wszelkie wartości, które zdecydujesz się zmienić w tej ramce danych, nie zmienią oryginalnej ramki danych.
Właśnie przed tym ostrzegają cię pandy.
Dlaczego
.ix
to zły pomysł.ix
Obiekt stara się zrobić więcej niż jedną rzecz, a dla każdego, kto czytał coś o czystego kodu, jest to silny zapach.Biorąc pod uwagę tę ramkę danych:
Dwa zachowania:
Zachowanie pierwsze:
dfcopy
jest teraz samodzielną ramką danych. Zmiana nie zmieni siędf
Zachowanie drugie: Zmienia oryginalną ramkę danych.
Użyj
.loc
zamiast tegoTwórcy pand rozpoznali, że
.ix
obiekt był śmierdzący [spekulacyjnie] i dlatego stworzyli dwa nowe obiekty, które pomagają w przystąpieniu i przypisaniu danych. (Druga istota.iloc
).loc
jest szybszy, ponieważ nie próbuje utworzyć kopii danych..loc
ma na celu zmodyfikowanie istniejącej ramki danych w miejscu, co jest bardziej wydajne pod względem pamięci..loc
jest przewidywalny, ma jedno zachowanie.Rozwiązanie
W przykładzie kodu ładujesz duży plik z dużą ilością kolumn, a następnie modyfikujesz go tak, aby był mniejszy.
Ta
pd.read_csv
funkcja może ci w tym pomóc, a także znacznie przyspieszyć ładowanie pliku.Zamiast tego robić
Zrób to
Spowoduje to jedynie odczytanie interesujących Cię kolumn i odpowiednie nazwanie ich. Nie trzeba używać złego
.ix
przedmiotu do robienia magicznych rzeczy.źródło
.iloc
. Są to dwie podstawowe metody indeksowania struktur danych pand. Przeczytaj więcej w dokumentacji.Tutaj odpowiadam bezpośrednio na pytanie. Jak sobie z tym poradzić?
Zrób
.copy(deep=False)
po krojeniu. Zobacz pandas.DataFrame.copy .Zaraz, czy plasterek nie zwraca kopii? W końcu to właśnie próbuje ostrzec komunikat ostrzegawczy? Przeczytaj długą odpowiedź:
To daje ostrzeżenie:
To nie:
Zarówno
df0
idf1
sąDataFrame
obiekty, ale coś o nich jest inny, który umożliwia pandy wydrukować ostrzeżenie. Dowiedzmy się, co to jest.Za pomocą wybranego narzędzia różnicowego zobaczysz, że poza kilkoma adresami jedyną istotną różnicą jest:
Metodą decydującą o tym, czy ostrzec, jest
DataFrame._check_setitem_copy
kontrola_is_copy
. Więc proszę bardzo. Zróbcopy
tak, aby twoja DataFrame nie była_is_copy
.Ostrzeżenie sugeruje użycie
.loc
, ale jeśli użyjesz.loc
go w ramce_is_copy
, nadal otrzymasz to samo ostrzeżenie. Zwodniczy? Tak. Denerwujący? Ty stawiasz Pomocny? Potencjalnie, gdy używane jest przypisanie łańcuchowe. Ale nie może poprawnie wykryć przypisania łańcucha i drukuje ostrzeżenie bez rozróżnienia.źródło
Ten temat jest naprawdę mylący z Pandami. 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ę lub widok DataFrame. Dalsze użycie takiej filtrowanej DataFrame może być mylące.
Proste rozwiązanie to (chyba że musisz pracować z bardzo dużymi zestawami danych):
Ilekroć musisz zaktualizować jakiekolwiek wartości, zawsze upewnij się, że niejawnie skopiujesz DataFrame przed przypisaniem.
źródło
Aby usunąć wszelkie wątpliwości, moim rozwiązaniem było wykonanie głębokiej kopii wycinka zamiast zwykłej kopii. Może to nie mieć zastosowania w zależności od kontekstu (ograniczenia pamięci / rozmiar wycinka, potencjał obniżenia wydajności - szczególnie jeśli kopia występuje w pętli, jak to zrobiłem dla mnie itp.)
Dla jasności, oto ostrzeżenie, które otrzymałem:
Ilustracja
Miałem wątpliwości, czy ostrzeżenie zostało rzucone z powodu kolumny, którą upuszczałem na kopię wycinka. Chociaż technicznie nie próbował ustawić wartości w kopii wycinka, była to modyfikacja kopii wycinka. Poniżej znajdują się (uproszczone) kroki, które podjąłem, aby potwierdzić to podejrzenie, mam nadzieję, że pomoże to tym z nas, którzy próbują zrozumieć ostrzeżenie.
Przykład 1: upuszczenie kolumny na oryginale wpływa na kopię
Wiedzieliśmy to już, ale jest to zdrowe przypomnienie. To nie to, co jest o ostrzeżenie.
Możliwe jest uniknięcie zmian wprowadzonych na df1, aby wpłynąć na df2
Przykład 2: upuszczenie kolumny na kopii może wpłynąć na oryginał
To faktycznie ilustruje ostrzeżenie.
Można uniknąć zmian wprowadzonych w df2, aby wpłynąć na df1
Twoje zdrowie!
źródło
To powinno działać:
źródło
Niektórzy mogą chcieć po prostu ukryć ostrzeżenie:
źródło
Jeśli plasterek został przypisany do zmiennej i chcesz ustawić ją za pomocą zmiennej, jak pokazano poniżej:
I nie chcesz używać rozwiązania Jeffsa, ponieważ obliczanie warunków
df2
jest zbyt długie lub z innego powodu, możesz użyć następujących opcji:df2.index.tolist()
zwraca indeksy ze wszystkich pozycji w df2, które zostaną następnie wykorzystane do ustawienia kolumny B w oryginalnej ramce danych.źródło
Dla mnie ten problem wystąpił w następującym> uproszczonym <przykładzie. Byłem też w stanie go rozwiązać (mam nadzieję, że z poprawnym rozwiązaniem):
stary kod z ostrzeżeniem:
To wydrukowało ostrzeżenie dla linii
old_row[field] = new_row[field]
Ponieważ wiersze w metodzie update_row są w rzeczywistości typem
Series
, zastąpiłem wiersz:to znaczy metoda uzyskiwania dostępu / wyszukiwania dla
Series
. Chociaż oba działają dobrze, a wynik jest taki sam, w ten sposób nie muszę wyłączać ostrzeżeń (= zachowaj je dla innych problemów z indeksowaniem łańcucha gdzie indziej).Mam nadzieję, że to może komuś pomóc.
źródło
Uważam, że można uniknąć całego problemu w ten sposób:
Korzystanie z Przypisz. Z dokumentacji : Przypisz nowe kolumny do DataFrame, zwracając nowy obiekt (kopię) ze wszystkimi oryginalnymi kolumnami oprócz nowych.
Zobacz artykuł Toma Augspurgera na temat łączenia łańcuchów w pandach: https://tomaugspurger.github.io/method-chaining
źródło
Dalsze pytanie / uwaga dla początkujących
Być może wyjaśnienie dla innych początkujących, takich jak ja (pochodzę z R, która 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 wydanie z „indeksowaniem łańcuchowym”, ale mój kod nie zawiera:
Ale później, o wiele za późno, przyjrzałem się nazwie funkcji plot ():
Zatem „df” nie jest ramką danych, ale obiektem, który w jakiś sposób pamięta, że został utworzony przez indeksowanie ramki danych (a więc to widok?), Który utworzyłby linię w plot ()
równoważny
czyli indeksowanie łańcuchowe. Czy dobrze to zrozumiałem?
Tak czy siak,
naprawione.
źródło
Ponieważ pytanie to zostało już w pełni wyjaśnione i omówione w istniejących odpowiedziach, przedstawię tylko schludne
pandas
podejście do menedżera kontekstu przy użyciupandas.option_context
(linki do dokumentów i przykład ) - absolutnie nie ma potrzeby tworzenia niestandardowej klasy ze wszystkimi metodami dundera i innymi dzwonkami i gwizdy.Najpierw sam kod menedżera kontekstu:
Następnie przykład:
Warto zauważyć, że oba approches nie modyfikują
a
, co jest dla mnie nieco zaskakujące, a nawet płytka kopia df.copy(deep=False)
uniemożliwiłaby wygenerowanie tego ostrzeżenia (o ile rozumiem, płytka kopia powinna przynajmniej zmodyfikowaća
, ale nie zmienia „t.pandas
magia.).źródło
Ten problem występował
.apply()
podczas przypisywania nowej ramki danych z wcześniej istniejącej ramki danych, w której użyłem tej.query()
metody. Na przykład:Zwróciłby ten błąd. Naprawą, która wydaje się rozwiązać problem w tym przypadku, jest zmiana na:
Jednak NIE jest to efektywne, szczególnie przy użyciu dużych ramek danych, ze względu na konieczność wykonania nowej kopii.
Jeśli używasz tej
.apply()
metody do generowania nowej kolumny i jej wartości, poprawką, która rozwiązuje błąd i jest bardziej wydajna, jest dodanie.reset_index(drop=True)
:źródło