N'agrandissez JAMAIS un DataFrame !
TLDR ; (lisez simplement le texte en gras)
La plupart des réponses ici vous diront comment créer un DataFrame vide et le remplir, mais personne ne vous dira que c'est une mauvaise chose à faire.
Voici mon conseil : Accumuler les données dans une liste, pas dans un DataFrame.
Utilisez une liste pour collecter vos données, puis initialisez un DataFrame lorsque vous êtes prêt. Le format list-of-lists ou list-of-dicts fonctionne, pd.DataFrame
accepte les deux.
data = []
for a, b, c in some_function_that_yields_data():
data.append([a, b, c])
df = pd.DataFrame(data, columns=['A', 'B', 'C'])
Les avantages de cette approche :
-
Il est toujours plus économique d'ajouter à une liste et de créer un DataFrame en une seule fois. qu'il ne l'est de créer un DataFrame vide (ou un DataFrame contenant des NaN) et d'y ajouter des données encore et encore.
-
Les listes occupent également moins de mémoire et constituent une structure de données beaucoup plus légère à utiliser. , append, et remove (si nécessaire).
-
dtypes
sont automatiquement déduits (plutôt que d'attribuer object
à tous).
-
A RangeIndex
est automatiquement créé pour vos données Au lieu de devoir prendre soin d'affecter l'index correct à la ligne que vous ajoutez à chaque itération.
Si vous n'êtes pas encore convaincus, cela est également mentionné dans le document documentation :
L'ajout itératif de lignes à un DataFrame peut être plus intensif en termes de calcul qu'une simple concaténation. qu'une simple concaténation. Une meilleure solution consiste à ajouter ces lignes à une liste, puis de concaténer cette liste avec le DataFrame DataFrame en une seule fois.
Mais que faire si ma fonction renvoie des DataFrames plus petits que je dois combiner en un grand DataFrame ?
Ce n'est pas grave, vous pouvez toujours faire cela en temps linéaire en faisant croître ou en créant une liste python de petits DataFrames, puis en appelant pd.concat
.
small_dfs = []
for small_df in some_function_that_yields_dataframes():
small_dfs.append(small_df)
large_df = pd.concat(small_dfs, ignore_index=True)
ou, de manière plus concise :
large_df = pd.concat(
list(some_function_that_yields_dataframes()), ignore_index=True)
Ces options sont horribles
append
o concat
à l'intérieur d'une boucle
Voici la plus grosse erreur que j'ai vue chez les débutants :
df = pd.DataFrame(columns=['A', 'B', 'C'])
for a, b, c in some_function_that_yields_data():
df = df.append({'A': i, 'B': b, 'C': c}, ignore_index=True) # yuck
# or similarly,
# df = pd.concat([df, pd.Series({'A': i, 'B': b, 'C': c})], ignore_index=True)
La mémoire est réaffectée pour chaque append
o concat
opération que vous avez. Combinez cela avec une boucle et vous avez un opération de complexité quadratique .
L'autre erreur associée à df.append
est que les utilisateurs ont tendance à oublier append n'est pas une fonction in-place Le résultat doit donc être réaffecté. Vous devez également vous préoccuper des dtypes :
df = pd.DataFrame(columns=['A', 'B', 'C'])
df = df.append({'A': 1, 'B': 12.3, 'C': 'xyz'}, ignore_index=True)
df.dtypes
A object # yuck!
B float64
C object
dtype: object
Traiter des colonnes d'objets n'est jamais une bonne chose, car pandas ne peut pas vectoriser les opérations sur ces colonnes. Vous devrez faire ceci pour résoudre ce problème :
df.infer_objects().dtypes
A int64
B float64
C object
dtype: object
loc
à l'intérieur d'une boucle
J'ai aussi vu loc
utilisé pour ajouter à un DataFrame qui a été créé vide :
df = pd.DataFrame(columns=['A', 'B', 'C'])
for a, b, c in some_function_that_yields_data():
df.loc[len(df)] = [a, b, c]
Comme précédemment, vous n'avez pas pré-alloué la quantité de mémoire dont vous avez besoin à chaque fois, donc la mémoire se reconstitue à chaque fois que vous créez un nouveau rang . C'est aussi mauvais que append
et encore plus moche.
DataFrame vide de NaNs
Et puis, il y a la création d'un DataFrame de NaNs, et tous les avertissements qui y sont associés.
df = pd.DataFrame(columns=['A', 'B', 'C'], index=range(5))
df
A B C
0 NaN NaN NaN
1 NaN NaN NaN
2 NaN NaN NaN
3 NaN NaN NaN
4 NaN NaN NaN
Il crée un DataFrame de colonnes d'objets, comme les autres.
df.dtypes
A object # you DON'T want this
B object
C object
dtype: object
La méthode de l'ajout présente les mêmes problèmes que les méthodes ci-dessus.
for i, (a, b, c) in enumerate(some_function_that_yields_data()):
df.iloc[i] = [a, b, c]
La preuve est dans le pudding
Le chronométrage de ces méthodes est le moyen le plus rapide de voir à quel point elles diffèrent en termes de mémoire et d'utilité.
Code d'évaluation comparative pour référence.
73 votes
Ne faites jamais croître un DataFrame ! Il est toujours plus économique d'ajouter une liste en python et de la convertir en DataFrame à la fin, tant en termes de mémoire que de performances.
1 votes
@cs95 Qu'est-ce qui est fonctionnellement différent entre
.append
dans pd et en ajoutant une liste ? Je sais.append
dans pandas, on copie l'ensemble des données dans un nouvel objet ', est-ce que python append fonctionne différemment ?4 votes
@Lamma veuillez trouver les détails dans ma réponse ci-dessous. Lors de l'ajout à df, un nouveau DataFrame est créé à chaque fois en mémoire au lieu d'utiliser celui existant, ce qui est franchement un gaspillage.