Jeg har lige opgraderet min Pandas fra 0.11 til 0.13.0rc1. Nu kommer programmet med mange nye advarsler. En af dem som denne:
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
Jeg vil gerne vide, hvad det præcist betyder? Skal jeg ændre noget?
Hvordan skal jeg suspendere advarslen, hvis jeg insisterer på at bruge quote_df['TVol'] = quote_df['TVol']/TVOL_SCALE
?
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
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])
Advarslen SettingWithCopyWarning
blev oprettet for at markere potentielt forvirrende "kædetildelinger" som f.eks. følgende, der ikke altid fungerer som forventet, især når det første valg returnerer en kopi. [se GH5390 og GH5597 for en baggrundsdiskussion].
df[df['A'] > 2]['B'] = new_val # new_val not set in df
Advarslen giver et forslag til at omskrive som følger:
df.loc[df['A'] > 2, 'B'] = new_val
Dette passer dog ikke til din brug, som svarer til:
df = df[df['A'] > 2]
df['B'] = new_val
Selv om det er klart, at du er ligeglad med at skrive tilbage til den oprindelige ramme (da du har overskrevet referencen til den), kan dette mønster desværre ikke adskilles fra det første eksempel med kædetildeling, og derfor er der en (falsk positiv) advarsel. Potentialet for falske positive resultater er behandlet i docs on indexing, hvis du ønsker at læse mere. Du kan sikkert deaktivere denne nye advarsel med følgende tildeling.
pd.options.mode.chained_assignment = None # default='warn'
Generelt er formålet med SettingWithCopyWarning
at vise brugere (og især nye brugere), at de kan operere på en kopi og ikke på originalen, som de tror. Der er falske positiver (dvs. hvis man ved, hvad man gør, kan det være ok). En mulighed er simpelthen at slå advarslen (som standard warn) fra, som @Garrett foreslår.
Her er en anden mulighed:
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
Du kan sætte is_copy
-flaget til False
, hvilket effektivt vil slå kontrollen fra for det pågældende objekt:
In [5]: dfa.is_copy = False
In [6]: dfa['A'] /= 2
Hvis du udtrykkeligt kopierer, vil der ikke ske yderligere advarsler:
In [7]: dfa = df.ix[:, [1, 0]].copy()
In [8]: dfa['A'] /= 2
Den kode, som OP viser ovenfor, er ganske vist legitim, og sandsynligvis noget jeg også gør, men teknisk set er det et tilfælde for denne advarsel og ikke en falsk positiv. En anden måde at ikke have advarslen ville være at foretage udvælgelsesoperationen via reindex
, f.eks.
quote_df = quote_df.reindex(columns=['STK', ...])
Eller,
quote_df = quote_df.reindex(['STK', ...], axis=1) # v.0.21
Når du gør noget som dette:
quote_df = quote_df.ix[:,[0,3,2,1,4,5,8,9,30,31]]
pandas.ix
i dette tilfælde returnerer et nyt, selvstændigt dataframe.
Alle værdier, du beslutter at ændre i dette dataramme, ændrer ikke det oprindelige dataramme.
Det er dette, som pandas forsøger at advare dig om.
.ix
er en dårlig idé.ix
-objektet forsøger at gøre mere end én ting, og for enhver, der har læst noget om ren kode, er dette en stærk lugt.
I betragtning af dette dataframe:
df = pd.DataFrame({"a": [1,2,3,4], "b": [1,1,2,2]})
To adfærdsmønstre:
dfcopy = df.ix[:,["a"]]
dfcopy.a.ix[0] = 2
Adfærd et: dfcopy
er nu en selvstændig dataframe. Ændring af den vil ikke ændre df
.
df.ix[0, "a"] = 3
Adfærd to: Dette ændrer den oprindelige dataframe.
.loc
i stedetPandas-udviklerne erkendte, at .ix
-objektet var ret ildelugtende[spekulativt] og skabte derfor to nye objekter, som hjælper med at tilgå og tildele data. (Det andet er .iloc
)
.loc
er hurtigere, fordi den ikke forsøger at oprette en kopi af dataene.
.loc
er beregnet til at ændre dit eksisterende dataframe inplace, hvilket er mere hukommelseseffektivt.
.loc
er forudsigelig, den har én adfærd.
Det, du gør i dit kodeeksempel, er at indlæse en stor fil med mange kolonner og derefter ændre den til at blive mindre.
Funktionen pd.read_csv
kan hjælpe dig med en masse af dette og også gøre indlæsningen af filen meget hurtigere.
Så i stedet for at gøre dette
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]]
Gør dette
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
Dette vil kun læse de kolonner, du er interesseret i, og navngive dem korrekt. Der er ikke behov for at bruge det onde .ix
objekt til at lave magiske ting.