Adicione uma linha ao pandas DataFrame

871

Eu entendo que os pandas são projetados para carregar totalmente preenchidos DataFrame mas preciso criar um DataFrame vazio e adicionar linhas, uma a uma . Qual é a melhor maneira de fazer isso?

Criei com sucesso um DataFrame vazio com:

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

Depois, posso adicionar uma nova linha e preencher um campo com:

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

Funciona, mas parece muito estranho: - / (falha ao adicionar valor da string)

Como posso adicionar uma nova linha ao meu DataFrame (com diferentes tipos de colunas)?

PhE
fonte
70
Observe que essa é uma maneira muito ineficiente de criar um DataFrame grande; novas matrizes precisam ser criadas (copiando os dados existentes) quando você anexa uma linha.
Wes McKinney
5
@WesMcKinney: Thx, isso é muito bom saber. É muito rápido adicionar colunas a tabelas enormes?
Max
4
Se for muito ineficiente para você, você pode pré-alocar uma linha adicional e atualizá-la.
user1154664

Respostas:

570
>>> 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
fonte
25
Considere adicionar o índice a memória preallocate (ver minha resposta)
FooBar
34
@ MaximG: Eu recomendo fortemente uma atualização. A versão atual do Pandas é 0.15.0.
fred
44
.locestá referenciando a coluna de índice, portanto, se você estiver trabalhando com um DataFrame preexistente com um índice que não seja uma sequência contínua de números inteiros começando com 0 (como no seu exemplo), .locsubstituirá as linhas existentes ou inserirá linhas, ou crie lacunas no seu índice. Uma abordagem mais robusta (mas não infalível) para anexar um dataframe de tamanho diferente de zero existente seria: df.loc[df.index.max() + 1] = [randint(...ou pré-preencher o índice como o @FooBar sugeriu.
hobs
4
@hobs df.index.max()é nanquando o DataFrame está vazio.
flow2k
4
@hobs Uma solução Pensei está usando o operador ternário:df.loc[0 if pd.isnull(df.index.max()) else df.index.max() + 1]
flow2k
475

Caso você possa obter todos os dados para o quadro de dados antecipadamente, existe uma abordagem muito mais rápida do que anexar a um quadro de dados:

  1. Crie uma lista de dicionários nos quais cada dicionário corresponde a uma linha de dados de entrada.
  2. Crie um quadro de dados a partir desta lista.

Eu tive uma tarefa semelhante para a qual o acréscimo a um quadro de dados linha por linha levou 30 minutos e a criação de um quadro de dados a partir de uma lista de dicionários concluídos em 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
fonte
48
Também mudei para isso em qualquer situação em que não posso obter todos os dados com antecedência. A diferença de velocidade é surpreendente.
fantabolous
47
Copiando de documentos do 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
Isso funciona muito bem! Exceto quando eu criei o quadro de dados, os nomes das colunas estavam todos na ordem errada ...
user5359531
5
@ user5359531 Você pode usar dict ordenada nesse caso
ShikharDua
21
@ user5359531 Você pode especificar manualmente as colunas e a ordem será preservada. pd.DataFrame (linhas_list, colunas = ['C1', 'C2', 'C3']) fará o truque
Marcello Grechi Lins
288

Você poderia usar pandas.concat()ou DataFrame.append(). Para detalhes e exemplos, consulte Mesclar, ingressar e concatenar .

NPE
fonte
6
Oi, então qual é a resposta para os métodos usando append () ou concat (). Eu tenho o mesmo problema, mas ainda estou tentando descobrir.
Noticia2020
109
Esta é a resposta certa, mas não é uma resposta muito boa (quase apenas link).
Jwg 18/05
5
Acho que a resposta de @ fred é mais correta. O problema com esta resposta é que ele desnecessariamente copia todo o DataFrame toda vez que uma linha é anexada. Usando o .locmecanismo que pode ser evitado, especialmente se você for cuidadoso.
Ken Williams
7
Mas se você quiser usar DataFrame.append(), verifique se os dados da linha também são um DataFrame em primeiro lugar, não uma lista.
StayFoolish
202

Já faz muito tempo, mas eu também enfrentei o mesmo problema. E encontrei aqui muitas respostas interessantes. Então, eu estava confuso qual método usar.

No caso de adicionar muitas linhas ao dataframe, eu me interessava em acelerar o desempenho . Então, tentei os 4 métodos mais populares e verifiquei a velocidade deles.

ATUALIZADO EM 2019 usando novas versões de pacotes. Também atualizado após o comentário do @FooBar

DESEMPENHO DA VELOCIDADE

  1. Usando .append ( resposta do NPE )
  2. Usando .loc ( resposta de fred )
  3. Usando .loc com pré-alocação ( resposta do FooBar )
  4. Usando dict e criar DataFrame no final ( resposta de ShikharDua )

Resultados (em 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     |
|------------|-------------|-------------|-------------|

Também obrigado a @krassowski pelo comentário útil - eu atualizei o código.

Então, eu uso adição através do dicionário para mim.


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)

PS Creio que minha realização não é perfeita e talvez haja alguma otimização.

Mikhail_Sam
fonte
4
O uso de df2.index.max()for .locdesnecessariamente aumenta a complexidade computacional. Simples df2.loc[i] = ...faria. Para mim, reduziu o tempo de 10s para 8.64s
Krassowski
Remova meu nome da lista, pois você não está seguindo minha abordagem em seu teste: Você não está pré-alocando a memória fornecendo um índice de tamanho adequado.
21419 FooBar
@FooBar Hi! Fico feliz que, como o autor viu minha resposta :) você está certo, eu perdi esse ponto importante. Prefiro adicionar mais uma linha à minha tabela de resultados, pois sua abordagem mostra o resultado diferente!
Mikhail_Sam
@Mikhail_Sam Como você usaria a tabela dinâmica para escrevê-la em um arquivo do Excel usando o método mais rápido, dict?
FabioSpaghetti
1
Só queria lançar outro comentário sobre por que o DataFrame do Dict to Pandas é uma maneira melhor. Na minha experiência com um conjunto de dados que possui vários tipos de dados diferentes na tabela, o uso dos métodos de adição do Pandas destrói a digitação, enquanto o uso de um Dict e a criação do DataFrame apenas uma vez, parece manter intactos os tipos de dados originais.
trumpetlicks
109

Se você souber o número de entradas ex ante, você deve pré-alocar o espaço também fornecendo o índice (usando o exemplo de dados de uma resposta 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

Comparação de velocidade

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

E - a partir dos comentários - com um tamanho de 6000, a diferença de velocidade se torna ainda maior:

Aumentar o tamanho da matriz (12) e o número de linhas (500) torna a diferença de velocidade mais impressionante: 313ms vs 2,29s

FooBar
fonte
3
Ótima resposta. Essa deve ser a norma para que o espaço da linha não precise ser alocado de forma incremental.
Ely
8
O aumento do tamanho da matriz (12) e o número de linhas (500) faz com que a diferença de velocidade mais impressionante: 313ms vs 2.29s
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
Lydia
fonte
2
Este! Estou pesquisando há um bom tempo, e este é o primeiro post que realmente mostra como atribuir valores específicos a uma linha! Pergunta de bônus: Qual é a sintaxe para os pares nome-coluna / valor? Eu acho que deve ser algo usando um ditado, mas não consigo acertar.
Jhin
3
isso não é eficiente, pois na verdade copia todo o DataFrame quando você o estende.
impermeável
72

Para anexar com eficiência, consulte Como adicionar uma linha extra a um dataframe do pandas e Setting With Enlargement .

Adicione linhas loc/ixnos dados do índice de chave não existentes . por exemplo :

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
Nasser Al-Wohaibi
fonte
Os usuários solicitaram o implemento (adicione uma nova linha). Aqui vemos como adicionar uma linha em um índice definido ou adicionar uma coluna.
Guilherme Felipe Reis
1
qualquer referência sobre como isso funciona em comparação com o método dict
PirateApp
isso não é eficiente, pois na verdade copia todo o DataFrame.
impermeável
66

Você pode anexar uma única linha como um dicionário usando a ignore_indexopção

>>> 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
fonte
37
Você também pode mencionar que f.append(<stuff>)cria um novo objeto, em vez de simplesmente acrescentar ao objeto atual no lugar, por isso, se você está tentando anexar a uma trama de dados em um script, você precisa dizerf = f.append(<stuff>)
Blairg23
2
existe uma maneira de fazer isso no lugar?
lol
@lol não. consulte github.com/pandas-dev/pandas/issues/2801 - as matrizes subjacentes não podem ser estendidas, portanto, elas precisam ser copiadas.
impermeável
46

Por uma questão de maneira pitônica, adicione aqui minha resposta:

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

Você também pode criar uma lista de listas e convertê-la em um quadro de dados -

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

    eu dobro quadrado
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
fonte
15

Esta não é uma resposta para a pergunta do OP, mas um exemplo de brinquedo para ilustrar a resposta do @ShikharDua acima da qual achei muito útil.

Embora esse fragmento seja trivial, nos dados reais eu tinha 1.000 linhas e muitas colunas e desejei poder agrupar por colunas diferentes e depois executar as estatísticas abaixo para mais de uma coluna de taget. Portanto, ter um método confiável para construir o quadro de dados uma linha por vez foi uma grande conveniência. Obrigado @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)
user3250815
fonte
14

Descobri uma maneira simples e agradável:

>>> 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
fonte
1
Observe que isso copiará todo o DataFrame sob o capô. As matrizes subjacentes não podem ser estendidas, portanto, elas precisam ser copiadas.
impermeável
10

Você pode usar o objeto gerador para criar o Dataframe, que terá mais eficiência de memória na 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 adicionar dados brutos ao DataFrame existente, você pode usar o método append.

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

Crie um novo registro (quadro de dados) e adicione a old_data_frame .
passar lista de valores e nomes de colunas correspondentes para criar um novo_record (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])
Jack Daniel
fonte
8

Aqui está a maneira de adicionar / acrescentar uma linha em 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]) 

Pode ser usado para inserir / acrescentar uma linha no DataFrame de pandas vazios ou preenchidos

Shivam Agrawal
fonte
1
esta é a adição com o índice em ordem decrescente
Parthiban Rajendran
5

Em vez de uma lista de dicionários, como na resposta do ShikharDua, também podemos representar nossa tabela como um dicionário de listas , onde cada lista armazena uma coluna em ordem de linha, desde que conheçamos nossas colunas de antemão. No final, construímos nosso DataFrame uma vez.

Para colunas c e n linhas, isso usa 1 dicionário ec listas, versus 1 lista e n dicionários. O método da lista de dicionários tem cada dicionário armazenando todas as chaves e requer a criação de um novo dicionário para cada linha. Aqui, apenas anexamos as listas, que são tempo constante e teoricamente muito rápidas.

# 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
fonte
5

se você quiser adicionar uma linha no final, adicione-a como uma lista

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

Outra maneira de fazer isso (provavelmente não muito bom desempenho):

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

Você também pode aprimorar a classe DataFrame assim:

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

Simplifique. Tomando a lista como entrada, que será anexada como linha no quadro de dados: -

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

Tudo que você precisa é 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] 
Giorgos Myrianthous
fonte
0

Geralmente vemos a construção df.loc[subscript] = …a ser atribuída a uma linha do DataFrame. Mikhail_Sam publicou benchmarks contendo, entre outros, esse construto, bem como o método usando dict e criar DataFrame no final . Ele considerou o último o mais rápido de longe. Mas se substituirmos o df3.loc[i] = …(com DataFrame pré-alocado) em seu código por df3.values[i] = …, o resultado será alterado significativamente, pois esse método terá um desempenho semelhante ao do dict. Portanto, devemos considerar com mais frequência o uso de df.values[subscript] = …. No entanto, observe que é .valuesnecessário um subscrito com base em zero, que pode ser diferente do DataFrame.index.

Armali
fonte
um exemplo de código disso seria útil
baxx 06/02
1
@baxx - Um exemplo de código está no link de benchmarks ( # .loc with prealloc), outro exemplo está na pergunta que eu tenho que comparar dados de cada linha de um DataFrame do Pandas com dados do restante das linhas, existe uma maneira de acelerar o cálculo ? e sua resposta aceita.
Armali 07/02
0

pandas.DataFrame.append

DataFrame.append (próprio, outro, ignore_index = False, verifique a integridade = False, classifique = 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)

Com ignore_index definido como True:

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

Antes de adicionar uma linha, temos que converter o quadro de dados em dicionário. Você pode ver as chaves como colunas no quadro de dados e os valores das colunas são novamente armazenados no dicionário, mas a chave de cada coluna é o número do índice no quadro de dados. Essa ideia me fez escrever o código abaixo.

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

Você pode concatenar dois DataFrames para isso. Basicamente, deparei-me com esse problema para adicionar uma nova linha a um DataFrame existente com um índice de caracteres (não numérico). Então, insiro os dados para uma nova linha em um duto () e indexo em uma 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
fonte
-1

Isso cuidará da adição de um item a um DataFrame vazio. A questão é que, df.index.max() == nanpara o primeiro í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
fonte