มีวิธีปรับความกว้างคอลัมน์ Excel โดยอัตโนมัติด้วย pandas.ExcelWriter หรือไม่?


111

ฉันถูกขอให้สร้างรายงาน Excel ตอนนี้ฉันใช้แพนด้าค่อนข้างหนักสำหรับข้อมูลของฉันดังนั้นฉันจึงอยากใช้เมธอดของแพนด้าเอ็กเซลวอเตอร์เพื่อสร้างรายงานเหล่านี้ อย่างไรก็ตามความกว้างของคอลัมน์คงที่เป็นปัญหา

รหัสที่ฉันมีนั้นง่ายพอ สมมติว่าฉันมี dataframe ชื่อ 'df':

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

ฉันกำลังดูรหัสแพนด้าและฉันไม่เห็นตัวเลือกใด ๆ ในการตั้งค่าความกว้างของคอลัมน์ มีเคล็ดลับในจักรวาลเพื่อให้คอลัมน์ปรับข้อมูลโดยอัตโนมัติหรือไม่? หรือมีบางอย่างที่ฉันสามารถทำได้หลังจากข้อเท็จจริงกับไฟล์ xlsx เพื่อปรับความกว้างของคอลัมน์

(ฉันใช้ไลบรารี OpenPyXL และสร้างไฟล์. xlsx - หากสิ่งนั้นสร้างความแตกต่าง)

ขอขอบคุณ.


1
ดูไม่เป็นไปได้ในขณะนี้โปรดเปิดปัญหาสำหรับการปรับปรุงนี้ใน github (และอาจเป็น PR?) ดูไม่ยากที่จะทำ
Jeff

ขอบคุณเจฟฟ์ฉันส่งปัญหาแล้ว ฉันไม่แน่ใจว่าฉันจะมีเวลาดำดิ่งสู่ codebase ของแพนด้าเพื่อแก้ปัญหานี้หรือไม่ แต่คุณไม่มีทางรู้ :)
badideas

อ๋อ .... เห็นปัญหาของคุณแล้ว ..... แสดงความคิดเห็นเกี่ยวกับปัญหานี้หากคุณต้องการความช่วยเหลือ! (โดยพื้นฐานแล้วจำเป็นต้องส่งอาร์กิวเมนต์ที่เป็นทางเลือกไปให้ซึ่งto_excelอาจcol_style=dictมีองค์ประกอบสไตล์ส่วนหัว col (แทนที่จะเป็นค่าเริ่มต้นheader_styleซึ่งดูเหมือนว่าจะเข้ารหัสยากในขณะนี้
Jeff

คำตอบ:


64

แรงบันดาลใจจากคำตอบของผู้ใช้ 6178746ฉันมีสิ่งต่อไปนี้:

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

8
FYI: ในกรณีของฉันฉันต้องใช้ "index = False" ในการเรียก "df.to_excel (... )" มิฉะนั้นคอลัมน์จะปิดโดย 1
denvar

1
ใช่ฉันต้องเพิ่ม df.to_excel ด้วย (writer, sheet_name = sheetname, index = False)
Heikki Pulkkinen

2
ถ้าคุณไม่สามารถใช้ดัชนี = false (เพราะคุณมี multiindex แถว) แล้วคุณจะได้รับความลึกของระดับดัชนีที่มี df.index.nlevels worksheet.set_column(idx+nlevels, idx+nlevels, max_len)แล้วใช้นี้เพื่อเพิ่มในการโทรคอลัมน์ชุดของคุณ: มิฉะนั้นความยาวจะถูกคำนวณสำหรับคอลัมน์แรกของเฟรมจากนั้นนำไปใช้กับคอลัมน์แรกใน excel ซึ่งอาจเป็นดัชนี
24

1
สำหรับใครก็ตามที่ยังคงมองหาคำตอบนี้enumerate(df)ควรenumerate(df.columns)ทำซ้ำในแต่ละคอลัมน์ในdf.
Dascienz

2
@Dascienz เช่นเดียวกับการวนซ้ำไปซ้ำมาdictจริง ๆ บนคีย์ในdict(คุณไม่จำเป็นต้องพูดด้วยตนเองdict.keys()) โดยทำpd.DataFrameซ้ำบนคอลัมน์ซ้ำ df.columnsคุณไม่จำเป็นต้องไปด้วยตนเองย้ำกว่า
alichaudry

29

ฉันโพสต์สิ่งนี้เนื่องจากฉันเพิ่งประสบปัญหาเดียวกันและพบว่าเอกสารอย่างเป็นทางการสำหรับ Xlsxwriter และแพนด้ายังคงมีฟังก์ชันนี้อยู่ในรายการว่าไม่รองรับ ฉันแฮ็คโซลูชันที่ช่วยแก้ปัญหาที่ฉันพบ โดยพื้นฐานแล้วฉันเพียงแค่วนซ้ำในแต่ละคอลัมน์และใช้ 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()

1
ทางออกที่ดี. ฉันชอบวิธีที่คุณใช้แพนด้าแทนแพ็คเกจอื่น

ฉันคิดว่าคุณต้องการ()ภายในฟังก์ชัน max: max (column_len (), len (col)) +
2`

23

มีแพ็คเกจดีๆที่ฉันเริ่มใช้เมื่อเร็ว ๆ นี้ชื่อว่า 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. ดูเอกสาร

อัปเดต 2

นี่คือตัวอย่างโค้ดที่ใช้ได้กับ StyleFrame 3.xx

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 อาจใช้งานง่าย แต่ฉันไม่เห็นว่า "โดยค่าเริ่มต้นความกว้างของคอลัมน์จะปรับอัตโนมัติ" อย่างไร เมื่อฉันเรียกใช้ตัวอย่างโค้ดที่คุณให้มาคอลัมน์ทั้งหมดมีความกว้างเท่ากันและทั้งสามส่วนหัวจะถูกรวมเข้าด้วยกัน นอกจากนี้ข้อมูลตัวอย่างของคุณยังถูกเลือกไม่ดีเนื่องจากข้อมูลเหล่านี้มีความกว้างเกือบเท่ากันตามธรรมชาติ เพื่อแสดงให้เห็นถึงการปรับอัตโนมัติอย่างแท้จริงคุณควรเลือกข้อมูลที่กว้างมากและข้อมูลแคบ ๆ เมื่อฉันทำสิ่งนี้ด้วยตัวเองความกว้างของคอลัมน์จะยังคงเหมือนเดิมทุกประการ ไม่มีการปรับเปลี่ยนใด ๆ
John Y

บางทีเมื่อถึงจุดหนึ่งในประวัติของ StyleFrame ความกว้างของคอลัมน์จะถูกปรับโดยอัตโนมัติตามค่าเริ่มต้น แต่อย่างน้อยวันนี้คุณต้องระบุคอลัมน์หรือคอลัมน์ที่คุณต้องการปรับในbest_fitพารามิเตอร์ นอกจากนี้เมื่อฉันพยายามนี้ผมได้ผลลัพธ์ที่น่าสงสารมาก
John Y

ความกว้างดูเหมือนจะปิด 1 คอลัมน์ ฉันพยายามเปิดใช้งานและปิดใช้งานindexพารามิเตอร์ แต่ไม่มีลูกเต๋า

1
ขอบคุณ! สำหรับผู้ที่กำลังมองหา: คุณจะเพิ่มสไตล์ให้กับส่วนหัวได้อย่างไร: sf.apply_headers_style(Styler(bold=False))ฉันใช้เวลานานในการคิดออก และในคำสั่งนำเข้าfrom StyleFrame import StyleFrame, Styler. นี่คือตัวเลือกทั้งหมดนอกเหนือจากตัวหนา: styleframe.readthedocs.io/th/2.0.5/…
Nikhil VJ

2
@Hagbard ในเวอร์ชัน 3 การนำเข้าควรเป็นfrom styleframe import StyleFrameไปตามอนุสัญญาชื่อ
PEP8

21

อาจไม่มีวิธีอัตโนมัติในตอนนี้ แต่เมื่อคุณใช้ openpyxl บรรทัดต่อไปนี้ (ดัดแปลงมาจากคำตอบอื่นโดยผู้ใช้Bufkeเกี่ยวกับวิธีการทำด้วยตนเอง ) ช่วยให้คุณสามารถระบุค่าที่มีเหตุผล (ในความกว้างของอักขระ):

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

แพนด้าเอ็นจิน ExcelWriter เริ่มต้นที่ใช้มีการเปลี่ยนแปลงตั้งแต่ปี 2013 เป็น Xlsxwriter ซึ่งไม่มีcolumn_dimensionsแอตทริบิวต์ หากคุณต้องการใช้ openpyxl ต่อไปเพียงระบุเมื่อสร้างนักเขียนโดยใช้pd.ExcelWriter(excel_filename, engine='openpyxl')
ojdo

@Sunil: ตรวจสอบคำตอบอื่น ๆ ที่ใช้Xlsxwriterเป็นเครื่องมือเพื่อดูวิธีระบุความกว้างของคอลัมน์ด้วยเครื่องมือเริ่มต้นของวันนี้
ojdo

12

ด้วยการใช้แพนด้าและ xlsxwriter คุณสามารถทำงานของคุณได้โค้ดด้านล่างนี้จะทำงานได้อย่างสมบูรณ์แบบใน Python 3.x สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับการทำงานกับ 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()

5

ปรับความยาวคอลัมน์ทั้งหมดแบบไดนามิก

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)

ปรับคอลัมน์ด้วยตนเองโดยใช้ชื่อคอลัมน์

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

5

ในที่ทำงานฉันมักจะเขียนดาต้าเฟรมลงในไฟล์ 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' ไม่มีแอตทริบิวต์ 'to_excel' คิดว่ามันเกี่ยวข้องกับวิธีสร้าง "dataframe_list" Mine เป็นรายการที่มีชื่อดาต้าเฟรม 6 ชื่อ
user3019973

ใช่ "dataframe_list" ควรมีดาต้าเฟรมและไม่ใช่ชื่อดาต้าเฟรม
rafat.ch

4

ฉันพบว่าการปรับคอลัมน์โดยยึดตามส่วนหัวของคอลัมน์นั้นมีประโยชน์มากกว่าเนื้อหาของคอลัมน์

การใช้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

3

การรวมคำตอบและความคิดเห็นอื่น ๆ และการสนับสนุนหลายดัชนี:

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

วิธีแก้ไขที่ง่ายที่สุดคือการระบุความกว้างของคอลัมน์ในวิธี set_column

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

1
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
รหัสไม่เพียง แต่ตอบคำถามที่คุณต้องเพิ่มคำอธิบายหรือใช้เวลาอ่านเอกสารเกี่ยวกับ ฉันจะเขียนคำตอบที่ดีได้อย่างไร
Umutambyi Gad

1
สวัสดี! แม้ว่ารหัสนี้จะช่วยแก้ปัญหาได้รวมถึงคำอธิบายว่าทำไมจึงแก้ปัญหานี้ได้จะช่วยปรับปรุงคุณภาพของโพสต์ของคุณได้อย่างแท้จริงและอาจส่งผลให้มีการโหวตเพิ่มขึ้น จำไว้ว่าคุณกำลังตอบคำถามสำหรับผู้อ่านในอนาคตไม่ใช่แค่คนที่ถามตอนนี้ โปรดแก้ไขคำตอบของคุณเพื่อเพิ่มคำอธิบายและระบุข้อ จำกัด และสมมติฐานที่ใช้
Brian

0

ใช่มีบางอย่างที่คุณสามารถทำได้หลังจากข้อเท็จจริงกับไฟล์ xlsx เพื่อปรับความกว้างของคอลัมน์ ใช้ xlwings เพื่อautofitคอลัมน์ เป็นวิธีแก้ปัญหาที่ค่อนข้างง่ายดูหกบรรทัดสุดท้ายของโค้ดตัวอย่าง ข้อดีของขั้นตอนนี้คือคุณไม่ต้องกังวลเกี่ยวกับขนาดฟอนต์ประเภทฟอนต์หรือสิ่งอื่นใด ความต้องการ: การติดตั้ง Excel

import pandas as pd
import xlwings as xw

report_file = "test.xlsx"

df1 = pd.DataFrame([
    ('this is a long term1', 1, 1, 3),
    ('this is a long term2', 1, 2, 5),
    ('this is a long term3', 1, 1, 6),
    ('this is a long term2', 1, 1, 9),
    ], columns=['term', 'aaaa', 'bbbbbbb', "cccccccccccccccccccccccccccccccccccccccccccccc"])

writer = pd.ExcelWriter(report_file, engine="xlsxwriter")
df1.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("B:D", cell_format=num_format)
writer.save()

# Autofit all columns with xlwings.
app = xw.App(visible=False)
wb = xw.Book(report_file)

for ws in wb.sheets:
    ws.autofit(axis="columns")

wb.save(report_file)
app.quit()

1
ทำงานได้เฉพาะบน Windows และ MacOS เท่านั้นไม่ใช่บน Linux
Guido
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.