Agregar una fila a pandas DataFrame

871

Entiendo que los pandas están diseñados para cargar completamente poblados, DataFramepero necesito crear un DataFrame vacío y luego agregar filas, una por una . Cuál es la mejor manera de hacer esto ?

Creé con éxito un DataFrame vacío con:

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

Luego puedo agregar una nueva fila y llenar un campo con:

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

Funciona pero parece muy extraño: - / (falla al agregar valor de cadena)

¿Cómo puedo agregar una nueva fila a mi DataFrame (con diferentes tipos de columnas)?

PhE
fuente
70
Tenga en cuenta que esta es una forma muy ineficiente de crear un gran DataFrame; se deben crear nuevas matrices (copiando sobre los datos existentes) cuando agrega una fila.
Wes McKinney
55
@WesMcKinney: Gracias, eso es realmente bueno saberlo. ¿Es muy rápido agregar columnas a tablas enormes?
máximo
44
Si es demasiado ineficiente para usted, puede preasignar una fila adicional y luego actualizarla.
user1154664

Respuestas:

571

Puede usar df.loc[i], donde la fila con índice iserá lo que especifique para que esté en el marco de datos.

>>> 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
Fred
fuente
25
Considere agregar el índice para preasignar memoria (vea mi respuesta)
FooBar
34
@MaximG: recomiendo una actualización. La versión actual de Pandas es 0.15.0.
Fred
44
.lochace referencia a la columna de índice, por lo que si está trabajando con un DataFrame preexistente con un índice que no es una secuencia continua de enteros que comienza con 0 (como en su ejemplo), .locsobrescribirá las filas existentes, o insertará filas, o crea lagunas en tu índice. Un enfoque más robusto (pero no infalible) para agregar un marco de datos existente de longitud distinta de cero sería: df.loc[df.index.max() + 1] = [randint(...o prepoblar el índice como sugirió @FooBar.
placas
44
@hobs df.index.max()es nancuando el DataFrame está vacío.
flow2k
44
@hobs Una solución que pensé es usar el operador ternario:df.loc[0 if pd.isnull(df.index.max()) else df.index.max() + 1]
flow2k
476

En caso de que pueda obtener todos los datos para el marco de datos por adelantado, hay un enfoque mucho más rápido que agregar un marco de datos:

  1. Cree una lista de diccionarios en los que cada diccionario corresponde a una fila de datos de entrada.
  2. Cree un marco de datos de esta lista.

Tuve una tarea similar para la cual agregar un marco de datos fila por fila tomó 30 minutos, y crear un marco de datos a partir de una lista de diccionarios completada en segundos.

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)               
ShikharDua
fuente
48
Me moví a hacer esto también para cualquier situación en la que no puedo obtener todos los datos por adelantado. La diferencia de velocidad es asombrosa.
fantástico
47
Copia de documentos 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
55
¡Esto funciona muy bien! Excepto cuando creé la trama de datos, los nombres de las columnas estaban todos en el orden equivocado ...
user5359531
55
@ user5359531 Puede usar dict ordenado en ese caso
ShikharDua
21
@ user5359531 Puede especificar manualmente las columnas y se preservará el orden. pd.DataFrame (rows_list, columnas = ['C1', 'C2', 'C3']) hará el truco
Marcello Grechi Lins
288

Podrías usar pandas.concat()o DataFrame.append(). Para obtener detalles y ejemplos, consulte Combinar, unir y concatenar .

NPE
fuente
66
Hola, ¿cuál es la respuesta para los métodos que usan append () o concat (). Tengo el mismo problema, pero aún estoy tratando de resolverlo.
Notilas
109
Esta es la respuesta correcta, pero no es una muy buena respuesta (casi solo enlace).
jwg
55
Creo que la respuesta de @ fred es más correcta. IIUC el problema con esta respuesta es que copia innecesariamente todo el DataFrame cada vez que se agrega una fila. Usar el .locmecanismo que se puede evitar, especialmente si tienes cuidado.
Ken Williams
77
Pero si desea usar DataFrame.append(), debe asegurarse de que sus datos de fila también sean un DataFrame en primer lugar, no una lista.
StayFoolish
202

Ha pasado mucho tiempo, pero también enfrenté el mismo problema. Y encontré aquí muchas respuestas interesantes. Entonces estaba confundido qué método usar.

En el caso de agregar muchas filas al marco de datos, me interesa el rendimiento de la velocidad . Así que probé los 4 métodos más populares y verifiqué su velocidad.

ACTUALIZADO EN 2019 utilizando nuevas versiones de paquetes. También actualizado después del comentario de @FooBar

RENDIMIENTO DE VELOCIDAD

  1. Usando .append ( respuesta de NPE )
  2. Usando .loc ( respuesta de fred )
  3. Usando .loc con preasignación ( respuesta de FooBar )
  4. Usar dict y crear DataFrame al final ( respuesta de ShikharDua )

Resultados (en segundos):

|------------|-------------|-------------|-------------|
|  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     |
|------------|-------------|-------------|-------------|

También gracias a @krassowski por sus útiles comentarios. Actualicé el código.

Entonces uso la suma a través del diccionario para mí.


Código:

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)

PD: Creo que mi realización no es perfecta, y tal vez hay algo de optimización.

Mikhail_Sam
fuente
44
El uso de df2.index.max()for .locinnecesariamente aumenta la complejidad computacional. Simple df2.loc[i] = ...haría. Para mí, redujo el tiempo de 10s a 8.64s
krassowski el
Elimine mi nombre de la lista, ya que no está siguiendo mi enfoque en su prueba: no está asignando previamente la memoria al proporcionar un índice de tamaño adecuado.
FooBar
@FooBar Hola! Me alegro de que como el autor vio mi respuesta :) tienes razón, me perdí este punto importante. ¡Prefiero agregar una fila más para mi tabla de resultados ya que su enfoque muestra un resultado diferente!
Mikhail_Sam
@Mikhail_Sam ¿Cómo usarías la tabla dinámica para escribirlo en un archivo de Excel usando el método más rápido, dict?
FabioSpaghetti
1
Solo quería lanzar otro comentario sobre por qué el Dict to Pandas DataFrame es una mejor manera. En mi experimentación con un conjunto de datos que tiene múltiples tipos de datos diferentes en la tabla, el uso de los métodos de anexión de Pandas destruye la escritura, mientras que usar un Dict y solo crear el DataFrame a partir de él una vez, parece mantener intactos los tipos de datos originales.
trumpetlicks
109

Si conoce el número de entradas ex ante, debe preasignar el espacio proporcionando también el índice (tomando el ejemplo de datos de una respuesta diferente):

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

Comparación de velocidad

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

Y, a partir de los comentarios, con un tamaño de 6000, la diferencia de velocidad se vuelve aún mayor:

Aumentar el tamaño de la matriz (12) y el número de filas (500) hace que la diferencia de velocidad sea más sorprendente: 313 ms frente a 2.29 s

FooBar
fuente
3
Gran respuesta. Esta debería ser la norma para que el espacio de fila no tenga que asignarse de forma incremental.
ely
8
Aumentar el tamaño de la matriz (12) y el número de filas (500) hace que la diferencia de velocidad sea más sorprendente: 313 ms frente a 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
Lidia
fuente
2
¡Esta! ¡He estado buscando durante bastante tiempo, y esta es la primera publicación que realmente muestra cómo asignar valores particulares a una fila! Pregunta adicional: ¿Cuál es la sintaxis para los pares nombre-columna / valor? Supongo que debe ser algo usando un dict, pero parece que no puedo hacerlo bien.
jhin
3
esto no es eficiente, ya que en realidad copia todo el DataFrame cuando lo extiende.
impermeable
72

Para un agregado eficiente, vea Cómo agregar una fila adicional a un marco de datos de pandas y Configuración con ampliación .

Agregue filas a través loc/ixde datos de índice clave no existentes . p.ej :

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

O:

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
Nasser Al-Wohaibi
fuente
Los usuarios pidieron implementar (agregar una nueva fila). Aquí vemos cómo agregar una fila en un índice definido o agregar una columna.
Guilherme Felipe Reis
1
cualquier punto de referencia sobre cómo funciona esto en comparación con el método dict
PirateApp
esto no es eficiente ya que en realidad copia todo el DataFrame.
impermeable
66

Puede agregar una sola fila como diccionario utilizando la ignore_indexopción

>>> 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
WP McNeill
fuente
37
También puede mencionar que f.append(<stuff>)crea un nuevo objeto, en lugar de simplemente agregar al objeto actual en su lugar, por lo que si está tratando de agregar a un marco de datos en un script, debe decirf = f.append(<stuff>)
Blairg23
2
¿Hay alguna manera de hacer esto en su lugar?
lol
@lol no. consulte github.com/pandas-dev/pandas/issues/2801 : las matrices subyacentes no se pueden extender, por lo que deben copiarse.
impermeable
46

Por el bien de Pythonic, aquí agregue mi respuesta:

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
hkyi
fuente
27

También puede crear una lista de listas y convertirla en un marco de datos:

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)

dando

    Yo doble cuadrado
0 0 0 0
1 1 2 1
2 2 4 4
3 3 6 9
4 4 8 16
5 5 10 25
Brian Burns
fuente
15

Esta no es una respuesta a la pregunta de OP sino un ejemplo de juguete para ilustrar la respuesta de @ShikharDua, que he encontrado muy útil.

Si bien este fragmento es trivial, en los datos reales tenía miles de filas y muchas columnas, y deseaba poder agrupar por diferentes columnas y luego realizar las estadísticas a continuación para más de una columna de taget. Por lo tanto, tener un método confiable para construir el marco de datos una fila a la vez fue una gran conveniencia. ¡Gracias @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)
usuario3250815
fuente
14

Descubrí una manera simple y agradable:

>>> 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
Qinsi
fuente
1
Tenga en cuenta que esto copiará todo el DataFrame debajo del capó. Las matrices subyacentes no se pueden extender, por lo que deben copiarse.
impermeable
10

Puede usar el objeto generador para crear un Marco de datos, que será más eficiente en la memoria en la lista.

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'))

Para agregar datos en bruto al DataFrame existente, puede usar el método append.

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

Cree un nuevo registro (marco de datos) y agréguelo a old_data_frame .
pasar la lista de valores y los nombres de columna correspondientes para crear un nuevo registro (marco_de_datos)

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])
Jack Daniel
fuente
8

Aquí está la forma de agregar / agregar una fila en 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]) 

Se puede usar para insertar / anexar una fila en pandas vacíos o poblados DataFrame

Shivam Agrawal
fuente
1
esto se agrega con el índice en orden descendente
Parthiban Rajendran
5

En lugar de una lista de diccionarios como en la respuesta de ShikharDua, también podemos representar nuestra tabla como un diccionario de listas , donde cada lista almacena una columna en orden de fila, dado que conocemos nuestras columnas de antemano. Al final construimos nuestro DataFrame una vez.

Para c columnas y n filas, esto usa 1 diccionario y c listas, versus 1 lista y n diccionarios. El método de la lista de diccionarios tiene cada diccionario almacenando todas las claves y requiere crear un nuevo diccionario para cada fila. Aquí solo agregamos listas, que es tiempo constante y teóricamente muy rápido.

# 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
qwr
fuente
5

si desea agregar una fila al final, agréguela como una lista

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

Otra forma de hacerlo (probablemente no muy eficiente):

# 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))

También puede mejorar la clase DataFrame de esta manera:

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

Todo lo que necesitas es loc[df.shape[0]]oloc[len(df)]


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

o

df.loc[len(df)] = ['col1Value', 100, 'col3Value', False] 
Giorgos Myrianthous
fuente
1

Hazlo simple. Al tomar la lista como entrada que se agregará como fila en el marco de datos: -

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)
Vineet Jain
fuente
0

A menudo vemos la construcción df.loc[subscript] = …para asignar a una fila de DataFrame. Mikhail_Sam publicó puntos de referencia que contienen, entre otros, esta construcción, así como el método que usa dict y crear DataFrame al final . Encontró que este último era el más rápido con diferencia. Pero si reemplazamos el df3.loc[i] = …(con DataFrame preasignado) en su código con df3.values[i] = …, el resultado cambia significativamente, ya que ese método funciona de manera similar al que usa dict. Por lo tanto, debemos tener más en cuenta el uso de df.values[subscript] = …. Sin embargo, tenga en cuenta que .valuestoma un subíndice basado en cero, que puede ser diferente del DataFrame.index.

Armali
fuente
un código de ejemplo de esto sería útil
baxx
1
@baxx: un ejemplo de código está en el enlace de los puntos de referencia ( # .loc with prealloc), otro ejemplo está en la pregunta que tengo que comparar los datos de cada fila de un Pandas DataFrame con los datos del resto de las filas, ¿hay alguna manera de acelerar el cálculo? ? y su respuesta aceptada.
Armali
0

pandas.DataFrame.append

DataFrame.append (self, other, ignore_index = False, verificar_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)

Con ignore_index establecido en True:

df.append(df2, ignore_index=True)
kamran kausar
fuente
0

antes de agregar una fila, tenemos que convertir el marco de datos al diccionario, allí puede ver las claves como columnas en el marco de datos y los valores de las columnas se almacenan nuevamente en el diccionario, pero la clave para cada columna es el número de índice en el marco de datos. Esa idea me hace escribir el siguiente código.

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
srikanth Gattu
fuente
0

Puede concatenar dos DataFrames para esto. Básicamente me encontré con este problema para agregar una nueva fila a un DataFrame existente con un índice de caracteres (no numérico). Entonces, ingreso los datos para una nueva fila en un ducto () y el índice en una lista.

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])
hansrajSwapnil
fuente
-1

Esto se encargará de agregar un elemento a un DataFrame vacío. El problema es que df.index.max() == nanpara el primer índice:

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)]
tomatom
fuente