Чи є спосіб автоматичного регулювання ширини стовпців Excel за допомогою pandas.ExcelWriter?


104

Мене просять створити кілька звітів Excel. В даний час я досить активно використовую pandas для своїх даних, тому, природно, я хотів би використовувати метод pandas.ExcelWriter для створення цих звітів. Однак фіксована ширина стовпців є проблемою.

Код, який я маю на сьогоднішній день, досить простий. Скажімо, у мене є фрейм даних під назвою 'df':

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

Я переглядав код панд, і насправді не бачу жодних варіантів встановлення ширини стовпців. Чи існує якийсь фокус у Всесвіті, щоб зробити його таким, щоб стовпці автоматично підлаштовувалися під дані? Або я можу щось зробити після того, як зробити файл xlsx, щоб відрегулювати ширину стовпців?

(Я використовую бібліотеку OpenPyXL і створюю файли .xlsx - якщо це має значення.)

Дякую.


1
На даний момент це не здається можливим, будь ласка, відкрийте випуск цього вдосконалення на github (і, можливо, PR?). не виглядає так складно.
Джефф,

дякую Джеффе, я подав питання. я не впевнений, чи
встигну

так .... побачив вашу проблему ..... прокоментуйте проблему, якщо вам потрібна допомога! (по суті потрібно передати необов’язковий аргумент to_excel, можливо, col_style=dictякий містить елементи стилю заголовка col (а не за замовчуванням, header_styleяке, здається, жорстко закодовано зараз
Джефф,

Відповіді:


59

Натхненний відповіддю користувача 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 (автор, ім'я аркуша = ім'я аркуша, індекс = False)
Хейккі Пулкінен

2
Якщо ви не можете використовувати індекс = False (бо у вас є мультііндексний за строками), то ви можете отримати глибину рівня індексу з df.index.nlevels , а потім використовувати це , щоб додати до вашого викликом колонки набору: worksheet.set_column(idx+nlevels, idx+nlevels, max_len). В іншому випадку довжина обчислюється для першого стовпця кадру, а потім застосовується до першого стовпця в Excel, що, ймовірно, є індексом.
ac24

1
Для тих, хто все ще шукає цю відповідь, enumerate(df)слід, enumerate(df.columns)оскільки ви перебираєте кожен стовпець у df.
Dascienz,

2
@Dascienz так само, як ітерація над dictфактично ітерацією по клавішах у dict(не потрібно вручну вимовляти dict.keys()), ітерація над ітераціями по pd.DataFrameстовпцях. Вам не потрібно перебирати вручну df.columns.
alichaudry

28

Я публікую це, оскільки я щойно зіткнувся з тим самим випуском і виявив, що в офіційній документації для 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`
Сердія

21

Напевно, зараз немає автоматичного способу зробити це, але, коли ви використовуєте openpyxl, наступний рядок (адаптований з іншої відповіді користувача Буфке про те, як це робити вручну ) дозволяє вказати розумне значення (у ширині символів):

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

Панди двигуна ExcelWriter за замовчуванням змінилися з 2013 року на Xlsxwriter, який не містить column_dimensionsатрибута. Якщо ви хочете продовжувати використовувати openpyxl, просто вкажіть його під час створення редактора за допомогоюpd.ExcelWriter(excel_filename, engine='openpyxl')
ojdo

@Sunil: перевірте інші відповіді за допомогою Xlsxwriter як движок, щоб побачити, як вказати ширину стовпця за сьогоднішнього движка за замовчуванням.
ojdo

21

Є приємний пакет, який я почав використовувати нещодавно під назвою 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/en/2.0.5/…
Nikhil VJ

1
@Hagbard, починаючи з версії 3, імпорт повинен здійснюватись from styleframe import StyleFrame, щоб відповідати конвенціям
імен PEP8

11

Використовуючи pandas і 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

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

4

На роботі я завжди пишу фрейми даних, щоб перевершити файли. Отже, замість того, щоб писати один і той самий код знову і знову, я створив модуль. Тепер я просто імпортую його і використовую для написання та форматування файлів 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". Мій список із 6 іменами
фреймів даних

Так, "dataframe_list" повинен містити фрейми даних, а не імена фреймів даних.
rafat.ch

2

Поєднуючи інші відповіді та коментарі, а також підтримуючи мультиіндекси:

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
коди лише не відповідають на запитання, вам потрібно додати деякі пояснення або зайняти час і прочитати документацію про те, як написати хорошу відповідь?
Гад,

1
Привіт! Хоча цей код може вирішити питання, включаючи пояснення того, як і чому це вирішує проблему, справді допомогло б поліпшити якість вашої публікації, і, можливо, призведе до більшої кількості голосів. Пам’ятайте, що ви відповідаєте на запитання читачам у майбутньому, а не лише тому, хто задає зараз. Будь ласка, відредагуйте свою відповідь, щоб додати пояснення та вказати, які обмеження та припущення застосовуються.
Брайан,

0

Так, є щось, що ви можете зробити фактично у файлі xlsx, щоб відрегулювати ширину стовпців. Використовуйте xlwings для автоматичного встановлення стовпців . Це досить просте рішення, див. Шість останніх рядків прикладу коду. Перевага цієї процедури полягає в тому, що вам не доведеться турбуватися про розмір шрифту, тип шрифту чи щось інше. Вимога: інсталяція 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()
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.