Có cách nào để tự động điều chỉnh độ rộng cột Excel với gấu trúc .ExcelWriter?


99

Tôi được yêu cầu tạo một số báo cáo Excel. Tôi hiện đang sử dụng gấu trúc khá nhiều cho dữ liệu của mình, vì vậy tôi muốn sử dụng phương thức pandas.ExcelWriter để tạo các báo cáo này. Tuy nhiên, độ rộng cột cố định là một vấn đề.

Mã tôi có cho đến nay là đủ đơn giản. Giả sử tôi có một khung dữ liệu được gọi là 'df':

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

Tôi đã xem qua mã gấu trúc và tôi không thực sự thấy bất kỳ tùy chọn nào để đặt độ rộng cột. Có một thủ thuật nào ngoài vũ trụ để làm cho các cột tự động điều chỉnh theo dữ liệu không? Hoặc có điều gì đó tôi có thể làm sau khi thực tế với tệp xlsx để điều chỉnh độ rộng cột?

(Tôi đang sử dụng thư viện OpenPyXL và tạo tệp .xlsx - nếu điều đó tạo ra bất kỳ sự khác biệt nào.)

Cảm ơn bạn.


1
hiện tại có vẻ không khả thi, vui lòng mở một vấn đề cho cải tiến này trên github (và có thể là một chiêu trò PR?). không khó để làm.
Jeff

cảm ơn Jeff, tôi đã gửi vấn đề. Tôi không chắc chắn nếu tôi sẽ có thời gian để thực sự bổ nhào vào gấu trúc codebase để giải quyết nó, nhưng bạn không bao giờ biết :)
badideas

vâng .... đã thấy vấn đề của bạn ..... bình luận về vấn đề nếu bạn cần trợ giúp! (về cơ bản cần phải vượt qua một đối số tùy chọn để to_excel, có thể col_style=dictchứa yếu tố phong cách tiêu đề col (chứ không phải là mặc định header_stylemà dường như được mã hóa cứng hiện nay
Jeff

Câu trả lời:


56

Lấy cảm hứng từ câu trả lời của user6178746 , tôi có những điều sau:

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

7
FYI: Trong trường hợp của tôi, tôi cần thiết để sử dụng "index = false" trong "df.to_excel (...)" cuộc gọi, hoặc nếu không các cột đã tắt bằng 1
denvar

1
vâng, tôi cũng đã có thêm df.to_excel (nhà văn, SHEET_NAME = tên này, chỉ số = False)
Heikki Pulkkinen

2
Nếu bạn không thể sử dụng index = False (vì bạn có một multiindex trên các hàng), sau đó bạn có thể nhận được độ sâu mức chỉ số với df.index.nlevels và sau đó sử dụng này để thêm vào lời kêu gọi thiết lập của bạn cột: worksheet.set_column(idx+nlevels, idx+nlevels, max_len). Nếu không, độ dài được tính cho cột đầu tiên của khung và sau đó được áp dụng cho cột đầu tiên trong excel, có thể là chỉ mục.
ac24

1
Đối với bất kỳ ai vẫn đang tìm kiếm câu trả lời này, enumerate(df)nên enumerate(df.columns)vì bạn đang lặp lại từng cột trong df.
Dascienz 13/09/18

2
@Dascienz theo cùng một cách lặp qua một lần dictlặp thực sự qua các khóa trong dict(bạn không cần phải nói theo cách thủ công dict.keys()), lặp qua một lần pd.DataFramelặp qua các cột. Bạn không phải lặp lại theo cách thủ công df.columns.
alichaudry

26

Tôi đăng bài này vì tôi vừa gặp phải vấn đề tương tự và thấy rằng tài liệu chính thức cho Xlsxwriter và gấu trúc vẫn có chức năng này được liệt kê là không được hỗ trợ. Tôi đã hack cùng một giải pháp để giải quyết vấn đề tôi đang gặp phải. Về cơ bản, tôi chỉ cần lặp qua từng cột và sử dụng worksheet.set_column để đặt chiều rộng cột == chiều dài tối đa của nội dung của cột đó.

Tuy nhiên, một lưu ý quan trọng. Giải pháp này không phù hợp với các tiêu đề cột, chỉ đơn giản là các giá trị của cột. Tuy nhiên, đó sẽ là một thay đổi dễ dàng nếu bạn cần thay thế các tiêu đề. Hy vọng điều này sẽ giúp ai đó :)

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

1
Giải pháp tốt. Tôi thích cách bạn sử dụng gấu trúc thay vì một gói khác.

Tôi nghĩ rằng bạn cần ()chức năng bên trong tối đa: `max (column_len (), len (col)) + 2`
Serdia

21

Có lẽ không có cách tự động nào để làm điều đó ngay bây giờ, nhưng khi bạn sử dụng openpyxl, dòng sau (được điều chỉnh từ một câu trả lời khác của người dùng Bufke về cách thực hiện theo cách thủ công ) cho phép bạn chỉ định một giá trị lành mạnh (theo độ rộng ký tự):

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

Công cụ ExcelWriter mặc định mà gấu trúc đang sử dụng đã thay đổi từ năm 2013 thành Xlsxwriter, không chứa column_dimensionsthuộc tính. Nếu bạn muốn tiếp tục sử dụng openpyxl, chỉ cần xác định nó khi tạo nhà văn sử dụngpd.ExcelWriter(excel_filename, engine='openpyxl')
ojdo

@Sunil: kiểm tra các câu trả lời khác sử dụng Xlsxwriterlàm công cụ để xem cách chỉ định chiều rộng cột với công cụ mặc định ngày nay.
ojdo

21

Có một gói tuyệt vời mà tôi đã bắt đầu sử dụng gần đây được gọi là StyleFrame.

nó nhận DataFrame và cho phép bạn tạo kiểu rất dễ dàng ...

theo mặc định, chiều rộng cột được tự động điều chỉnh.

ví dụ:

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

bạn cũng có thể thay đổi chiều rộng cột:

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


CẬP NHẬT

Trong phiên bản 1.4, best_fitđối số đã được thêm vào StyleFrame.to_excel. Xem tài liệu .


Gói StyleFrame có thể dễ sử dụng, nhưng tôi không thấy làm thế nào "theo mặc định chiều rộng cột được tự động điều chỉnh". Khi tôi chạy mẫu mã bạn đã cung cấp, tất cả các cột có cùng chiều rộng và cả ba tiêu đề đều được bao bọc. Dữ liệu mẫu của bạn cũng được chọn kém, bởi vì chúng gần như có cùng chiều rộng một cách tự nhiên. Để thực sự minh họa cho việc điều chỉnh tự động, bạn nên chọn một số dữ liệu thực sự rộng và một số dữ liệu hẹp. Khi tôi làm điều này cho chính mình, độ rộng của cột vẫn giống hệt như trước đây. Không có bất kỳ điều chỉnh nào.
John Y

Có thể tại một thời điểm trong lịch sử của StyleFrame, độ rộng của các cột được tự động điều chỉnh theo mặc định, nhưng ít nhất hôm nay, bạn phải chỉ định cột hoặc các cột bạn muốn điều chỉnh trong best_fittham số. Ngoài ra, khi tôi thử điều này, tôi đã nhận được kết quả rất kém .
John Y

chiều rộng dường như lệch khỏi 1 cột. Tôi đã thử bật và tắt indextham số nhưng không có xúc xắc.

1
cảm ơn! cho những người đang tìm kiếm: Ví dụ: cách bạn thêm nhiều kiểu dáng vào tiêu đề: sf.apply_headers_style(Styler(bold=False))tôi đã mất nhiều thời gian để tìm ra điều đó. Và trong câu lệnh nhập from StyleFrame import StyleFrame, Styler,. đây là tất cả các tùy chọn ngoại trừ in đậm: styleframe.readthedocs.io/en/2.0.5/…
Nikhil VJ

Rất tiếc, câu trả lời này đã lỗi thời và tôi chỉ gặp lỗi nhập nếu cố gắng áp dụng vì API dường như đã thay đổi đáng kể.
Hagbard

10

Bằng cách sử dụng pandas và xlsxwriter, bạn có thể thực hiện nhiệm vụ của mình, mã dưới đây sẽ hoạt động hoàn hảo trong Python 3.x. Để biết thêm chi tiết về cách làm việc với XlsxWriter với gấu trúc, liên kết này có thể hữu ích 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()

4

Tôi thấy rằng việc điều chỉnh cột dựa trên tiêu đề cột sẽ hữu ích hơn thay vì nội dung cột.

Sử dụng, df.columns.values.tolist()tôi tạo danh sách các tiêu đề cột và sử dụng độ dài của các tiêu đề này để xác định chiều rộng của các cột.

Xem mã đầy đủ bên dưới:

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

4

Trong công việc, tôi luôn ghi dataframe vào các tệp excel. Vì vậy, thay vì viết đi viết lại cùng một đoạn mã, tôi đã tạo một mô đun. Bây giờ tôi chỉ cần nhập nó và sử dụng nó để viết và định dạng các tệp excel. Tuy nhiên, có một nhược điểm là sẽ mất nhiều thời gian nếu khung dữ liệu quá lớn. Vì vậy, đây là mã:

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()
    writerReport.close()
    return output_dir + output_name

Tôi gặp lỗi sau khi sao chép mã này: AttributeError: Đối tượng 'str' không có thuộc tính 'to_excel'. Nó nghĩ rằng nó có liên quan đến cách tạo "dataframe_list". Của tôi là một danh sách với 6 tên
khung dữ liệu

Có, "dataframe_list" phải có dataframe chứ không phải tên dataframe.
rafat.ch

4

Tự động điều chỉnh tất cả độ dài cột

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)

Điều chỉnh thủ công một cột bằng cách sử dụng Tên cột

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

Điều chỉnh thủ công một cột bằng cách sử dụng Chỉ mục cột

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

Trong trường hợp bất kỳ điều nào ở trên không thành công với

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

đảm bảo cài đặt xlsxwriter:

pip install xlsxwriter

2

Kết hợp các câu trả lời và nhận xét khác và cũng hỗ trợ nhiều chỉ số:

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

2
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

1

Giải pháp dễ nhất là chỉ định độ rộng của cột trong phương thức set_column.

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

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

1
mã chỉ không trả lời câu hỏi bạn phải thêm một số giải thích hoặc mất thời gian và đọc tài liệu về Làm cách nào để viết một câu trả lời tốt?
Gad

1
Xin chào! Mặc dù mã này có thể giải quyết câu hỏi, bao gồm giải thích về cách thức và lý do tại sao điều này giải quyết vấn đề sẽ thực sự giúp cải thiện chất lượng bài đăng của bạn và có thể dẫn đến nhiều phiếu bầu hơn. Hãy nhớ rằng bạn đang trả lời câu hỏi cho độc giả trong tương lai, không chỉ người hỏi bây giờ. Vui lòng chỉnh sửa câu trả lời của bạn để thêm giải thích và đưa ra dấu hiệu về những giới hạn và giả định áp dụng.
Brian
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.