Pandalar ile Excel sütun genişliklerini otomatik olarak ayarlamanın bir yolu var mı?


107

Benden bazı Excel raporları oluşturmam isteniyor. Şu anda verilerim için oldukça yoğun bir şekilde panda kullanıyorum, bu nedenle doğal olarak bu raporları oluşturmak için pandas.ExcelWriter yöntemini kullanmak istiyorum. Ancak sabit sütun genişlikleri bir sorundur.

Şimdiye kadar sahip olduğum kod yeterince basit. "Df" adında bir veri çerçevem ​​olduğunu varsayalım:

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

Pandaların koduna bakıyordum ve sütun genişliklerini ayarlamak için herhangi bir seçenek görmüyorum. Evrende, sütunların verilere göre otomatik olarak ayarlanmasını sağlayacak bir numara var mı? Yoksa sütun genişliğini ayarlamak için xlsx dosyasına yapabileceğim bir şey var mı?

(OpenPyXL kitaplığını kullanıyorum ve .xlsx dosyaları oluşturuyorum - eğer bu herhangi bir fark yaratırsa.)

Teşekkür ederim.


1
şu anda mümkün görünmüyor, lütfen github'da (ve belki bir PR?) bu geliştirme için bir sorun açın. o kadar da zor görünmüyor.
Jeff

teşekkürler Jeff, sorunu gönderdim. Bunu çözmek için pandaların kod tabanına gerçekten
dalacak zamanım

evet .... sorununuzu gördüm ..... yardıma ihtiyacınız olursa konu hakkında yorum yapın! (aslında isteğe bağlı argüman geçmesine gerek to_excelbelki, col_style=dictöndeğer yerine (col başlık stili öğeleri içeren header_stylesert şimdi kodlanmış gibi görünüyor
Jeff

Yanıtlar:


61

User6178746'nın cevabından esinlenerek aşağıdakilere sahibim:

# 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
Bilginize: Benim durumumda "df.to_excel (...)" çağrısında "index = False" kullanmam gerekiyordu, yoksa sütunlar 1
denvar

1
evet, ayrıca df.to_excel (yazar, sayfa_adı = sayfa adı, dizin = Yanlış) eklemem gerekiyordu
Heikki Pulkkinen

2
Dizin kullanamıyorsanız = (satırların üzerinde bir multiindex çünkü) Yanlış, o zaman set sütun çağrısına üzerine eklemek için kullanabilir sonra df.index.nlevels ile endeks seviyesi derinliği elde edebilir: worksheet.set_column(idx+nlevels, idx+nlevels, max_len). Aksi takdirde uzunluk, çerçevenin ilk sütunu için hesaplanır ve daha sonra excel'deki muhtemelen indeks olan ilk sütuna uygulanır .
ac24

1
Hala bu yanıtı arayan herkes için, içindeki her sütunu yinelediğinizden beri enumerate(df)olmalıdır . enumerate(df.columns)df
Dascienz

2
@Dascienz aynı şekilde bir üzerinde yineleme yapmak dict, dict(manuel olarak söylemek zorunda değilsiniz) içindeki anahtarlar dict.keys()üzerinde yineleyerek pd.DataFrame, sütunlar üzerinde yinelemeler üzerinde yineleme yapar . Manuel olarak yinelemeniz gerekmez df.columns.
alichaudry

28

Bunu gönderiyorum çünkü aynı sorunla karşılaştım ve Xlsxwriter ve pandalar için resmi belgelerin hala desteklenmeyen olarak listelenen bu işlevselliğe sahip olduğunu gördüm. Karşılaştığım sorunu çözen bir çözümü bir araya getirdim. Temelde her sütunu yineliyorum ve sütun genişliğini == o sütunun içeriğinin maksimum uzunluğunu ayarlamak için çalışma sayfası.set_column'u kullanıyorum.

Ancak önemli bir not. Bu çözüm sütun başlıklarına uymuyor, sadece sütun değerlerine uyuyor. Bunun yerine başlıkları sığdırmanız gerekiyorsa, bu kolay bir değişiklik olmalıdır. Umarım bu birine yardımcı olur :)

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
Güzel çözüm. Başka bir paket yerine pandaları kullanma şeklini beğendim.

()Maksimum işlevin içinde ihtiyacınız olduğunu düşünüyorum : `max (column_len (), len (col)) + 2`
Serdia

22

Son zamanlarda kullanmaya başladığım StyleFrame isimli güzel bir paket var.

DataFrame'i alır ve çok kolay bir şekilde biçimlendirmenizi sağlar ...

varsayılan olarak sütun genişliği otomatik olarak ayarlanır.

Örneğin:

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

ayrıca sütun genişliğini de değiştirebilirsiniz:

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

GÜNCELLEME 1

1.4 sürümünde best_fitargüman eklendi StyleFrame.to_excel. Belgelere bakın .

GÜNCELLEME 2

İşte StyleFrame 3.xx için çalışan bir kod örneği

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 paketinin kullanımı kolay olabilir, ancak "varsayılan olarak sütun genişliğinin otomatik olarak ayarlandığını" anlamıyorum. Verdiğiniz kod örneğini çalıştırdığımda, tüm sütunlar aynı genişlikte ve üç başlık da sarılmış. Örnek verileriniz de, doğal olarak neredeyse aynı genişlikte olduğu için yetersiz bir şekilde seçilmiştir. Otomatik ayarlamayı gerçekten göstermek için, gerçekten geniş bir veri ve biraz dar veri seçmelisiniz. Bunu kendim için yaptığımda, sütun genişlikleri hala öncekiyle tamamen aynı. Herhangi bir ayarlama yoktu.
John Y

Belki StyleFrame geçmişinin bir noktasında, sütun genişlikleri varsayılan olarak otomatik olarak ayarlanmıştır, ancak en azından bugün, best_fitparametrede ayarlanmasını istediğiniz sütun veya sütunları belirtmeniz gerekir . Ayrıca bunu denediğimde çok kötü sonuçlar aldım .
John Y

genişlik 1 sütun kapalı görünüyor. indexParametreyi etkinleştirmeyi ve devre dışı bırakmayı denedim ama zar yok.

1
Teşekkürler! arayanlar için: Örneğin başlığa nasıl daha fazla stil eklersiniz: sf.apply_headers_style(Styler(bold=False))Bunu anlamam uzun zaman aldı. Ve ithalat beyannamesinde from StyleFrame import StyleFrame, Styler,. kalın dışındaki tüm seçenekler burada: styleframe.readthedocs.io/en/2.0.5/…
Nikhil VJ

1
@Hagbard sürüm 3'ten itibaren içe aktarma from styleframe import StyleFrame, PEP8 ad kurallarına uymak için yapılmalıdır
DeepSpace

21

Şu anda bunu yapmanın muhtemelen otomatik bir yolu yoktur, ancak openpyxl'i kullanırken, aşağıdaki satır (kullanıcı Bufke tarafından manuel olarak nasıl yapılacağına dair başka bir cevaptan uyarlanmıştır ), mantıklı bir değer (karakter genişliğinde) belirlemenize olanak tanır:

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

Varsayılan ExcelWriter motoru pandalarının kullandığı varsayılan ExcelWriter motoru, 2013'ten beri bir column_dimensionsöznitelik içermeyen Xlsxwriter olarak değiştirildi . Openpyxl'i kullanmaya devam etmek istiyorsanız, yazarı oluştururken bunu belirtmeniz yeterlidirpd.ExcelWriter(excel_filename, engine='openpyxl')
ojdo

@Sunil: XlsxwriterSütun genişliğini bugünün varsayılan motoruyla nasıl belirleyeceğinizi görmek için motor olarak kullanarak diğer yanıtları kontrol edin .
ojdo

12

Pandalar ve xlsxwriter kullanarak görevinizi yapabilirsiniz, aşağıdaki kod Python 3.x'te mükemmel şekilde çalışacaktır. XlsxWriter ile pandalarla çalışma hakkında daha fazla ayrıntı için bu bağlantı yararlı olabilir 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

Tüm sütun uzunluklarını dinamik olarak ayarlayın

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)

Sütun Adını kullanarak bir sütunu manuel olarak ayarlayın

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

Sütun Dizini'ni kullanarak bir sütunu manuel olarak ayarlayın

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

Yukarıdakilerden herhangi birinin başarısız olması durumunda

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

kurduğunuzdan emin olun xlsxwriter:

pip install xlsxwriter

4

Sütunu, sütun içeriğinden ziyade sütun başlığına göre ayarlamanın daha yararlı olduğunu buldum.

Kullanarak df.columns.values.tolist()sütun başlıklarının bir listesini oluşturuyorum ve sütunların genişliğini belirlemek için bu başlıkların uzunluklarını kullanıyorum.

Aşağıdaki kodun tamamına bakın:

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

İş yerinde, her zaman veri çerçevelerini excel dosyalarına yazıyorum. Bu yüzden aynı kodu defalarca yazmak yerine bir modül oluşturdum. Şimdi onu içe aktarıyorum ve excel dosyalarını yazmak ve biçimlendirmek için kullanıyorum. Yine de bir dezavantajı var, eğer veri çerçevesi çok büyükse uzun zaman alıyor. İşte kod:

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


Bu kodu çoğalttığımda şu hatayı aldım: AttributeError: 'str' nesnesinin 'to_excel' özniteliği yok. "Dataframe_list" oluşturulma biçimiyle bir ilgisi olduğunu düşünüyor. Benimki 6 dataframe isimli bir liste
user3019973

Evet, "dataframe_list" veri çerçevelerine sahip olmalı ve veri çerçevesi adlarına sahip olmamalıdır.
rafat.ch

2

Diğer cevapları ve yorumları birleştirmek ve ayrıca çoklu endeksleri desteklemek:

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

En kolay çözüm, set_column yönteminde sütun genişliğini belirlemektir.

    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
kodlar yalnızca bazı açıklamalar eklemeniz veya zaman ayırmanız ve nasıl iyi bir yanıt yazabilirim?
Umutambyi Gad

1
Merhaba! Bu kod soruyu çözebilirken, sorunun nasıl ve neden çözüldüğüne dair bir açıklama da dahil olmak üzere , gönderinizin kalitesini artırmaya gerçekten yardımcı olur ve muhtemelen daha fazla oy almanıza neden olur. Sadece şimdi soran kişi için değil, gelecekte okuyucular için soruyu yanıtladığınızı unutmayın. Açıklamalar eklemek ve hangi sınırlamaların ve varsayımların geçerli olduğuna dair bir gösterge vermek için lütfen cevabınızı düzenleyin .
Brian

0

Evet, xlsx dosyasına sütun genişliklerini ayarlamak için yapabileceğiniz bir şey var. İçin xlwings kullanın AutoFit sütunlar. Oldukça basit bir çözüm, örnek kodun son altı satırına bakın. Bu prosedürün avantajı, yazı tipi boyutu, yazı tipi türü veya başka herhangi bir şey hakkında endişelenmenize gerek olmamasıdır. Gereksinim: Excel kurulumu.

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

Sadece Windows ve MacOS'ta çalışıyor, Linux'ta değil
Guido
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.