Impresión de listas como datos tabulares


366

Soy bastante nuevo en Python y ahora estoy luchando por formatear bien mis datos para la salida impresa.

Tengo una lista que se usa para dos encabezados y una matriz que debería ser el contenido de la tabla. Al igual que:

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

Tenga en cuenta que los nombres de encabezado no son necesariamente de la misma longitud. Sin embargo, las entradas de datos son todos enteros.

Ahora, quiero representar esto en un formato de tabla, algo como esto:

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

Tengo el presentimiento de que debe haber una estructura de datos para esto, pero no puedo encontrarla. He intentado usar un diccionario y formatear la impresión, he intentado bucles for con sangría y he intentado imprimir como cadenas.

Estoy seguro de que debe haber una manera muy simple de hacer esto, pero probablemente me la estoy perdiendo debido a la falta de experiencia.


1
+1, estaba tratando de hacer lo mismo anoche. ¿Está intentando imprimir en la línea de comando o está utilizando un módulo GUI?
HellaMad

Solo imprime en la línea de comando. Sin embargo, debe pasar un caso de prueba unitaria, por lo que el formato es bastante importante aquí.
hjweide



Tenga en cuenta que el requisito aquí es bastante especializado, ya que las etiquetas de fila y columna son las mismas. Entonces, para este caso particular, el código ad-hoc es un buen ejemplo de lo fácil que puede ser. Pero las otras soluciones aquí pueden ser mejores para una visualización de tabla más genérica.
nealmcb

Respuestas:


189

Algún 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))

Esto se basa en str.format()la especificación de formato Mini-idioma .


3
Si usa python2.6 recuerde agregar el índice team_list en row_format: row_format = "{0:> 15} {1:> 15} {2:> 15}"
Luis Muñoz

1
Si los datos en el cuerpo son más grandes que los encabezados, puede establecer el ancho de columna en función de la primera fila de datos. para t en datos [0]: row_format + = "{: <" + str (len (t) +5) + "}"
morgantaschuk

587

Hay algunos paquetes de Python ligeros y útiles para este propósito:

1. tabular : 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 tiene muchas opciones para especificar encabezados y formato de tabla.

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 tiene opciones para leer datos de csv, html, base de datos sql. También puede seleccionar un subconjunto de datos, ordenar la tabla y cambiar los estilos de la tabla.

3. tabla 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  |
+-------+-----+

con texttable puede controlar la alineación horizontal / vertical, el estilo de borde y los tipos de datos.

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 puede controlar la alineación horizontal / vertical, el estilo de borde y los tipos de datos.

Otras opciones:

  • terminalestables Dibuje fácilmente tablas en aplicaciones de terminal / consola de una lista de listas de cadenas. Admite filas de varias líneas.
  • Asciitable Asciitable puede leer y escribir una amplia gama de formatos de tabla ASCII a través de las clases de lector de extensión incorporadas.

13
He encontrado que tabular es una herramienta muy útil para crear herramientas CLI centradas en datos. Eso, combinado con un clic (pip install click), y tienes un verdadero guiso.
alexbw

44
Esto es maravilloso, gracias. Personalmente, ¿cuál preferirías entre esos tres?
Jim Raynor

Brillante respuesta! PrettyTable es tan bueno: el equilibrio perfecto entre las otras dos opciones.
edesz

2
terminaltables es bueno para chino, tal vez otros idiomas que no sean inglés
thinker3

55
Acabo de jugar con los paquetes principales y la IMO "beautifultable" - mejor, mantenida, buena API y doco, soporte para color. "tabla de texto": API agradable, mantenida y buena, pero el uso del uso coloreado arroja las tablas fuera de alineación. "terminalestables" - bueno, doco solo a través de ejemplos de código. "PrettyTable" - ok, pero los viejos 'títulos' de tabla no funcionan para mí. "Tabular": bueno, pero la coalignpalabra clave de alineación de columnas no es compatible con la versión oficial de pypi. "tableprint": promedio, complejo de API, no hay suficientes ejemplos de uso común.
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        

66
Esto parece muy prometedor, gracias, pero estoy tratando de hacerlo sin usar más bibliotecas importadas de las absolutamente necesarias.
hjweide

26
El uso de pandas solo para el formato de salida parece ser Overkill (O mayúscula).
Niels Bom

66
@NielsBom: venga por el formato de salida, quédese para el análisis y modelado de datos :)
jfs

30
@JFSebastian para mí fue más como "venga por el formato de salida, huya gritando por la compilación de 10 minutos que hizo que mi computadora sonara como un secador de pelo" ;-)
Niels Bom

44
@NielsBom: pip install numpyahora usa ruedas binarias en la mayoría de las plataformas (sin compilación) . Obviamente, otras opciones de instalación binaria estaban disponibles incluso antes de eso.
jfs

68

Python en realidad hace esto bastante fácil.

Algo como

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

tendrá la salida

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

El% dentro de la cadena es esencialmente un carácter de escape y los caracteres que lo siguen le dicen a Python qué tipo de formato deben tener los datos. El% afuera y después de la cadena le dice a Python que tiene la intención de usar la cadena anterior como la cadena de formato y que los siguientes datos deben ponerse en el formato especificado.

En este caso usé "% -12i" dos veces. Para desglosar cada parte:

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

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


¡Esta respuesta me encaminó a encontrar lo que estaba buscando! Para Python 3, terminé usándolo como print('%-20.2f' % position['deg'], '%-17.2f' % position['v2'])donde .2especifica la precisión del flotadorf
Ross

25

Actualizando la respuesta de Sven Marnach para trabajar en 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

Cuando hago esto, me gusta tener cierto control sobre los detalles de cómo se formatea la tabla. En particular, quiero que las celdas del encabezado tengan un formato diferente que las celdas del cuerpo, y los anchos de columna de la tabla solo sean tan anchos como cada uno debe ser. Aquí está mi solución:

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

Aquí está la salida:

                   | 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

Creo que esto es lo que estás buscando.

Es un módulo simple que solo calcula el ancho máximo requerido para las entradas de la tabla y luego solo usa rjust y ljust para hacer una impresión bonita de los datos.

Si desea que su rumbo izquierdo esté alineado a la derecha, simplemente cambie esta llamada:

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

Desde la línea 53 con:

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

8

Sé que llego tarde a la fiesta, pero acabo de hacer una biblioteca para esto que creo que realmente podría ayudar. Es extremadamente simple, por eso creo que deberías usarlo. Se llama TableIT .

Uso básico

Para usarlo, primero siga las instrucciones de descarga en la página de GitHub .

Luego impórtalo:

import TableIt

Luego haga una lista de listas donde cada lista interna sea una fila:

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

Entonces todo lo que tienes que hacer es imprimirlo:

TableIt.printTable(table)

Este es el resultado que obtienes:

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

Nombres de campo

Puede usar nombres de campo si lo desea ( si no está usando nombres de campo, no tiene que decir useFieldNames = False porque está configurado de forma predeterminada ):


TableIt.printTable(table, useFieldNames=True)

De eso obtendrás:

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

Hay otros usos para, por ejemplo, podría hacer esto:

import TableIt

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

TableIt.print(myList, useFieldNames=True)

A partir de ese:

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

O podrías hacer:

import TableIt

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

TableIt.printTable(myList, useFieldNames=True)

Y de eso obtienes:

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

Colores

También puedes usar colores.

Puede usar colores mediante la opción de color ( de forma predeterminada está establecida en Ninguno ) y especificando valores RGB.

Usando el ejemplo de arriba:

import TableIt

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

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

Entonces obtendrás:

ingrese la descripción de la imagen aquí

Tenga en cuenta que la impresión de colores puede no funcionar para usted, pero funciona exactamente igual que las otras bibliotecas que imprimen texto en color. Lo he probado y cada color funciona. El azul tampoco está desordenado como lo haría si usara el valor predeterminado34m secuencia de escape ANSI (si no sabe qué es eso, no importa). De todos modos, todo proviene del hecho de que cada color es un valor RGB en lugar de un valor predeterminado del sistema.

Más información

Para obtener más información, consulte la página de GitHub


TableIt es realmente una buena herramienta. Simple pero poderoso. La única desventaja que creo es que TableIt no ha declarado una LICENCIA
Endle_Zhenbo

@Endle_Zhenbo ¡Hola! ¡Muchas gracias, trabajaré en eso lo antes posible!
BeastCoder

@Endle_Zhenbo, sé que ha pasado un tiempo, pero finalmente puse una licencia en el proyecto.
BeastCoder

7

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

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

5

Una manera simple de hacer esto es recorrer todas las columnas, medir su ancho, crear una plantilla de fila para ese ancho máximo y luego imprimir las filas. No es exactamente lo que está buscando , porque en este caso, primero tiene que poner sus títulos dentro de la mesa, pero creo que podría ser útil para otra persona.

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 usas así:

>>> 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 siguiente función creará la tabla solicitada (con o sin numpy) con Python 3 (quizás también Python 2). Elegí establecer el ancho de cada columna para que coincida con el nombre del equipo más largo. Puede modificarlo si desea utilizar la longitud del nombre del equipo para cada columna, pero será más complicado.

Nota: Para un equivalente directo en Python 2 podría reemplazar el zipcon 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)

Esto producirá la siguiente tabla:

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

Si desea tener separadores de línea vertical, puede reemplazar " ".joincon " | ".join.

Referencias


2

Intentaría recorrer la lista y usar un formateador CSV para representar los datos que desea.

Puede especificar pestañas, comas o cualquier otro carácter como delimitador.

De lo contrario, simplemente recorra la lista e imprima "\ t" después de cada elemento

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


Este fue mi intento inicial, probablemente se puede hacer, pero parece ser un gran esfuerzo para que el formato sea perfecto.
hjweide

2

Encontré esto solo buscando una forma de generar columnas simples. Si solo necesita columnas sin complicaciones , puede usar esto:

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

EDITAR: estaba tratando de ser lo más simple posible, y así hice algunas cosas manualmente en lugar de usar la lista de equipos. Para generalizar a la pregunta real del 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   

Pero esto ya no parece más simple que las otras respuestas, con tal vez el beneficio de que no requiere más importaciones. Pero la respuesta de @ campkeith ya cumplió con eso y es más robusta ya que puede manejar una variedad más amplia de longitudes de etiquetas.


Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.