web-dev-qa-db-fra.com

Python - Méthode efficace pour ajouter des lignes à une image de données

À partir de cette question et d’autres, il semble qu’il n’est pas recommandé d’utiliser concat ou append pour créer une trame de données pandas car elle recopie la trame complète à chaque fois. 

Mon projet consiste à récupérer une petite quantité de données toutes les 30 secondes. Cela peut durer un week-end de 3 jours, de sorte que quelqu'un peut facilement s'attendre à ce que plus de 8 000 lignes soient créées une ligne à la fois. Quel serait le moyen le plus efficace d’ajouter des lignes à cette base de données?

13
Jarrod

Vous pouvez ajouter des lignes à un DataFrame in-situ en utilisant loc sur un index inexistant. De la documentation Pandas :

In [119]: dfi
Out[119]: 
   A  B  C
0  0  1  0
1  2  3  2
2  4  5  4

In [120]: dfi.loc[3] = 5

In [121]: dfi
Out[121]: 
   A  B  C
0  0  1  0
1  2  3  2
2  4  5  4
3  5  5  5

Comme prévu, utiliser loc est considérablement plus rapide que append (environ 14x):

import pandas as pd
df = pd.DataFrame({"A": [1,2,3], "B": [1,2,3], "C": [1,2,3]})

%%timeit
df2 = pd.DataFrame({"A": [4], "B": [4], "C": [4]})
df.append(df2)

# 1000 loops, best of 3: 1.61 ms per loop

%%timeit
df.loc[3] = 4

# 10000 loops, best of 3: 113 µs per loop
17
sundance

J'ai utilisé la suggestion df.loc[i] = [new_data] de cette réponse, mais j'ai> 500 000 lignes et c'était très lent.

Alors que les réponses données sont bonnes pour la question du PO, je l’ai trouvé plus efficace, lorsqu’il s’agissait de traiter un grand nombre de lignes à l’avant (au lieu de la ruse décrite par le PO) d’utiliser csvwriter pour ajouter des données à un objet CSV en mémoire Enfin, utilisez pandas.read_csv(csv) pour générer la sortie DataFrame souhaitée.

from io import BytesIO
from csv import writer 
import pandas as pd

output = BytesIO()
csv_writer = writer(output)

for row in iterable_object:
    csv_writer.writerow(row)

output.seek(0) # we need to get back to the start of the BytesIO
df = pd.read_csv(output)
return df

Cela, pour environ 500 000 lignes, était 1 000 fois plus rapide et, à mesure que le nombre de lignes augmente, l'amélioration de la vitesse ne fera qu'augmenter (the df.loc[1] = [data] sera beaucoup plus lent comparativement)

J'espère que cela aidera quelqu'un qui a besoin d'efficacité lorsqu'il traite plus de lignes que le PO.

27
Tom Harvey

Vous devez diviser le problème en deux parties:

  1. Accepter les données (les collecter) toutes les 30 secondes efficacement.
  2. Traitement des données une fois celles-ci collectées.

Si vos données sont critiques (c’est-à-dire que vous ne pouvez pas vous permettre de les perdre), envoyez-les dans une file d’attente, puis relisez-les par lots.

La file d'attente fournira une acceptation fiable (garantie) et que vos données ne seront pas perdues.

Vous pouvez lire les données de la file d'attente et les sauvegarder dans une base de données.

Désormais, votre application Python lit simplement dans la base de données et effectue l'analyse à n'importe quel intervalle logique pour l'application. Vous souhaitez peut-être faire des moyennes horaires; dans ce cas, vous exécuteriez votre script toutes les heures pour extraire les données de la base de données et éventuellement écrire les résultats dans une autre base de données/table/fichier.

La ligne du bas - diviser la collecte et l'analyse des parties de votre application.

2
Burhan Khalid

En supposant que votre cadre de données soit indexé dans l'ordre, vous pouvez:

Commencez par vérifier quelle est la prochaine valeur d'index pour créer une nouvelle ligne:

myindex = df.shape[0]+1 

Puis utilisez 'at' pour écrire dans chaque colonne souhaitée

df.at[myindex,'A']=val1
df.at[myindex,'B']=val2
df.at[myindex,'C']=val3
1
sparrow