J'ai une fonction qui traite un DataFrame, en grande partie pour traiter les données dans des compartiments créer une matrice binaire de fonctionnalités dans une colonne particulière en utilisant pd.get_dummies(df[col])
.
Pour éviter de traiter toutes mes données à l'aide de cette fonction à la fois (ce qui manque de mémoire et provoque le blocage d'iPython), j'ai divisé le grand DataFrame en morceaux en utilisant:
chunks = (len(df) / 10000) + 1
df_list = np.array_split(df, chunks)
pd.get_dummies(df)
créera automatiquement de nouvelles colonnes basées sur le contenu de df[col]
et celles-ci sont susceptibles de différer pour chaque df
dans df_list
.
Après le traitement, je concatène les DataFrames ensemble en utilisant:
for i, df_chunk in enumerate(df_list):
print "chunk", i
[x, y] = preprocess_data(df_chunk)
super_x = pd.concat([super_x, x], axis=0)
super_y = pd.concat([super_y, y], axis=0)
print datetime.datetime.utcnow()
Le temps de traitement du premier bloc est parfaitement acceptable, cependant, il augmente par bloc! Cela n'a rien à voir avec la preprocess_data(df_chunk)
car il n'y a aucune raison pour qu'elle augmente. Cette augmentation de temps se produit-elle à la suite de l'appel à pd.concat()
?
Veuillez consulter le journal ci-dessous:
chunks 6
chunk 0
2016-04-08 00:22:17.728849
chunk 1
2016-04-08 00:22:42.387693
chunk 2
2016-04-08 00:23:43.124381
chunk 3
2016-04-08 00:25:30.249369
chunk 4
2016-04-08 00:28:11.922305
chunk 5
2016-04-08 00:32:00.357365
Existe-t-il une solution de contournement pour accélérer cela? J'ai 2900 morceaux à traiter, donc toute aide est appréciée!
Ouvert à toutes autres suggestions en Python!
N'appelez jamais DataFrame.append
Ou pd.concat
Dans une boucle for. Cela conduit à une copie quadratique.
pd.concat
Renvoie un nouveau DataFrame. De l'espace doit être alloué pour le nouveau DataFrame, et les données des anciens DataFrames doivent être copiées dans le nouveau DataFrame. Considérez la quantité de copie requise par cette ligne dans le for-loop
(En supposant que chaque x
a la taille 1):
super_x = pd.concat([super_x, x], axis=0)
| iteration | size of old super_x | size of x | copying required |
| 0 | 0 | 1 | 1 |
| 1 | 1 | 1 | 2 |
| 2 | 2 | 1 | 3 |
| ... | | | |
| N-1 | N-1 | 1 | N |
1 + 2 + 3 + ... + N = N(N+1)/2
. Il y a donc O(N**2)
copies nécessaires pour terminer la boucle.
Considérez maintenant
super_x = []
for i, df_chunk in enumerate(df_list):
[x, y] = preprocess_data(df_chunk)
super_x.append(x)
super_x = pd.concat(super_x, axis=0)
L'ajout à une liste est une opération O(1)
et ne nécessite pas de copie. Il y a maintenant un seul appel à pd.concat
Après la boucle. Cet appel à pd.concat
Nécessite N copies, car super_x
Contient N
DataFrames de taille 1. Ainsi, lorsqu'il est construit de cette façon, super_x
Nécessite O(N)
copies.
Chaque fois que vous concaténez, vous retournez une copie des données.
Vous souhaitez conserver une liste de vos morceaux, puis tout concaténer comme étape finale.
df_x = []
df_y = []
for i, df_chunk in enumerate(df_list):
print "chunk", i
[x, y] = preprocess_data(df_chunk)
df_x.append(x)
df_y.append(y)
super_x = pd.concat(df_x, axis=0)
del df_x # Free-up memory.
super_y = pd.concat(df_y, axis=0)
del df_y # Free-up memory.