Contexte
Je viens de mettre à jour mes Pandas de 0,11 à 0,13.0rc1. Maintenant, l'application fait apparaître de nombreux nouveaux avertissements. L'un d'eux aime ça:
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
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 de messages d'erreur
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
gros bug
la source
la source
df.set_value
, docs ici - pandas.pydata.org/pandas-docs/stable/generated/…df.set_value
est obsolète. Pandas recommande désormais d'utiliser.at[]
ou à la.iat[]
place. docs ici pandas.pydata.org/pandas-docs/stable/generated/…option_context
ici: pandas.pydata.org/pandas-docs/stable/user_guide/options.html , utilisez aswith pd.option_context("mode.chained_assignment", None): [...]
Réponses:
Le a
SettingWithCopyWarning
été créé pour signaler les affectations "chaînées" potentiellement déroutantes, telles que les suivantes, qui ne fonctionnent pas toujours comme prévu, en particulier lorsque la première sélection renvoie une copie . [Voir GH5390 et GH5597 pour une discussion de fond.]L'avertissement propose une suggestion de réécriture comme suit:
Cependant, cela ne correspond pas à votre utilisation, ce qui équivaut à:
Bien qu'il soit clair que vous ne vous souciez pas des écritures qui reviennent au cadre d'origine (puisque vous écrasez la référence), ce modèle ne peut malheureusement pas être différencié du premier exemple d'affectation chaînée. D'où l'avertissement (faux positif). Le potentiel de faux positifs est traité dans la documentation sur l'indexation , si vous souhaitez en savoir plus. Vous pouvez désactiver ce nouvel avertissement en toute sécurité avec l'affectation suivante.
la source
.ix
, améliorées.iloc
, etc.) peuvent certainement être considérées comme "la voie principale" sans avertir sans cesse tout le monde des autres voies. Au lieu de cela, laissez-les devenir des adultes et s'ils veulent faire une affectation enchaînée, qu'il en soit ainsi. Mes deux sous quand même. On voit souvent des commentaires mécontents des développeurs Pandas lorsque les affectations enchaînées fonctionneront pour résoudre un problème, mais ne seraient pas considérées comme la manière "principale" de le faire.pd.options.mode.chained_assignment = None
a fait en sorte que mon code s'exécute environ 6 fois plus rapidement. Quelqu'un d'autre a connu des résultats similaires?Ce message est destiné aux lecteurs qui,
Installer
C'est quoi
SettingWithCopyWarning
?Pour savoir comment gérer cet avertissement, il est important de comprendre ce qu'il signifie et pourquoi il est émis en premier lieu.
Lors du filtrage des DataFrames, il est possible de découper / indexer un cadre pour renvoyer soit une vue , soit une copie , selon la disposition interne et divers détails d'implémentation. Une "vue" est, comme le terme l'indique, une vue dans les données d'origine, donc la modification de la vue peut modifier l'objet d'origine. D'un autre côté, une "copie" est une réplication des données de l'original, et la modification de la copie n'a aucun effet sur l'original.
Comme mentionné dans d'autres réponses, le a
SettingWithCopyWarning
été créé pour signaler les opérations d '"affectation chaînée". Considérezdf
dans la configuration ci-dessus. Supposons que vous souhaitiez sélectionner toutes les valeurs de la colonne "B" où les valeurs de la colonne "A" sont> 5. Pandas vous permet de le faire de différentes manières, certaines plus correctes que d'autres. Par exemple,Et,
Ceux-ci renvoient le même résultat, donc si vous ne lisez que ces valeurs, cela ne fait aucune différence. Alors, quel est le problème? Le problème avec l'affectation chaînée, c'est qu'il est généralement difficile de prédire si une vue ou une copie est retournée, donc cela devient largement un problème lorsque vous essayez de réattribuer des valeurs. Pour construire sur l'exemple précédent, considérez comment ce code est exécuté par l'interpréteur:
Avec un seul
__setitem__
appel àdf
. OTOH, considérez ce code:Maintenant, selon qu'il a
__getitem__
renvoyé une vue ou une copie, l'__setitem__
opération peut ne pas fonctionner .En général, vous devez utiliser
loc
pour l'affectation basée sur les étiquettes etiloc
pour l' affectation basée sur les nombres entiers / positionnels, car la spécification garantit qu'ils fonctionnent toujours sur l'original. De plus, pour définir une seule cellule, vous devez utiliserat
etiat
.Plus d'informations peuvent être trouvées dans la documentation .
Dites-moi simplement comment supprimer l'avertissement!
Considérez une opération simple sur la colonne "A" de
df
. La sélection de "A" et la division par 2 déclencheront l'avertissement, mais l'opération fonctionnera.Il existe deux façons de désactiver directement cet avertissement:
Faire un
deepcopy
Changement
pd.options.mode.chained_assignment
peut -il être réglé sur
None
,"warn"
ou"raise"
."warn"
est la valeur par défaut.None
supprimera complètement l'avertissement et"raise"
lancera unSettingWithCopyError
, empêchant l'opération de se poursuivre.@Peter Cotton dans les commentaires, a trouvé une belle façon de changer le mode de manière non intrusive (modifié à partir de cet essentiel ) en utilisant un gestionnaire de contexte, pour définir le mode uniquement aussi longtemps qu'il est nécessaire, et le réinitialiser à la état d'origine une fois terminé.
L'utilisation est la suivante:
Ou, pour soulever l'exception
Le "problème XY": qu'est-ce que je fais mal?
La plupart du temps, les utilisateurs tentent de rechercher des moyens de supprimer cette exception sans bien comprendre pourquoi elle a été levée en premier lieu. Ceci est un bon exemple d'un problème XY , où les utilisateurs tentent de résoudre un problème "Y" qui est en fait le symptôme d'un problème enraciné plus profond "X". Des questions seront posées sur la base des problèmes courants rencontrés par cet avertissement, et des solutions seront ensuite présentées.
Mauvaise façon de procéder:
Bonne façon d'utiliser
loc
:Vous pouvez utiliser l'une des méthodes suivantes pour ce faire.
C'est en fait probablement à cause du code plus haut dans votre pipeline. Avez-vous créé
df2
quelque chose de plus grand, comme? Dans ce cas, l'indexation booléenne retournera une vue, donc
df2
référencera l'original. Ce que vous devez faire est d'assignerdf2
à une copie :En effet,
df2
doit avoir été créé en tant que vue à partir d'une autre opération de découpage, telle queLa solution est ici pour faire soit un
copy()
desdf
, ou l' utilisationloc
, comme avant.la source
En général, l'objectif de la
SettingWithCopyWarning
est de montrer aux utilisateurs (et en particulier aux nouveaux utilisateurs) qu'ils peuvent fonctionner sur une copie et non sur l'original comme ils le pensent. Il y a des faux positifs (OIEau si vous savez ce que vous faites , il pourrait être ok ). Une possibilité consiste simplement à désactiver l' avertissement (par défaut d' avertissement ) comme le suggère @Garrett.Voici une autre option:
Vous pouvez définir l'
is_copy
indicateur surFalse
, ce qui désactivera efficacement la vérification, pour cet objet :Si vous copiez explicitement, aucun autre avertissement ne se produira:
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 non un faux positif. Une autre façon de ne pas avoir d'avertissement serait de faire l'opération de sélection via
reindex
, par exempleOu,
la source
0.16
je vois beaucoup plus de faux positifs, le problème des faux positifs est qu'on apprend à l'ignorer, même si parfois c'est légitime.undefined
comportement. Si quoi que ce soit, il devrait alors générer une erreur (pour éviter les pièges observés dansC
), carapi
étant gelé, le comportement actuel de l'avertissement a un sens pour la compatibilité descendante. Et je vais les faire lancer pour les attraper comme des erreurs dans mon code de production (warnings.filterwarnings('error', r'SettingWithCopyWarning
). De plus, la suggestion d'utiliser.loc
parfois n'aide pas non plus (si elle est en groupe).Avertissement de copie de trame de données Pandas
Lorsque vous allez faire quelque chose comme ça:
pandas.ix
dans ce cas, renvoie une nouvelle trame de données autonome.Les valeurs que vous décidez de modifier dans ce cadre de données ne changeront pas le cadre de données d'origine.
C'est ce que les pandas essaient de vous avertir.
Pourquoi
.ix
est-ce une mauvaise idéeL'
.ix
objet essaie de faire plus d'une chose, et pour toute personne qui a lu quelque chose sur le code propre, c'est une forte odeur.Compte tenu de cette trame de données:
Deux comportements:
Comportement un:
dfcopy
est désormais une trame de données autonome. Le changer ne changera pasdf
Comportement deux: cela modifie la trame de données d'origine.
Utiliser à la
.loc
placeLes développeurs de pandas ont reconnu que l'
.ix
objet é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é à modifier votre cadre de données existant sur place, ce qui est plus efficace en mémoire..loc
est prévisible, il a un comportement.La solution
Ce que vous faites dans votre exemple de code, c'est charger un gros fichier avec beaucoup de colonnes, puis le modifier pour qu'il soit plus petit.
La
pd.read_csv
fonction peut vous aider avec beaucoup de cela et également accélérer le chargement du fichier.Donc au lieu de faire ça
Faites ceci
Cela ne lira que les colonnes qui vous intéressent et les nommera correctement. Pas besoin d'utiliser l'
.ix
objet diabolique pour faire des trucs magiques.la source
.iloc
. Ce sont les deux principales méthodes d'indexation des structures de données des pandas. En savoir plus dans la documentation.Ici, je réponds directement à la question. Comment y faire face?
Faites un
.copy(deep=False)
après avoir coupé. Voir pandas.DataFrame.copy .Attendez, une tranche ne retourne pas une copie? Après tout, c'est ce que le message d'avertissement tente de dire? Lisez la longue réponse:
Cela donne un avertissement:
Cela ne signifie pas:
Les deux
df0
etdf1
sont desDataFrame
objets, mais quelque chose à leur sujet est différent qui permet d'imprimer Pandas l'avertissement. Voyons ce que c'est.En utilisant votre outil de diff de choix, vous verrez qu'au-delà de quelques adresses, la seule différence matérielle est la suivante:
La méthode qui décide d'avertir est celle
DataFrame._check_setitem_copy
qui vérifie_is_copy
. Alors voilà. Faites encopy
sorte que votre DataFrame ne soit pas_is_copy
.L'avertissement suggère d'utiliser
.loc
, mais si vous l'utilisez.loc
sur un cadre_is_copy
, vous obtiendrez toujours le même avertissement. Trompeur? Oui. Ennuyeux? Tu paries. Utile? Potentiellement, lorsque l'affectation chaînée est utilisée. Mais il ne peut pas détecter correctement l'attribution de chaîne et imprime l'avertissement sans discrimination.la source
Ce sujet est vraiment déroutant avec les Pandas. Heureusement, il a une solution relativement simple.
Le problème est qu'il n'est pas toujours clair si les opérations de filtrage des données (par exemple loc) renvoient une copie ou une vue du DataFrame. Une utilisation ultérieure de ces DataFrame filtrés pourrait donc prêter à confusion.
La solution simple est (sauf si vous devez travailler avec de très grands ensembles de données):
Chaque fois que vous devez mettre à jour des valeurs, assurez-vous toujours de copier implicitement le DataFrame avant l'affectation.
la source
Pour lever tout doute, ma solution était de faire une copie complète de la tranche au lieu d'une copie régulière. Cela peut ne pas être applicable en fonction de 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 ce fut le cas pour moi, etc ...)
Pour être clair, voici l'avertissement que j'ai reçu:
Illustration
J'avais des doutes que l'avertissement a été lancé à cause d'une colonne que je déposais sur une copie de la tranche. Sans essayer techniquement de définir une valeur dans la copie de la tranche, il s'agissait tout de même d'une modification de la copie de la tranche. Voici les étapes (simplifiées) que j'ai prises pour confirmer les soupçons, j'espère que cela aidera ceux d'entre nous qui tentent de comprendre l'avertissement.
Exemple 1: déposer une colonne sur l'original affecte la copie
Nous le savions déjà, mais c'est un rappel sain. Ce n'est PAS l'objet de l'avertissement.
Il est possible d'éviter les modifications apportées sur df1 pour affecter df2
Exemple 2: la suppression d'une colonne sur la copie peut affecter l'original
Cela illustre en fait l'avertissement.
Il est possible d'éviter les modifications apportées sur df2 pour affecter df1
À votre santé!
la source
Cela devrait fonctionner:
la source
Certains voudront peut-être simplement supprimer l'avertissement:
la source
Si vous avez affecté la tranche à une variable et que vous souhaitez définir à l'aide de la variable comme suit:
Et vous ne voulez pas utiliser la solution Jeffs car votre calcul de condition
df2
est trop long ou pour une autre raison, vous pouvez utiliser les éléments suivants:df2.index.tolist()
renvoie les indices de toutes les entrées de df2, qui seront ensuite utilisés pour définir la colonne B dans la trame de données d'origine.la source
Pour moi, ce problème s'est produit dans un exemple> simplifié <suivant. Et j'ai également pu le résoudre (avec une bonne solution, espérons-le):
ancien code avec avertissement:
Cela a imprimé l'avertissement pour la ligne
old_row[field] = new_row[field]
Étant donné que les lignes de la méthode update_row sont en fait de type
Series
, j'ai remplacé la ligne par:c'est-à-dire la méthode d'accès / recherche pour a
Series
. Bien que les deux fonctionnent très bien et le résultat est le même, de cette façon, je n'ai pas à désactiver les avertissements (= conservez-les pour d'autres problèmes d'indexation de la chaîne ailleurs).J'espère que cela peut aider quelqu'un.
la source
Vous pourriez éviter tout le problème comme celui-ci, je crois:
Utilisation de l'attribution. De la documentation : Attribuez de nouvelles colonnes à un DataFrame, en renvoyant un nouvel objet (une copie) avec toutes les colonnes d'origine en plus des nouvelles.
Voir l'article de Tom Augspurger sur le chaînage de méthodes dans les pandas: https://tomaugspurger.github.io/method-chaining
la source
Question / remarque pour débutant
Peut-être une clarification pour d'autres débutants comme moi (je viens de R qui semble fonctionner un peu différemment sous le capot). Le code fonctionnel et inoffensif suivant a continué de produire l'avertissement SettingWithCopy, et je n'ai pas pu comprendre pourquoi. J'avais à la fois lu et compris le produit avec "l'indexation chaînée", mais mon code n'en contient pas:
Mais ensuite, beaucoup trop tard, j'ai regardé où la fonction plot () est appelée:
Donc, "df" n'est pas un bloc de données mais un objet qui se souvient en quelque sorte qu'il a été créé en indexant un bloc de données (est-ce donc une vue?) Qui ferait la ligne dans plot ()
équivalent à
qui est une indexation chaînée. Ai-je bien compris?
En tous cas,
l'a corrigé.
la source
Comme cette question est déjà entièrement expliquée et discutée dans les réponses existantes, je vais simplement fournir une
pandas
approche soignée au gestionnaire de contexte à l'aide depandas.option_context
(liens vers des documents et des exemples ) - il n'est absolument pas nécessaire de créer une classe personnalisée avec toutes les méthodes de dunder et d'autres cloches et sifflets.Tout d'abord, le code du gestionnaire de contexte lui-même:
Puis un exemple:
Il convient de noter que les deux approches ne modifient pas
a
, ce qui est un peu surprenant pour moi, et même une copie df peu profonde avec.copy(deep=False)
empêcherait que cet avertissement ne soit émis (pour autant que je sache, la copie peu profonde devrait au moins également être modifiéea
, mais elle ne le fait pas paspandas
magique.).la source
J'avais eu ce problème avec
.apply()
lors de l'attribution d'une nouvelle trame de données à partir d'une trame de données préexistante sur laquelle j'ai utilisé la.query()
méthode. Par exemple:Renverrait cette erreur. Le correctif qui semble résoudre l'erreur dans ce cas consiste à le remplacer par:
Cependant, cela n'est PAS efficace, en particulier lorsque vous utilisez des trames de données volumineuses, car vous devez faire une nouvelle copie.
Si vous utilisez la
.apply()
méthode pour générer une nouvelle colonne et ses valeurs, un correctif qui résout l'erreur et est plus efficace consiste à ajouter.reset_index(drop=True)
:la source