Comment gérer la mise en place D'un système D'alerte rapide dans les Pandas?
arrière-plan
je viens de mettre à jour mes Pandas de 0.11 à 0.13.0rc1. Maintenant, l'application affiche beaucoup de nouveaux avertissements. L'un d'eux comme ceci:
E:FinReporterFM_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
je veux savoir ce que cela signifie exactement? Dois-je changer quelque chose?
Comment dois-je suspendre l'avertissement si j'insiste pour utiliser quote_df['TVol'] = quote_df['TVol']/TVOL_SCALE
?
la fonction qui donne des erreurs
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
plus d'erreur les messages
E:FinReporterFM_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:FinReporterFM_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:FinReporterFM_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])
4 réponses
le SettingWithCopyWarning
a été créé pour signaler des affectations "enchaînées" potentiellement déroutantes, comme les suivantes, qui ne fonctionnent pas toujours comme prévu, particulièrement lorsque la première sélection renvoie une copie . [voir GH5390 et GH5597 pour une discussion générale.]
df[df['A'] > 2]['B'] = new_val # new_val not set in df
l'avertissement propose de réécrire comme suit:
df.loc[df['A'] > 2, 'B'] = new_val
cependant, ce ne correspond pas à votre usage, ce qui équivaut à:
df = df[df['A'] > 2]
df['B'] = new_val
bien qu'il soit clair que vous ne vous souciez pas des Écritures de retour au cadre d'origine (puisque vous avez surligné la référence à elle), malheureusement ce modèle ne peut pas être différencié du premier exemple d'assignation enchaînée, d'où l'avertissement (faux positif). Le potentiel de faux positifs est traité dans le docs sur l'indexation , si vous souhaitez lire la suite. Vous pouvez désactivez ce nouvel avertissement en toute sécurité avec la tâche suivante.
pd.options.mode.chained_assignment = None # default='warn'
En général, le point de la SettingWithCopyWarning
est de montrer aux utilisateurs (et esp nouveaux utilisateurs) qu'ils peut sur une copie et non l'original qu'ils le pensent. Il sont faux positifs (IOW vous savez ce que vous faites, il ok ). Une possibilité est simplement d'éteindre l'avertissement (par défaut warn ) comme @Garrett le suggère.
voici un autre, par option.
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
vous pouvez mettre le drapeau is_copy
à False
, ce qui éteindra effectivement le contrôle, *pour cet objet "
In [5]: dfa.is_copy = False
In [6]: dfa['A'] /= 2
si vous copiez explicitement alors vous savez ce que vous faites , donc aucun autre avertissement n'arrivera.
In [7]: dfa = df.ix[:,[1,0]].copy()
In [8]: dfa['A'] /= 2
le code que l'OP montre ci-dessus, bien que légitime, et probablement quelque chose que je fais aussi, est techniquement un cas pour cet avertissement, et pas un Faux positif. Une autre façon de et non serait d'effectuer la sélection par reindex
, par exemple
quote_df = quote_df.reindex(columns=['STK',.......])
Ou,
quote_df = quote_df.reindex(['STK',.......], axis=1) # v.0.21
Pandas dataframe copie d'avertissement
quand vous allez faire quelque chose comme ça:
quote_df = quote_df.ix[:,[0,3,2,1,4,5,8,9,30,31]]
pandas.ix
dans ce cas renvoie une nouvelle, autonome dataframe.
toutes les valeurs que vous décidez de changer dans cette dataframe, ne changeront pas la dataframe originale.
c'est ce que pandas essaie de vous mettre en garde.
pourquoi .ix
est un mauvaise idée
l'objet .ix
tente de faire plus d'une chose, et pour quiconque a lu quelque chose au sujet du code propre, c'est une forte odeur.
étant donné cette base de données:
df = pd.DataFrame({"a": [1,2,3,4], "b": [1,1,2,2]})
deux comportements:
dfcopy = df.ix[:,["a"]]
dfcopy.a.ix[0] = 2
un Comportement: dfcopy
est maintenant autonome dataframe. Changer cela ne changera pas df
df.ix[0, "a"] = 3
le Comportement des deux: Ce modifications de l'original dataframe.
utiliser .loc
au lieu de
les développeurs de pandas ont reconnu que l'objet .ix
était assez malodorant[spéculativement] et ont donc créé deux nouveaux objets qui aident à l'accession et l'attribution des données. (L'autre étant .iloc
)
.loc
est plus rapide, car il n'essaie pas de créer une copie des données.
.loc
est destiné à modifiez votre dataframe existant en place, ce qui est plus efficace en termes de mémoire.
.loc
est prévisible, il a un comportement.
la solution
ce que vous faites dans votre exemple de code est de charger un gros fichier avec beaucoup de Colonnes, puis de le modifier pour être plus petit.
Le pd.read_csv
fonction peut vous aider avec beaucoup de cela, et de rendre le chargement du fichier beaucoup plus vite.
ainsi au lieu de faire ceci
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]]
Faire
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
ceci ne lira que les colonnes qui vous intéressent, et les nommera correctement. Pas besoin d'utiliser l'objet maléfique .ix
pour faire des choses magiques.
pour lever tout doute, ma solution était de faire une copie profonde de la tranche au lieu d'une copie régulière. Cela peut ne pas être applicable selon votre contexte (contraintes de mémoire / Taille de la tranche, potentiel de dégradation des performances - surtout si la copie se produit dans une boucle comme elle l'a fait pour moi, etc...)
Pour être clair, voici l'avertissement que j'ai reçu:
/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
Illustration
je doutais que l'avertissement a été jeté à cause d'une colonne que je déposais sur une copie de la tranche. Bien que techniquement pas essayer de mettre une valeur dans la copie de la tranche, qui était encore une modification de la copie de la tranche. Voici les mesures (simplifiées) que j'ai prises pour confirmer le soupçon, j'espère que cela aidera ceux d'entre nous qui essaient de comprendre l'avertissement.
exemple 1: la chute d'une colonne sur l'original affecte la copie
Nous le savions déjà, mais c'est un rappel salutaire. Il S'agit de et non de .
>> 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
il est possible d'éviter les modifications apportées sur df1 pour affecter df2
>> 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
exemple 2: la chute d'une colonne sur la copie peut affecter l'original
cela illustre bien l'avertissement.
>> 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
il est possible d'éviter les modifications apportées sur df2 pour affecter 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
santé!