【问题标题】:Export a DataFrame to Excel, values only without the formatting将 DataFrame 导出到 Excel,仅值不带格式
【发布时间】:2020-11-20 02:36:55
【问题描述】:

我需要将 pandas DataFrame 写入 Excel 工作表。有货币、百分比和文本。该脚本预计会定期运行,更新数据而不更改手动定义的格式。

pandas to_excel() 似乎是硬编码以强制使用特定格式。

我创建了自己的函数来将 DataFrame 写入文件:

def write_sheet1(filename, data_ws, df, start_row=2, start_col=2):
    """Working for xlsx and xls only.

    args:
        start_row: df row +2; does not include header and is 1 based indexed.
    """
    writer = pd.ExcelWriter(filename.lower(), engine='openpyxl')
    import openpyxl
    try:
        wb = openpyxl.load_workbook(filename)
    except FileNotFoundError:
        wb = openpyxl.Workbook()
    if data_ws not in wb.sheetnames:
        wb.create_sheet(data_ws)

    # Create the worksheet if it does not yet exist.
    writer.book = wb
    writer.sheets = {x.title: x for x in wb.worksheets}

    ws = writer.sheets[data_ws]
    # Fill with blanks.
    for row in ws:
        for cell in row:
            cell.value = None

    # Write manually to avoid overwriting formats.

    # Column names.
    ws.cell(1, 1).value = df.columns.name
    for icol, col_name in zip(range(2, len(df.columns) + 2), df.columns):
        ws.cell(1, icol).value = col_name

    # Row headers.
    for irow, row_name in zip(range(2, len(df.index) + 2), df.index):
        ws.cell(irow, 1).value = row_name

    # Body cells.
    for row, irow in zip([x[1] for x in df.iloc[start_row - 2:].iterrows()], list(range(start_row, len(df.index) + 2))):
        """"""
        for cell, icol in zip([x[1] for x in row.iloc[start_col - 2:].items()], list(range(start_col, len(df.columns) + 2))):
            """"""
            ws.cell(irow, icol).value = cell  # Skip the index.

    for row in ws.values:
        print('\t'.join(str(x or '') for x in row))
    print('Saving.')
    while True:
        try:
            writer.save()
            break
        except PermissionError:
            print(f'Please close {filename} before we can write to it!')
            time.sleep(2)
    writer.close()
    print('Done saving df.')

结果:它第一次工作,但随后的运行将 DataFrame 附加到工作表中的现有数据。

[编辑:实际上,第一个函数完成了工作!错误在上游; DataFrame 本身有附加的数据。]

我也试过猴子修补熊猫:

def write_sheet2(filename, data_ws, df, start_row=2, start_col=2):
    """Write df values only to excel.

    Monkey patch pandas' built-in .to_excel()
    """
    def get_formatted_cells(self):
        for cell in itertools.chain(self._format_header(), self._format_body()):
            yield cell
    from pandas.io.formats.excel import ExcelFormatter
    ExcelFormatter.get_formatted_cells = get_formatted_cells

    writer = pd.ExcelWriter(filename, engine='xlsxwriter', mode='w')
    df.to_excel(writer, sheet_name=data_ws)
    writer.save()
    writer.close()
    print('Done writing.')

结果:与原生 .to_excel() 相同。

如何编写一个通用函数来仅将 pandas DataFrame 的值导出到 Excel 文件而不修改现有格式?

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    .to_excel() 的替换,仅写入值:

    def to_excel(filename, data_ws, df, start_row=2, start_col=2):
        """Replacement for pandas .to_excel(). 
    
        For .xlsx and .xls formats only.
    
        args:
            start_row: df row +2; does not include header and is 1 based indexed.
        """
        writer = pd.ExcelWriter(filename.lower(), engine='openpyxl')
        import openpyxl
        try:
            wb = openpyxl.load_workbook(filename)
        except FileNotFoundError:
            wb = openpyxl.Workbook()
        if data_ws not in wb.sheetnames:
            wb.create_sheet(data_ws)
    
        # Create the worksheet if it does not yet exist.
        writer.book = wb
        writer.sheets = {x.title: x for x in wb.worksheets}
    
        ws = writer.sheets[data_ws]
        # Fill with blanks.
        try:
            for row in ws:
                for cell in row:
                    cell.value = None
        except TypeError:
            pass
    
        # Write manually to avoid overwriting formats.
    
        # Column names.
        ws.cell(1, 1).value = df.columns.name
        for icol, col_name in zip(range(2, len(df.columns) + 2), df.columns):
            ws.cell(1, icol).value = col_name
    
        # Row headers.
        for irow, row_name in zip(range(2, len(df.index) + 2), df.index):
            ws.cell(irow, 1).value = row_name
    
        # Body cells.
        for row, irow in zip([x[1] for x in df.iloc[start_row - 2:].iterrows()], list(range(start_row, len(df.index) + 2))):
            for cell, icol in zip([x[1] for x in row.iloc[start_col - 2:].items()], list(range(start_col, len(df.columns) + 2))):
                ws.cell(irow, icol).value = cell  # Skip the index.
    
        for row in ws.values:
            print('\t'.join(str(x or '') for x in row))
        print('Saving.')
        while True:
            try:
                writer.save()
                break
            except PermissionError:
                print(f'Please close {filename} before we can write to it!')
                time.sleep(2)
        writer.close()
        print('Done saving df.')
    

    如果以后有人提出这个问题,这段代码似乎可以工作。不过可以稍微清理一下,可能通过将 DataFrame 转换为列表列表来避免 DataFrame 开销并避免分别处理列名和索引名。

    【讨论】:

      猜你喜欢
      • 2019-09-03
      • 2017-02-22
      • 2010-10-25
      • 2019-10-28
      • 2019-10-26
      • 2017-04-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多