【问题标题】:Is there a way to auto-adjust Excel column widths with pandas.ExcelWriter?有没有办法使用 pandas.ExcelWriter 自动调整 Excel 列宽?
【发布时间】:2013-06-23 23:59:30
【问题描述】:

我被要求生成一些 Excel 报告。我目前正在大量使用 pandas 来处理我的数据,所以我很自然地想使用 pandas.ExcelWriter 方法来生成这些报告。然而,固定的列宽是一个问题。

到目前为止,我的代码很简单。假设我有一个名为“df”的数据框:

writer = pd.ExcelWriter(excel_file_path, engine='openpyxl')
df.to_excel(writer, sheet_name="Summary")

我查看了 pandas 代码,并没有看到任何设置列宽的选项。宇宙中是否有一个技巧可以使列自动调整到数据?或者,事后我可以对 xlsx 文件做些什么来调整列宽?

(我正在使用 OpenPyXL 库,并生成 .xlsx 文件 - 如果这有什么不同的话。)

谢谢。

【问题讨论】:

  • 目前看起来不太可能,请在 github 上为此增强功能打开一个问题(也许是 PR?)。看起来并不难做到。
  • 感谢 Jeff,我已提交问题。我不确定我是否有时间真正深入研究 pandas 代码库来解决它,但你永远不知道 :)
  • 是的......看到你的问题......如果你需要帮助,请评论这个问题! (本质上需要将一个可选参数传递给to_excel,可能是col_style=dict,其中包含col 标头样式元素(而不是默认的header_style,它现在似乎是硬编码的

标签: python excel pandas openpyxl


【解决方案1】:

user6178746's answer的启发,我有以下几点:

# Given a dict of dataframes, for example:
# dfs = {'gadgets': df_gadgets, 'widgets': df_widgets}

writer = pd.ExcelWriter(filename, engine='xlsxwriter')
for sheetname, df in dfs.items():  # loop through `dict` of dataframes
    df.to_excel(writer, sheet_name=sheetname)  # send df to writer
    worksheet = writer.sheets[sheetname]  # pull worksheet object
    for idx, col in enumerate(df):  # loop through all columns
        series = df[col]
        max_len = max((
            series.astype(str).map(len).max(),  # len of largest item
            len(str(series.name))  # len of column name/header
            )) + 1  # adding a little extra space
        worksheet.set_column(idx, idx, max_len)  # set column width
writer.save()

【讨论】:

  • 仅供参考:在我的情况下,我需要在“df.to_excel(...)”调用中使用“index=False”,否则列会偏离 1
  • 是的,我还必须添加 df.to_excel(writer, sheet_name=sheetname, index=False)
  • 如果您不能使用 index=False (因为您在行上有一个多索引),那么您可以使用 df.index.nlevels 获取索引级别深度,然后使用它添加到您的设置列调用:worksheet.set_column(idx+nlevels, idx+nlevels, max_len)。否则为框架的第一列计算长度,然后应用到excel中的第一列,这可能是索引。
  • 对于仍在寻找此答案的任何人,enumerate(df) 应该是 enumerate(df.columns),因为您正在遍历 df 中的每一列。
  • @Dascienz 与迭代dict 的方式相同,实际上迭代dict 中的键(您不必手动说dict.keys()),迭代pd.DataFrame 迭代在列上。您不必手动迭代 df.columns
【解决方案2】:

我发布这个是因为我刚刚遇到了同样的问题,发现 Xlsxwriter 和 pandas 的官方文档仍然将此功能列为不受支持。我拼凑了一个解决方案,解决了我遇到的问题。我基本上只是遍历每一列并使用 worksheet.set_column 设置列宽 == 该列内容的最大长度。

不过,有一个重要提示。此解决方案不适合列标题,仅适合列值。如果您需要改为适合标题,那应该是一个简单的更改。希望这可以帮助某人:)

import pandas as pd
import sqlalchemy as sa
import urllib


read_server = 'serverName'
read_database = 'databaseName'

read_params = urllib.quote_plus("DRIVER={SQL Server};SERVER="+read_server+";DATABASE="+read_database+";TRUSTED_CONNECTION=Yes")
read_engine = sa.create_engine("mssql+pyodbc:///?odbc_connect=%s" % read_params)

#Output some SQL Server data into a dataframe
my_sql_query = """ SELECT * FROM dbo.my_table """
my_dataframe = pd.read_sql_query(my_sql_query,con=read_engine)

#Set destination directory to save excel.
xlsFilepath = r'H:\my_project' + "\\" + 'my_file_name.xlsx'
writer = pd.ExcelWriter(xlsFilepath, engine='xlsxwriter')

#Write excel to file using pandas to_excel
my_dataframe.to_excel(writer, startrow = 1, sheet_name='Sheet1', index=False)

#Indicate workbook and worksheet for formatting
workbook = writer.book
worksheet = writer.sheets['Sheet1']

#Iterate through each column and set the width == the max length in that column. A padding length of 2 is also added.
for i, col in enumerate(my_dataframe.columns):
    # find length of column i
    column_len = my_dataframe[col].astype(str).str.len().max()
    # Setting the length if the column header is larger
    # than the max column value length
    column_len = max(column_len, len(col)) + 2
    # set the column length
    worksheet.set_column(i, i, column_len)
writer.save()

【讨论】:

  • 很好的解决方案。我喜欢你使用 pandas 而不是其他包的方式。
  • 我认为你需要在 max 函数中使用():`max(column_len(), len(col)) + 2`
【解决方案3】:

我最近开始使用一个不错的包,叫做 StyleFrame。

它获取 DataFrame 并让您非常轻松地对其进行样式设置...

默认情况下,列宽是自动调整的。

例如:

from StyleFrame import StyleFrame
import pandas as pd

df = pd.DataFrame({'aaaaaaaaaaa': [1, 2, 3], 
                   'bbbbbbbbb': [1, 1, 1],
                   'ccccccccccc': [2, 3, 4]})
excel_writer = StyleFrame.ExcelWriter('example.xlsx')
sf = StyleFrame(df)
sf.to_excel(excel_writer=excel_writer, row_to_add_filters=0,
            columns_and_rows_to_freeze='B2')
excel_writer.save()

您还可以更改列宽:

sf.set_column_width(columns=['aaaaaaaaaaa', 'bbbbbbbbb'],
                    width=35.3)

更新 1

在 1.4 版中,best_fit 参数已添加到 StyleFrame.to_excel。 请参阅documentation

更新 2

这是适用于 StyleFrame 3.x.x 的代码示例

from styleframe import StyleFrame
import pandas as pd

columns = ['aaaaaaaaaaa', 'bbbbbbbbb', 'ccccccccccc', ]
df = pd.DataFrame(data={
        'aaaaaaaaaaa': [1, 2, 3, ],
        'bbbbbbbbb': [1, 1, 1, ],
        'ccccccccccc': [2, 3, 4, ],
    }, columns=columns,
)
excel_writer = StyleFrame.ExcelWriter('example.xlsx')
sf = StyleFrame(df)
sf.to_excel(
    excel_writer=excel_writer, 
    best_fit=columns,
    columns_and_rows_to_freeze='B2', 
    row_to_add_filters=0,
)
excel_writer.save()

【讨论】:

  • StyleFrame 包可能很容易使用,但我看不到“默认情况下列宽是自动调整的”。当我运行您提供的代码示例时,所有列的宽度都相同,并且所有三个标题都被包装了。您的样本数据也选得不好,因为它们自然都是几乎相同的宽度。为了真正说明自动调整,您应该选择一些非常宽的数据和一些窄的数据。当我为自己这样做时,列宽 仍然 与以前完全相同。没有任何调整。
  • 也许在 StyleFrame 历史的某一时刻,默认情况下会自动调整列宽,但至少今天,您必须在 best_fit 参数中指定要调整的列。另外,当我尝试这个时,我得到了very poor results
  • 宽度似乎偏离了 1 列。我尝试启用和禁用index 参数,但没有骰子。
  • 谢谢!对于那些寻找:如何为标题添加更多样式例如:sf.apply_headers_style(Styler(bold=False)) 我花了很长时间才弄清楚。在导入语句中,from StyleFrame import StyleFrame, Styler。这是除粗体之外的所有选项:styleframe.readthedocs.io/en/2.0.5/…
  • @Hagbard 从版本 3 开始,导入应为 from styleframe import StyleFrame 以符合 PEP8 名称约定
【解决方案4】:

动态调整所有列长

writer = pd.ExcelWriter('/path/to/output/file.xlsx') 
df.to_excel(writer, sheet_name='sheetName', index=False, na_rep='NaN')

for column in df:
    column_length = max(df[column].astype(str).map(len).max(), len(column))
    col_idx = df.columns.get_loc(column)
    writer.sheets['sheetName'].set_column(col_idx, col_idx, column_length)

writer.save()

使用列名手动调整列

col_idx = df.columns.get_loc('columnName')
writer.sheets['sheetName'].set_column(col_idx, col_idx, 15)

使用列索引手动调整列

writer.sheets['sheetName'].set_column(col_idx, col_idx, 15)

如果上述任何一项都失败了

AttributeError: 'Worksheet' object has no attribute 'set_column'

确保安装xlsxwriter:

pip install xlsxwriter

有关更全面的说明,您可以阅读 TDS 上的文章How to Auto-Adjust the Width of Excel Columns with Pandas ExcelWriter

【讨论】:

  • 这里的df 是什么?能否请您显示代码,包括df 初始化?
  • @parsecer 你可以参考我在帖子底部分享的文章。
  • 工作得很好,包括自动宽度、列名的显式宽度和通过安装 xlswriter 解决的异常。谢谢:)
【解决方案5】:

现在可能没有自动的方法可以做到这一点,但是当您使用 openpyxl 时,以下行(改编自用户 Bufkehow to do in manually 上的另一个答案)允许您指定一个合理的值(以字符宽度为单位) ):

writer.sheets['Summary'].column_dimensions['A'].width = 15

【讨论】:

  • 熊猫使用的默认 ExcelWriter 引擎自 2013 年以来已更改为 Xlsxwriter,它不包含 column_dimensions 属性。如果你想继续使用openpyxl,只需在使用pd.ExcelWriter(excel_filename, engine='openpyxl')创建编写器时指定它
  • @Sunil:使用Xlsxwriter 作为引擎查看其他答案,了解如何使用当今的默认引擎指定列宽。
【解决方案6】:

通过使用 pandas 和 xlsxwriter,您可以完成您的任务,以下代码将在 Python 3.x 中完美运行。有关使用 Pandas 使用 XlsxWriter 的更多详细信息,此链接可能有用https://xlsxwriter.readthedocs.io/working_with_pandas.html

import pandas as pd
writer = pd.ExcelWriter(excel_file_path, engine='xlsxwriter')
df.to_excel(writer, sheet_name="Summary")
workbook = writer.book
worksheet = writer.sheets["Summary"]
#set the column width as per your requirement
worksheet.set_column('A:A', 25)
writer.save()

【讨论】:

    【解决方案7】:

    我发现根据列标题而不是列内容调整列更有用。

    使用df.columns.values.tolist() 生成列标题列表并使用这些标题的长度来确定列的宽度。

    查看下面的完整代码:

    import pandas as pd
    import xlsxwriter
    
    writer = pd.ExcelWriter(filename, engine='xlsxwriter')
    df.to_excel(writer, index=False, sheet_name=sheetname)
    
    workbook = writer.book # Access the workbook
    worksheet= writer.sheets[sheetname] # Access the Worksheet
    
    header_list = df.columns.values.tolist() # Generate list of headers
    for i in range(0, len(header_list)):
        worksheet.set_column(i, i, len(header_list[i])) # Set column widths based on len(header)
    
    writer.save() # Save the excel file
    

    【讨论】:

      【解决方案8】:

      在工作中,我总是将数据框写入 excel 文件。因此,我没有一遍又一遍地编写相同的代码,而是创建了一个模数。现在我只需将其导入并使用它来编写和格式化 Excel 文件。但是有一个缺点,如果数据框特别大,则需要很长时间。 所以这里是代码:

      def result_to_excel(output_name, dataframes_list, sheet_names_list, output_dir):
          out_path = os.path.join(output_dir, output_name)
          writerReport = pd.ExcelWriter(out_path, engine='xlsxwriter',
                          datetime_format='yyyymmdd', date_format='yyyymmdd')
          workbook = writerReport.book
          # loop through the list of dataframes to save every dataframe into a new sheet in the excel file
          for i, dataframe in enumerate(dataframes_list):
              sheet_name = sheet_names_list[i]  # choose the sheet name from sheet_names_list
              dataframe.to_excel(writerReport, sheet_name=sheet_name, index=False, startrow=0)
              # Add a header format.
              format = workbook.add_format({
                  'bold': True,
                  'border': 1,
                  'fg_color': '#0000FF',
                  'font_color': 'white'})
              # Write the column headers with the defined format.
              worksheet = writerReport.sheets[sheet_name]
              for col_num, col_name in enumerate(dataframe.columns.values):
                  worksheet.write(0, col_num, col_name, format)
              worksheet.autofilter(0, 0, 0, len(dataframe.columns) - 1)
              worksheet.freeze_panes(1, 0)
              # loop through the columns in the dataframe to get the width of the column
              for j, col in enumerate(dataframe.columns):
                  max_width = max([len(str(s)) for s in dataframe[col].values] + [len(col) + 2])
                  # define a max width to not get to wide column
                  if max_width > 50:
                      max_width = 50
                  worksheet.set_column(j, j, max_width)
          writerReport.save()
          return output_dir + output_name
      
      

      【讨论】:

      • 复制此代码时出现以下错误:AttributeError: 'str' object has no attribute 'to_excel'。它认为这与“dataframe_list”的创建方式有关。我的是一个包含 6 个数据框名称的列表
      • 是的,“dataframe_list”应该有数据框而不是数据框名称。
      【解决方案9】:

      您可以通过调用以下函数来解决问题,其中 df 是您要获取大小的数据框,而 sheetname 是您希望进行修改的 excel 表格

      def auto_width_columns(df, sheetname):
              workbook = writer.book  
              worksheet= writer.sheets[sheetname] 
          
              for i, col in enumerate(df.columns):
                  column_len = max(df[col].astype(str).str.len().max(), len(col) + 2)
                  worksheet.set_column(i, i, column_len)
      

      【讨论】:

      • codes only 不能回答您必须添加一些解释或花时间阅读有关How do I write a good answer?的文档的问题
      • 您好!虽然这段代码可以解决问题,including an explanation 解决问题的方式和原因确实有助于提高您的帖子质量,并可能导致更多的赞成票。请记住,您正在为将来的读者回答问题,而不仅仅是现在提问的人。请edit您的答案添加解释并说明适用的限制和假设。
      • 好,简单的解决方案。请记住,如果您使用索引,df.columns 的形状将与df.to_excel(writer,sheet_name=...) 在 excel 文件中输出的形状不同。这可能会使enumerateiworksheet.set_column 的预期不一致。我用df.reset_index().to_excel(...) 解决了这个问题,但可能有更好的解决方案。
      【解决方案10】:

      结合其他答案和 cmets 并支持多指标:

      def autosize_excel_columns(worksheet, df):
        autosize_excel_columns_df(worksheet, df.index.to_frame())
        autosize_excel_columns_df(worksheet, df, offset=df.index.nlevels)
      
      def autosize_excel_columns_df(worksheet, df, offset=0):
        for idx, col in enumerate(df):
          series = df[col]
          max_len = max((
            series.astype(str).map(len).max(),
            len(str(series.name))
          )) + 1
          worksheet.set_column(idx+offset, idx+offset, max_len)
      
      sheetname=...
      df.to_excel(writer, sheet_name=sheetname, freeze_panes=(df.columns.nlevels, df.index.nlevels))
      worksheet = writer.sheets[sheetname]
      autosize_excel_columns(worksheet, df)
      writer.save()
      

      【讨论】:

        【解决方案11】:
        import re
        import openpyxl
        ..
        for col in _ws.columns:
            max_lenght = 0
            print(col[0])
            col_name = re.findall('\w\d', str(col[0]))
            col_name = col_name[0]
            col_name = re.findall('\w', str(col_name))[0]
            print(col_name)
            for cell in col:
                try:
                    if len(str(cell.value)) > max_lenght:
                        max_lenght = len(cell.value)
                except:
                    pass
            adjusted_width = (max_lenght+2)
            _ws.column_dimensions[col_name].width = adjusted_width
        

        【讨论】:

          【解决方案12】:

          最简单的解决方案是在 set_column 方法中指定列的宽度。

              for worksheet in writer.sheets.values():
                  worksheet.set_column(0,last_column_value, required_width_constant)
          

          【讨论】:

            【解决方案13】:

            这个功能对我有用,也修复了索引宽度

            def write_to_excel(writer, X, sheet_name, sep_only=False):
                #writer=writer object
                #X=dataframe
                #sheet_name=name of sheet
                #sep_only=True:write only as separate excel file, False: write as sheet to the writer object
                if sheet_name=="": 
                    print("specify sheet_name!")
                else:
                    X.to_excel(f"{output_folder}{prefix_excel_save}_{sheet_name}.xlsx")
                    if not sep_only: 
                        X.to_excel(writer, sheet_name=sheet_name)
                        
                        #fix column widths
                        worksheet = writer.sheets[sheet_name]  # pull worksheet object
                        for idx, col in enumerate(X.columns):  # loop through all columns
                            series = X[col]
                            max_len = max((
                                series.astype(str).map(len).max(),  # len of largest item
                                len(str(series.name))  # len of column name/header
                                )) + 1  # adding a little extra space
                            worksheet.set_column(idx+1, idx+1, max_len)  # set column width (=1 because index = 1)
                            
                        #fix index width
                        max_len=pd.Series(X.index.values).astype(str).map(len).max()+1
                        worksheet.set_column(0, 0, max_len)
                        
                    if sep_only: 
                        print(f'{sheet_name} is written as seperate file')
                    else:
                        print(f'{sheet_name} is written as seperate file')
                        print(f'{sheet_name} is written as sheet')
                return writer
            

            调用示例:

            writer = write_to_excel(writer, dataframe, "Statistical_Analysis")
            

            【讨论】:

              【解决方案14】:

              是的,事后您可以对 xlsx 文件执行一些操作来调整列宽。 使用 xlwings 到 autofit 列。这是一个非常简单的解决方案,请参见示例代码的最后六行。此过程的优点是您不必担心字体大小、字体类型或其他任何事情。 要求:Excel安装。

              import pandas as pd
              import xlwings as xw
              
              file_path = r"report_formtting_files.xlsx"
              
              df = pd._testing.makeDataFrame()
              
              writer = pd.ExcelWriter(file_path, engine="xlsxwriter")
              df.to_excel(writer, sheet_name="Sheet1", index=False)
              workbook = writer.book
              worksheet1 = writer.sheets["Sheet1"]
              num_format = workbook.add_format({"num_format": '#,##0.00'})
              worksheet1.set_column("A:D", cell_format=num_format)
              writer.close()
              
              # Autofit all columns with xlwings.
              with xw.App(visible=False) as app:
                  wb = xw.Book(file_path)
              
                  for ws in wb.sheets:
                      ws.autofit(axis="columns")
              
                  wb.save(file_path)
                  wb.close()
              

              【讨论】:

              • 仅适用于 Windows 和 MacOS,但不适用于 Linux
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2016-01-21
              • 2018-02-02
              • 2022-10-05
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-04-16
              相关资源
              最近更新 更多