Jak pisać do istniejącego pliku Excela bez nadpisywania danych (używając pand)?

120

Używam pand, aby pisać do pliku Excela w następujący sposób:

import pandas

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

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

writer.save()

Masterfile.xlsx zawiera już kilka różnych zakładek. Jednak nie zawiera jeszcze „Main”.

Pandy poprawnie zapisują do arkusza "Głównego", niestety kasują też wszystkie inne zakładki.

BP_
źródło
1
czy możesz podać przykład lub ExcelReader? Nie znalazłem czegoś takiego w dokumentacji.
BP_
1
Myślę, że w pandach nie ma czegoś takiego jak ExcelReader. Do odczytu danych z programu Excel używam read_excel. Nie sądzę, aby zapisywał dane w celu osiągnięcia doskonałości.
BP_
1
@nrathaus nie wydaje się byćExcelReader
virtualxtc
Zwróć uwagę, że odpowiedzi na pytanie, o co dokładnie chodzi, są niejasne. Niektóre odpowiedzi zakładają, że „Główny” jeszcze nie istnieje, a PO po prostu dodaje nowy arkusz do istniejącego skoroszytu programu Excel. Inni zakładają, że „Główny” już istnieje i że PO chce dołączyć nowe dane na dole „Głównego”.
TC Proctor

Odpowiedzi:

143

Pandas Docs twierdzi, że używa openpyxl dla plików xlsx. Szybkie przejrzenie kodu ExcelWriterdaje wskazówkę, że może się udać coś takiego:

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()
Narty
źródło
2
Czy możesz wyjaśnić, do czego służy writer.sheets?
BP_
5
ExcelWriter z jakiegoś powodu używa tej zmiennej, aby uzyskać dostęp do arkusza. Jeśli zostawisz to pole puste, nie będzie wiedział, że arkusz Główny już tam jest i utworzy nowy arkusz.
Ski
2
To rozwiązanie działa dobrze. Ma jednak jedną wadę. Łamie formuły i połączenia w arkuszu kalkulacyjnym. Jakieś pomysły, jak zmienić to zachowanie?
BP_
1
Co dokładnie się zepsułeś ..? Możesz zadać to jako osobne pytanie i oznaczyć je tagiem openpyxli podać wystarczającą ilość szczegółów: jakie masz formuły, w jaki sposób dane są aktualizowane, w jaki sposób hamuje formuły. Teraz po prostu nie mogę pomóc, zbyt wiele rzeczy nie wiem.
Ski
2
czy można go używać z plikami .xlsm?
dapaz
40

Oto funkcja pomocnicza:

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

UWAGA: dla pand <0.21.0 wymienić sheet_namez sheetname!

Przykłady użycia:

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
źródło
1
To rozwiązanie działało idealnie dla mnie, inne zamieszczone tutaj nie działają. Wielkie dzięki! Tylko jeden komentarz: gdy plik nie istnieje, pojawia się błąd „NameError: nazwa globalna„ FileNotFoundError ”nie jest zdefiniowana”
cholo14
1
@ cholo14, dziękujemy za wskazanie tego! Przetestowałem to na Pythonie 3.x, więc przegapiłem ten błąd.
Poprawiłem
1
To zadziałało, ale czy istnieje sposób na zachowanie formatowania xlsx (z oryginalnego pliku xlsx)?
2one
@ 2one, nie wiem dokładnie - spróbuj lub zadaj nowe pytanie SO
MaxU
czy istnieje sposób zapisywania w kolumnach zamiast tylko do wierszy? Tak jak chcę automatycznie aktualizować arkusz, ale nie dołączać nowych wierszy, ale dzięki kolumnom!
doomdaam
21

W przypadku openpyxlwersji 2.4.0i pandaswersji 0.19.2proces, który wymyślił @ski, staje się nieco prostszy:

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
źródło
11
To nie działa na mnie. Jeśli istnieje już arkusz „Główny”, utworzy on nowy o nazwie „Główny1” zawierający tylko nowe dane i pozostawi niezmienioną zawartość arkusza „Główny”.
Qululu,
3
@Qululu Myślę, że w tej kwestii może być niejasność między dwoma różnymi celami. Pozwala to na dodanie dodatkowych arkuszy do istniejącego skoroszytu. To jest nie przeznaczone do dołączania dodatkowych danych do istniejącego arkusza. Jeśli występuje konflikt nazw arkuszy, zmienia nazwę arkusza. To jest funkcja, a nie błąd.
TC Proctor
Jak powiedział @Qululu, tworzy to tylko więcej arkuszy o różnych nazwach. Pierwszym rozwiązaniem, z MaxU Works, a wynik, który otrzymasz, będzie df w pierwszym arkuszu, tyle razy, ile chcesz (to znaczy z nagłówkami mnożonymi tyle razy). Jedna prosta technika: każda iteracja dołączasz ramkę danych do listy. W końcu wystarczy konkatować. Jeśli podążają za tą samą strukturą, będą działać jak urok. list_my_dfs = [df1, df2, ...] # Lista twoich ramek danych my_dfs_together = pd.concat (list_my_df) # concat my dataframes in a single df
Susana Silva Santos
@SusanaSilvaSantos, zobacz, co skomentował TC Proctor tuż przed tobą. OP chciał dodać nieistniejący arkusz do istniejącego skoroszytu. Ten kod to robi. Dołączanie danych do istniejącego arkusza w skoroszycie nie było częścią zakresu. Jeśli to nie jest potrzebne, wystarczy.
mvbentes
16

Począwszy od pandy 0.24, możesz to uprościć za pomocą modeargumentu słowa kluczowego ExcelWriter:

import pandas as pd

with pd.ExcelWriter('the_file.xlsx', engine='openpyxl', mode='a') as writer: 
     data_filtered.to_excel(writer) 
Will Ayd
źródło
3
nadpisuje dla mnie.
keramat
10
@keramat Myślę, że w tej kwestii może być niejasność między dwoma różnymi celami. Pozwala to na dodanie dodatkowych arkuszy do istniejącego skoroszytu. To jest nie przeznaczone do dołączania dodatkowych danych do istniejącego arkusza.
TC Proctor
1
mode = 'a'dodaje więcej arkuszy, ale co jeśli chcę nadpisać dane w istniejących arkuszach?
Zakłopotany
11

Stare pytanie, ale zgaduję, że niektórzy wciąż tego szukają - więc ...

Uważam tę metodę za fajną, ponieważ wszystkie arkusze są ładowane do słownika par nazw arkuszy i par ramek danych, utworzonych przez pandy z opcją Sheetname = None. Dodawanie, usuwanie lub modyfikowanie arkuszy roboczych między odczytem arkusza kalkulacyjnego w formacie dykta a zapisaniem go z powrotem z dyktowania jest proste. Dla mnie xlsxwriter działa lepiej niż openpyxl w tym konkretnym zadaniu pod względem szybkości i formatu.

Uwaga: przyszłe wersje pand (0.21.0+) zmienią parametr „nazwa arkusza” na „nazwa_arkusza”.

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

Na przykład w pytaniu z 2013 roku:

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
źródło
Ten rodzaj zadziałał, jednak moje scalone komórki, kolory i szerokości komórek nie zostały zachowane.
virtualxtc
1
Tak, dzięki tej metodzie ten typ formatowania zostanie utracony, ponieważ każdy arkusz roboczy jest konwertowany na ramkę danych pandy (bez formatowania programu Excel), a następnie konwertowany z ramek danych na arkusze w nowym skoroszycie programu Excel (który ma taką samą nazwę jak oryginał plik). Wygląda na to, że może pojawić się nowa metoda „dołączania” wykorzystująca openpyxl, która może zachować oryginalne formatowanie arkusza plików? github.com/pandas-dev/pandas/pull/21251
b2002
11

Wiem, że to starszy wątek, ale jest to pierwsza pozycja, którą znajdziesz podczas wyszukiwania, a powyższe rozwiązania nie działają, jeśli musisz zachować wykresy w skoroszycie, który już utworzyłeś. W takim przypadku xlwings jest lepszą opcją - pozwala pisać do książki Excela i zachowuje wykresy / dane wykresów.

prosty przykład:

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()
latająca kulka mięsna
źródło
Czy istnieje sposób na utworzenie pliku, jeśli nie istnieje jako pierwszy?
Tinkinc
Tak, czy przeglądałeś dokumenty? docs.xlwings.org/en/stable/api.html
flyingmeatball
wb = xw.Book (nazwa pliku) na swojej stronie internetowej podaje, że tworzy książkę. ale tak nie jest
Tinkinc
wb = xw.Book () tworzy nową, pustą książkę, gdy przekazujesz jej ścieżkę, którą próbujesz załadować istniejącą książkę.
flyingmeatball
1
Uwaga: xlwings współdziała z uruchomionym wystąpieniem programu Excel i dlatego nie działa w systemie Linux.
virtualxtc
5

W pandach 0.24 jest lepsze rozwiązanie:

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

przed:

wprowadź opis obrazu tutaj

po:

wprowadź opis obrazu tutaj

więc ulepsz teraz swoje pandy:

pip install --upgrade pandas
czarna Owca
źródło
1
To jest duplikat tej wcześniejszej odpowiedzi
TC Proctor
1
To tylko ostrzeżenie na przyszłość, ale ta XslxWriteropcja nie działa .
metinsenturk
domyślnie nie działa, engine=openpyxlponieważ po prostu doda nowy arkusz o nazwiethe 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

Działa to doskonale, tylko że formatowanie głównego pliku (pliku, do którego dodajemy nowy arkusz) zostaje utracone.

Manish Mehra
źródło
0
writer = pd.ExcelWriter('prueba1.xlsx'engine='openpyxl',keep_date_col=True)

Nadzieja "keep_date_col" ci pomoże

Edward
źródło
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
źródło
3
Chociaż może to odpowiedzieć na pytanie autorów, brakuje w nim słów wyjaśniających i / lub linków do dokumentacji. Fragmenty surowego kodu nie są zbyt pomocne bez otaczających je wyrażeń. Może się również okazać, że bardzo pomocne może być napisanie dobrej odpowiedzi . Zmień swoją odpowiedź.
Roy Scheffers
0

Metoda:

  • Może utworzyć plik, jeśli go nie ma
  • Dołącz do istniejącego programu Excel zgodnie z nazwą arkusza
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)

Stosowanie:

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
źródło