Ik heb zojuist mijn Panda’s geüpgraded van 0.11 naar 0.13.0rc1. Nu laat de applicatie veel nieuwe waarschuwingen zien. Een van hen ziet er zo uit:
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
Ik wil weten wat het precies betekent? Moet ik iets veranderen?
Hoe moet ik de waarschuwing opschorten als ik erop sta quote_df['TVol'] = quote_df['TVol']/TVOL_SCALE
te gebruiken?
De functie die fouten geeft
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
Meer foutmeldingen
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])
Antwoord 1, autoriteit 100%
De SettingWithCopyWarning
is gemaakt om mogelijk verwarrende "geketende" opdrachten, zoals de volgende, die niet altijd werken zoals verwacht, vooral wanneer de eerste selectie een kopie retourneert. [zie GH5390 en GH5597 voor achtergronddiscussie.]
df[df['A'] > 2]['B'] = new_val # new_val not set in df
De waarschuwing biedt een suggestie om als volgt te herschrijven:
df.loc[df['A'] > 2, 'B'] = new_val
Dit past echter niet bij uw gebruik, wat overeenkomt met:
df = df[df['A'] > 2]
df['B'] = new_val
Hoewel het duidelijk is dat het u niet uitmaakt dat schrijfacties terugkeren naar het oorspronkelijke frame (aangezien u de verwijzing ernaar overschrijft), kan dit patroon helaas niet worden onderscheiden van het eerste voorbeeld van een geketende opdracht. Vandaar de (false positive) waarschuwing. Het potentieel voor valse positieven wordt behandeld in de docs over indexeren, als u verder wilt lezen. Je kunt deze nieuwe waarschuwing veilig uitschakelen met de volgende opdracht.
import pandas as pd
pd.options.mode.chained_assignment = None # default='warn'
Andere bronnen
- gebruikershandleiding voor panda’s: gegevens indexeren en selecteren
- Python Data Science-handboek: gegevensindexering en -selectie
- Real Python: SettingWithCopyWarning in Pandas: Views vs Copies
- Dataquest: SettingwithCopyWarning: deze waarschuwing in Panda’s oplossen
- Op weg naar datawetenschap: de SettingWithCopyWarning bij panda’s uitleggen
Antwoord 2, autoriteit 38%
Hoe om te gaan met
SettingWithCopyWarning
in Panda’s?
Dit bericht is bedoeld voor lezers die,
- Ik wil graag weten wat deze waarschuwing betekent
- Ik zou graag meer willen weten over verschillende manieren om deze waarschuwing te onderdrukken
- Zou graag willen weten hoe ze hun code kunnen verbeteren en goede praktijken willen volgen om deze waarschuwing in de toekomst te vermijden.
Instellen
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
Wat is de SettingWithCopyWarning
?
Om te weten hoe met deze waarschuwing om te gaan, is het belangrijk om te begrijpen wat het betekent en waarom het in de eerste plaats wordt gesteld.
Bij het filteren van DataFrames is het mogelijk een frame te segmenteren/indexeren om ofwel een weergave of een kopie te retourneren, afhankelijk van de interne lay-out en verschillende implementatiedetails. Een "zicht" is, zoals de term suggereert, een weergave van de oorspronkelijke gegevens, dus het wijzigen van de weergave kan het oorspronkelijke object wijzigen. Aan de andere kant, een "kopie" is een replicatie van gegevens van het origineel, en het wijzigen van de kopie heeft geen effect op het origineel.
Zoals vermeld in andere antwoorden, is de SettingWithCopyWarning
gemaakt om “geketende toewijzing” te markeren. activiteiten. Overweeg df
in de bovenstaande setup. Stel dat u alle waarden in kolom “B” wilt selecteren. waarbij waarden in kolom “A” is > 5. Met Panda’s kun je dit op verschillende manieren doen, de ene al correcter dan de andere. Bijvoorbeeld,
df[df.A > 5]['B']
1 3
2 6
Name: B, dtype: int64
En,
df.loc[df.A > 5, 'B']
1 3
2 6
Name: B, dtype: int64
Deze geven hetzelfde resultaat, dus als u alleen deze waarden leest, maakt het geen verschil. Dus, wat is het probleem? Het probleem met geketende toewijzing is dat het over het algemeen moeilijk te voorspellen is of een weergave of een kopie wordt geretourneerd, dus dit wordt grotendeels een probleem wanneer u probeert waarden terug toe te wijzen. Om voort te bouwen op de eerdere overweeg bijvoorbeeld hoe deze code wordt uitgevoerd door de interpreter:
df.loc[df.A > 5, 'B'] = 4
# becomes
df.__setitem__((df.A > 5, 'B'), 4)
Met een enkele __setitem__
aanroep naar df
. OTOH, overweeg deze code:
df[df.A > 5]['B'] = 4
# becomes
df.__getitem__(df.A > 5).__setitem__('B", 4)
Afhankelijk van het feit of __getitem__
een weergave of een kopie heeft geretourneerd, werkt de bewerking __setitem__
mogelijk niet.
Over het algemeen moet u loc
voor op labels gebaseerde toewijzing en iloc
voor toewijzing op basis van integer/positioneel, omdat de specificatie garandeert dat ze altijd op het origineel werken. Bovendien moet u voor het instellen van een enkele cel at
en iat
.
Meer is te vinden in de documentatie.
Opmerking
Alle booleaanse indexeringsbewerkingen gedaan metloc
kunnen ook worden gedaan metiloc
. Het enige verschil is datiloc
een van beide verwacht:
gehele getallen/posities voor index of een numpy array van booleaanse waarden, en
integer/positie-indexen voor de kolommen.Bijvoorbeeld
df.loc[df.A > 5, 'B'] = 4
Kan nas worden geschreven
df.iloc[(df.A > 5).values, 1] = 4
En,
df.loc[1, 'A'] = 100
Kan worden geschreven als
df.iloc[1, 0] = 100
Enzovoort.
Vertel me gewoon hoe ik de waarschuwing kan onderdrukken!
Overweeg een eenvoudige handeling op de "A" kolom van df
. “A” selecteren en delen door 2 verhoogt de waarschuwing, maar de bewerking zal werken.
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
Er zijn een aantal manieren om deze waarschuwing direct het zwijgen op te leggen:
-
(aanbevolen) Gebruik
loc
om subsets te segmenteren:df2 = df.loc[:, ['A']] df2['A'] /= 2 # Does not raise
-
Wijzig
pd.options.mode.chained_assignment
Kan worden ingesteld opNone
,"warn"
of"raise"
."warn"
is de standaardinstelling.None
zal de waarschuwing volledig onderdrukken, en"raise"
zal eenSettingWithCopyError
geven, waardoor de bewerking niet kan doorgaan.pd.options.mode.chained_assignment = None df2['A'] /= 2
-
Maak een
deepcopy
df2 = df[['A']].copy(deep=True) df2['A'] /= 2
@Peter Cotton in de reacties, kwam naar voren met een leuke manier om de modus niet-intrusief te wijzigen (aangepast van deze kern) met behulp van een contextmanager , om de modus alleen in te stellen zolang als nodig is, en om deze terug te zetten naar de oorspronkelijke staat wanneer u klaar bent.
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
Het gebruik is als volgt:
# some code here
with ChainedAssignent():
df2['A'] /= 2
# more code follows
Of, om de uitzondering op te heffen
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
Het "XY-probleem": wat doe ik verkeerd?
Vaak proberen gebruikers manieren te vinden om deze uitzondering te onderdrukken zonder volledig te begrijpen waarom deze in de eerste plaats is ontstaan. Dit is een goed voorbeeld van een XY-probleem, waarbij gebruikers proberen een probleem “Y” op te lossen. dat is eigenlijk een symptoom van een dieper geworteld probleem “X”. Er zullen vragen worden gesteld op basis van veelvoorkomende problemen die met deze waarschuwing te maken hebben, en vervolgens zullen oplossingen worden gepresenteerd.
Vraag 1
Ik heb een DataFramedf A B C D E 0 5 0 3 3 7 1 9 3 5 2 4 2 7 6 8 8 1
Ik wil waarden toekennen in col "A" > 5 tot 1000. Mijn verwachte output is
A B C D E 0 5 0 3 3 7 1 1000 3 5 2 4 2 1000 6 8 8 1
Onjuiste manier om dit te doen:
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
Juiste manier met behulp van loc
:
df.loc[df.A > 5, 'A'] = 1000
Vraag 21
Ik probeer de waarde in cel (1, ‘D’) in te stellen op 12345. Mijn verwachte output isA B C D E 0 5 0 3 3 7 1 9 3 5 12345 4 2 7 6 8 8 1
Ik heb verschillende manieren geprobeerd om toegang te krijgen tot deze cel, zoals:
df['D'][1]
. Wat is de beste manier om dit te doen?1. Deze vraag is niet specifiek gerelateerd aan de waarschuwing, maar
het is goed om te begrijpen hoe u deze specifieke bewerking correct uitvoert
om situaties te vermijden waarin de waarschuwing mogelijk zou kunnen ontstaan in
toekomst.
U kunt hiervoor een van de volgende methoden gebruiken.
df.loc[1, 'D'] = 12345
df.iloc[1, 3] = 12345
df.at[1, 'D'] = 12345
df.iat[1, 3] = 12345
Vraag 3
Ik probeer waarden in te stellen op basis van een bepaalde voorwaarde. ik heb een
DataFrameA B C D E 1 9 3 5 2 4 2 7 6 8 8 1
Ik wil waarden toewijzen in "D" tot 123 zodat "C" == 5. Ik
geprobeerddf2.loc[df2.C == 5, 'D'] = 123
Dat lijkt prima, maar ik krijg nog steeds de
SettingWithCopyWarning
! Hoe los ik dit op?
Dit komt waarschijnlijk door code hoger in je pijplijn. Heb je df2
gemaakt van iets groters, zoals
df2 = df[df.A > 5]
? In dit geval zal booleaanse indexering een weergave retourneren, dus df2
zal verwijzen naar het origineel. Wat u moet doen is df2
toewijzen aan een kopie:
df2 = df[df.A > 5].copy()
# Or,
# df2 = df.loc[df.A > 5, :]
Vraag 4
Ik probeer kolom "C" ter plaatse van
A B C D E 1 9 3 5 2 4 2 7 6 8 8 1
Maar met
df2.drop('C', axis=1, inplace=True)
Gooit
SettingWithCopyWarning
. Waarom gebeurt dit?
Dit komt omdat df2
moet zijn gemaakt als een weergave van een andere slicing-bewerking, zoals
df2 = df[df.A > 5]
De oplossing hier is om ofwel een copy()
van df
te maken, of loc
te gebruiken, zoals eerder.
Antwoord 3, autoriteit 14%
Over het algemeen is het doel van de SettingWithCopyWarning
om gebruikers (en vooral nieuwe gebruikers) te laten zien dat ze mogelijk werken op een kopie en niet op het origineel zoals ze denken. Er zijn zijn valse positieven (IOW als je weet wat je doet, kan ok zijn). Een mogelijkheid is om simpelweg de (standaard waarschuwing) waarschuwing uit te schakelen, zoals @Garrett suggereert.
Hier is een andere optie:
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
U kunt de vlag is_copy
instellen op False
, waardoor de controle voor dat object effectief wordt uitgeschakeld:
In [5]: dfa.is_copy = False
In [6]: dfa['A'] /= 2
Als u expliciet kopieert, zal er geen verdere waarschuwing plaatsvinden:
In [7]: dfa = df.ix[:, [1, 0]].copy()
In [8]: dfa['A'] /= 2
De code die de OP hierboven laat zien, hoewel legitiem, en waarschijnlijk iets wat ik ook doe, is technisch gezien een reden voor deze waarschuwing, en niet vals positief. Een andere manier om de waarschuwing niet te krijgen, is door de selectiebewerking uit te voeren via reindex
, bijvoorbeeld
quote_df = quote_df.reindex(columns=['STK', ...])
Of,
quote_df = quote_df.reindex(['STK', ...], axis=1) # v.0.21
Antwoord 4, autoriteit 4%
Pandas dataframe kopieerwaarschuwing
Als je zoiets gaat doen:
quote_df = quote_df.ix[:,[0,3,2,1,4,5,8,9,30,31]]
pandas.ix
in dit geval retourneert een nieuw, op zichzelf staand dataframe.
Waarden die u besluit te wijzigen in dit dataframe, zullen het oorspronkelijke dataframe niet veranderen.
Dit is waar panda’s je voor proberen te waarschuwen.
Waarom .ix
een slecht idee is
Het .ix
object probeert meer dan één ding te doen, en voor iedereen die iets heeft gelezen over schone code, is dit een sterke geur.
Gezien dit dataframe:
df = pd.DataFrame({"a": [1,2,3,4], "b": [1,1,2,2]})
Twee gedragingen:
dfcopy = df.ix[:,["a"]]
dfcopy.a.ix[0] = 2
Gedrag één: dfcopy
is nu een op zichzelf staand dataframe. Als u dit wijzigt, verandert df
niet
df.ix[0, "a"] = 3
Gedrag twee: dit verandert het oorspronkelijke dataframe.
Gebruik in plaats daarvan .loc
De ontwikkelaars van panda’s erkenden dat het .ix
-object behoorlijk stonk [speculatief] en creëerden dus twee nieuwe objecten die helpen bij de toetreding en toewijzing van gegevens. (De andere is .iloc
)
.loc
is sneller, omdat het niet probeert een kopie van de gegevens te maken.
.loc
is bedoeld om uw bestaande dataframe ter plekke aan te passen, wat meer geheugenefficiënt is.
.loc
is voorspelbaar, het heeft één gedrag.
De oplossing
Wat u in uw codevoorbeeld doet, is een groot bestand met veel kolommen laden en het vervolgens aanpassen om het kleiner te maken.
De functie pd.read_csv
kan u hierbij helpen en maakt het laden van het bestand ook een stuk sneller.
Dus in plaats van dit te doen
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]]
Doe dit
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
Hierdoor worden alleen de kolommen gelezen waarin u geïnteresseerd bent en krijgt u de juiste naam. Het is niet nodig om het kwaadaardige .ix
object te gebruiken om magische dingen te doen.
Antwoord 5, autoriteit 3%
Hier beantwoord ik de vraag direct. Hoe ermee om te gaan?
Maak een .copy(deep=False)
nadat je hebt gesneden. Zie pandas.DataFrame.copy.
Wacht, levert een schijfje geen kopie op? Dit is tenslotte wat het waarschuwingsbericht probeert te zeggen? Lees het lange antwoord:
import pandas as pd
df = pd.DataFrame({'x':[1,2,3]})
Dit geeft een waarschuwing:
df0 = df[df.x>2]
df0['foo'] = 'bar'
Dit doet niet:
df1 = df[df.x>2].copy(deep=False)
df1['foo'] = 'bar'
Zowel df0
als df1
zijn DataFrame
-objecten, maar iets aan hen is anders waardoor panda’s de waarschuwing kunnen afdrukken. Laten we eens kijken wat het is.
import inspect
slice= df[df.x>2]
slice_copy = df[df.x>2].copy(deep=False)
inspect.getmembers(slice)
inspect.getmembers(slice_copy)
Als je je diff-tool naar keuze gebruikt, zul je zien dat naast een paar adressen het enige wezenlijke verschil dit is:
| | slice | slice_copy |
| _is_copy | weakref | None |
De methode die beslist of er moet worden gewaarschuwd, is DataFrame._check_setitem_copy
die _is_copy
controleert. Dus hier ga je. Maak een copy
zodat uw DataFrame niet _is_copy
is.
De waarschuwing stelt voor om .loc
te gebruiken, maar als je .loc
gebruikt op een frame dat _is_copy
is, krijg je nog steeds de dezelfde waarschuwing. Misleidend? Ja. Vervelend? Zeker weten. Behulpzaam? Mogelijk wanneer geketende toewijzing wordt gebruikt. Maar het kan de ketentoewijzing niet correct detecteren en de waarschuwing willekeurig afdrukken.
Antwoord 6, autoriteit 2%
Dit onderwerp is erg verwarrend met Panda’s. Gelukkig heeft het een relatief eenvoudige oplossing.
Het probleem is dat het niet altijd duidelijk is of gegevensfilterbewerkingen (bijv. loc) een kopie of een weergave van het DataFrame retourneren. Verder gebruik van een dergelijk gefilterd DataFrame kan daarom verwarrend zijn.
De eenvoudige oplossing is (tenzij u met zeer grote gegevenssets moet werken):
Als je waarden moet bijwerken, zorg er dan altijd voor dat je het DataFrame expliciet kopieert vóór de toewijzing.
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)
Antwoord 7
Ik kreeg dit probleem met .apply()
bij het toewijzen van een nieuw dataframe van een reeds bestaand dataframe waarop ik de .query()
heb gebruikt methode. Bijvoorbeeld:
prop_df = df.query('column == "value"')
prop_df['new_column'] = prop_df.apply(function, axis=1)
Zou deze fout retourneren. De oplossing die de fout in dit geval lijkt op te lossen, is door dit te wijzigen in:
prop_df = df.copy(deep=True)
prop_df = prop_df.query('column == "value"')
prop_df['new_column'] = prop_df.apply(function, axis=1)
Dit is echter NIET efficiënt, vooral niet bij het gebruik van grote dataframes, omdat er een nieuwe kopie moet worden gemaakt.
Als u de methode .apply()
gebruikt bij het genereren van een nieuwe kolom en de bijbehorende waarden, is een oplossing die de fout verhelpt en efficiënter is, door .reset_index(drop=True)
:
prop_df = df.query('column == "value"').reset_index(drop=True)
prop_df['new_column'] = prop_df.apply(function, axis=1)
Antwoord 8
Om alle twijfel weg te nemen, was mijn oplossing om een diepe kopie van het segment te maken in plaats van een gewone kopie.
Dit is mogelijk niet van toepassing, afhankelijk van uw context (geheugenbeperkingen / grootte van het segment, potentieel voor prestatievermindering – vooral als de kopie in een lus plaatsvindt zoals bij mij, enz…)
Voor alle duidelijkheid, hier is de waarschuwing die ik heb ontvangen:
/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
Illustratie
Ik had mijn twijfels of de waarschuwing werd gegeven vanwege een kolom die ik op een kopie van de slice liet vallen. Hoewel technisch gezien niet geprobeerd om een waarde in de kopie van de slice in te stellen, was dat nog steeds een wijziging van de kopie van de slice.
Hieronder staan de (vereenvoudigde) stappen die ik heb genomen om het vermoeden te bevestigen, ik hoop dat het degenen onder ons zal helpen die de waarschuwing proberen te begrijpen.
Voorbeeld 1: het neerzetten van een kolom op het origineel heeft invloed op de kopie
Dat wisten we al, maar dit is een goede herinnering. Dit is NIET waar de waarschuwing over gaat.
>> 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
Het is mogelijk om te voorkomen dat wijzigingen die in df1 zijn aangebracht, van invloed zijn op df2. Opmerking: u kunt het importeren van copy.deepcopy
vermijden door in plaats daarvan df.copy()
te doen.
>> 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
Voorbeeld 2: het laten vallen van een kolom op de kopie kan het origineel beïnvloeden
Dit illustreert eigenlijk de waarschuwing.
>> 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
Het is mogelijk om te voorkomen dat wijzigingen die op df2 zijn aangebracht, van invloed zijn op 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
Proost!
Antwoord 9
Dit zou moeten werken:
quote_df.loc[:,'TVol'] = quote_df['TVol']/TVOL_SCALE
Antwoord 10
Sommigen willen de waarschuwing misschien gewoon onderdrukken:
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
Antwoord 11
Vervolgvraag/opmerking voor beginners
Misschien een verduidelijking voor andere beginners zoals ik (ik kom uit R, wat onder de motorkap een beetje anders lijkt te werken). De volgende onschuldig ogende en functionele code bleef de SettingWithCopy-waarschuwing produceren en ik begreep niet waarom. Ik had zowel de uitgave met “chained indexing” gelezen en begrepen, maar mijn code bevat geen:
def plot(pdb, df, title, **kw):
df['target'] = (df['ogg'] + df['ugg']) / 2
# ...
Maar later, veel te laat, heb ik gekeken waar de functie plot() wordt aangeroepen:
df = data[data['anz_emw'] > 0]
pixbuf = plot(pdb, df, title)
Dus “df” is geen dataframe maar een object dat zich op de een of andere manier herinnert dat het is gemaakt door een dataframe te indexeren (dus is dat een weergave?) die de regel in plot() zou maken
df['target'] = ...
gelijk aan
data[data['anz_emw'] > 0]['target'] = ...
wat een geketende indexering is. Heb ik dat goed begrepen?
Hoe dan ook,
def plot(pdb, df, title, **kw):
df.loc[:,'target'] = (df['ogg'] + df['ugg']) / 2
het is opgelost.
Antwoord 12
Je zou het hele probleem zo kunnen vermijden, denk ik:
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]),
)
)
Toewijzen gebruiken. Uit de documentatie: nieuwe kolommen toewijzen aan een DataFrame, waarbij een nieuw object (een kopie) wordt geretourneerd met alle originele kolommen naast de nieuwe.
Zie het artikel van Tom Augspurger over method chaining bij panda’s: https://tomaugspurger.github.io/method -ketenen
Antwoord 13
Als u het segment aan een variabele hebt toegewezen en u wilt de variabele als volgt instellen:
df2 = df[df['A'] > 2]
df2['B'] = value
En u wilt Jeffs oplossing niet gebruiken omdat uw conditieberekening df2
te lang is of om een andere reden, dan kunt u het volgende gebruiken:
df.loc[df2.index.tolist(), 'B'] = value
df2.index.tolist()
retourneert de indices van alle items in df2, die vervolgens worden gebruikt om kolom B in het oorspronkelijke dataframe in te stellen.
Antwoord 14
Omdat deze vraag al volledig is uitgelegd en besproken in bestaande antwoorden, zal ik een nette pandas
-benadering bieden aan de contextmanager met behulp van pandas.option_context
(links naar docs en voorbeeld) – het is absoluut niet nodig om een aangepaste klasse te maken met alle dunder-methoden en andere toeters en bellen.
Eerst de code van de contextmanager zelf:
from contextlib import contextmanager
@contextmanager
def SuppressPandasWarning():
with pd.option_context("mode.chained_assignment", None):
yield
Dan een voorbeeld:
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
Opmerkelijk is dat beide benaderingen a
niet wijzigen, wat voor mij een beetje verrassend is, en zelfs een ondiepe df-kopie met .copy(deep=False)
zou voorkomen dat deze waarschuwing wordt weergegeven (voor zover ik begrijp zou een oppervlakkige kopie op zijn minst a
moeten wijzigen, maar dat doet het niet. pandas
magie.).
Antwoord 15
Voor mij deed dit probleem zich voor in een volgende >vereenvoudigde< voorbeeld. En ik heb het ook kunnen oplossen (hopelijk met een juiste oplossing):
oude code met waarschuwing:
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
Hiermee werd de waarschuwing afgedrukt voor de regel old_row[field] = new_row[field]
Aangezien de rijen in de update_row-methode eigenlijk van het type Series
zijn, heb ik de regel vervangen door:
old_row.at[field] = new_row.at[field]
d.w.z. methode voor toegang tot/opzoeken voor een voor toegang/opzoeken voor Series
. Hoewel beide prima werken en het resultaat hetzelfde is, hoef ik op deze manier de waarschuwingen niet uit te schakelen (=bewaar ze voor andere problemen met ketenindexering ergens anders).
Ik hoop dat dit iemand kan helpen.
Antwoord 16
Ik kreeg dezelfde waarschuwing te zien, terwijl ik dit deel van mijn code uitvoerde:
def scaler(self, numericals):
scaler = MinMaxScaler()
self.data.loc[:, numericals[0]] = scaler.fit_transform(self.data.loc[:, numericals[0]])
self.data.loc[:, numericals[1]] = scaler.fit_transform(self.data.loc[:, numericals[1]])
welke scaler
een MinMaxScaler is en numericals[0]
de namen bevat van 3 van mijn numerieke kolommen.
de waarschuwing werd verwijderd toen ik de code veranderde in:
def scaler(self, numericals):
scaler = MinMaxScaler()
self.data.loc[:][numericals[0]] = scaler.fit_transform(self.data.loc[:][numericals[0]])
self.data.loc[:][numericals[1]] = scaler.fit_transform(self.data.loc[:][numericals[1]])
Dus, verander gewoon [:, ~]
in [:][~]