xlabel python xlsxwriter

xlabel - subplot title python



Simular la columna de ajuste automático en xslxwriter (7)

Me gustaría simular la función de ajuste automático de Excel en xlsxwriter de Python. Según esta URL, no se admite directamente: http://xlsxwriter.readthedocs.io/worksheet.html

Sin embargo, debería ser bastante sencillo recorrer cada celda de la hoja y determinar el tamaño máximo de la columna y usar worksheet.set_column (fila, columna, ancho) para establecer el ancho.

Las complicaciones que me impide escribir esto son:

  1. Esa URL no especifica cuáles son las unidades para el tercer argumento de set_column.
  2. No puedo encontrar una manera de medir el ancho del elemento que quiero insertar en la celda.
  3. xlsxwriter no parece tener un método para leer una celda en particular. Esto significa que necesito hacer un seguimiento del ancho de cada celda mientras escribo la celda. Sería mejor si pudiera recorrer todas las celdas, de esa manera se podría escribir una rutina genérica.

Aquí hay una versión del código que admite MultiIndex para filas y columnas. No es bonito pero funciona para mí. Se expande en la respuesta @ 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)


Como regla general, desea que el ancho de las columnas sea un poco más grande que el tamaño de la cadena más larga de la columna. El valor de 1 unidad de las columnas xlsxwriter es aproximadamente igual al ancho de un carácter. Por lo tanto, puede simular el ajuste automático configurando cada columna al número máximo de caracteres en esa columna.

Por ejemplo, tiendo a usar el siguiente código cuando trabajo con marcos de datos de pandas y xlsxwriter.

Primero encuentra el ancho máximo del índice, que es siempre la columna izquierda para que los pandas sobresalgan el marco de datos representado. Luego, devuelve el máximo de todos los valores y el nombre de la columna para cada una de las columnas restantes moviéndose de izquierda a derecha.

No debería ser demasiado difícil adaptar este código a los datos que esté utilizando.

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)


Estoy de acuerdo con Cole Diamond. Necesitaba hacer algo muy similar, funcionó bien para mí. donde self.columns es mi lista de columnas

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)


Hay otra solución para simular el ajuste automático que he encontrado en el sitio de Github de xlsxwriter . Lo modifiqué para devolver el tamaño aproximado del texto horizontal (ancho de columna) o del texto girado a 90 ° (altura de fila):

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

Esto no aborda el texto en negrita u otros elementos de formato que puedan afectar el tamaño del texto. De lo contrario, funciona bastante bien.

Para encontrar el ancho de sus columnas para la adaptación automática:

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


Mi versión que revisará la hoja de trabajo y establecerá automáticamente las longitudes de campo:

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)

simplemente llame a set_column_autowidth con la columna.


Recientemente me encontré con este mismo problema y esto es lo que se me ocurrió:

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

En mi ejemplo, r sería el número de la fila a la que está enviando, c sería el número de la columna a la que está enviando (ambos 0 indexados), y x sería el valor de la list que desea que esté en la celda.

la pieza ''{0}:{0}''.format(chr(c + ord(''A''))) toma el número de columna proporcionado y lo convierte a la letra de columna aceptada por xlsxwriter, por lo que si c = 0 set_column vería ''A:A'' , si c = 1 entonces vería ''B:B'' , y así sucesivamente.

la pieza len(str(x)) + 2 determina la longitud de la cadena que está intentando emitir y luego le agrega 2 para garantizar que la celda de Excel sea lo suficientemente ancha, ya que la longitud de la cadena no se correlaciona exactamente con el ancho de la célula. Es posible que desee jugar en lugar de agregar 2 o posiblemente más dependiendo de sus datos.

Las unidades que acepta xlsxwriter son un poco más difíciles de explicar. Cuando está en Excel y se desplaza sobre el lugar donde puede cambiar el ancho de la columna, verá Width: 8.43 (64 pixels) . En este ejemplo, la unidad que acepta es la 8.43 , que creo que son centímetros? Pero excel ni siquiera proporciona una unidad, al menos no explícitamente.

Nota: solo he probado esta respuesta en archivos de Excel que contienen 1 fila de datos. Si tendrá varias filas, tendrá que tener una forma de determinar qué fila tendrá la información "más larga" y solo aplicarla a esa fila. Pero si cada columna tendrá aproximadamente el mismo tamaño, independientemente de la fila, esto debería funcionar bien para usted.

Buena suerte y espero que esto ayude!


La respuesta de Cole Diamond es impresionante. Acabo de actualizar la subrutina para manejar filas y columnas de múltiples índices.

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]