Ich suche nach einer Möglichkeit, Tabellen wie diese hübsch zu drucken:
=======================
| column 1 | column 2 |
=======================
| value1 | value2 |
| value3 | value4 |
=======================
Ich habe die Asciitable- Bibliothek gefunden, aber sie macht keine Grenzen usw. Ich brauche keine komplexe Formatierung von Datenelementen, sie sind nur Zeichenfolgen. Ich brauche es, um Spalten automatisch zu dimensionieren.
Gibt es andere Bibliotheken oder Methoden oder muss ich einige Minuten damit verbringen, meine eigenen zu schreiben?
Antworten:
Ich habe diese Frage vor langer Zeit gelesen und meinen eigenen hübschen Drucker für Tabellen fertig geschrieben :
tabulate
.Mein Anwendungsfall ist:
In Ihrem Beispiel
grid
ist wahrscheinlich das ähnlichste Ausgabeformat:from tabulate import tabulate print tabulate([["value1", "value2"], ["value3", "value4"]], ["column 1", "column 2"], tablefmt="grid") +------------+------------+ | column 1 | column 2 | +============+============+ | value1 | value2 | +------------+------------+ | value3 | value4 | +------------+------------+
Andere unterstützte Formate sind
plain
(keine Zeilen),simple
(einfache Pandoc-Tabellen)pipe
(wie Tabellen in PHP Markdown Extra)orgtbl
(wie Tabellen im Emacs-Organisationsmodus)rst
(wie einfache Tabellen in reStructuredText).grid
undorgtbl
sind in Emacs leicht zu bearbeiten.In Bezug auf die Leistung
tabulate
ist etwas langsamer alsasciitable
, aber viel schneller alsPrettyTable
undtexttable
.PS Ich bin auch ein großer Fan davon, Zahlen durch eine Dezimalspalte auszurichten . Dies ist also die Standardausrichtung für Zahlen, falls vorhanden (überschreibbar).
quelle
Hier ist eine schnelle und schmutzige kleine Funktion, die ich geschrieben habe, um die Ergebnisse von SQL-Abfragen anzuzeigen, die ich nur über eine SOAP-API durchführen kann. Es erwartet eine Eingabe einer Folge von einer oder mehreren
namedtuples
als Tabellenzeilen. Wenn es nur einen Datensatz gibt, wird dieser anders ausgedruckt.Es ist praktisch für mich und könnte ein Ausgangspunkt für Sie sein:
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])
Beispielausgabe:
Beispiel
>>> 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
quelle
len(str(max(...)))
in der Zeile object.append erstellt habe. Wenn also eine Zahl in einer Spalte breiter als die Spaltenüberschrift ist, sind wir immer noch gut. Übrigens, MattH - niedliche Verwendung des "Schlüssel" -Arguments für max ()!Aus irgendeinem Grund bin ich bei der Aufnahme von "docutils" in meine Google-Suche auf eine Texttabelle gestoßen , nach der ich anscheinend gesucht habe.
quelle
Auch ich habe meine eigene Lösung dafür geschrieben. Ich habe versucht, es einfach zu halten.
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 | +--------------+--------------+
quelle
Ich habe gerade Termtabellen für diesen Zweck veröffentlicht. Zum Beispiel dies
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" )
holt dich
+-----------------+-----------------+-----------------+ | a | bb | ccc | +=================+=================+=================+ | 1 | 2 | 3 | +-----------------+-----------------+-----------------+ | 613.23236243236 | 613.23236243236 | 613.23236243236 | +-----------------+-----------------+-----------------+
Standardmäßig wird die Tabelle mit Unicode gerendert Box-Zeichnen von Buchstaben ,
┌─────────────────┬─────────────────┬─────────────────┐ │ a │ bb │ ccc │ ╞═════════════════╪═════════════════╪═════════════════╡ │ 1 │ 2 │ 3 │ ├─────────────────┼─────────────────┼─────────────────┤ │ 613.23236243236 │ 613.23236243236 │ 613.23236243236 │ └─────────────────┴─────────────────┴─────────────────┘
Termtabellen sind sehr konfigurierbar; Weitere Beispiele finden Sie in den Tests .
quelle
Sie können BeautifulTable ausprobieren . Es macht was du willst. Hier ist ein Beispiel aus der Dokumentation
>>> 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 | +----------+------+--------+
quelle
Version mit w3m, die für die Typen entwickelt wurde Die Version von MattH akzeptiert:
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])
Ergebnisse in:
┌───────┬─┐ │ first │1│ ├───────┼─┤ │second │2│ ├───────┼─┤ │ third │3│ └───────┴─┘ ┌─────┬───────┬─────┐ │first│second │third│ ├─────┼───────┼─────┤ │1 │2 │3 │ ├─────┼───────┼─────┤ │4 │5 │6 │ └─────┴───────┴─────┘
quelle
Wenn Sie eine Tabelle mit Spalten- und Zeilenbereichen möchten, versuchen Sie es mit meiner Bibliothek dashtable
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))
Welche Ausgänge:
+----------+------------+----------+----------+ | 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 | +----------+------------+---------------------+
quelle
ERROR: Spans must be a list of lists
Ich weiß, dass die Frage etwas alt ist, aber hier ist mein Versuch:
https://gist.github.com/lonetwin/4721748
Es ist meiner Meinung nach etwas besser lesbar (obwohl es nicht zwischen einzelnen / mehreren Zeilen unterscheidet, wie es die Lösungen von @ MattH tun, noch NamedTuples verwendet).
quelle
Ich benutze diese kleine Utility-Funktion.
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'])
Ausgabe
----------------- |header 1|header 2| ----------------- |1 |2 | |3 |4 | -----------------
quelle
output += '|' + ''.join([h + ' ' * (l - len(h)) + '|' for h, l in zip(header, max_len)]) + '\n'
jedoch nicht in den Trennlinien. Kann diese Reihe von-
s mit etwas so Einfachem wieoutput = '-' * (sum(max_len) + 1 + len(header)) + '\n'
Hier ist meine Lösung:
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)
quelle
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()
Beispiel:
table = { "uid": ["0", "1", "2", "3"], "name": ["Jon", "Doe", "Lemma", "Hemma"] } create_table(table)
Ausgabe:
uid | name | ------+------------+- 0 | Jon | 1 | Doe | 2 | Lemma | 3 | Hemma | ------+------------+-
quelle
Dies kann mit nur eingebauten Modulen ziemlich kompakt unter Verwendung von Listen- und Zeichenfolgenverständnissen durchgeführt werden. Akzeptiert eine Liste von Wörterbüchern im gleichen Format ...
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
quelle