Imprimir listas como dados tabulares

366

Eu sou bastante novo no Python e agora estou lutando com a formatação de meus dados para a saída impressa.

Eu tenho uma lista que é usada para dois títulos e uma matriz que deve ser o conteúdo da tabela. Igual a:

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

Observe que os nomes dos cabeçalhos não são necessariamente do mesmo comprimento. As entradas de dados são todas inteiras, no entanto.

Agora, eu quero representar isso em um formato de tabela, algo como isto:

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

Tenho um palpite de que deve haver uma estrutura de dados para isso, mas não consigo encontrá-la. Tentei usar um dicionário e formatar a impressão, tentei loops com recuo e tentei imprimir como strings.

Estou certo de que deve haver uma maneira muito simples de fazer isso, mas provavelmente estou sentindo falta disso por falta de experiência.

hjweide
fonte
11
+1, eu estava tentando fazer a mesma coisa ontem à noite. Você está apenas tentando imprimir na linha de comando ou está usando um módulo da GUI?
HellaMad
Apenas imprimindo na linha de comando. No entanto, ele precisa passar em um caso de teste de unidade, portanto, a formatação é muito importante aqui.
hjweide
3
possível duplicação de impressão de dados tabulares em Python
Ciro Santilli
11
Possível duplicata do Python: tabelas ascii com impressão bonita?
Martin Thoma
Observe que o requisito aqui é bastante especializado, pois os rótulos de linha e coluna são os mesmos. Portanto, nesse caso em particular, o código ad-hoc é um bom exemplo de como isso pode ser fácil. Mas as outras soluções aqui podem ser melhores para exibição de tabela mais genérica.
Nealmcb

Respostas:

189

Algum código ad-hoc para 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))

Isso depende str.format()e o Mini-idioma de especificação de formato .

Sven Marnach
fonte
3
Se estiver usando python2.6, lembre-se de adicionar o índice team_list em row_format: row_format = "{0:> 15} {1:> 15} {2:> 15}"
Luis Muñoz
11
Se os dados no corpo forem maiores que os cabeçalhos, você poderá definir a largura da coluna com base na primeira linha de dados. para t nos dados [0]: row_format + = "{: <" + str (len (t) +5) + "}"
morgantaschuk 23/02/16
587

Existem alguns pacotes python leves e úteis para esse fim:

1. tabule : 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

tabular tem muitas opções para especificar cabeçalhos e formato de tabela.

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 tem opções para ler dados do banco de dados csv, html e sql. Também é possível selecionar subconjunto de dados, classificar tabela e alterar estilos de tabela.

3. tabela de texto : 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  |
+-------+-----+

Com a tabela de texto, você pode controlar o alinhamento horizontal / vertical, o estilo da borda e os tipos de dados.

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  |
+-------+-----+

Com a tabela de texto, você pode controlar o alinhamento horizontal / vertical, o estilo da borda e os tipos de dados.

Outras opções:

  • terminaltables Crie facilmente tabelas em aplicativos de terminal / console a partir de uma lista de listas de strings. Suporta linhas de várias linhas.
  • asciitable O Asciitable pode ler e gravar uma ampla variedade de formatos de tabela ASCII por meio de classes de leitor de extensão integradas.
iman
fonte
13
Eu achei o tabulate uma ferramenta muito útil para criar ferramentas CLI centradas em dados. Isso, combinado com o clique (clique de instalação do pip), e você tem um verdadeiro ensopado.
23414 alexbw
4
Isso é maravilhoso, obrigado. Pessoalmente, qual você prefere entre os três?
Jim Raynor
Resposta brilhante! PrettyTable é tão bom - o equilíbrio perfeito entre as outras duas opções.
Edesz
2
terminaltables é bom para o chinês, talvez outros idiomas que não o inglês
thinker3
5
Eu apenas brinquei com os pacotes principais e o IMO "beautifultable" - melhor, mantido, boa API e doco, suporte para cores. "texttable" - API agradável, mantida e boa, mas o uso de cores deixa as tabelas desalinhadas. "terminaltables" - bom, apenas através de exemplos de código. "PrettyTable" - ok, mas os títulos da tabela antigos não funcionam para mim. "Tabular" - bom, mas a coalignpalavra-chave de alinhamento de coluna não é suportada na versão oficial do pypi. "tableprint" - média, complexo da API, exemplos de uso comuns insuficientes.
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        
jfs
fonte
6
Isso parece muito promissor, obrigado, mas estou tentando fazer isso sem usar mais bibliotecas importadas do que o absolutamente necessário.
hjweide
26
Usar pandas apenas para formatação de saída parece Overkill (capital O pretendido).
Niels Bom
66
@NielsBom: vir para a saída de formatação, estadia de análise de dados e modelagem :)
jfs
30
@JFSebastian para mim, era mais como "vir para a saída de formatação, fugir gritando por causa da compilação numpy 10 minutos que fez o meu som do computador como um secador de cabelo" ;-)
Niels Bom
4
@ NielsBom: pip install numpyusa rodas binárias agora na maioria das plataformas (sem compilação) . Obviamente, outras opções de instalação binária estavam disponíveis mesmo antes disso.
JFS
68

O Python realmente facilita isso.

Algo como

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

terá a saída

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

O% dentro da string é essencialmente um caractere de escape e os caracteres a seguir informam ao python que tipo de formato os dados devem ter. O% outside e after a string está dizendo ao python que você pretende usar a string anterior como a string de formato e que os seguintes dados devem ser colocados no formato especificado.

Nesse caso, usei "% -12i" duas vezes. Para dividir cada parte:

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

Dos documentos: https://docs.python.org/2/library/stdtypes.html#string-formatting

Austin
fonte
Essa resposta me colocou no caminho certo para encontrar o que estava procurando! Para python 3, acabei usando-o como print('%-20.2f' % position['deg'], '%-17.2f' % position['v2'])onde .2especifica a precisão do flutuadorf
Ross
25

Atualizando a resposta de Sven Marnach para trabalhar em 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))
SmrtGrunt saiu porque Monica
fonte
9

Quando faço isso, gosto de ter algum controle sobre os detalhes de como a tabela é formatada. Em particular, quero que as células de cabeçalho tenham um formato diferente das células do corpo, e as larguras das colunas da tabela sejam tão largas quanto cada uma precisa. Aqui está a minha solução:

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', ' | ')

Aqui está a saída:

                   | 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
campkeith
fonte
8

Eu acho que é isso que você está procurando.

É um módulo simples que apenas calcula a largura máxima necessária para as entradas da tabela e depois usa apenas rjust e ljust para fazer uma boa impressão dos dados.

Se você deseja que o cabeçalho esquerdo fique alinhado à direita, basta alterar esta chamada:

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

Da linha 53 com:

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

Sei que estou atrasado para a festa, mas acabei de criar uma biblioteca para isso que acho que poderia realmente ajudar. É extremamente simples, é por isso que acho que você deveria usá-lo. É chamado TableIT .

Uso básico

Para usá-lo, primeiro siga as instruções de download na página do GitHub .

Em seguida, importe-o:

import TableIt

Em seguida, faça uma lista de listas em que cada lista interna é uma linha:

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

Então tudo que você precisa fazer é imprimi-lo:

TableIt.printTable(table)

Esta é a saída que você obtém:

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

Nomes dos campos

Você pode usar nomes de campos se desejar ( se não estiver usando nomes de campos, não precisará dizer useFieldNames = False porque está definido como padrão por padrão ):


TableIt.printTable(table, useFieldNames=True)

A partir disso, você obterá:

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

Existem outros usos para, por exemplo, você pode fazer isso:

import TableIt

myList = [
    ["Name", "Email"],
    ["Richard", "[email protected]"],
    ["Tasha", "[email protected]"]
]

TableIt.print(myList, useFieldNames=True)

A partir desse:

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

Ou você pode fazer:

import TableIt

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

TableIt.printTable(myList, useFieldNames=True)

E a partir disso você obtém:

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

Cores

Você também pode usar cores.

Você usa cores usando a opção de cor ( por padrão, é definida como Nenhuma ) e especificando valores RGB.

Usando o exemplo acima:

import TableIt

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

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

Então você receberá:

insira a descrição da imagem aqui

Observe que as cores de impressão podem não funcionar para você, mas funcionam exatamente da mesma forma que as outras bibliotecas que imprimem texto colorido. Eu testei e todas as cores funcionam. O azul também não está bagunçado, como seria se estivesse usando a 34msequência de escape ANSI padrão (se você não souber o que é isso, não importa). Enfim, tudo vem do fato de que todas as cores têm valor RGB e não o padrão do sistema.

Mais informações

Para mais informações, consulte a página do GitHub

BeastCoder
fonte
TableIt é realmente uma boa ferramenta. Simples, mas poderoso. A única desvantagem que eu acho que é TableIt não declarou uma LICENÇA
Endle_Zhenbo
@Endle_Zhenbo Hey! Muito obrigado, vou trabalhar nisso o mais rápido possível!
BeastCoder 14/01
@Endle_Zhenbo, eu sei que já faz um tempo, mas finalmente coloquei uma licença no projeto.
BeastCoder 20/02
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)

Vai imprimir

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
O cientista de dados
fonte
5

Uma maneira simples de fazer isso é fazer um loop sobre todas as colunas, medir sua largura, criar um modelo de linha para essa largura máxima e imprimir as linhas. Não é exatamente o que você está procurando , porque nesse caso, primeiro você precisa colocar seus cabeçalhos dentro da mesa, mas acho que pode ser útil para outra pessoa.

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))

Você usa assim:

>>> print_table(table)

            Man Utd   Man City   T Hotspur
  Man Utd         1          0           0
 Man City         1          1           0
T Hotspur         0          1           2
Emil Stenström
fonte
3

A função a seguir criará a tabela solicitada (com ou sem numpy) com o Python 3 (talvez também o Python 2). Eu escolhi definir a largura de cada coluna para corresponder à do nome da equipe mais longo. Você pode modificá-lo se quiser usar o comprimento do nome da equipe para cada coluna, mas será mais complicado.

Nota: Para um equivalente direto no Python 2, você poderia substituir o zipcom izipde 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)

Isso produzirá a seguinte tabela:

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

Se você deseja ter separadores de linhas verticais, é possível substituir " ".joinpor " | ".join.

Referências:

Diomedea
fonte
2

Eu tentaria percorrer a lista e usar um formatador CSV para representar os dados que você deseja.

Você pode especificar guias, vírgulas ou qualquer outro caractere como delimitador.

Caso contrário, basta percorrer a lista e imprimir "\ t" após cada elemento

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

Jeffrey Kevin Pry
fonte
Essa foi minha tentativa inicial, provavelmente pode ser feita, mas parece ser muito esforço para obter a formatação perfeita.
hjweide
2

Achei isso apenas procurando uma maneira de gerar colunas simples. Se você apenas precisar de colunas sem complicações , poderá usar o seguinte:

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

Edição: Eu estava tentando ser o mais simples possível e, assim, fiz algumas coisas manualmente, em vez de usar a lista de equipes. Para generalizar a pergunta real do OP:

#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()

Ouputs:

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

Mas isso não parece mais simples do que as outras respostas, talvez com o benefício de não exigir mais importações. Mas a resposta da @ campkeith já cumpriu isso e é mais robusta, pois pode lidar com uma variedade maior de comprimentos de etiquetas.

pr3sidentspence
fonte
11
isso é discutido em meta meta.stackoverflow.com/questions/381571/…
Félix Gagnon-Grenier