Ajouter une ligne aux pandas DataFrame


872

Je comprends que les pandas sont conçus pour charger entièrement rempli,DataFrame mais je dois créer un DataFrame vide puis ajouter des lignes, une par une . Quelle est la meilleure façon de procéder ?

J'ai réussi à créer un DataFrame vide avec:

res = DataFrame(columns=('lib', 'qty1', 'qty2'))

Ensuite, je peux ajouter une nouvelle ligne et remplir un champ avec:

res = res.set_value(len(res), 'qty1', 10.0)

Cela fonctionne mais semble très étrange: - / (il échoue pour ajouter une valeur de chaîne)

Comment puis-je ajouter une nouvelle ligne à mon DataFrame (avec différents types de colonnes)?


70
Notez que c'est une manière très inefficace de construire un grand DataFrame; de nouveaux tableaux doivent être créés (en copiant les données existantes) lorsque vous ajoutez une ligne.
Wes McKinney

5
@WesMcKinney: Thx, c'est vraiment bon à savoir. Est-il très rapide d'ajouter des colonnes à d'énormes tables?
max

4
S'il est trop inefficace pour vous, vous pouvez préallouer une ligne supplémentaire, puis la mettre à jour.
user1154664

Réponses:


571

Vous pouvez utiliser df.loc[i], où la ligne avec index icorrespondra à ce que vous spécifiez dans la trame de données.

>>> import pandas as pd
>>> from numpy.random import randint

>>> df = pd.DataFrame(columns=['lib', 'qty1', 'qty2'])
>>> for i in range(5):
>>>     df.loc[i] = ['name' + str(i)] + list(randint(10, size=2))

>>> df
     lib qty1 qty2
0  name0    3    3
1  name1    2    4
2  name2    2    8
3  name3    2    1
4  name4    9    6

25
Pensez à ajouter l'index pour préallouer de la mémoire (voir ma réponse)
FooBar

34
@MaximG: Je recommande fortement une mise à niveau. La version actuelle de Pandas est 0.15.0.
Fred

44
.locfait référence à la colonne d'index, donc si vous travaillez avec un DataFrame préexistant avec un index qui n'est pas une séquence continue d'entiers commençant par 0 (comme dans votre exemple), .locremplacera les lignes existantes ou insérera des lignes, ou créer des lacunes dans votre index. Une approche plus robuste (mais pas infaillible) pour ajouter une trame de données existante de longueur non nulle serait: df.loc[df.index.max() + 1] = [randint(...ou de préremplir l'index comme l'a suggéré @FooBar.
plaques de cuisson

4
@hobs df.index.max()est nanlorsque le DataFrame est vide.
flow2k

4
@hobs Une solution à laquelle j'ai pensé utilise l'opérateur ternaire:df.loc[0 if pd.isnull(df.index.max()) else df.index.max() + 1]
flow2k

477

Dans le cas où vous pouvez obtenir toutes les données pour la trame de données à l'avance, il existe une approche beaucoup plus rapide que l'ajout à une trame de données:

  1. Créez une liste de dictionnaires dans lesquels chaque dictionnaire correspond à une ligne de données d'entrée.
  2. Créez un bloc de données à partir de cette liste.

J'ai eu une tâche similaire pour laquelle l'ajout à un bloc de données ligne par ligne a pris 30 minutes, et la création d'un bloc de données à partir d'une liste de dictionnaires s'est terminée en quelques secondes.

rows_list = []
for row in input_rows:

        dict1 = {}
        # get input row in dictionary format
        # key = col_name
        dict1.update(blah..) 

        rows_list.append(dict1)

df = pd.DataFrame(rows_list)               

48
Je suis passé à cela aussi pour toute situation où je ne peux pas obtenir toutes les données à l'avance. La différence de vitesse est étonnante.
fantabolous

47
Copie à partir des documents de pandas: It is worth noting however, that concat (and therefore append) makes a full copy of the data, and that constantly reusing this function can create a significant performance hit. If you need to use the operation over several datasets, use a list comprehension.( pandas.pydata.org/pandas-docs/stable/… )
thikonom

5
Cela fonctionne très bien! Sauf quand j'ai créé le
bloc de

5
@ user5359531 Vous pouvez utiliser le dict ordonné dans ce cas
ShikharDua

21
@ user5359531 Vous pouvez spécifier manuellement les colonnes et l'ordre sera conservé. pd.DataFrame (lignes_liste, colonnes = ['C1', 'C2', 'C3']) fera l'affaire
Marcello Grechi Lins

288

Vous pouvez utiliser pandas.concat()ou DataFrame.append(). Pour plus d'informations et d'exemples, voir Fusionner, joindre et concaténer .


6
Salut, alors quelle est la réponse pour les méthodes utilisant append () ou concat (). J'ai le même problème, mais j'essaie toujours de le résoudre.
notilas

109
C'est la bonne réponse, mais ce n'est pas une très bonne réponse (presque un lien uniquement).
jwg

5
Je pense que la réponse de @ fred est plus correcte. IIUC Le problème avec cette réponse est qu'elle copie inutilement l'intégralité du DataFrame chaque fois qu'une ligne est ajoutée. Utiliser le .locmécanisme qui peut être évité, surtout si vous faites attention.
Ken Williams

7
Mais si vous souhaitez utiliser DataFrame.append(), vous devez vous assurer que vos données de ligne sont également un DataFrame en premier lieu, pas une liste.
StayFoolish

203

Ça fait longtemps, mais j'ai aussi fait face au même problème. Et j'ai trouvé ici beaucoup de réponses intéressantes. Donc, je ne savais pas quelle méthode utiliser.

Dans le cas de l'ajout de nombreuses lignes à la trame de données, je me suis intéressé aux performances de vitesse . J'ai donc essayé les 4 méthodes les plus populaires et vérifié leur vitesse.

MISE À JOUR EN 2019 à l' aide de nouvelles versions de packages. Également mis à jour après le commentaire @FooBar

PERFORMANCE DE VITESSE

  1. Utilisation de .append ( réponse de NPE )
  2. Utiliser .loc ( réponse de Fred )
  3. Utilisation de .loc avec préallocation ( réponse de FooBar )
  4. Utiliser dict et créer DataFrame à la fin ( réponse de ShikharDua )

Résultats (en secondes):

|------------|-------------|-------------|-------------|
|  Approach  |  1000 rows  |  5000 rows  | 10 000 rows |
|------------|-------------|-------------|-------------|
| .append    |    0.69     |    3.39     |    6.78     |
|------------|-------------|-------------|-------------|
| .loc w/o   |    0.74     |    3.90     |    8.35     |
| prealloc   |             |             |             |
|------------|-------------|-------------|-------------|
| .loc with  |    0.24     |    2.58     |    8.70     |
| prealloc   |             |             |             |
|------------|-------------|-------------|-------------|
|  dict      |    0.012    |   0.046     |   0.084     |
|------------|-------------|-------------|-------------|

Merci également à @krassowski pour ses commentaires utiles - J'ai mis à jour le code.

J'utilise donc l'addition par le biais du dictionnaire pour moi-même.


Code:

import pandas as pd
import numpy as np
import time

del df1, df2, df3, df4
numOfRows = 1000
# append
startTime = time.perf_counter()
df1 = pd.DataFrame(np.random.randint(100, size=(5,5)), columns=['A', 'B', 'C', 'D', 'E'])
for i in range( 1,numOfRows-4):
    df1 = df1.append( dict( (a,np.random.randint(100)) for a in ['A','B','C','D','E']), ignore_index=True)
print('Elapsed time: {:6.3f} seconds for {:d} rows'.format(time.perf_counter() - startTime, numOfRows))
print(df1.shape)

# .loc w/o prealloc
startTime = time.perf_counter()
df2 = pd.DataFrame(np.random.randint(100, size=(5,5)), columns=['A', 'B', 'C', 'D', 'E'])
for i in range( 1,numOfRows):
    df2.loc[i]  = np.random.randint(100, size=(1,5))[0]
print('Elapsed time: {:6.3f} seconds for {:d} rows'.format(time.perf_counter() - startTime, numOfRows))
print(df2.shape)

# .loc with prealloc
df3 = pd.DataFrame(index=np.arange(0, numOfRows), columns=['A', 'B', 'C', 'D', 'E'] )
startTime = time.perf_counter()
for i in range( 1,numOfRows):
    df3.loc[i]  = np.random.randint(100, size=(1,5))[0]
print('Elapsed time: {:6.3f} seconds for {:d} rows'.format(time.perf_counter() - startTime, numOfRows))
print(df3.shape)

# dict
startTime = time.perf_counter()
row_list = []
for i in range (0,5):
    row_list.append(dict( (a,np.random.randint(100)) for a in ['A','B','C','D','E']))
for i in range( 1,numOfRows-4):
    dict1 = dict( (a,np.random.randint(100)) for a in ['A','B','C','D','E'])
    row_list.append(dict1)

df4 = pd.DataFrame(row_list, columns=['A','B','C','D','E'])
print('Elapsed time: {:6.3f} seconds for {:d} rows'.format(time.perf_counter() - startTime, numOfRows))
print(df4.shape)

PS Je crois que ma réalisation n'est pas parfaite, et il y a peut-être une optimisation.


4
L'utilisation de df2.index.max()for .locaugmente inutilement la complexité de calcul. Simple df2.loc[i] = ...ferait l'affaire. Pour moi, cela a réduit le temps de 10 s à 8,64 s
krassowski

Veuillez supprimer mon nom de la liste, car vous ne suivez pas mon approche dans votre test: vous ne préallouez pas la mémoire en fournissant un index de taille appropriée.
FooBar

@FooBar Salut! Je suis content que l'auteur ait vu ma réponse :) vous avez raison, j'ai raté ce point important. Je préfère ajouter une ligne de plus pour mon tableau de résultats car votre approche montre le résultat différent!
Mikhail_Sam

@Mikhail_Sam Comment utiliseriez-vous le tableau croisé dynamique pour l'écrire sur un fichier Excel en utilisant la méthode la plus rapide, dict?
FabioSpaghetti

1
Je voulais juste jeter un autre commentaire pour expliquer pourquoi le Dict to Pandas DataFrame est une meilleure façon. Dans mon expérimentation avec un ensemble de données qui a plusieurs types de données différents dans la table, l'utilisation des méthodes d'ajout Pandas détruit le typage, tandis que l'utilisation d'un Dict, et en créant seulement le DataFrame à partir de lui UNE FOIS, semble garder les types de données originaux intacts.
trumpetlicks

109

Si vous connaissez le nombre d'entrées ex ante, vous devez préallouer l'espace en fournissant également l'index (en prenant l'exemple de données d'une réponse différente):

import pandas as pd
import numpy as np
# we know we're gonna have 5 rows of data
numberOfRows = 5
# create dataframe
df = pd.DataFrame(index=np.arange(0, numberOfRows), columns=('lib', 'qty1', 'qty2') )

# now fill it up row by row
for x in np.arange(0, numberOfRows):
    #loc or iloc both work here since the index is natural numbers
    df.loc[x] = [np.random.randint(-1,1) for n in range(3)]
In[23]: df
Out[23]: 
   lib  qty1  qty2
0   -1    -1    -1
1    0     0     0
2   -1     0    -1
3    0    -1     0
4   -1     0     0

Comparaison de vitesse

In[30]: %timeit tryThis() # function wrapper for this answer
In[31]: %timeit tryOther() # function wrapper without index (see, for example, @fred)
1000 loops, best of 3: 1.23 ms per loop
100 loops, best of 3: 2.31 ms per loop

Et - à partir des commentaires - avec une taille de 6000, la différence de vitesse devient encore plus grande:

L'augmentation de la taille du tableau (12) et du nombre de lignes (500) rend la différence de vitesse plus frappante: 313 ms contre 2,29 s


3
Très bonne réponse. Cela devrait être la norme pour que l'espace de ligne ne soit pas alloué de manière incrémentielle.
le

8
L'augmentation de la taille du tableau (12) et du nombre de lignes (500) rend la différence de vitesse plus frappante: 313 ms contre 2,29 s
Tickon

80
mycolumns = ['A', 'B']
df = pd.DataFrame(columns=mycolumns)
rows = [[1,2],[3,4],[5,6]]
for row in rows:
    df.loc[len(df)] = row

2
Cette! Je recherche depuis pas mal de temps, et c'est le premier article qui montre vraiment comment assigner des valeurs particulières à une ligne! Question bonus: Quelle est la syntaxe des paires nom-colonne / valeur? Je suppose que ce doit être quelque chose utilisant un dicton, mais je n'arrive pas à faire les choses correctement.
2016

3
ce n'est pas efficace car il copie en fait l'intégralité du DataFrame lorsque vous l'étendez.
étanche

72

Pour un ajout efficace, voir Comment ajouter une ligne supplémentaire à une trame de données pandas et Configuration avec élargissement .

Ajoutez des lignes loc/ixsur des données d'index de clés inexistantes. par exemple :

In [1]: se = pd.Series([1,2,3])

In [2]: se
Out[2]: 
0    1
1    2
2    3
dtype: int64

In [3]: se[5] = 5.

In [4]: se
Out[4]: 
0    1.0
1    2.0
2    3.0
5    5.0
dtype: float64

Ou:

In [1]: dfi = pd.DataFrame(np.arange(6).reshape(3,2),
   .....:                 columns=['A','B'])
   .....: 

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

In [3]: dfi.loc[:,'C'] = dfi.loc[:,'A']

In [4]: dfi
Out[4]: 
   A  B  C
0  0  1  0
1  2  3  2
2  4  5  4
In [5]: dfi.loc[3] = 5

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

Les utilisateurs ont demandé la mise en œuvre (ajoutez une nouvelle ligne). Ici, nous voyons comment ajouter une ligne dans un index défini ou ajouter une colonne.
Guilherme Felipe Reis

1
des repères sur la façon dont cela fonctionne par rapport à la méthode dict
PirateApp

ce n'est pas efficace car il copie en fait l'intégralité du DataFrame.
étanche

66

Vous pouvez ajouter une seule ligne en tant que dictionnaire à l'aide de l' ignore_indexoption.

>>> f = pandas.DataFrame(data = {'Animal':['cow','horse'], 'Color':['blue', 'red']})
>>> f
  Animal Color
0    cow  blue
1  horse   red
>>> f.append({'Animal':'mouse', 'Color':'black'}, ignore_index=True)
  Animal  Color
0    cow   blue
1  horse    red
2  mouse  black

37
Vous pouvez également mentionner que f.append(<stuff>)crée un nouvel objet, plutôt que de simplement ajouter à l'objet actuel en place, donc si vous essayez d'ajouter à une trame de données dans un script, vous devez diref = f.append(<stuff>)
Blairg23

2
existe-t-il un moyen de le faire en place?
lol

@lol no. voir github.com/pandas-dev/pandas/issues/2801 - les tableaux sous-jacents ne peuvent pas être étendus, ils doivent donc être copiés.
étanche

46

Pour des raisons de Pythonic, ajoutez ici ma réponse:

res = pd.DataFrame(columns=('lib', 'qty1', 'qty2'))
res = res.append([{'qty1':10.0}], ignore_index=True)
print(res.head())

   lib  qty1  qty2
0  NaN  10.0   NaN

27

Vous pouvez également créer une liste de listes et la convertir en une trame de données -

import pandas as pd

columns = ['i','double','square']
rows = []

for i in range(6):
    row = [i, i*2, i*i]
    rows.append(row)

df = pd.DataFrame(rows, columns=columns)

donnant

    je double carré
0 0 0 0
1 1 2 1
2 2 4 4
3 3 6 9
4 4 8 16
5 5 10 25

15

Ce n'est pas une réponse à la question OP mais un exemple de jouet pour illustrer la réponse de @ShikharDua ci-dessus que j'ai trouvé très utile.

Bien que ce fragment soit trivial, dans les données réelles, j'avais des milliers de lignes et de nombreuses colonnes, et je souhaitais pouvoir regrouper par différentes colonnes, puis effectuer les statistiques ci-dessous pour plus d'une colonne de taget. Il était donc très pratique d'avoir une méthode fiable pour construire le bloc de données une ligne à la fois. Merci @ShikharDua!

import pandas as pd 

BaseData = pd.DataFrame({ 'Customer' : ['Acme','Mega','Acme','Acme','Mega','Acme'],
                          'Territory'  : ['West','East','South','West','East','South'],
                          'Product'  : ['Econ','Luxe','Econ','Std','Std','Econ']})
BaseData

columns = ['Customer','Num Unique Products', 'List Unique Products']

rows_list=[]
for name, group in BaseData.groupby('Customer'):
    RecordtoAdd={} #initialise an empty dict 
    RecordtoAdd.update({'Customer' : name}) #
    RecordtoAdd.update({'Num Unique Products' : len(pd.unique(group['Product']))})      
    RecordtoAdd.update({'List Unique Products' : pd.unique(group['Product'])})                   

    rows_list.append(RecordtoAdd)

AnalysedData = pd.DataFrame(rows_list)

print('Base Data : \n',BaseData,'\n\n Analysed Data : \n',AnalysedData)

14

Compris de manière simple et agréable:

>>> df
     A  B  C
one  1  2  3
>>> df.loc["two"] = [4,5,6]
>>> df
     A  B  C
one  1  2  3
two  4  5  6

1
Notez que cela copiera l'intégralité du DataFrame sous le capot. Les tableaux sous-jacents ne peuvent pas être étendus, ils doivent donc être copiés.
étanche

10

Vous pouvez utiliser un objet générateur pour créer Dataframe, qui sera plus efficace en mémoire sur la liste.

num = 10

# Generator function to generate generator object
def numgen_func(num):
    for i in range(num):
        yield ('name_{}'.format(i), (i*i), (i*i*i))

# Generator expression to generate generator object (Only once data get populated, can not be re used)
numgen_expression = (('name_{}'.format(i), (i*i), (i*i*i)) for i in range(num) )

df = pd.DataFrame(data=numgen_func(num), columns=('lib', 'qty1', 'qty2'))

Pour ajouter des données brutes à DataFrame existant, vous pouvez utiliser la méthode append.

df = df.append([{ 'lib': "name_20", 'qty1': 20, 'qty2': 400  }])

9

Créez un nouvel enregistrement ( bloc de données) et ajoutez-le à old_data_frame .
transmettre la liste des valeurs et les noms de colonne correspondants pour créer un nouveau_enregistrement (data_frame)

new_record = pd.DataFrame([[0,'abcd',0,1,123]],columns=['a','b','c','d','e'])

old_data_frame = pd.concat([old_data_frame,new_record])

8

Voici comment ajouter / ajouter une ligne dans pandas DataFrame

def add_row(df, row):
    df.loc[-1] = row
    df.index = df.index + 1  
    return df.sort_index()

add_row(df, [1,2,3]) 

Il peut être utilisé pour insérer / ajouter une ligne dans des pandas DataFrame vides ou remplis


1
cela ajoute avec l'index dans l'ordre décroissant
Parthiban Rajendran

5

Au lieu d'une liste de dictionnaires comme dans la réponse de ShikharDua, nous pouvons également représenter notre table comme un dictionnaire de listes , où chaque liste stocke une colonne dans l'ordre des lignes, étant donné que nous connaissons nos colonnes au préalable. À la fin, nous construisons notre DataFrame une fois.

Pour c colonnes et n lignes, cela utilise 1 dictionnaire et c listes, contre 1 liste et n dictionnaires. La méthode de la liste des dictionnaires a chaque dictionnaire stockant toutes les clés et nécessite la création d'un nouveau dictionnaire pour chaque ligne. Ici, nous ajoutons uniquement aux listes, ce qui est à temps constant et théoriquement très rapide.

# current data
data = {"Animal":["cow", "horse"], "Color":["blue", "red"]}

# adding a new row (be careful to ensure every column gets another value)
data["Animal"].append("mouse")
data["Color"].append("black")

# at the end, construct our DataFrame
df = pd.DataFrame(data)
#   Animal  Color
# 0    cow   blue
# 1  horse    red
# 2  mouse  black

5

si vous souhaitez ajouter une ligne à la fin, ajoutez-la sous forme de liste

valuestoappend = [va1,val2,val3]
res = res.append(pd.Series(valuestoappend,index = ['lib', 'qty1', 'qty2']),ignore_index = True)

4

Une autre façon de le faire (probablement pas très performante):

# add a row
def add_row(df, row):
    colnames = list(df.columns)
    ncol = len(colnames)
    assert ncol == len(row), "Length of row must be the same as width of DataFrame: %s" % row
    return df.append(pd.DataFrame([row], columns=colnames))

Vous pouvez également améliorer la classe DataFrame comme ceci:

import pandas as pd
def add_row(self, row):
    self.loc[len(self.index)] = row
pd.DataFrame.add_row = add_row

2

Tout ce dont vous avez besoin est loc[df.shape[0]]ouloc[len(df)]


# Assuming your df has 4 columns (str, int, str, bool)
df.loc[df.shape[0]] = ['col1Value', 100, 'col3Value', False] 

ou

df.loc[len(df)] = ['col1Value', 100, 'col3Value', False] 

1

Faites simple. En prenant la liste en entrée qui sera ajoutée en tant que ligne dans la trame de données: -

import pandas as pd  
res = pd.DataFrame(columns=('lib', 'qty1', 'qty2'))  
for i in range(5):  
    res_list = list(map(int, input().split()))  
    res = res.append(pd.Series(res_list,index=['lib','qty1','qty2']), ignore_index=True)

0

Nous voyons souvent la construction df.loc[subscript] = …à affecter à une ligne DataFrame. Mikhail_Sam a posté des benchmarks contenant, entre autres, cette construction ainsi que la méthode utilisant dict et create DataFrame à la fin . Il a trouvé que ce dernier était de loin le plus rapide. Mais si nous remplaçons le df3.loc[i] = …(avec DataFrame préalloué) dans son code par df3.values[i] = …, le résultat change considérablement, en ce que cette méthode fonctionne de manière similaire à celle utilisant dict. Nous devons donc plus souvent prendre df.values[subscript] = …en considération l'utilisation de . Cependant, notez que cela .valuesprend un indice de base zéro, qui peut être différent du DataFrame.index.


un exemple de code serait utile
baxx

1
@baxx - Un exemple de code est sur le lien benchmarks ( # .loc with prealloc), un autre exemple est dans la question que je dois comparer les données de chaque ligne d'un Pandas DataFrame avec les données du reste des lignes, est-il un moyen d'accélérer le calcul ? et sa réponse acceptée.
Armali

0

pandas.DataFrame.append

DataFrame.append (auto, autre, ignore_index = False, verify_integrity = False, sort = False) → 'DataFrame'

df = pd.DataFrame([[1, 2], [3, 4]], columns=list('AB'))
df2 = pd.DataFrame([[5, 6], [7, 8]], columns=list('AB'))
df.append(df2)

Avec ignore_index défini sur True:

df.append(df2, ignore_index=True)

0

avant d'aller ajouter une ligne, nous devons convertir la trame de données en dictionnaire, vous pouvez voir les clés sous forme de colonnes dans la trame de données et les valeurs des colonnes sont à nouveau stockées dans le dictionnaire, mais la clé pour chaque colonne est le numéro d'index dans la trame de données. Cette idée m'a fait écrire le code ci-dessous.

df2=df.to_dict()
values=["s_101","hyderabad",10,20,16,13,15,12,12,13,25,26,25,27,"good","bad"] #this is total row that we are going to add
i=0
for x in df.columns:   #here df.columns gives us the main dictionary key
    df2[x][101]=values[i]   #here the 101 is our index number it is also key of sub dictionary
    i+=1

0

Vous pouvez concaténer deux DataFrames pour cela. J'ai rencontré ce problème pour ajouter une nouvelle ligne à un DataFrame existant avec un index de caractères (non numérique). Donc, j'entre les données d'une nouvelle ligne dans un conduit () et l'index dans une liste.

new_dict = {put input for new row here}
new_list = [put your index here]

new_df = pd.DataFrame(data=new_dict, index=new_list)

df = pd.concat([existing_df, new_df])

-1

Cela prendra soin d'ajouter un élément à un DataFrame vide. Le problème est que df.index.max() == nanpour le premier index:

df = pd.DataFrame(columns=['timeMS', 'accelX', 'accelY', 'accelZ', 'gyroX', 'gyroY', 'gyroZ'])

df.loc[0 if math.isnan(df.index.max()) else df.index.max() + 1] = [x for x in range(7)]
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.