Como escrever em um arquivo Excel existente sem sobrescrever dados (usando o pandas)?


120

Eu uso o pandas para escrever arquivos do Excel da seguinte maneira:

import pandas

writer = pandas.ExcelWriter('Masterfile.xlsx') 

data_filtered.to_excel(writer, "Main", cols=['Diff1', 'Diff2'])

writer.save()

Masterfile.xlsx já consiste em várias guias diferentes. No entanto, ele ainda não contém "Principal".

O Pandas grava corretamente na página "Principal", mas infelizmente também exclui todas as outras guias.


1
você pode dar um exemplo ou ExcelReader? Não encontrei nada parecido com isso na documentação.
— BP_

1
Acho que não existe ExcelReader nos pandas. Eu uso read_excel para ler dados do Excel. Não acho que economizaria dados para o Excel.
— BP_

1
@nrathaus, não parece haver umExcelReader
— virtualxtc

Observe que há alguma confusão nas respostas sobre o que exatamente a pergunta está perguntando. Algumas respostas pressupõem que "Principal" ainda não existe e o OP está simplesmente adicionando uma nova planilha a uma pasta de trabalho do Excel existente. Outros presumem que "Principal" já existe e que o OP deseja acrescentar novos dados ao final de "Principal".
— TC Proctor

Respostas:


143

O Pandas docs diz que usa openpyxl para arquivos xlsx. Uma rápida olhada no código ExcelWriterdá uma pista de que algo assim pode funcionar:

import pandas
from openpyxl import load_workbook

book = load_workbook('Masterfile.xlsx')
writer = pandas.ExcelWriter('Masterfile.xlsx', engine='openpyxl') 
writer.book = book

## ExcelWriter for some reason uses writer.sheets to access the sheet.
## If you leave it empty it will not know that sheet Main is already there
## and will create a new sheet.

writer.sheets = dict((ws.title, ws) for ws in book.worksheets)

data_filtered.to_excel(writer, "Main", cols=['Diff1', 'Diff2'])

writer.save()

2
Você pode explicar para que serve o writer.sheets?
— BP_

5
O ExcelWriter, por algum motivo, usa essa variável para acessar a planilha. Se você deixá-lo vazio, ele não saberá que a planilha principal já está lá e criará uma nova planilha.
— Ski

2
Esta solução funciona bem. Porém, tem uma desvantagem. Ele quebra fórmulas e conexões dentro da planilha. Alguma ideia de como mudar esse comportamento?
— BP_

1
O que exatamente você quebra ..? Você pode fazer isso como uma pergunta separada e marcá-la com openpyxldetalhes suficientes: que tipo de fórmulas você possui, como os dados são atualizados, como eles quebram as fórmulas. Agora eu simplesmente não posso ajudar, muitas coisas que eu não sei.
— Ski

2
ele pode ser usado com arquivos .xlsm em vez disso?
— Dapaz

39

Aqui está uma função auxiliar:

def append_df_to_excel(filename, df, sheet_name='Sheet1', startrow=None,
                       truncate_sheet=False, 
                       **to_excel_kwargs):
    """
    Append a DataFrame [df] to existing Excel file [filename]
    into [sheet_name] Sheet.
    If [filename] doesn't exist, then this function will create it.

    Parameters:
      filename : File path or existing ExcelWriter
                 (Example: '/path/to/file.xlsx')
      df : dataframe to save to workbook
      sheet_name : Name of sheet which will contain DataFrame.
                   (default: 'Sheet1')
      startrow : upper left cell row to dump data frame.
                 Per default (startrow=None) calculate the last row
                 in the existing DF and write to the next row...
      truncate_sheet : truncate (remove and recreate) [sheet_name]
                       before writing DataFrame to Excel file
      to_excel_kwargs : arguments which will be passed to `DataFrame.to_excel()`
                        [can be dictionary]

    Returns: None
    """
    from openpyxl import load_workbook

    # ignore [engine] parameter if it was passed
    if 'engine' in to_excel_kwargs:
        to_excel_kwargs.pop('engine')

    writer = pd.ExcelWriter(filename, engine='openpyxl')

    # Python 2.x: define [FileNotFoundError] exception if it doesn't exist 
    try:
        FileNotFoundError
    except NameError:
        FileNotFoundError = IOError


    try:
        # try to open an existing workbook
        writer.book = load_workbook(filename)

        # get the last row in the existing Excel sheet
        # if it was not specified explicitly
        if startrow is None and sheet_name in writer.book.sheetnames:
            startrow = writer.book[sheet_name].max_row

        # truncate sheet
        if truncate_sheet and sheet_name in writer.book.sheetnames:
            # index of [sheet_name] sheet
            idx = writer.book.sheetnames.index(sheet_name)
            # remove [sheet_name]
            writer.book.remove(writer.book.worksheets[idx])
            # create an empty sheet [sheet_name] using old index
            writer.book.create_sheet(sheet_name, idx)

        # copy existing sheets
        writer.sheets = {ws.title:ws for ws in writer.book.worksheets}
    except FileNotFoundError:
        # file does not exist yet, we will create it
        pass

    if startrow is None:
        startrow = 0

    # write out the new sheet
    df.to_excel(writer, sheet_name, startrow=startrow, **to_excel_kwargs)

    # save the workbook
    writer.save()

NOTA: para Pandas <0.21.0, substitua sheet_namepor sheetname!

Exemplos de uso:

append_df_to_excel('d:/temp/test.xlsx', df)

append_df_to_excel('d:/temp/test.xlsx', df, header=None, index=False)

append_df_to_excel('d:/temp/test.xlsx', df, sheet_name='Sheet2', index=False)

append_df_to_excel('d:/temp/test.xlsx', df, sheet_name='Sheet2', index=False, startrow=25)

1
Esta solução funcionou perfeitamente para mim, as outras postadas aqui não funcionam. Muito obrigado! Apenas um comentário: quando o arquivo não existe, recebo um erro "NameError: nome global 'FileNotFoundError' não está definido"
— cholo14

1
@ cholo14, obrigado por apontar isso! Eu testei no Python 3.x, então não percebi o bug. Eu
— consertei

1
Isso funcionou para mim, mas há uma maneira de manter a formatação xlsx (do arquivo xlsx original)?
— 2 em

@ 2um, não sei exatamente - experimente ou faça uma nova pergunta SO
— MaxU

existe uma maneira de escrever em colunas em vez de apenas linhas? Como se eu quisesse atualizar uma planilha automaticamente, mas não acrescentando novas linhas, mas colunas, obrigado!
— doomdaam

21

Com a openpyxlversão 2.4.0e a pandasversão 0.19.2, o processo criado por @ski fica um pouco mais simples:

import pandas
from openpyxl import load_workbook

with pandas.ExcelWriter('Masterfile.xlsx', engine='openpyxl') as writer:
    writer.book = load_workbook('Masterfile.xlsx')
    data_filtered.to_excel(writer, "Main", cols=['Diff1', 'Diff2'])
#That's it!

11
Isso não funciona para mim. Se já houver uma planilha "Principal", ela criará uma nova chamada "Principal1" apenas com os novos dados e deixará o conteúdo da planilha "Principal" inalterado.
— Qululu

2
@Qululu Acho que pode haver confusão nesta questão entre dois objetivos diferentes. Isso permite que você adicione outras folhas a uma pasta de trabalho existente. É não pretende anexar dados adicionais a uma folha existente. Se houver um conflito de nomenclatura de planilha, ele renomeia a planilha. Este é um recurso, não um bug.
— TC Proctor de

Como @Qululu disse, isso só cria mais planilhas, com nomes diferentes. A primeira solução, da MaxU funciona, e a saída que você obterá, será o df na primeira planilha, quantas vezes você desejar (isto é, com cabeçalhos multiplicados quantas vezes também). Uma técnica simples: cada iteração você anexa o dataframe a uma lista. No final, você só precisa concatenar. Se seguirem a mesma estrutura vai funcionar como um encanto. list_my_dfs = [df1, df2, ...] # Lista de seus dataframes my_dfs_together = pd.concat (list_my_df) # concat meus dataframes em um único df
— Susana Silva Santos

@SusanaSilvaSantos, dê uma olhada no que TC Proctor comentou antes de você. O OP queria adicionar uma planilha inexistente a uma pasta de trabalho existente. Este código faz isso. Anexar dados a uma planilha existente na pasta de trabalho não fazia parte do escopo. Se isso não for necessário, será o suficiente.
— mvbentes

16

Começando no pandas 0,24, você pode simplificar isso com o modeargumento de palavra - chave de ExcelWriter:

import pandas as pd

with pd.ExcelWriter('the_file.xlsx', engine='openpyxl', mode='a') as writer: 
     data_filtered.to_excel(writer) 

3
sobrescreve para mim.
— keramat

10
@keramat Acho que pode haver confusão nesta questão entre dois objetivos diferentes. Isso permite que você adicione outras folhas a uma pasta de trabalho existente. É não pretende anexar dados adicionais a uma folha existente.
— TC Proctor de

1
mode = 'a'adiciona mais planilhas, mas e se eu quiser sobrescrever os dados nas planilhas existentes?
— Confundido em

11

Pergunta antiga, mas acho que algumas pessoas ainda procuram por isso - então ...

Acho esse método bom porque todas as planilhas são carregadas em um dicionário de pares de nome de planilha e dataframe, criado por pandas com a opção sheetname = None. É simples adicionar, excluir ou modificar planilhas entre a leitura da planilha no formato dict e a gravação do dicionário. Para mim, o xlsxwriter funciona melhor do que o openpyxl para esta tarefa específica em termos de velocidade e formato.

Nota: versões futuras do pandas (0.21.0+) irão alterar o parâmetro "sheetname" para "sheet_name".

# read a single or multi-sheet excel file
# (returns dict of sheetname(s), dataframe(s))
ws_dict = pd.read_excel(excel_file_path,
                        sheetname=None)

# all worksheets are accessible as dataframes.

# easy to change a worksheet as a dataframe:
mod_df = ws_dict['existing_worksheet']

# do work on mod_df...then reassign
ws_dict['existing_worksheet'] = mod_df

# add a dataframe to the workbook as a new worksheet with
# ws name, df as dict key, value:
ws_dict['new_worksheet'] = some_other_dataframe

# when done, write dictionary back to excel...
# xlsxwriter honors datetime and date formats
# (only included as example)...
with pd.ExcelWriter(excel_file_path,
                    engine='xlsxwriter',
                    datetime_format='yyyy-mm-dd',
                    date_format='yyyy-mm-dd') as writer:

    for ws_name, df_sheet in ws_dict.items():
        df_sheet.to_excel(writer, sheet_name=ws_name)

Para o exemplo da pergunta de 2013:

ws_dict = pd.read_excel('Masterfile.xlsx',
                        sheetname=None)

ws_dict['Main'] = data_filtered[['Diff1', 'Diff2']]

with pd.ExcelWriter('Masterfile.xlsx',
                    engine='xlsxwriter') as writer:

    for ws_name, df_sheet in ws_dict.items():
        df_sheet.to_excel(writer, sheet_name=ws_name)

Isso meio que funcionou, no entanto, minhas células mescladas, cores de células e larguras de células não foram preservadas.
— virtualxtc

1
Sim, com este método esse tipo de formatação será perdido porque cada planilha é convertida em um dataframe do pandas (sem nenhuma formatação do Excel), então convertida de dataframes para planilhas dentro de uma nova pasta de trabalho do Excel (que tem o mesmo nome do original Arquivo). Parece que um novo método "append" usando openpyxl pode ser lançado, o que pode preservar a formatação da planilha do arquivo original. github.com/pandas-dev/pandas/pull/21251
— b2002

11

Eu sei que este é um segmento mais antigo, mas este é o primeiro item que você encontra ao pesquisar, e as soluções acima não funcionam se você precisar manter gráficos em uma pasta de trabalho que você já criou. Nesse caso, o xlwings é a melhor opção - ele permite que você escreva para o livro do Excel e mantém os gráficos / dados do gráfico.

exemplo simples:

import xlwings as xw
import pandas as pd

#create DF
months = ['2017-01','2017-02','2017-03','2017-04','2017-05','2017-06','2017-07','2017-08','2017-09','2017-10','2017-11','2017-12']
value1 = [x * 5+5 for x in range(len(months))]
df = pd.DataFrame(value1, index = months, columns = ['value1'])
df['value2'] = df['value1']+5
df['value3'] = df['value2']+5

#load workbook that has a chart in it
wb = xw.Book('C:\\data\\bookwithChart.xlsx')

ws = wb.sheets['chartData']

ws.range('A1').options(index=False).value = df

wb = xw.Book('C:\\data\\bookwithChart_updated.xlsx')

xw.apps[0].quit()

Existe uma maneira de criar um arquivo se ele não existir primeiro?
— Tinkinc

Sim, você explorou os documentos? docs.xlwings.org/en/stable/api.html
— flyingmeatball

wb = xw.Book (nome do arquivo) em seu site informa que cria um livro. mas não
— Tinkinc

wb = xw.Book () cria um novo livro vazio, quando você passa um caminho para ele, você está tentando carregar um livro existente.
— Flyingmeatball de

1
Observação: o xlwings interage com uma instância em execução do Excel e, portanto, não é executado no Linux.
— virtualxtc

5

Existe uma solução melhor no pandas 0,24:

with pd.ExcelWriter(path, mode='a') as writer:
    s.to_excel(writer, sheet_name='another sheet', index=False)

antes:

insira a descrição da imagem aqui

depois de:

insira a descrição da imagem aqui

então atualize seus pandas agora:

pip install --upgrade pandas

1
Esta é uma duplicata desta resposta anterior
— TC Proctor

1
Apenas um alerta para o futuro, isso não funciona com a XslxWriteropção.
— metinsenturk

ele também não funciona por padrão engine=openpyxl, pois apenas adicionará uma nova planilha chamadathe only worksheet1
— Björn B

1
def append_sheet_to_master(self, master_file_path, current_file_path, sheet_name):
    try:
        master_book = load_workbook(master_file_path)
        master_writer = pandas.ExcelWriter(master_file_path, engine='openpyxl')
        master_writer.book = master_book
        master_writer.sheets = dict((ws.title, ws) for ws in master_book.worksheets)
        current_frames = pandas.ExcelFile(current_file_path).parse(pandas.ExcelFile(current_file_path).sheet_names[0],
                                                               header=None,
                                                               index_col=None)
        current_frames.to_excel(master_writer, sheet_name, index=None, header=False)

        master_writer.save()
    except Exception as e:
        raise e

Isso funciona perfeitamente bem, a única coisa é que a formatação do arquivo mestre (arquivo ao qual adicionamos uma nova folha) é perdida.


0
writer = pd.ExcelWriter('prueba1.xlsx'engine='openpyxl',keep_date_col=True)

O "keep_date_col" espero ajudá-lo


0
book = load_workbook(xlsFilename)
writer = pd.ExcelWriter(self.xlsFilename)
writer.book = book
writer.sheets = dict((ws.title, ws) for ws in book.worksheets)
df.to_excel(writer, sheet_name=sheetName, index=False)
writer.save()

3
Embora possa responder à pergunta dos autores, faltam algumas palavras explicativas e / ou links para a documentação. Trechos de código bruto não são muito úteis sem algumas frases em torno deles. Você também pode descobrir como escrever uma boa resposta muito útil. Edite sua resposta.
— Roy Scheffers
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.