¿Cómo escribir en un archivo de Excel existente sin sobrescribir datos (usando pandas)?

120

Utilizo pandas para escribir en un archivo de Excel de la siguiente manera:

import pandas

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

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

writer.save()

Masterfile.xlsx ya consta de varias pestañas diferentes. Sin embargo, todavía no contiene "Main".

Pandas escribe correctamente en la hoja "Principal", desafortunadamente también elimina todas las demás pestañas.

BP_
fuente
1
¿Puede dar un ejemplo o ExcelReader? No he encontrado nada como esto en la documentación.
BP_
1
Creo que no existe ExcelReader en pandas. Utilizo read_excel para leer datos de Excel. No creo que se ahorrarían datos para sobresalir.
BP_
1
@nrathaus no parece haber unExcelReader
virtualxtc
Tenga en cuenta que hay cierta confusión en las respuestas acerca de qué es exactamente la pregunta. Algunas respuestas asumen que "Principal" aún no existe, y el OP simplemente agrega una nueva hoja a un libro de Excel existente. Otros asumen que "Main" ya existe, y que el OP quiere agregar nuevos datos al final de "Main".
TC Proctor

Respuestas:

143

Pandas docs dice que usa openpyxl para archivos xlsx. Un vistazo rápido al código en ExcelWriterda una pista de que algo como esto podría 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()
Esquí
fuente
2
¿Puede explicar para qué sirve Writer.sheets?
BP_
5
ExcelWriter por alguna razón usa esta variable para acceder a la hoja. Si lo deja vacío, no sabrá que la hoja Main ya está allí y creará una nueva hoja.
Esquí
2
Esta solución funciona bien. Sin embargo, tiene un inconveniente. Rompe fórmulas y conexiones dentro de la hoja de cálculo. ¿Alguna idea de cómo cambiar este comportamiento?
BP_
1
¿Qué es exactamente lo que se rompe ...? Puede hacer esto como una pregunta separada y etiquetarlo openpyxly proporcionar suficientes detalles: qué tipo de fórmulas tiene, cómo se actualizan los datos, cómo frenan las fórmulas. Ahora simplemente no puedo ayudar, hay demasiadas cosas que no sé.
Esquí
2
¿se puede usar con archivos .xlsm en su lugar?
dapaz
39

Aquí hay una función 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, reemplace sheet_namecon sheetname!

Ejemplos 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)
MaxU
fuente
1
Esta solución funcionó perfectamente para mí, las otras publicadas aquí no funcionan. ¡Muchas gracias! Solo un comentario: cuando el archivo no existe, aparece un error "NameError: el nombre global 'FileNotFoundError' no está definido"
cholo14
1
@ cholo14, ¡gracias por señalar esto! Lo probé en Python 3.x, así que me perdí ese error. Lo arreglé en la respuesta ...
MaxU
1
Esto funcionó para mí, pero ¿hay alguna manera de mantener el formato xlsx (del archivo xlsx original)?
2uno
@ 2one, no sé exactamente - pruébalo o
haz
¿Hay alguna forma de escribir en columnas en lugar de solo en filas? Como si quisiera actualizar una hoja automáticamente, pero sin agregar nuevas filas, ¡sino columnas, gracias!
doomdaam
21

Con la openpyxlversión 2.4.0y la pandasversión 0.19.2, el proceso que se le ocurrió a @ski se vuelve un poco más simple:

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!
mvbentes
fuente
11
Esto no me funciona. Si ya existe una hoja de trabajo "Principal", creará una nueva llamada "Main1" con los nuevos datos solamente y dejará el contenido de la hoja de trabajo "Principal" sin cambios.
Qululu
3
@Qululu Creo que podría haber confusión sobre esta pregunta entre dos objetivos diferentes. Esto le permite agregar hojas adicionales a un libro existente. Se no pretende añadir datos adicionales a una hoja existente. Si hay un conflicto de nombres de hojas, cambia el nombre de la hoja. Esto es una característica, no un error.
TC Proctor
Como dijo @Qululu, esto solo crea más hojas, con diferentes nombres. La primera solución, de MaxU funciona, y el resultado que obtendrá será el df en la primera hoja, tantas veces como desee (esto es, con los encabezados multiplicados tantas veces también). Una técnica simple: cada iteración anexas el marco de datos a una lista. Al final solo necesitas concat. Si siguen la misma estructura funcionará como un encanto. list_my_dfs = [df1, df2, ...] # Lista de sus marcos de datos my_dfs_together = pd.concat (list_my_df) # concat mis marcos de datos en un solo df
Susana Silva Santos
@SusanaSilvaSantos, eche un vistazo a lo que comentó TC Proctor justo antes que usted. El OP quería agregar una hoja de trabajo inexistente a un libro de trabajo existente. Este código hace eso. Agregar datos a una hoja existente dentro del libro de trabajo no era parte del alcance. Si no es necesario, será suficiente.
mvbentes
16

A partir de pandas 0.24, puede simplificar esto con el modeargumento de palabra clave de ExcelWriter:

import pandas as pd

with pd.ExcelWriter('the_file.xlsx', engine='openpyxl', mode='a') as writer: 
     data_filtered.to_excel(writer) 
Will Ayd
fuente
3
sobrescribe para mí.
keramat
10
@keramat Creo que puede haber confusión en esta pregunta entre dos objetivos diferentes. Esto le permite agregar hojas adicionales a un libro existente. Se no pretende añadir datos adicionales a una hoja existente.
TC Proctor
1
mode = 'a'agrega más hojas, pero ¿qué sucede si quiero sobrescribir datos en las hojas existentes?
Confundido el
11

Antigua pregunta, pero supongo que algunas personas todavía buscan esto, así que ...

Encuentro este método agradable porque todas las hojas de trabajo se cargan en un diccionario de pares de nombre de hoja y marco de datos, creado por pandas con la opción sheetname = None. Es simple agregar, eliminar o modificar hojas de trabajo entre leer la hoja de cálculo en el formato dict y escribirla desde el dict. Para mí, xlsxwriter funciona mejor que openpyxl para esta tarea en particular en términos de velocidad y formato.

Nota: las versiones futuras de pandas (0.21.0+) cambiarán el parámetro "sheetname" a "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 el ejemplo de la pregunta 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)
b2002
fuente
Esto funcionó, sin embargo, mis celdas combinadas, colores de celda y anchos de celda no se conservaron.
virtualxtc
1
Sí, con este método, ese tipo de formato se perderá porque cada hoja de trabajo se convierte en un marco de datos de pandas (sin ningún formato de Excel), luego se convierte de marcos de datos a hojas de trabajo dentro de un nuevo libro de trabajo de Excel (que tiene el mismo nombre que el original expediente). ¿Parece que un nuevo método de "agregar" usando openpyxl podría estar disponible para preservar el formato de la hoja de trabajo del archivo original? github.com/pandas-dev/pandas/pull/21251
b2002
11

Sé que este es un hilo más antiguo, pero este es el primer elemento que encuentra al buscar, y las soluciones anteriores no funcionan si necesita retener gráficos en un libro de trabajo que ya ha creado. En ese caso, xlwings es una mejor opción: le permite escribir en el libro de Excel y mantiene los gráficos / datos del gráfico.

ejemplo simple:

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()
bola de carne
fuente
¿Hay alguna forma de crear un archivo si no existe primero?
Tinkinc
Sí, ¿exploraste los documentos? docs.xlwings.org/en/stable/api.html
flyingmeatball
wb = xw.Book (nombre de archivo) en su sitio web dice que crea un libro. pero no lo hace
Tinkinc
wb = xw.Book () crea un nuevo libro vacío, cuando le pasa una ruta está intentando cargar un libro existente.
Flyingmeatball
1
Nota: xlwings interactúa con una instancia en ejecución de Excel y, por lo tanto, no se ejecuta en Linux.
virtualxtc
5

Hay una mejor solución en pandas 0.24:

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

antes de:

ingrese la descripción de la imagen aquí

después:

ingrese la descripción de la imagen aquí

así que actualiza tus pandas ahora:

pip install --upgrade pandas
oveja negra
fuente
1
Este es un duplicado de esta respuesta anterior
TC Proctor
1
Solo un aviso para el futuro, esto no funciona con la XslxWriteropción.
metinsenturk
tampoco funciona de forma predeterminada, engine=openpyxlya que solo agregará una nueva hoja de trabajo llamadathe 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

Esto funciona perfectamente bien, lo único es que se pierde el formato del archivo maestro (archivo al que agregamos una nueva hoja).

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

Espero que el "keep_date_col" te ayude

Eduardo
fuente
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()
Pedro Machado
fuente
3
Si bien esto podría responder a la pregunta de los autores, carece de algunas palabras explicativas y / o enlaces a la documentación. Los fragmentos de código sin formato no son muy útiles sin algunas frases a su alrededor. También puede resultarle muy útil cómo escribir una buena respuesta . Edite su respuesta.
Roy Scheffers
0

Método:

  • Puede crear un archivo si no está presente
  • Agregar a Excel existente según el nombre de la hoja
import pandas as pd
from openpyxl import load_workbook

def write_to_excel(df, file):
    try:
        book = load_workbook(file)
        writer = pd.ExcelWriter(file, engine='openpyxl') 
        writer.book = book
        writer.sheets = dict((ws.title, ws) for ws in book.worksheets)
        df.to_excel(writer, **kwds)
        writer.save()
    except FileNotFoundError as e:
        df.to_excel(file, **kwds)

Uso:

df_a = pd.DataFrame(range(10), columns=["a"])
df_b = pd.DataFrame(range(10, 20), columns=["b"])
write_to_excel(df_a, "test.xlsx", sheet_name="Sheet a", columns=['a'], index=False)
write_to_excel(df_b, "test.xlsx", sheet_name="Sheet b", columns=['b'])
BPPuneeth Pai
fuente