Je voudrais simuler la fonction d'ajustement automatique d'Excel dans xlsxwriter de Python. Selon cette URL, il n'est pas directement pris en charge: http://xlsxwriter.readthedocs.io/worksheet.html
Cependant, il devrait être assez simple de parcourir chaque cellule de la feuille, de déterminer la taille maximale de la colonne et d’utiliser simplement worksheet.set_column (row, col, width) pour définir la largeur.
Les complications qui m'empêchent d'écrire ceci sont:
En règle générale, vous voulez que la largeur des colonnes soit un peu plus grande que la taille de la chaîne la plus longue de la colonne. Le avec une unité des colonnes xlsxwriter est à peu près égal à la largeur d’un caractère. Vous pouvez donc simuler un ajustement automatique en définissant chaque colonne sur le nombre maximal de caractères de cette colonne.
Par exemple, j’ai tendance à utiliser le code ci-dessous lorsque je travaille avec des bases de données pandas et xlsxwriter.
Il trouve d’abord la largeur maximale de l’index, qui est toujours la colonne de gauche d’un cadre de données rendu pandas à Excel. Ensuite, il renvoie le maximum de toutes les valeurs et le nom de la colonne pour chacune des colonnes restantes en se déplaçant de gauche à droite.
Il ne devrait pas être trop difficile d’adapter ce code aux données que vous utilisez.
def get_col_widths(dataframe):
# First we find the maximum length of the index column
idx_max = max([len(str(s)) for s in dataframe.index.values] + [len(str(dataframe.index.name))])
# Then, we concatenate this to the max of the lengths of column name and its values for each column, left to right
return [idx_max] + [max([len(str(s)) for s in dataframe[col].values] + [len(col)]) for col in dataframe.columns]
for i, width in enumerate(get_col_widths(dataframe)):
worksheet.set_column(i, i, width)
Je suis d'accord avec Cole Diamond. J'avais besoin de faire quelque chose de très similaire, cela a bien fonctionné pour moi. où self.columns est ma liste de colonnes
def set_column_width(self):
length_list = [len(x) for x in self.columns]
for i, width in enumerate(length_list):
self.worksheet.set_column(i, i, width)
Il existe une autre solution de contournement de simulation automatique que j'ai trouvée sur le site Github de xlsxwriter . Je l'ai modifié pour retourner la taille approximative du texte horizontal (largeur de colonne) ou du texte pivoté à 90 ° (hauteur de la ligne):
from PIL import ImageFont
def get_cell_size(value, font_name, font_size, dimension="width"):
""" value: cell content
font_name: The name of the font in the target cell
font_size: The size of the font in the target cell """
font = ImageFont.truetype(font_name, size=font_size)
(size, h) = font.getsize(str(value))
if dimension == "height":
return size * 0.92 # fit value experimentally determined
return size * 0.13 # fit value experimentally determined
Cela ne concerne pas le texte en gras ou d'autres éléments de format pouvant affecter la taille du texte. Sinon, ça marche plutôt bien.
Pour trouver la largeur de vos colonnes pour l'ajustement automatique:
def get_col_width(data, font_name, font_size, min_width=1):
""" Assume 'data' to be an iterable (rows) of iterables (columns / cells)
Also, every cell is assumed to have the same font and font size.
Returns a list with the autofit-width per column """
colwidth = [min_width for col in data[0]]
for x, row in enumerate(data):
for y, value in enumerate(row):
colwidth[y] = max(colwidth[y], get_cell_size(value, font_name, font_size))
return colwidth
J'ai récemment rencontré le même problème et voici ce que j'ai proposé:
r = 0
c = 0
for x in list:
worksheet.set_column('{0}:{0}'.format(chr(c + ord('A'))), len(str(x)) + 2)
worksheet.write(r, c, x)
c += 1
Dans mon exemple, r
correspond au numéro de la ligne vers laquelle vous écrivez, c
correspond au numéro de la colonne sur laquelle vous indiquez (les deux indexés), et x
correspond à la valeur de list
que vous souhaitez insérer dans la cellule.
la pièce '{0}:{0}'.format(chr(c + ord('A')))
prend le numéro de colonne fourni et la convertit en lettre de colonne acceptée par xlsxwriter. Ainsi, si c = 0
set_column
verrait 'A:A'
, si c = 1
, il verrait aussi 'B:B'
, etc.
la pièce len(str(x)) + 2
détermine la longueur de la chaîne que vous essayez de générer, puis ajoute 2 unités pour garantir que la cellule Excel est suffisamment large, car la longueur de la chaîne ne correspond pas exactement à la largeur de la cellule. Vous voudrez peut-être jouer avec plutôt que vous ajoutez 2
ou éventuellement plus en fonction de vos données.
Les unités acceptées par xlsxwriter sont un peu plus difficiles à expliquer. Lorsque vous êtes dans Excel et que vous survolez la position où vous pouvez modifier la largeur de la colonne, vous verrez Width: 8.43 (64 pixels)
. Dans cet exemple, l’unité qu’elle accepte est le 8.43
, que j’estime être le centimètre? Mais Excel ne fournit même pas une unité, du moins pas explicitement.
Remarque: Je n'ai essayé cette réponse que sur des fichiers Excel contenant une ligne de données. Si vous avez plusieurs lignes, vous aurez besoin d'un moyen de déterminer quelle ligne contiendra l'information la plus longue et de l'appliquer uniquement à cette ligne. Mais si chaque colonne aura à peu près la même taille quelle que soit la ligne, cela devrait fonctionner correctement pour vous.
Bonne chance et j'espère que cela aide!
Voici une version du code qui prend en charge MultiIndex pour les lignes et les colonnes - ce n’est pas joli mais fonctionne pour moi. Il développe la réponse @ cole-diamond:
def _xls_make_columns_wide_enough(dataframe, worksheet, padding=1.1, index=True):
def get_col_widths(dataframe, padding, index):
max_width_idx = []
if index and isinstance(dataframe.index, pd.MultiIndex):
# Index name lengths
max_width_idx = [len(v) for v in dataframe.index.names]
# Index value lengths
for column, content in enumerate(dataframe.index.levels):
max_width_idx[column] = max(max_width_idx[column],
max([len(str(v)) for v in content.values]))
Elif index:
max_width_idx = [
max([len(str(s))
for s in dataframe.index.values] + [len(str(dataframe.index.name))])
]
if isinstance(dataframe.columns, pd.MultiIndex):
# Take care of columns - headers first.
max_width_column = [0] * len(dataframe.columns.get_level_values(0))
for level in range(len(dataframe.columns.levels)):
values = dataframe.columns.get_level_values(level).values
max_width_column = [
max(v1, len(str(v2))) for v1, v2 in Zip(max_width_column, values)
]
# Now content.
for idx, col in enumerate(dataframe.columns):
max_width_column[idx] = max(max_width_column[idx],
max([len(str(v)) for v in dataframe[col].values]))
else:
max_width_column = [
max([len(str(s)) for s in dataframe[col].values] + [len(col)])
for col in dataframe.columns
]
return [round(v * padding) for v in max_width_idx + max_width_column]
for i, width in enumerate(get_col_widths(dataframe, padding, index)):
worksheet.set_column(i, i, width)
La réponse de Cole Diamond est géniale. Je viens de mettre à jour le sous-programme pour gérer les lignes et les colonnes multi-index.
def get_col_widths(dataframe):
# First we find the maximum length of the index columns
idx_max = [max([len(str(s)) for s in dataframe.index.get_level_values(idx)] + [len(str(idx))]) for idx in dataframe.index.names]
# Then, we concatenate this to the max of the lengths of column name and its values for each column, left to right
return idx_max + [max([len(str(s)) for s in dataframe[col].values] + \
[len(str(x)) for x in col] if dataframe.columns.nlevels > 1 else [len(str(col))]) for col in dataframe.columns]
Ma version qui va parcourir la feuille de calcul et définir automatiquement la longueur des champs:
from typing import Optional
from xlsxwriter.worksheet import (
Worksheet, cell_number_Tuple, cell_string_Tuple)
def get_column_width(worksheet: Worksheet, column: int) -> Optional[int]:
"""Get the max column width in a `Worksheet` column."""
strings = getattr(worksheet, '_ts_all_strings', None)
if strings is None:
strings = worksheet._ts_all_strings = sorted(
worksheet.str_table.string_table,
key=worksheet.str_table.string_table.__getitem__)
lengths = set()
for row_id, colums_dict in worksheet.table.items(): # type: int, dict
data = colums_dict.get(column)
if not data:
continue
if type(data) is cell_string_Tuple:
iter_length = len(strings[data.string])
if not iter_length:
continue
lengths.add(iter_length)
continue
if type(data) is cell_number_Tuple:
iter_length = len(str(data.number))
if not iter_length:
continue
lengths.add(iter_length)
if not lengths:
return None
return max(lengths)
def set_column_autowidth(worksheet: Worksheet, column: int):
"""
Set the width automatically on a column in the `Worksheet`.
!!! Make sure you run this function AFTER having all cells filled in
the worksheet!
"""
maxwidth = get_column_width(worksheet=worksheet, column=column)
if maxwidth is None:
return
worksheet.set_column(first_col=column, last_col=column, width=maxwidth)
il suffit d'appeler set_column_autowidth
avec la colonne.