Stampa degli elenchi come dati tabulari


366

Sono abbastanza nuovo in Python e ora sto lottando con la formattazione dei miei dati per l'output stampato.

Ho un elenco che viene utilizzato per due titoli e una matrice che dovrebbe essere il contenuto della tabella. Così:

teams_list = ["Man Utd", "Man City", "T Hotspur"]
data = np.array([[1, 2, 1],
                 [0, 1, 0],
                 [2, 4, 2]])

Si noti che i nomi delle intestazioni non hanno necessariamente le stesse lunghezze. Le voci di dati sono comunque numeri interi.

Ora, voglio rappresentarlo in un formato tabella, qualcosa del genere:

            Man Utd   Man City   T Hotspur
  Man Utd         1          0           0
 Man City         1          1           0
T Hotspur         0          1           2

Ho la sensazione che ci debba essere una struttura di dati per questo, ma non riesco a trovarlo. Ho provato a usare un dizionario e a formattare la stampa, ho provato i for-loop con rientro e ho provato a stampare come stringhe.

Sono sicuro che ci deve essere un modo molto semplice per farlo, ma probabilmente mi manca per mancanza di esperienza.


1
+1, stavo solo cercando di fare la stessa cosa ieri sera. Stai solo cercando di stampare sulla riga di comando o stai utilizzando un modulo GUI?
HellaMad,

Basta stampare sulla riga di comando. Tuttavia, deve superare un caso di unit test, quindi la formattazione è piuttosto importante qui.
hjweide,



Si noti che il requisito qui è piuttosto specializzato, poiché le etichette di riga e colonna sono le stesse. Quindi, in questo caso particolare, il codice ad hoc è un bell'esempio di quanto possa essere facile. Ma le altre soluzioni qui potrebbero essere migliori per una visualizzazione di tabella più generica.
nealmcb,

Risposte:


189

Alcuni codici ad hoc per Python 2.7:

row_format ="{:>15}" * (len(teams_list) + 1)
print(row_format.format("", *teams_list))
for team, row in zip(teams_list, data):
    print(row_format.format(team, *row))

Questo si basa su Mini-Languagestr.format() e sulla specifica del formato .


3
Se si utilizza python2.6 ricordarsi di aggiungere l'indice team_list su row_format: row_format = "{0:> 15} {1:> 15} {2:> 15}"
Luis Muñoz,

1
Se i dati nel corpo sono più grandi delle intestazioni, è possibile impostare la larghezza della colonna in base alla prima riga di dati. per t nei dati [0]: row_format + = "{: <" + str (len (t) +5) + "}"
morgantaschuk

587

Ci sono alcuni pacchetti python leggeri e utili per questo scopo:

1. tabulato : https://pypi.python.org/pypi/tabulate

from tabulate import tabulate
print(tabulate([['Alice', 24], ['Bob', 19]], headers=['Name', 'Age']))
Name      Age
------  -----
Alice      24
Bob        19

tabulate ha molte opzioni per specificare le intestazioni e il formato della tabella.

print(tabulate([['Alice', 24], ['Bob', 19]], headers=['Name', 'Age'], tablefmt='orgtbl'))
| Name   |   Age |
|--------+-------|
| Alice  |    24 |
| Bob    |    19 |

2. PrettyTable : https://pypi.python.org/pypi/PrettyTable

from prettytable import PrettyTable
t = PrettyTable(['Name', 'Age'])
t.add_row(['Alice', 24])
t.add_row(['Bob', 19])
print(t)
+-------+-----+
|  Name | Age |
+-------+-----+
| Alice |  24 |
|  Bob  |  19 |
+-------+-----+

PrettyTable ha opzioni per leggere i dati dal database csv, html, sql. Inoltre, è possibile selezionare un sottoinsieme di dati, ordinare la tabella e modificare gli stili di tabella.

3. texttable : https://pypi.python.org/pypi/texttable

from texttable import Texttable
t = Texttable()
t.add_rows([['Name', 'Age'], ['Alice', 24], ['Bob', 19]])
print(t.draw())
+-------+-----+
| Name  | Age |
+=======+=====+
| Alice | 24  |
+-------+-----+
| Bob   | 19  |
+-------+-----+

con texttable puoi controllare l'allineamento orizzontale / verticale, lo stile del bordo e i tipi di dati.

4. termtables : https://github.com/nschloe/termtables

import termtables as tt

string = tt.to_string(
    [["Alice", 24], ["Bob", 19]],
    header=["Name", "Age"],
    style=tt.styles.ascii_thin_double,
    # alignment="ll",
    # padding=(0, 1),
)
print(string)
+-------+-----+
| Name  | Age |
+=======+=====+
| Alice | 24  |
+-------+-----+
| Bob   | 19  |
+-------+-----+

con texttable puoi controllare l'allineamento orizzontale / verticale, lo stile del bordo e i tipi di dati.

Altre opzioni:

  • terminaltatable Disegna facilmente le tabelle nelle applicazioni terminal / console da un elenco di elenchi di stringhe. Supporta righe multilinea.
  • asciitable Asciitable è in grado di leggere e scrivere una vasta gamma di formati di tabella ASCII tramite le classi di Reader estensione integrate.

13
Ho scoperto che tabulate è uno strumento molto utile per la creazione di strumenti CLI incentrati sui dati. Questo, combinato con il clic (pip install click), e hai un vero spezzatino in corso.
alexbw,

4
Questo è meraviglioso, grazie. Personalmente, quale preferiresti tra quei tre?
Jim Raynor,

Risposta brillante! PrettyTable è semplicemente ottimo: l'equilibrio perfetto tra le altre due opzioni.
Edesz,

2
terminaltables è buono per il cinese, forse altre lingue non inglesi
thinker3

5
Ho appena giocato con i pacchetti principali e IMO "beautifultable" - migliore, mantenuto, buona API e doco, supporto per i colori. "texttable" - API piacevole, mantenuta, buona ma l'uso dell'uso colorato getta le tabelle fuori allineamento. "terminali" - buono, solo con esempi di codice. "PrettyTable" - ok, ma i vecchi "titoli" della tabella non funzionano per me. "Tabulate" - buona coalignparola chiave di allineamento di colonna non supportata nella versione ufficiale di pypi. "tabella" - media, complessa API, non abbastanza esempi di utilizzo comuni.
abulka,

79
>>> import pandas
>>> pandas.DataFrame(data, teams_list, teams_list)
           Man Utd  Man City  T Hotspur
Man Utd    1        2         1        
Man City   0        1         0        
T Hotspur  2        4         2        

6
Sembra molto promettente, grazie, ma sto provando a farlo senza usare più librerie importate di quanto assolutamente necessario.
hjweide,

26
L'uso dei panda solo per la formattazione dell'output sembra Overkill (capitale O previsto).
Niels Bom,

66
@NielsBom: vieni per la formattazione dell'output, resta per l'analisi e la modellazione dei dati :)
jfs

30
@JFSebastian per me è stato più come "vieni per la formattazione dell'output, scappa urlando a causa della compilazione numpy di 10 minuti che ha fatto sembrare il mio computer un asciugacapelli" ;-)
Niels Bom

4
@NielsBom: pip install numpyutilizza le ruote binarie ora sulla maggior parte delle piattaforme (nessuna compilazione) . Ovviamente, altre opzioni di installazione binaria erano disponibili anche prima.
jfs,

68

Python in realtà lo rende abbastanza semplice.

Qualcosa di simile a

for i in range(10):
    print '%-12i%-12i' % (10 ** i, 20 ** i)

avrà l'output

1           1           
10          20          
100         400         
1000        8000        
10000       160000      
100000      3200000     
1000000     64000000    
10000000    1280000000  
100000000   25600000000
1000000000  512000000000

La% all'interno della stringa è essenzialmente un carattere di escape e i caratteri che la seguono indicano a Python il tipo di formato che i dati dovrebbero avere. % Outside e after the string indica a Python che si intende utilizzare la stringa precedente come stringa di formato e che i seguenti dati devono essere inseriti nel formato specificato.

In questo caso ho usato due volte "% -12i". Per scomporre ogni parte:

'-' (left align)
'12' (how much space to be given to this part of the output)
'i' (we are printing an integer)

Dai documenti: https://docs.python.org/2/library/stdtypes.html#string-formatting


Questa risposta mi ha messo sulla buona strada per trovare quello che stavo cercando! Per Python 3, ho finito per usarlo come print('%-20.2f' % position['deg'], '%-17.2f' % position['v2'])dove .2specifica la precisione del floatf
Ross

25

Aggiornamento della risposta di Sven Marnach per funzionare in Python 3.4:

row_format ="{:>15}" * (len(teams_list) + 1)
print(row_format.format("", *teams_list))
for team, row in zip(teams_list, data):
    print(row_format.format(team, *row))

9

Quando lo faccio, mi piace avere un certo controllo sui dettagli di come viene formattata la tabella. In particolare, desidero che le celle dell'intestazione abbiano un formato diverso rispetto alle celle del corpo e che le larghezze delle colonne della tabella siano larghe solo quanto ognuna deve essere. Ecco la mia soluzione:

def format_matrix(header, matrix,
                  top_format, left_format, cell_format, row_delim, col_delim):
    table = [[''] + header] + [[name] + row for name, row in zip(header, matrix)]
    table_format = [['{:^{}}'] + len(header) * [top_format]] \
                 + len(matrix) * [[left_format] + len(header) * [cell_format]]
    col_widths = [max(
                      len(format.format(cell, 0))
                      for format, cell in zip(col_format, col))
                  for col_format, col in zip(zip(*table_format), zip(*table))]
    return row_delim.join(
               col_delim.join(
                   format.format(cell, width)
                   for format, cell, width in zip(row_format, row, col_widths))
               for row_format, row in zip(table_format, table))

print format_matrix(['Man Utd', 'Man City', 'T Hotspur', 'Really Long Column'],
                    [[1, 2, 1, -1], [0, 1, 0, 5], [2, 4, 2, 2], [0, 1, 0, 6]],
                    '{:^{}}', '{:<{}}', '{:>{}.3f}', '\n', ' | ')

Ecco l'output:

                   | Man Utd | Man City | T Hotspur | Really Long Column
Man Utd            |   1.000 |    2.000 |     1.000 |             -1.000
Man City           |   0.000 |    1.000 |     0.000 |              5.000
T Hotspur          |   2.000 |    4.000 |     2.000 |              2.000
Really Long Column |   0.000 |    1.000 |     0.000 |              6.000

8

Penso che questo sia quello che stai cercando.

È un modulo semplice che calcola solo la larghezza massima richiesta per le voci della tabella e quindi utilizza solo rjust e ljust per fare una bella stampa dei dati.

Se vuoi che la tua rotta a sinistra sia allineata a destra basta cambiare questa chiamata:

 print >> out, row[0].ljust(col_paddings[0] + 1),

Dalla linea 53 con:

 print >> out, row[0].rjust(col_paddings[0] + 1),

8

So di essere in ritardo alla festa, ma ho appena creato una biblioteca per questo che penso possa davvero aiutare. È estremamente semplice, ecco perché penso che dovresti usarlo. Si chiama TableIT .

Uso di base

Per usarlo, segui prima le istruzioni per il download nella Pagina GitHub .

Quindi importalo:

import TableIt

Quindi crea un elenco di elenchi in cui ogni elenco interno è una riga:

table = [
    [4, 3, "Hi"],
    [2, 1, 808890312093],
    [5, "Hi", "Bye"]
]

Quindi tutto ciò che devi fare è stamparlo:

TableIt.printTable(table)

Questo è l'output che ottieni:

+--------------------------------------------+
| 4            | 3            | Hi           |
| 2            | 1            | 808890312093 |
| 5            | Hi           | Bye          |
+--------------------------------------------+

Nomi dei campi

Puoi usare i nomi dei campi se vuoi ( se non stai usando i nomi dei campi non devi dire useFieldNames = False perché è impostato su quello di default ):


TableIt.printTable(table, useFieldNames=True)

Da ciò otterrai:

+--------------------------------------------+
| 4            | 3            | Hi           |
+--------------+--------------+--------------+
| 2            | 1            | 808890312093 |
| 5            | Hi           | Bye          |
+--------------------------------------------+

Ci sono altri usi per, ad esempio potresti farlo:

import TableIt

myList = [
    ["Name", "Email"],
    ["Richard", "richard@fakeemail.com"],
    ["Tasha", "tash@fakeemail.com"]
]

TableIt.print(myList, useFieldNames=True)

Da quello:

+-----------------------------------------------+
| Name                  | Email                 |
+-----------------------+-----------------------+
| Richard               | richard@fakeemail.com |
| Tasha                 | tash@fakeemail.com    |
+-----------------------------------------------+

Oppure potresti fare:

import TableIt

myList = [
    ["", "a", "b"],
    ["x", "a + x", "a + b"],
    ["z", "a + z", "z + b"]
]

TableIt.printTable(myList, useFieldNames=True)

E da ciò ottieni:

+-----------------------+
|       | a     | b     |
+-------+-------+-------+
| x     | a + x | a + b |
| z     | a + z | z + b |
+-----------------------+

Colori

Puoi anche usare i colori.

I colori si utilizzano utilizzando l'opzione colore ( per impostazione predefinita è Nessuno ) e specificando i valori RGB.

Utilizzando l'esempio sopra:

import TableIt

myList = [
    ["", "a", "b"],
    ["x", "a + x", "a + b"],
    ["z", "a + z", "z + b"]
]

TableIt.printTable(myList, useFieldNames=True, color=(26, 156, 171))

Quindi otterrai:

inserisci qui la descrizione dell'immagine

Si noti che la stampa dei colori potrebbe non funzionare per voi, ma funziona esattamente come le altre librerie che stampano testo colorato. Ho testato e ogni singolo colore funziona. Neanche il blu è incasinato come farebbe se si usasse il default34m sequenza di escape ANSI (se non si sa di cosa si tratta, non importa). Comunque, tutto deriva dal fatto che ogni colore è un valore RGB anziché un valore predefinito di sistema.

Ulteriori informazioni

Per maggiori informazioni consulta la pagina GitHub


TableIt è davvero uno strumento piacevole. Semplice ma potente. L'unico svantaggio penso che TableIt non abbia dichiarato una LICENZA
Endle_Zhenbo

@Endle_Zhenbo Hey! Grazie mille, ci lavorerò il prima possibile!
BeastCoder

@Endle_Zhenbo, so che è passato un po 'di tempo, ma alla fine ho inserito una licenza per il progetto.
BeastCoder

7

Pure Python 3

def print_table(data, cols, wide):
    '''Prints formatted data on columns of given width.'''
    n, r = divmod(len(data), cols)
    pat = '{{:{}}}'.format(wide)
    line = '\n'.join(pat * cols for _ in range(n))
    last_line = pat * r
    print(line.format(*data))
    print(last_line.format(*data[n*cols:]))

data = [str(i) for i in range(27)]
print_table(data, 6, 12)

Stamperà

0           1           2           3           4           5           
6           7           8           9           10          11          
12          13          14          15          16          17          
18          19          20          21          22          23          
24          25          26

5

Un modo semplice per eseguire questa operazione è eseguire il ciclo su tutte le colonne, misurare la loro larghezza, creare una riga_templata per quella larghezza massima e quindi stampare le righe. Non è esattamente quello che stai cercando , perché in questo caso devi prima inserire i titoli all'interno del tavolo, ma sto pensando che potrebbe essere utile a qualcun altro.

table = [
    ["", "Man Utd", "Man City", "T Hotspur"],
    ["Man Utd", 1, 0, 0],
    ["Man City", 1, 1, 0],
    ["T Hotspur", 0, 1, 2],
]
def print_table(table):
    longest_cols = [
        (max([len(str(row[i])) for row in table]) + 3)
        for i in range(len(table[0]))
    ]
    row_format = "".join(["{:>" + str(longest_col) + "}" for longest_col in longest_cols])
    for row in table:
        print(row_format.format(*row))

Lo usi in questo modo:

>>> print_table(table)

            Man Utd   Man City   T Hotspur
  Man Utd         1          0           0
 Man City         1          1           0
T Hotspur         0          1           2

3

La seguente funzione creerà la tabella richiesta (con o senza numpy) con Python 3 (forse anche Python 2). Ho scelto di impostare la larghezza di ogni colonna in modo che corrisponda a quella del nome della squadra più lunga. È possibile modificarlo se si desidera utilizzare la lunghezza del nome del team per ogni colonna, ma sarà più complicato.

Nota: per un equivalente diretto in Python 2 è possibile sostituire il zipcon izipda itertools.

def print_results_table(data, teams_list):
    str_l = max(len(t) for t in teams_list)
    print(" ".join(['{:>{length}s}'.format(t, length = str_l) for t in [" "] + teams_list]))
    for t, row in zip(teams_list, data):
        print(" ".join(['{:>{length}s}'.format(str(x), length = str_l) for x in [t] + row]))

teams_list = ["Man Utd", "Man City", "T Hotspur"]
data = [[1, 2, 1],
        [0, 1, 0],
        [2, 4, 2]]

print_results_table(data, teams_list)

Questo produrrà la seguente tabella:

            Man Utd  Man City T Hotspur
  Man Utd         1         2         1
 Man City         0         1         0
T Hotspur         2         4         2

Se si desidera disporre di separatori di linea verticali, è possibile sostituirli " ".joincon " | ".join.

Riferimenti:


2

Vorrei provare a scorrere l'elenco e utilizzare un formattatore CSV per rappresentare i dati desiderati.

È possibile specificare schede, virgole o qualsiasi altro carattere come delimitatore.

Altrimenti, basta scorrere l'elenco e stampare "\ t" dopo ogni elemento

http://docs.python.org/library/csv.html


Questo è stato il mio tentativo iniziale, probabilmente può essere fatto, ma sembra essere un grande sforzo per ottenere una formattazione perfetta.
hjweide,

2

Ho trovato questo solo alla ricerca di un modo per produrre colonne semplici. Se hai solo bisogno di colonne senza complicazioni , puoi usare questo:

print("Titlex\tTitley\tTitlez")
for x, y, z in data:
    print(x, "\t", y, "\t", z)

EDIT: stavo cercando di essere il più semplice possibile, e quindi ho fatto alcune cose manualmente invece di usare l'elenco delle squadre. Per generalizzare alla domanda effettiva del PO:

#Column headers
print("", end="\t")
for team in teams_list:
    print(" ", team, end="")
print()
# rows
for team, row in enumerate(data):
    teamlabel = teams_list[team]
    while len(teamlabel) < 9:
        teamlabel = " " + teamlabel
    print(teamlabel, end="\t")
    for entry in row:
        print(entry, end="\t")
    print()

USCITE:

          Man Utd  Man City  T Hotspur
  Man Utd       1       2       1   
 Man City       0       1       0   
T Hotspur       2       4       2   

Ma questo non sembra più più semplice delle altre risposte, con forse il vantaggio di non richiedere più importazioni. Ma la risposta di @ campkeith l'ha già soddisfatta ed è più robusta in quanto può gestire una più ampia varietà di lunghezze di etichette.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.