Я ищу способ красиво распечатать такие таблицы:
=======================
| column 1 | column 2 |
=======================
| value1 | value2 |
| value3 | value4 |
=======================
Я нашел аскетичный библиотеку но она не делает границ и т.д. Мне не нужно сложное форматирование элементов данных, это просто строки. Мне это нужно для автоматического изменения размера столбцов.
Существуют ли другие библиотеки или методы, или мне нужно потратить несколько минут на написание собственных?
Ответы:
Я читал этот вопрос давно, и закончил писать свой собственный довольно-принтер для таблиц:
tabulate
.Мой вариант использования:
Учитывая ваш пример,
grid
вероятно, наиболее похожий формат вывода:from tabulate import tabulate print tabulate([["value1", "value2"], ["value3", "value4"]], ["column 1", "column 2"], tablefmt="grid") +------------+------------+ | column 1 | column 2 | +============+============+ | value1 | value2 | +------------+------------+ | value3 | value4 | +------------+------------+
Другие поддерживаемые форматы:
plain
(без строк),simple
(простые таблицы Pandoc),pipe
(например, таблицы в PHP Markdown Extra)orgtbl
(например, таблицы в организационном режиме Emacs)rst
(например, простые таблицы в reStructuredText).grid
иorgtbl
их легко редактировать в Emacs.С точки зрения
tabulate
производительности немного медленнееasciitable
, но намного быстрее, чемPrettyTable
иtexttable
.PS Еще я большой поклонник выравнивания чисел по десятичному столбцу . Так что это выравнивание по умолчанию для чисел, если они есть (переопределяемые).
источник
Вот небольшая быстрая и грязная функция, которую я написал для отображения результатов SQL-запросов, которые я могу выполнить только через SOAP API. Ожидается ввод последовательности из одного или нескольких
namedtuples
строк таблицы. Если запись только одна, она распечатывается по-другому.Это удобно для меня и может стать отправной точкой для вас:
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])
Пример вывода:
пример
>>> 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
источник
len(str(max(...)))
добавив ее в строку lens.append. Итак, теперь, если число в столбце шире, чем заголовок столбца, мы все еще в порядке. Кстати, MattH - милое использование аргумента «ключ» для max ()!По какой-то причине, когда я включил «documenttils» в свои поисковые запросы в Google, я наткнулся на текстовую таблицу , которая, похоже, была тем, что я ищу.
источник
Я тоже написал свое решение по этому поводу. Я старался не усложнять.
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 | +--------------+--------------+
источник
Я только что выпустил таблицы терминов для этой цели. Например, это
import termtables as tt tt.print( [[1, 2, 3], [613.23236243236, 613.23236243236, 613.23236243236]], header=["a", "bb", "ccc"], style=tt.styles.ascii_thin_double, padding=(0, 1), alignment="lcr" )
получает тебя
+-----------------+-----------------+-----------------+ | a | bb | ccc | +=================+=================+=================+ | 1 | 2 | 3 | +-----------------+-----------------+-----------------+ | 613.23236243236 | 613.23236243236 | 613.23236243236 | +-----------------+-----------------+-----------------+
По умолчанию таблица отображается с помощью символов рисования прямоугольников Unicode ,
┌─────────────────┬─────────────────┬─────────────────┐ │ a │ bb │ ccc │ ╞═════════════════╪═════════════════╪═════════════════╡ │ 1 │ 2 │ 3 │ ├─────────────────┼─────────────────┼─────────────────┤ │ 613.23236243236 │ 613.23236243236 │ 613.23236243236 │ └─────────────────┴─────────────────┴─────────────────┘
termtables очень настраиваемый; посмотрите тесты, чтобы увидеть больше примеров.
источник
Вы можете попробовать BeautifulTable . Он делает то, что вы хотите. Вот пример из его документации
>>> 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 | +----------+------+--------+
источник
/usr/local/lib/python3.8/site-packages/beautifultable/utils.py:113: FutureWarning: 'BeautifulTable.column_headers' has been deprecated in 'v1.0.0' and will be removed in 'v1.2.0'. Use 'BTColumnCollection.header' instead. warnings.warn(message, FutureWarning)
/usr/local/lib/python3.8/site-packages/beautifultable/utils.py:113: FutureWarning: 'BeautifulTable.append_row' has been deprecated in 'v1.0.0' and will be removed in 'v1.2.0'. Use 'BTRowCollection.append' instead. warnings.warn(message, FutureWarning)
Версия с использованием w3m разработана для обработки типов, которые принимает версия 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])
приводит к:
┌───────┬─┐ │ first │1│ ├───────┼─┤ │second │2│ ├───────┼─┤ │ third │3│ └───────┴─┘ ┌─────┬───────┬─────┐ │first│second │third│ ├─────┼───────┼─────┤ │1 │2 │3 │ ├─────┼───────┼─────┤ │4 │5 │6 │ └─────┴───────┴─────┘
источник
Если вам нужна таблица с промежутками между столбцами и строками, попробуйте мою сводную таблицу библиотеки
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))
Какие выходы:
+----------+------------+----------+----------+ | 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 | +----------+------------+---------------------+
источник
ERROR: Spans must be a list of lists
Я знаю, что вопрос немного устарел, но вот моя попытка:
https://gist.github.com/lonetwin/4721748
Это немного более читабельно IMHO (хотя он не различает одну / несколько строк, как решения @ MattH, и не использует NamedTuples).
источник
Я использую эту небольшую служебную функцию.
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'])
вывод
----------------- |header 1|header 2| ----------------- |1 |2 | |3 |4 | -----------------
источник
output += '|' + ''.join([h + ' ' * (l - len(h)) + '|' for h, l in zip(header, max_len)]) + '\n'
но не в разделительных линиях. Можно расширить эту строку-
с помощью чего-нибудь столь же простого, какoutput = '-' * (sum(max_len) + 1 + len(header)) + '\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)
источник
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()
Пример:
table = { "uid": ["0", "1", "2", "3"], "name": ["Jon", "Doe", "Lemma", "Hemma"] } create_table(table)
Вывод:
uid | name | ------+------------+- 0 | Jon | 1 | Doe | 2 | Lemma | 3 | Hemma | ------+------------+-
источник
Это можно сделать с помощью только встроенных модулей, достаточно компактно, используя списки и строковые представления. Принимает список словарей одного формата ...
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
источник