Lors de la sélection d'une sous-trame de données à partir d'une trame de données parent, j'ai remarqué que certains programmeurs font une copie du trame de données à l'aide de la méthode .copy()
.
Pourquoi font-ils une copie du bloc de données? Que se passera-t-il si je ne fais pas de copie?
Cela développe la réponse de Paul. Dans les pandas, l'indexation d'un DataFrame renvoie une référence au DataFrame initial. Ainsi, la modification du sous-ensemble modifiera le DataFrame initial. Ainsi, vous voudriez utiliser la copie si vous voulez vous assurer que le DataFrame initial ne devrait pas changer. Considérons le code suivant:
df = DataFrame({'x': [1,2]})
df_sub = df[0:1]
df_sub.x = -1
print(df)
Tu auras:
x
0 -1
1 2
En revanche, les feuilles suivantes restent inchangées:
df_sub_copy = df[0:1].copy()
df_sub_copy.x = -1
En effet, si vous ne faites pas de copie, les index peuvent toujours être manipulés ailleurs, même si vous attribuez à dataFrame un nom différent.
Par exemple:
df2 = df
func1(df2)
func2(df)
func1 peut modifier df en modifiant df2, afin d'éviter cela:
df2 = df.copy()
func1(df2)
func2(df)
Il est nécessaire de mentionner que le retour de la copie ou de la vue dépend du type d'indexation.
La documentation de pandas indique:
Renvoyer une vue par rapport à une copie
Les règles relatives au moment où une vue sur les données est renvoyée dépendent entièrement de NumPy. Chaque fois qu'un tableau d'étiquettes ou un vecteur booléen sont impliqués dans l'opération d'indexation, le résultat sera une copie. Avec indexage et découpage à étiquette unique/scalaire, par ex. df.ix [3: 6] ou df.ix [:, 'A'], une vue sera renvoyée.
Le but principal est d’éviter l’indexation en chaîne et d’éliminer la SettingWithCopyWarning
.
Ici, l'indexation en chaîne ressemble à quelque chose comme dfc['A'][0] = 111
Le document dit que l'indexation chaînée doit être évitée dans Retour d'une vue par rapport à une copie . Voici un exemple légèrement modifié de ce document:
In [1]: import pandas as pd
In [2]: dfc = pd.DataFrame({'A':['aaa','bbb','ccc'],'B':[1,2,3]})
In [3]: dfc
Out[3]:
A B
0 aaa 1
1 bbb 2
2 ccc 3
In [4]: aColumn = dfc['A']
In [5]: aColumn[0] = 111
SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame
In [6]: dfc
Out[6]:
A B
0 111 1
1 bbb 2
2 ccc 3
Ici, la aColumn
est une vue et non une copie du DataFrame d'origine. Par conséquent, la modification de aColumn
entraînera la modification de l'original dfc
. Ensuite, si nous indexons la ligne en premier:
In [7]: zero_row = dfc.loc[0]
In [8]: zero_row['A'] = 222
SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame
In [9]: dfc
Out[9]:
A B
0 111 1
1 bbb 2
2 ccc 3
Cette fois zero_row
est une copie, l’original dfc
n’est pas modifié.
Dans ces deux exemples ci-dessus, nous voyons qu'il est ambigu de savoir si vous souhaitez modifier le DataFrame d'origine. Ceci est particulièrement dangereux si vous écrivez quelque chose comme ceci:
In [10]: dfc.loc[0]['A'] = 333
SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame
In [11]: dfc
Out[11]:
A B
0 111 1
1 bbb 2
2 ccc 3
Cette fois, ça n'a pas marché du tout. Ici, nous voulions changer dfc
, mais nous avons en fait modifié une valeur intermédiaire dfc.loc[0]
qui est une copie et qui est immédiatement rejetée. Il est très difficile de prédire si la valeur intermédiaire telle que dfc.loc[0]
ou dfc['A']
est une vue ou une copie. Il n’est donc pas garanti que DataFrame original soit mis à jour. C'est pourquoi il faut éviter l'indexation en chaîne et pandas génère la SettingWithCopyWarning
pour ce type de mise à jour d'indexation en chaîne.
C'est maintenant l'utilisation de .copy()
. Pour éliminer l'avertissement, faites une copie pour exprimer votre intention de manière explicite:
In [12]: zero_row_copy = dfc.loc[0].copy()
In [13]: zero_row_copy['A'] = 444 # This time no warning
Puisque vous modifiez une copie, vous savez que l'original dfc
ne changera jamais et vous ne vous attendez pas à ce qu'il change. Votre attente correspond au comportement, puis la SettingWithCopyWarning
disparaît.
Remarque: Si vous souhaitez modifier le DataFrame d'origine, le document vous suggère d'utiliser loc
:
In [14]: dfc.loc[0,'A'] = 555
In [15]: dfc
Out[15]:
A B
0 555 1
1 bbb 2
2 ccc 3
En général, il est préférable de travailler sur des copies plutôt que sur des blocs de données d'origine, sauf si vous savez que vous n'avez plus besoin de l'original et souhaitez utiliser la version manipulée. Normalement, vous pouvez toujours utiliser le bloc de données d'origine pour le comparer à la version manipulée, etc. Par conséquent, la plupart des gens travaillent sur des copies et fusionnent à la fin.