Comment écrire dans un fichier Excel existant sans écraser les données (en utilisant des pandas)?


120

J'utilise des pandas pour écrire dans un fichier Excel de la manière suivante:

import pandas

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

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

writer.save()

Masterfile.xlsx se compose déjà de plusieurs onglets différents. Cependant, il ne contient pas encore "Main".

Pandas écrit correctement dans la feuille "Main", malheureusement il supprime également tous les autres onglets.


1
pouvez-vous donner un exemple ou ExcelReader? Je n'ai rien trouvé de tel dans la documentation.
BP_

1
Je pense qu'il n'y a pas une telle chose comme ExcelReader dans les pandas. J'utilise read_excel pour lire les données d'Excel. Je ne pense pas que cela sauverait des données pour exceller.
BP_

1
@nrathaus il ne semble pas y avoir deExcelReader
virtualxtc

Notez qu'il y a une certaine confusion dans les réponses sur ce que la question pose exactement. Certaines réponses supposent que «Main» n'existe pas encore et que l'OP ajoute simplement une nouvelle feuille à un classeur Excel existant. D'autres supposent que "Main" existe déjà, et que l'OP veut ajouter de nouvelles données au bas de "Main".
TC Proctor

Réponses:


143

La documentation Pandas dit qu'elle utilise openpyxl pour les fichiers xlsx. Un rapide coup d'œil à travers le code ExcelWriterdonne un indice que quelque chose comme ça pourrait fonctionner:

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
Pouvez-vous expliquer à quoi sert writer.sheets?
BP_

5
ExcelWriter pour une raison quelconque utilise cette variable pour accéder à la feuille. Si vous le laissez vide, il ne saura pas que la feuille principale est déjà là et créera une nouvelle feuille.
Ski

2
Cette solution fonctionne très bien. Il a cependant un inconvénient. Il rompt les formules et les connexions dans la feuille de calcul. Des idées pour changer ce comportement?
BP_

1
Qu'est-ce que tu te brises exactement ..? Vous pouvez poser cette question sous forme de question distincte openpyxl, y ajouter des balises et fournir suffisamment de détails: de quel type de formules disposez-vous, comment les données sont mises à jour, comment cela freine les formules. Maintenant, je ne peux pas m'empêcher, trop de choses que je ne sais pas.
Ski

2
peut-il être utilisé avec des fichiers .xlsm à la place?
dapaz

39

Voici une fonction d'assistance:

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

REMARQUE: pour Pandas <0.21.0, remplacez sheet_namepar sheetname!

Exemples d'utilisation:

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
Cette solution a parfaitement fonctionné pour moi, les autres publiées ici ne fonctionnent pas. Merci beaucoup! Juste un commentaire: lorsque le fichier n'existe pas, j'obtiens une erreur "NameError: le nom global 'FileNotFoundError' n'est pas défini"
cholo14

1
@ cholo14, merci de l'avoir signalé! Je l'ai testé sur Python 3.x, donc j'ai raté ce bug. Je l'ai corrigé dans la réponse ...
MaxU

1
Cela a fonctionné pour moi, mais existe-t-il un moyen de conserver le formatage xlsx (à partir du fichier xlsx d'origine)?
2 le

@ 2one, je ne sais pas exactement - essayez-le ou posez une nouvelle question SO
MaxU

existe-t-il un moyen d'écrire dans des colonnes au lieu de seulement des lignes? Comme je veux mettre à jour une feuille automatiquement, mais sans ajouter de nouvelles lignes, mais des colonnes merci!
doomdaam

21

Avec la openpyxlversion 2.4.0et la pandasversion 0.19.2, le processus proposé par @ski devient un peu plus 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!

11
Cela ne fonctionne pas pour moi. S'il existe déjà une feuille de calcul «principale», elle en créera une nouvelle appelée «Main1» avec les nouvelles données uniquement et laissera le contenu de la feuille de calcul «principale» inchangé.
Qululu

3
@Qululu Je pense qu'il pourrait y avoir confusion sur cette question entre deux objectifs différents. Cela vous permet d'ajouter des feuilles supplémentaires à un classeur existant. Il n'est pas prévu d'ajouter des données supplémentaires à une feuille existante. En cas de conflit de dénomination de feuille, il renomme la feuille. Ceci est une fonctionnalité, pas un bug.
TC Proctor

Comme l'a dit @Qululu, cela ne crée que plus de feuilles, avec des noms différents. La première solution, de MaxU fonctionne, et la sortie que vous obtiendrez, sera le df dans la première feuille, autant de fois que vous le souhaitez (c'est-à-dire avec des en-têtes multipliés autant de fois également.) Une technique simple: chaque itération vous ajoutez le dataframe à une liste. En fin de compte, vous n'avez qu'à concater. S'ils suivent la même structure fonctionnera comme un charme. list_my_dfs = [df1, df2, ...] # Liste de vos dataframes my_dfs_together = pd.concat (list_my_df) # concat mes dataframes en un seul df
Susana Silva Santos

@SusanaSilvaSantos, jetez un œil à ce que TC Proctor a commenté juste avant vous. L'OP voulait ajouter une feuille de calcul inexistante à un classeur existant. Ce code fait cela. L'ajout de données à une feuille existante dans le classeur ne faisait pas partie de l'étendue. Si cela n'est pas nécessaire, cela suffira.
mvbentes le

16

À partir de pandas 0.24, vous pouvez simplifier cela avec l' modeargument mot - clé de ExcelWriter:

import pandas as pd

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

3
écrase pour moi.
keramat

10
@keramat Je pense qu'il pourrait y avoir confusion sur cette question entre deux objectifs différents. Cela vous permet d'ajouter des feuilles supplémentaires à un classeur existant. Il n'est pas prévu d'ajouter des données supplémentaires à une feuille existante.
TC Proctor

1
mode = 'a'ajoute plus de feuilles, mais que faire si je veux écraser les données sur les feuilles existantes?
Confondu le

11

Ancienne question, mais je suppose que certaines personnes recherchent toujours cela - alors ...

Je trouve cette méthode agréable car toutes les feuilles de calcul sont chargées dans un dictionnaire de paires de noms de feuilles et de cadres de données, créé par des pandas avec l'option sheetname = None. Il est simple d'ajouter, de supprimer ou de modifier des feuilles de calcul entre la lecture de la feuille de calcul au format dict et sa réécriture à partir du dict. Pour moi, le xlsxwriter fonctionne mieux que openpyxl pour cette tâche particulière en termes de vitesse et de format.

Remarque: les futures versions de pandas (0.21.0+) changeront le paramètre "sheetname" en "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)

Pour l'exemple de la question 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)

Cela a fonctionné, cependant, mes cellules fusionnées, mes couleurs de cellule et ma largeur de cellule n'étaient pas conservées.
virtualxtc

1
Oui, avec cette méthode, ce type de mise en forme sera perdu car chaque feuille de calcul est convertie en un dataframe pandas (sans aucun de ce formatage Excel), puis converti des dataframes en feuilles de calcul dans un nouveau classeur Excel (qui porte le même nom que l'original fichier). Il semble qu'une nouvelle méthode "d'ajout" utilisant openpyxl peut être à venir qui pourrait préserver le formatage de la feuille de calcul du fichier d'origine? github.com/pandas-dev/pandas/pull/21251
b2002

11

Je sais qu'il s'agit d'un fil de discussion plus ancien, mais c'est le premier élément que vous trouvez lors de la recherche, et les solutions ci-dessus ne fonctionnent pas si vous devez conserver des graphiques dans un classeur que vous avez déjà créé. Dans ce cas, xlwings est une meilleure option - il vous permet d'écrire dans le livre Excel et de conserver les graphiques / données des graphiques.

exemple 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()

Existe-t-il un moyen de créer un fichier s'il n'existe pas en premier?
Tinkinc

Oui, avez-vous exploré la documentation? docs.xlwings.org/en/stable/api.html
flyingmeatball

wb = xw.Book (nom de fichier) sur leur site Web indique qu'il crée un livre. mais ça ne
marche

wb = xw.Book () crée un nouveau livre vide, lorsque vous lui passez un chemin, vous essayez de charger un livre existant.
flyingmeatball

1
Remarque: xlwings interagit avec une instance en cours d'exécution d'Excel et ne fonctionne donc pas sous Linux.
virtualxtc

5

Il existe une meilleure solution dans pandas 0.24:

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

avant:

entrez la description de l'image ici

après:

entrez la description de l'image ici

alors améliorez vos pandas maintenant:

pip install --upgrade pandas

1
Ceci est une copie de cette réponse précédente
TC Proctor

1
Juste un avertissement pour l'avenir, cela ne fonctionne pas avec l' XslxWriteroption.
metinsenturk

il ne fonctionne pas non plus par défaut avec engine=openpyxlcar il ajoutera simplement une nouvelle feuille de calcul appeléethe 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

Cela fonctionne parfaitement bien, la seule chose est que le formatage du fichier maître (fichier auquel nous ajoutons une nouvelle feuille) est perdu.


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

L'espoir "keep_date_col" vous aidera


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
Bien que cela puisse répondre à la question des auteurs, il manque des mots explicatifs et / ou des liens vers la documentation. Les extraits de code bruts ne sont pas très utiles sans quelques phrases autour d'eux. Vous pouvez également trouver comment rédiger une bonne réponse très utile. Veuillez modifier votre réponse.
Roy Scheffers

0

Méthode:

  • Peut créer un fichier s'il n'est pas présent
  • Ajouter à Excel existant selon le nom de la feuille
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)

Usage:

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