【问题标题】:Can Pandas read and modify a single Excel file worksheet (tab) without modifying the rest of the file?Pandas 可以在不修改文件其余部分的情况下读取和修改单个 Excel 文件工作表(选项卡)吗?
【发布时间】:2015-03-24 09:52:30
【问题描述】:

许多电子表格都具有用于读写 Excel 文件的 Python 工具无法忠实再现的公式和格式。这意味着我想以编程方式创建的任何文件都必须是我基本上从头开始创建的,然后其他 Excel 文件(具有上述复杂性)必须引用该文件(这会产生各种其他依赖问题)。

我对 Excel 文件“选项卡”的理解是它们实际上只是 XML 文件的集合。那么,是否可以使用 pandas(或诸如 xlsxwriter 或 openpyxl 之类的底层读/写引擎之一来修改其中一个选项卡,而保留其他选项卡(其中包含更多邪恶的东西)完好无损?

编辑:我将尝试通过一个示例进一步阐明问题。

  • Excel Sheet test.xlsx 有四个选项卡(又名工作表):Sheet1、Sheet2、Sheet3、Sheet4
  • 我使用 pandas.read_excel() 将 Sheet3 读入 DataFrame(我们称之为 df)
  • Sheet1 和 Sheet2 包含公式、图表和各种格式,openpyxl 和 xlrd 都无法成功解析,Sheet4 包含其他数据。我根本不想碰那些标签。
  • Sheet2 实际上对 Sheet3 上的单元格有一些引用
  • 我对 df 进行了一些编辑,现在想将其写回 sheet3,而其他工作表保持不变(并且工作簿中其他工作表中对它的引用保持不变)

我可以这样做吗?如果可以,怎么做?

【问题讨论】:

    标签: python excel pandas


    【解决方案1】:

    关于excel和python(特别是pandas)之间的交互我有一个类似的问题,我被提到了这个问题。

    感谢 stackoverflow 社区的一些建议,我找到了一个名为 xlwings 的包,它似乎涵盖了 HaPsantran 所需的许多功能。

    使用 OP 的例子:

    使用现有的 excel 文件,您可以在要导入 pandas 的数据块 (Sheet3) 中放置一个锚点,方法是在 excel 中命名它并执行以下操作:

    # opened an existing excel file
    

    wb = Workbook(Existing_file)

    # Find in the excel file a named cell and reach the boundary of the cell block (boundary defined by empty column / row) and read the cell 
    

    df = Range(Anchor).table.value

    # import pandas and manipulate the data block
    df = pd.DataFrame(df) # into Pandas DataFrame
    df['sum'] = df.sum(axis= 1)
    
    # write back to Sheet3
    Range(Anchor).value = df.values
    

    测试此实现没有调整 excel 文件中的现有公式

    让我知道这是否能解决您的问题以及我可以提供什么帮助。

    非常感谢 xlwings 的开发者,他们让这一切成为可能。


    以下是在@jamzsabb 提出进一步问题后对我之前回答的更新,并反映在 xlwings 更新到 >= 0.9.0 后更改的 API。

    import xlwings as xw
    import pandas as pd
    target_df = xw.Range('A7').options(pd.DataFrame, expand='table').value # only do this if the 'A7' cell (the cell within area of interest) is in active worksheet
    #otherwise do:
    #sht = xw.Book(r'path to your xlxs file\name_of_file.xlsx`).sheets['name of sheet']
    #target_df = sht.Range('A7').options(pd.DataFrame, expand='table').value # you can also change 'A7' to any name that you've given to a cell like 'interest_table`
    

    【讨论】:

    • 这看起来很有希望。我会在时间允许的时候对其进行测试,然后再返回评论。感谢您对 xlwings 的提醒。
    • 我已经搞砸了一段时间,我无法理解它。什么是锚对象?那是工作簿中的工作表名称吗?当 df 被声明时,它是如何链接到 Workbook 对象的?我一直在查看 xlwings 文档,但在任何地方都没有看到任何对在 excel 中编辑单个工作表有用的东西.....
    • 抱歉不清楚,Anchor 表示工作表中的命名单元格。它应该是一个像“Hero”这样的字符串。我只是认为命名一个单元格然后在 python 中快速访问它可能更容易,而不是通过列、行坐标来定位它。这样你的业务逻辑也会变得更清晰。我的回答范围更侧重于编辑特定电子表格上的特定区域。
    • 感谢@PaulDong,看起来我遇到的另一个问题是我的 xlwings 出于某种原因是 0.7 版,即使我使用 pip 进行了更新。必须手动安装,现在一切正常。感谢您的详细帮助!
    • @PaulDong,您能否在回复中详细说明一下?如果我有一个 pandas 数据框,例如,带有 3 个选项卡(工作表)并且我想完全替换其中的 2 个,每个都来自一个单独的 pandas 数据框,我试图了解如何使用它。我并没有真正遵循您的代码,我该如何将其应用于这种情况?
    【解决方案2】:

    我有 90% 的把握对“pandas 可以做到这一点”的答案是否定的。发布负面消息很困难,因为我可能总是错过了一些聪明的东西,但这里有一个案例:

    可能的接口引擎有xlrd/xlwt/xlutilsopenpyxlxlsxwriter。没有一个可以满足您的需要,因为xlrd/wt 不支持所有公式,xlsxwriter 无法修改现有的xlsx 文件,而openpyxl 会丢失图像和图表。

    由于我经常需要这样做,所以我只将简单的输出写入单独的文件,然后直接调用 win32api 在工作簿之间复制数据,同时保留我同事所有闪亮的数字。这很烦人,因为这意味着我必须在 Windows 下而不是 *nix 下进行,但它可以工作。

    如果您在 Windows 下工作,您可以执行类似的操作。 (我想知道使用这种方法添加原生插入选项来帮助处于这种情况的人们是否有意义,或者我们是否应该简单地发布一个食谱。)


    P.S.:这个问题不时让我很恼火,以至于我想学习足够多的现代 Excel 格式来为其中一个库添加对此的支持。

    PPS:但是,由于忽略您未处理的事情并将其原封不动地退回似乎很容易,但似乎没有人支持它的事实让我觉得有些头疼,而雷德蒙德涉及的地方我愿意相信. @john-machin 会知道细节,如果他是关于..

    【讨论】:

    • 谢谢,@DSM;这就是我所怀疑的。您是否碰巧知道是否有 Mac 可以像使用 win32api 一样修改 Excel 文件?有this 之类的答案,但很高兴知道它是否值得探索。
    • 请注意,xlwings 在 Windows 上对 pywin32 和 Mac 上的 appscript 进行了封装,以获得完全跨平台兼容的解决方案,同时也解决了 pywin32 的一些限制。
    • @DSM,这个更简化的版本怎么样?如果我只需要在 excel 中修改 3 个工作表中的 2 个,这两个工作表都是 pandas 数据框。我怎样才能做到这一点?我看了很多答案,甚至发布了我自己的问题,但没有人回答,我认为这应该比这里提出的原始问题更容易并且可行?:stackoverflow.com/questions/49413824/…
    【解决方案3】:

    我正在添加一个使用 openpyxl 的答案。从 2.5 版开始,您可以在现有文件中保留图表(有关该问题的更多详细信息,请访问 here)。

    出于演示目的,我按照 OPs 指南使用 pandas 创建了一个 xlsx 文件。名为“Sheet2”的选项卡包含引用“Sheet3”的公式并包含一个图表。

    import pandas as pd
    
    df = pd.DataFrame({'col_a': [1,2,3],
                      'col_b': [4,5,6]})
    
    writer = pd.ExcelWriter('test.xlsx', engine='xlsxwriter')
    df.to_excel(writer, sheet_name='Sheet1', index=False)
    workbook=writer.book
    worksheet = writer.sheets['Sheet1']
    df.head(0).to_excel(writer, sheet_name='Sheet2', index=False)
    workbook=writer.book
    worksheet = writer.sheets['Sheet2']
    for i in range(2, len(df) + 2):
        worksheet.write_formula('A%d' % (i), "=Sheet3!A%d" % (i))
        worksheet.write_formula('B%d' % (i), "=Sheet3!B%d" % (i))
    chart = workbook.add_chart({'type': 'column'})
    
    chart.add_series({'values': '=Sheet2!$A$2:$A$4'})
    chart.add_series({'values': '=Sheet2!$B$2:$B$4'})
    
    worksheet.insert_chart('A7', chart)
    
    df.to_excel(writer, sheet_name='Sheet3', index=False)
    df.to_excel(writer, sheet_name='Sheet4', index=False)
    
    writer.save()
    

    运行上述代码后的预期 test.xlsx:

    然后如果我们使用openpyxl运行下面的代码,我们可以修改'Sheet3'中的数据,同时保留'Sheet2'中的公式和图表,并且更新的数据现在在这个文件中。

    from openpyxl import load_workbook
    
    wb = load_workbook('test.xlsx')
    ws = wb['Sheet3']
    ws['B2'] = 7
    ws['B3'] = 8
    ws['B4'] = 9
    wb.save('test.xlsx')
    

    运行第二个代码块后的预期 test.xlsx:

    【讨论】:

      【解决方案4】:

      据我所知,Pandas 自己不会这样做。

      我编写了一些小型实用程序库pandasxltable(基于 openpyxl)以促进 excel 模板和 pandas 数据帧之间的交互。该库允许您获取数据框并从数据框更新 Excel 数据表(不是真正的选项卡,而是其中的一部分)。

      【讨论】:

        【解决方案5】:

        如果您将“工作表”称为“选项卡”,则可以通过使用parse(sheet_name) 函数访问特定选项卡来仅修改其中一个选项卡。

        这里有一个例子: Reading an Excel file in python using pandas

        要写回 excel,(在控制工作表的同时)使用 to_excel 函数,这里: http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.to_excel.html

        【讨论】:

        • 如果您可以发布一段代码来演示这一点(例如,除了您读入的文件之外,还包含 其他表格 的源文件是除了您写的表格外,其他内容保持不变),这会很有帮助。
        【解决方案6】:

        必填:存在的excels文件的调用路径。

        输入:列表字符串。

        输出:追加行。

        from datetime import datetime,timedelta
        from openpyxl import load_workbook,Workbook
               
           def write_log_excels(status):
                       try:
                           # Point
                           log_list = ["1","2","3","4","5,"6","7","8",  "9"]
                           date_n = datetime.now()
                           date_n = date_n.strftime("%Y-%m-%d %H:%M:%S")
                           sdate = date_n
           
                           wk = load_workbook('filename.xlsx')
                           wh = wk.active
                           lenth = wh.max_row
                           # wk.close()
                           pl = log_list
                           if lenth == 0:
                               # ws = Workbook()
                               # wb = ws.active
                               wh['A1'] = 'TITLE1'
                               wh['B1'] = 'TITLE2'
                               wh['C1'] = 'TITLE3'
                               wh['D1'] = 'TITLE4'
                               wh['E1'] = 'TITLE5'
                               wh['F1'] = 'TITLE6'
                               wh['G1'] = 'TITLE7'
                               wh['H1'] = 'TITLE8'
                               wh['I1'] = 'TITLE9'
                               lenth = 1
                           if pl is not None:
                               w = lenth + 1
                               wh['A{}'.format(w)] =  pl[0]
                               wh['B{}'.format(w)] =  pl[1]
                               wh['C{}'.format(w)] =  pl[2]
                               wh['D{}'.format(w)] =  pl[3]
                               wh['E{}'.format(w)] =  pl[4]
                               wh['F{}'.format(w)] =  pl[5]
                               wh['G{}'.format(w)] =  pl[3]
                               wh['H{}'.format(w)] =  pl[4]
                               wh['I{}'.format(w)] =  pl[5]
                           wk.save('filename.xlsx')
               
                           log_list.clear()
                       except Exception as e:
                           print('write_log_excels :' + str(e))
               write_log_excels('')
        

        或者使用它来自动创建 col,row。

        def work_sheet(wsheet):
            data_sheet = []
            col = [] #column in sheet
            for c in range(wsheet.max_column):
                #got alphabels with max_(len)_column found in worksheet
                col.append(string.ascii_uppercase[c])
        
            for r in range(2,wsheet.max_row + 1):
                data_row = []
                for c in range(len(col)):
                    #got values exactly with "sheet[colum-row]"
                    data = wsheet['{}{}'.format(col[c],r)].value
                    data_row.append(data)
                data_sheet.append(data_row)
            return data_sheet
        

        【讨论】:

          猜你喜欢
          • 2019-08-09
          • 2021-09-20
          • 1970-01-01
          • 2010-10-03
          • 2013-12-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多