codigo - ¿Cómo puedo imprimir bastante las tablas ASCII con Python?
imprimir codigo ascii en python (13)
Estoy buscando una biblioteca de Python para imprimir tablas como esta:
=======================
| column 1 | column 2 |
=======================
| value1 | value2 |
| value3 | value4 |
=======================
Me pareció asciitable pero no hace los bordes, etc. No necesito ningún formato complejo de elementos de datos, solo son cadenas. Lo necesito para auto-tamaño de columnas.
¿Existe tal cosa o necesito dedicar unos minutos a escribir la mía?
Acabo de lanzar asciiplotlib , y también tiene bonitas tablas. Por ejemplo, esto
import asciiplotlib as apl
data = [
[["a", "bb", "ccc"]],
[[1, 2, 3], [613.23236243236, 613.23236243236, 613.23236243236]],
]
fig = apl.figure()
fig.table(data, border_style="thin", ascii_mode=True, padding=(0, 1), alignment="lcr")
fig.show()
te lleva
+-----------------+-----------------+-----------------+
| a | bb | ccc |
+=================+=================+=================+
| 1 | 2 | 3 |
+-----------------+-----------------+-----------------+
| 613.23236243236 | 613.23236243236 | 613.23236243236 |
+-----------------+-----------------+-----------------+
De forma predeterminada, la tabla se representa con caracteres de dibujo de cuadro Unicode,
┌─────────────────┬─────────────────┬─────────────────┐
│ a │ bb │ ccc │
╞═════════════════╪═════════════════╪═════════════════╡
│ 1 │ 2 │ 3 │
├─────────────────┼─────────────────┼─────────────────┤
│ 613.23236243236 │ 613.23236243236 │ 613.23236243236 │
└─────────────────┴─────────────────┴─────────────────┘
las tablas de apl son muy configurables; echa un vistazo a las pruebas para ver más ejemplos.
Aquí está mi solución:
def make_table(columns, data):
"""Create an ASCII table and return it as a string.
Pass a list of strings to use as columns in the table and a list of
dicts. The strings in ''columns'' will be used as the keys to the dicts in
''data.''
Not all column values have to be present in each data dict.
>>> print(make_table(["a", "b"], [{"a": "1", "b": "test"}]))
| a | b |
|----------|
| 1 | test |
"""
# Calculate how wide each cell needs to be
cell_widths = {}
for c in columns:
values = [str(d.get(c, "")) for d in data]
cell_widths[c] = len(max(values + [c]))
# Used for formatting rows of data
row_template = "|" + " {} |" * len(columns)
# CONSTRUCT THE TABLE
# The top row with the column titles
justified_column_heads = [c.ljust(cell_widths[c]) for c in columns]
header = row_template.format(*justified_column_heads)
# The second row contains separators
sep = "|" + "-" * (len(header) - 2) + "|"
# Rows of data
rows = []
for d in data:
fields = [str(d.get(c, "")).ljust(cell_widths[c]) for c in columns]
row = row_template.format(*fields)
rows.append(row)
return "/n".join([header, sep] + rows)
Aquí hay una pequeña función rápida y sucia que escribí para mostrar los resultados de las consultas SQL que solo puedo hacer sobre una API SOAP. Espera una entrada de una secuencia de una o más namedtuples
como filas de la tabla. Si solo hay un registro, lo imprime de manera diferente.
Es útil para mí y podría ser un punto de partida para ti:
def pprinttable(rows):
if len(rows) > 1:
headers = rows[0]._fields
lens = []
for i in range(len(rows[0])):
lens.append(len(max([x[i] for x in rows] + [headers[i]],key=lambda x:len(str(x)))))
formats = []
hformats = []
for i in range(len(rows[0])):
if isinstance(rows[0][i], int):
formats.append("%%%dd" % lens[i])
else:
formats.append("%%-%ds" % lens[i])
hformats.append("%%-%ds" % lens[i])
pattern = " | ".join(formats)
hpattern = " | ".join(hformats)
separator = "-+-".join([''-'' * n for n in lens])
print hpattern % tuple(headers)
print separator
_u = lambda t: t.decode(''UTF-8'', ''replace'') if isinstance(t, str) else t
for line in rows:
print pattern % tuple(_u(t) for t in line)
elif len(rows) == 1:
row = rows[0]
hwidth = len(max(row._fields,key=lambda x: len(x)))
for i in range(len(row)):
print "%*s = %s" % (hwidth,row._fields[i],row[i])
Muestra de salida:
pkid | fkn | npi -------------------------------------+--------------------------------------+---- 405fd665-0a2f-4f69-7320-be01201752ec | 8c9949b9-552e-e448-64e2-74292834c73e | 0 5b517507-2a42-ad2e-98dc-8c9ac6152afa | f972bee7-f5a4-8532-c4e5-2e82897b10f6 | 0 2f960dfc-b67a-26be-d1b3-9b105535e0a8 | ec3e1058-8840-c9f2-3b25-2488f8b3a8af | 1 c71b28a3-5299-7f4d-f27a-7ad8aeadafe0 | 72d25703-4735-310b-2e06-ff76af1e45ed | 0 3b0a5021-a52b-9ba0-1439-d5aafcf348e7 | d81bb78a-d984-e957-034d-87434acb4e97 | 1 96c36bb7-c4f4-2787-ada8-4aadc17d1123 | c171fe85-33e2-6481-0791-2922267e8777 | 1 95d0f85f-71da-bb9a-2d80-fe27f7c02fe2 | 226f964c-028d-d6de-bf6c-688d2908c5ae | 1 132aa774-42e5-3d3f-498b-50b44a89d401 | 44e31f89-d089-8afc-f4b1-ada051c01474 | 1 ff91641a-5802-be02-bece-79bca993fdbc | 33d8294a-053d-6ab4-94d4-890b47fcf70d | 1 f3196e15-5b61-e92d-e717-f00ed93fe8ae | 62fa4566-5ca2-4a36-f872-4d00f7abadcf | 1
Ejemplo
>>> from collections import namedtuple
>>> Row = namedtuple(''Row'',[''first'',''second'',''third''])
>>> data = Row(1,2,3)
>>> data
Row(first=1, second=2, third=3)
>>> pprinttable([data])
first = 1
second = 2
third = 3
>>> pprinttable([data,data])
first | second | third
------+--------+------
1 | 2 | 3
1 | 2 | 3
Esto puede hacerse solo con módulos integrados de forma bastante compacta utilizando listas y comprensiones de cadenas. Acepta una lista de diccionarios con el mismo formato ...
def tableit(dictlist):
lengths = [ max(map(lambda x:len(x.get(k)), dictlist) + [len(k)]) for k in dictlist[0].keys() ]
lenstr = " | ".join("{:<%s}" % m for m in lengths)
lenstr += "/n"
outmsg = lenstr.format(*dictlist[0].keys())
outmsg += "-" * (sum(lengths) + 3*len(lengths))
outmsg += "/n"
outmsg += "".join(
lenstr.format(*v) for v in [ item.values() for item in dictlist ]
)
return outmsg
Hace mucho tiempo que leí esta pregunta y terminé de escribir mi propia bonita impresora para tablas: tabulate
.
Mi caso de uso es:
- Quiero un one-liner la mayor parte del tiempo
- que es lo suficientemente inteligente como para encontrar el mejor formato para mí
- y puede dar salida a diferentes formatos de texto sin formato
Dado su ejemplo, la grid
es probablemente el formato de salida más similar:
from tabulate import tabulate
print tabulate([["value1", "value2"], ["value3", "value4"]], ["column 1", "column 2"], tablefmt="grid")
+------------+------------+
| column 1 | column 2 |
+============+============+
| value1 | value2 |
+------------+------------+
| value3 | value4 |
+------------+------------+
Otros formatos compatibles son plain
(sin líneas), simple
(tablas simples de Pandoc), pipe
(como tablas en PHP Markdown Extra), orgtbl
(como tablas en el modo orgtbl
de Emacs), rst
(como tablas simples en reStructuredText). grid
y orgtbl
son fácilmente editables en Emacs.
En cuanto al rendimiento, tabulate
es ligeramente más lento que asciitable
, pero mucho más rápido que PrettyTable
y texttable
.
PD. También soy un gran fan de alinear números por una columna decimal . Así que esta es la alineación predeterminada para los números si hay alguno (overridable).
Por alguna razón, cuando incluí ''docutils'' en mis búsquedas de google me tropecé con texttable , que parece ser lo que estoy buscando.
Puedes probar BeautifulTable . Hace lo que quieres hacer. Aquí hay un ejemplo de su documentation
>>> from beautifultable import BeautifulTable
>>> table = BeautifulTable()
>>> table.column_headers = ["name", "rank", "gender"]
>>> table.append_row(["Jacob", 1, "boy"])
>>> table.append_row(["Isabella", 1, "girl"])
>>> table.append_row(["Ethan", 2, "boy"])
>>> table.append_row(["Sophia", 2, "girl"])
>>> table.append_row(["Michael", 3, "boy"])
>>> print(table)
+----------+------+--------+
| name | rank | gender |
+----------+------+--------+
| Jacob | 1 | boy |
+----------+------+--------+
| Isabella | 1 | girl |
+----------+------+--------+
| Ethan | 2 | boy |
+----------+------+--------+
| Sophia | 2 | girl |
+----------+------+--------+
| Michael | 3 | boy |
+----------+------+--------+
Sé que la pregunta es un poco vieja, pero aquí está mi intento de esto:
https://gist.github.com/lonetwin/4721748
Es un poco más fácil de leer en mi humilde opinión (aunque no distingue entre filas simples / múltiples como las soluciones de @ MattH, ni usa NamedTuples).
Si quieres una tabla con tramos de columna y fila, prueba la dashtable biblioteca
from dashtable import data2rst
table = [
["Header 1", "Header 2", "Header3", "Header 4"],
["row 1", "column 2", "column 3", "column 4"],
["row 2", "Cells span columns.", "", ""],
["row 3", "Cells/nspan rows.", "- Cells/n- contain/n- blocks", ""],
["row 4", "", "", ""]
]
# [Row, Column] pairs of merged cells
span0 = ([2, 1], [2, 2], [2, 3])
span1 = ([3, 1], [4, 1])
span2 = ([3, 3], [3, 2], [4, 2], [4, 3])
my_spans = [span0, span1, span2]
print(data2rst(table, spans=my_spans, use_headers=True))
Qué salidas:
+----------+------------+----------+----------+
| Header 1 | Header 2 | Header3 | Header 4 |
+==========+============+==========+==========+
| row 1 | column 2 | column 3 | column 4 |
+----------+------------+----------+----------+
| row 2 | Cells span columns. |
+----------+----------------------------------+
| row 3 | Cells | - Cells |
+----------+ span rows. | - contain |
| row 4 | | - blocks |
+----------+------------+---------------------+
Versión que usa w3m diseñada para manejar los tipos que acepta la versión de MattH:
import subprocess
import tempfile
import html
def pprinttable(rows):
esc = lambda x: html.escape(str(x))
sour = "<table border=1>"
if len(rows) == 1:
for i in range(len(rows[0]._fields)):
sour += "<tr><th>%s<td>%s" % (esc(rows[0]._fields[i]), esc(rows[0][i]))
else:
sour += "<tr>" + "".join(["<th>%s" % esc(x) for x in rows[0]._fields])
sour += "".join(["<tr>%s" % "".join(["<td>%s" % esc(y) for y in x]) for x in rows])
with tempfile.NamedTemporaryFile(suffix=".html") as f:
f.write(sour.encode("utf-8"))
f.flush()
print(
subprocess
.Popen(["w3m","-dump",f.name], stdout=subprocess.PIPE)
.communicate()[0].decode("utf-8").strip()
)
from collections import namedtuple
Row = namedtuple(''Row'',[''first'',''second'',''third''])
data1 = Row(1,2,3)
data2 = Row(4,5,6)
pprinttable([data1])
pprinttable([data1,data2])
resultados en:
┌───────┬─┐
│ first │1│
├───────┼─┤
│second │2│
├───────┼─┤
│ third │3│
└───────┴─┘
┌─────┬───────┬─────┐
│first│second │third│
├─────┼───────┼─────┤
│1 │2 │3 │
├─────┼───────┼─────┤
│4 │5 │6 │
└─────┴───────┴─────┘
Yo también escribí mi propia solución a esto. Traté de mantenerlo simple.
https://github.com/Robpol86/terminaltables
from terminaltables import AsciiTable
table_data = [
[''Heading1'', ''Heading2''],
[''row1 column1'', ''row1 column2''],
[''row2 column1'', ''row2 column2'']
]
table = AsciiTable(table_data)
print table.table
+--------------+--------------+
| Heading1 | Heading2 |
+--------------+--------------+
| row1 column1 | row1 column2 |
| row2 column1 | row2 column2 |
+--------------+--------------+
table.inner_heading_row_border = False
print table.table
+--------------+--------------+
| Heading1 | Heading2 |
| row1 column1 | row1 column2 |
| row2 column1 | row2 column2 |
+--------------+--------------+
table.inner_row_border = True
table.justify_columns[1] = ''right''
table.table_data[1][1] += ''/nnewline''
print table.table
+--------------+--------------+
| Heading1 | Heading2 |
+--------------+--------------+
| row1 column1 | row1 column2 |
| | newline |
+--------------+--------------+
| row2 column1 | row2 column2 |
+--------------+--------------+
Yo uso esta pequeña función de utilidad.
def get_pretty_table(iterable, header):
max_len = [len(x) for x in header]
for row in iterable:
row = [row] if type(row) not in (list, tuple) else row
for index, col in enumerate(row):
if max_len[index] < len(str(col)):
max_len[index] = len(str(col))
output = ''-'' * (sum(max_len) + 1) + ''/n''
output += ''|'' + ''''.join([h + '' '' * (l - len(h)) + ''|'' for h, l in zip(header, max_len)]) + ''/n''
output += ''-'' * (sum(max_len) + 1) + ''/n''
for row in iterable:
row = [row] if type(row) not in (list, tuple) else row
output += ''|'' + ''''.join([str(c) + '' '' * (l - len(str(c))) + ''|'' for c, l in zip(row, max_len)]) + ''/n''
output += ''-'' * (sum(max_len) + 1) + ''/n''
return output
print get_pretty_table([[1, 2], [3, 4]], [''header 1'', ''header 2''])
salida
-----------------
|header 1|header 2|
-----------------
|1 |2 |
|3 |4 |
-----------------
from sys import stderr, stdout
def create_table(table: dict, full_row: bool = False) -> None:
min_len = len(min((v for v in table.values()), key=lambda q: len(q)))
max_len = len(max((v for v in table.values()), key=lambda q: len(q)))
if min_len < max_len:
stderr.write("Table is out of shape, please make sure all columns have the same length.")
stderr.flush()
return
additional_spacing = 1
heading_separator = ''| ''
horizontal_split = ''| ''
rc_separator = ''''
key_list = list(table.keys())
rc_len_values = []
for key in key_list:
rc_len = len(max((v for v in table[key]), key=lambda q: len(str(q))))
rc_len_values += ([rc_len, [key]] for n in range(len(table[key])))
heading_line = (key + (" " * (rc_len + (additional_spacing + 1)))) + heading_separator
stdout.write(heading_line)
rc_separator += ("-" * (len(key) + (rc_len + (additional_spacing + 1)))) + ''+-''
if key is key_list[-1]:
stdout.flush()
stdout.write(''/n'' + rc_separator + ''/n'')
value_list = [v for vl in table.values() for v in vl]
aligned_data_offset = max_len
row_count = len(key_list)
next_idx = 0
newline_indicator = 0
iterations = 0
for n in range(len(value_list)):
key = rc_len_values[next_idx][1][0]
rc_len = rc_len_values[next_idx][0]
line = (''{:{}} '' + " " * len(key)).format(value_list[next_idx], str(rc_len + additional_spacing)) + horizontal_split
if next_idx >= (len(value_list) - aligned_data_offset):
next_idx = iterations + 1
iterations += 1
else:
next_idx += aligned_data_offset
if newline_indicator >= row_count:
if full_row:
stdout.flush()
stdout.write(''/n'' + rc_separator + ''/n'')
else:
stdout.flush()
stdout.write(''/n'')
newline_indicator = 0
stdout.write(line)
newline_indicator += 1
stdout.write(''/n'' + rc_separator + ''/n'')
stdout.flush()
Ejemplo:
table = {
"uid": ["0", "1", "2", "3"],
"name": ["Jon", "Doe", "Lemma", "Hemma"]
}
create_table(table)
Salida:
uid | name |
------+------------+-
0 | Jon |
1 | Doe |
2 | Lemma |
3 | Hemma |
------+------------+-