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])
301
demandé sur Brad Solomon 0000-00-00 00:00:00

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'
415
répondu Garrett 2018-03-15 07:54:48

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
106
répondu Jeff 2017-12-27 10:18:45

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.

31
répondu firelynx 2016-11-14 08:48:49

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é!

5
répondu Raphvanns 2017-07-27 22:25:13