Pravkar sem nadgradil svoj program Pandas z različice 0.11 na 0.13.0rc1. Zdaj aplikacija izpisuje številna nova opozorila. Eno od njih je takšno:
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
Zanima me, kaj točno pomeni? Ali moram kaj spremeniti?
Kako naj prekinem opozorilo, če vztrajam pri uporabi 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])
Opozorilo SettingWithCopyWarning
je bilo ustvarjeno za označevanje potencialno zmedenih "verižnih" dodelitev, kot so naslednje, ki ne delujejo vedno po pričakovanjih, zlasti če prva izbira vrne kopijo. [glej GH5390 in GH5597 za razpravo o ozadju.]
df[df['A'] > 2]['B'] = new_val # new_val not set in df
Opozorilo ponuja predlog, da se prepiše na naslednji način:
df.loc[df['A'] > 2, 'B'] = new_val
Vendar to ne ustreza vaši uporabi, ki je enakovredna:
df = df[df['A'] > 2]
df['B'] = new_val
Čeprav je jasno, da vam ni mar, da se zapisi vrnejo v prvotni okvir (ker ste prepisali referenco nanj), tega vzorca žal ni mogoče ločiti od prvega primera verižne dodelitve, zato je opozorilo (lažno pozitivno). Možnost lažno pozitivnih rezultatov je obravnavana v dokumentih o indeksiranju, če želite prebrati več. To novo opozorilo lahko varno onemogočite z naslednjo nalogo.
pd.options.mode.chained_assignment = None # default='warn'
Na splošno je namen opozorila Nastavitev s kopijo
pokazati uporabnikom (zlasti novim uporabnikom), da morda uporabljajo kopijo in ne izvirnika, kot mislijo. Obstajajo tudi lažno pozitivni rezultati (če veste, kaj počnete, je lahko vse v redu). Ena od možnosti je, da preprosto izklopite (privzeto opozorilo), kot predlaga @Garrett.
Tu je še ena možnost:
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
Zastavo is_copy
lahko nastavite na False
, kar bo dejansko izklopilo preverjanje za ta objekt:
In [5]: dfa.is_copy = False
In [6]: dfa['A'] /= 2
Če izrecno kopirate, se ne bo pojavilo nobeno nadaljnje opozorilo:
In [7]: dfa = df.ix[:, [1, 0]].copy()
In [8]: dfa['A'] /= 2
Koda, ki jo OP prikazuje zgoraj, je sicer legitimna in jo verjetno tudi sam uporabljam, vendar je tehnično gledano primer za to opozorilo in ne lažno pozitivna. Drug način, da opozorila ne bi bilo, bi bil, da bi operacijo izbire izvedli z reindeksom
, npr.
quote_df = quote_df.reindex(columns=['STK', ...])
Ali,
quote_df = quote_df.reindex(['STK', ...], axis=1) # v.0.21
Ko naredite nekaj takega:
quote_df = quote_df.ix[:,[0,3,2,1,4,5,8,9,30,31]]
pandas.ix
v tem primeru vrne nov, samostojen podatkovni okvir.
Vse vrednosti, ki jih spremenite v tem podatkovnem okviru, ne bodo spremenile prvotnega podatkovnega okvira.
Na to vas poskuša opozoriti program pandas.
.ix
slaba zamiselObjekt .ix
poskuša početi več kot eno stvar, kar je za vsakogar, ki je prebral kaj o čisti kodi, močan vonj.
Ob upoštevanju tega podatkovnega okvira:
df = pd.DataFrame({"a": [1,2,3,4], "b": [1,1,2,2]})
Dve vedenji:
dfcopy = df.ix[:,["a"]]
dfcopy.a.ix[0] = 2
Prvo vedenje: dfcopy
je zdaj samostojen podatkovni okvir. Njegova sprememba ne bo spremenila df
df.ix[0, "a"] = 3
Drugo vedenje: To spremeni prvotni podatkovni okvir.
.loc
Razvijalci programa pandas so ugotovili, da objekt .ix
precej smrdi, zato so ustvarili dva nova objekta, ki pomagata pri pristopu in dodeljevanju podatkov. (Drugi je .iloc
)
.loc
je hitrejši, ker ne poskuša ustvariti kopije podatkov.
Namen .loc
je spremeniti obstoječi podatkovni okvir na mestu, kar je pomnilniško učinkovitejše.
.loc
je predvidljiv, ima eno samo obnašanje.
V svojem primeru kode nalagate veliko datoteko z veliko stolpci, nato pa jo spremenite, da bo manjša.
Funkcija pd.read_csv
vam lahko pri tem veliko pomaga, poleg tega pa je nalaganje datoteke veliko hitrejše.
Torej namesto tega naredite 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]]
Naredite 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
To bo prebralo samo stolpce, ki vas zanimajo, in jih pravilno poimenovalo. Ni potrebe po uporabi zlobnega predmeta .ix
za izvajanje čarobnih stvari.