2012-04-03 13 views
64

Próbuję utworzyć ładną listę kolumn w pythonie do użycia z narzędziami administracyjnymi wiersza poleceń, które utworzę.Utwórz ładne dane wyjściowe kolumny w pythonie

Basicly Chcę listę:

[['a', 'b', 'c'], ['aaaaaaaaaa', 'b', 'c'], ['a', 'bbbbbbbbbb', 'c']] 

przekształcić:

a   b   c 
aaaaaaaaaa b   c 
a   bbbbbbbbbb c 

użyciu zwykłego zakładek przyzwyczajenie rade tutaj, bo nie wiem, a najdłuższe danych w każdym wierszu .

To samo zachowanie jako „-t kolumnie” w Linuksie ..

$ echo -e "a b c\naaaaaaaaaa b c\na bbbbbbbbbb c" 
a b c 
aaaaaaaaaa b c 
a bbbbbbbbbb c 

$ echo -e "a b c\naaaaaaaaaa b c\na bbbbbbbbbb c" | column -t 
a   b   c 
aaaaaaaaaa b   c 
a   bbbbbbbbbb c 

Szukałem wokół różnych bibliotek Pythona, aby to zrobić, ale nie mogę znaleźć nic pożytecznego.

+0

co z używaniem ncurses? – sherpya

+3

Używanie ncurses to trochę przesada, gdy wyświetlam małe ~ 10 linii informacji, które chcę .. Ale używamy ncurses do innych rzeczy. – xeor

Odpowiedz

76
data = [['a', 'b', 'c'], ['aaaaaaaaaa', 'b', 'c'], ['a', 'bbbbbbbbbb', 'c']] 

col_width = max(len(word) for row in data for word in row) + 2 # padding 
for row in data: 
    print "".join(word.ljust(col_width) for word in row) 

a   b   c    
aaaaaaaaaa b   c    
a   bbbbbbbbbb c 

Co to robi to obliczyć najdłuższy wprowadzania danych, aby określić szerokość kolumny, a następnie użyć .ljust() aby dodać konieczne dopełnienie podczas drukowania każdej kolumny.

+1

Nazwa 'longest' jest myląca, ponieważ nie jest to najdłuższy element, ale maksymalna długość. BTW najdłuższy mógłby być wykonany z czymś takim jak: 'max ((w dla sub w danych dla w w pod), key = len)'. [P.S. Nie byłem tym, który zestrzelił] –

+1

'max ((w dla ...), klucz = len)' daje ci najdłuższy wpis i będziesz musiał ponownie wykonać 'len'. Nie mogłem zdecydować, co było jasne, więc utknąłem z pierwszym. Dobry punkt na mylącej nazwie var. Zmieniono. –

+1

Tak, nie ma dużej różnicy w jednym lub drugim, tylko kwestia gustu, który powiedziałbym. Poza tym, jak zauważyliście, linia ta jest nieco (zbyt) zagmatwana. Byłoby lepiej zrobić to bezpośrednio: 'max (len (x) dla sub w danych dla x w pod)', który również nie tworzy niepotrzebnych list. –

7

Trzeba to zrobić w 2 przejściach:

  1. uzyskać maksymalną szerokość każdej kolumny.
  2. formatowanie kolumn za pomocą naszej wiedzy o maksymalnej szerokości od pierwszego przebiegu używając str.ljust() i str.rjust()
7

transponujących kolumny jak to jest zadanie dla zip:

>>> a = [['a', 'b', 'c'], ['aaaaaaaaaa', 'b', 'c'], ['a', 'bbbbbbbbbb', 'c']] 
>>> list(zip(*a)) 
[('a', 'aaaaaaaaaa', 'a'), ('b', 'b', 'bbbbbbbbbb'), ('c', 'c', 'c')] 

Aby znaleźć żądaną długość każdej kolumny, można użyć max:

>>> trans_a = zip(*a) 
>>> [max(len(c) for c in b) for b in trans_a] 
[10, 10, 1] 

które można wykorzystać, z odpowiednimi wypełnienie, aby utworzyć ciągi znaków do przesłania do: print:

>>> col_lenghts = [max(len(c) for c in b) for b in trans_a] 
>>> padding = ' ' # You might want more 
>>> padding.join(s.ljust(l) for s,l in zip(a[0], col_lenghts)) 
'a   b   c' 
75

W języku Python 2.6+, można użyć następującej format string, aby ustawić kolumny na co najmniej 20 znaków i wyrównać tekst na prawo.

>>> table_data = [['a', 'b', 'c'], ['aaaaaaaaaa', 'b', 'c'], ['a', 'bbbbbbbbbb', 'c']] 
>>> for row in table_data: 
...  print("{: >20} {: >20} {: >20}".format(*row)) 
... 
        a     b     c 
      aaaaaaaaaa     b     c 
        a   bbbbbbbbbb     c 
+1

zdecydowanie najlepsze rozwiązanie od teraz – zlr

+0

To pokazuje tylko 9 pozycji, gdy próbowałem go użyć. –

+0

A możesz po prostu dodawać '{:> 20}', aby wyświetlić więcej pól – PercussiveRepair

28

Przyjechałem tu z tych samych wymagań, ale @lvc i @ odpowiedzi preet wydaje więcej inline z co column -t powoduje, że kolumny mają różne szerokości:

>>> rows = [ ['a',   'b',   'c', 'd'] 
...   , ['aaaaaaaaaa', 'b',   'c', 'd'] 
...   , ['a',   'bbbbbbbbbb', 'c', 'd'] 
...   ] 
... 

>>> widths = [max(map(len, col)) for col in zip(*rows)] 
>>> for row in rows: 
...  print " ".join((val.ljust(width) for val, width in zip(row, widths))) 
... 
a   b   c d 
aaaaaaaaaa b   c d 
a   bbbbbbbbbb c d 
+1

Nice. Jest to najczystsze rozwiązanie, które faktycznie odpowiada oryginalnemu "spec". – intuited

+1

To jest rozwiązanie, które sprawdziło się u mnie. Inne rozwiązania wytwarzały kolumnowy wynik, ale ten zapewniał największą kontrolę nad wypełnieniem oraz dokładne szerokości kolumn. –

+0

To powinno być akceptowane rozwiązanie. –

4

Aby dostać stoły hodowców, takie jak

--------------------------------------------------- 
| First Name | Last Name  | Age | Position | 
--------------------------------------------------- 
| John  | Smith   | 24 | Software | 
|   |     |  | Engineer | 
--------------------------------------------------- 
| Mary  | Brohowski  | 23 | Sales  | 
|   |     |  | Manager | 
--------------------------------------------------- 
| Aristidis | Papageorgopoulos | 28 | Senior | 
|   |     |  | Reseacher | 
--------------------------------------------------- 

można użyć tego Python recipe:

''' 
From http://code.activestate.com/recipes/267662-table-indentation/ 
PSF License 
''' 
import cStringIO,operator 

def indent(rows, hasHeader=False, headerChar='-', delim=' | ', justify='left', 
      separateRows=False, prefix='', postfix='', wrapfunc=lambda x:x): 
    """Indents a table by column. 
     - rows: A sequence of sequences of items, one sequence per row. 
     - hasHeader: True if the first row consists of the columns' names. 
     - headerChar: Character to be used for the row separator line 
     (if hasHeader==True or separateRows==True). 
     - delim: The column delimiter. 
     - justify: Determines how are data justified in their column. 
     Valid values are 'left','right' and 'center'. 
     - separateRows: True if rows are to be separated by a line 
     of 'headerChar's. 
     - prefix: A string prepended to each printed row. 
     - postfix: A string appended to each printed row. 
     - wrapfunc: A function f(text) for wrapping text; each element in 
     the table is first wrapped by this function.""" 
    # closure for breaking logical rows to physical, using wrapfunc 
    def rowWrapper(row): 
     newRows = [wrapfunc(item).split('\n') for item in row] 
     return [[substr or '' for substr in item] for item in map(None,*newRows)] 
    # break each logical row into one or more physical ones 
    logicalRows = [rowWrapper(row) for row in rows] 
    # columns of physical rows 
    columns = map(None,*reduce(operator.add,logicalRows)) 
    # get the maximum of each column by the string length of its items 
    maxWidths = [max([len(str(item)) for item in column]) for column in columns] 
    rowSeparator = headerChar * (len(prefix) + len(postfix) + sum(maxWidths) + \ 
           len(delim)*(len(maxWidths)-1)) 
    # select the appropriate justify method 
    justify = {'center':str.center, 'right':str.rjust, 'left':str.ljust}[justify.lower()] 
    output=cStringIO.StringIO() 
    if separateRows: print >> output, rowSeparator 
    for physicalRows in logicalRows: 
     for row in physicalRows: 
      print >> output, \ 
       prefix \ 
       + delim.join([justify(str(item),width) for (item,width) in zip(row,maxWidths)]) \ 
       + postfix 
     if separateRows or hasHeader: print >> output, rowSeparator; hasHeader=False 
    return output.getvalue() 

# written by Mike Brown 
# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061 
def wrap_onspace(text, width): 
    """ 
    A word-wrap function that preserves existing line breaks 
    and most spaces in the text. Expects that existing line 
    breaks are posix newlines (\n). 
    """ 
    return reduce(lambda line, word, width=width: '%s%s%s' % 
        (line, 
        ' \n'[(len(line[line.rfind('\n')+1:]) 
         + len(word.split('\n',1)[0] 
          ) >= width)], 
        word), 
        text.split(' ') 
       ) 

import re 
def wrap_onspace_strict(text, width): 
    """Similar to wrap_onspace, but enforces the width constraint: 
     words longer than width are split.""" 
    wordRegex = re.compile(r'\S{'+str(width)+r',}') 
    return wrap_onspace(wordRegex.sub(lambda m: wrap_always(m.group(),width),text),width) 

import math 
def wrap_always(text, width): 
    """A simple word-wrap function that wraps text on exactly width characters. 
     It doesn't split the text in words.""" 
    return '\n'.join([ text[width*i:width*(i+1)] \ 
         for i in xrange(int(math.ceil(1.*len(text)/width))) ]) 

if __name__ == '__main__': 
    labels = ('First Name', 'Last Name', 'Age', 'Position') 
    data = \ 
    '''John,Smith,24,Software Engineer 
     Mary,Brohowski,23,Sales Manager 
     Aristidis,Papageorgopoulos,28,Senior Reseacher''' 
    rows = [row.strip().split(',') for row in data.splitlines()] 

    print 'Without wrapping function\n' 
    print indent([labels]+rows, hasHeader=True) 
    # test indent with different wrapping functions 
    width = 10 
    for wrapper in (wrap_always,wrap_onspace,wrap_onspace_strict): 
     print 'Wrapping function: %s(x,width=%d)\n' % (wrapper.__name__,width) 
     print indent([labels]+rows, hasHeader=True, separateRows=True, 
        prefix='| ', postfix=' |', 
        wrapfunc=lambda x: wrapper(x,width)) 

    # output: 
    # 
    #Without wrapping function 
    # 
    #First Name | Last Name  | Age | Position   
    #------------------------------------------------------- 
    #John  | Smith   | 24 | Software Engineer 
    #Mary  | Brohowski  | 23 | Sales Manager  
    #Aristidis | Papageorgopoulos | 28 | Senior Reseacher 
    # 
    #Wrapping function: wrap_always(x,width=10) 
    # 
    #---------------------------------------------- 
    #| First Name | Last Name | Age | Position | 
    #---------------------------------------------- 
    #| John  | Smith  | 24 | Software E | 
    #|   |   |  | ngineer | 
    #---------------------------------------------- 
    #| Mary  | Brohowski | 23 | Sales Mana | 
    #|   |   |  | ger  | 
    #---------------------------------------------- 
    #| Aristidis | Papageorgo | 28 | Senior Res | 
    #|   | poulos  |  | eacher  | 
    #---------------------------------------------- 
    # 
    #Wrapping function: wrap_onspace(x,width=10) 
    # 
    #--------------------------------------------------- 
    #| First Name | Last Name  | Age | Position | 
    #--------------------------------------------------- 
    #| John  | Smith   | 24 | Software | 
    #|   |     |  | Engineer | 
    #--------------------------------------------------- 
    #| Mary  | Brohowski  | 23 | Sales  | 
    #|   |     |  | Manager | 
    #--------------------------------------------------- 
    #| Aristidis | Papageorgopoulos | 28 | Senior | 
    #|   |     |  | Reseacher | 
    #--------------------------------------------------- 
    # 
    #Wrapping function: wrap_onspace_strict(x,width=10) 
    # 
    #--------------------------------------------- 
    #| First Name | Last Name | Age | Position | 
    #--------------------------------------------- 
    #| John  | Smith  | 24 | Software | 
    #|   |   |  | Engineer | 
    #--------------------------------------------- 
    #| Mary  | Brohowski | 23 | Sales  | 
    #|   |   |  | Manager | 
    #--------------------------------------------- 
    #| Aristidis | Papageorgo | 28 | Senior | 
    #|   | poulos  |  | Reseacher | 
    #--------------------------------------------- 

Python recipe page zawiera kilka ulepszeń na nim.

5

pandas rozwiązanie opiera się tworzeniem dataframe:

import pandas as pd 
l = [['a', 'b', 'c'], ['aaaaaaaaaa', 'b', 'c'], ['a', 'bbbbbbbbbb', 'c']] 
df = pd.DataFrame(l) 

print(df) 
      0   1 2 
0   a   b c 
1 aaaaaaaaaa   b c 
2   a bbbbbbbbbb c 

Aby usunąć indeks i header wartości stworzyć wyjście co chcesz można użyć to_string metody:

result = df.to_string(index=False, header=False) 

print(result) 
      a   b c 
aaaaaaaaaa   b c 
      a bbbbbbbbbb c 
0

uświadamiam sobie to pytanie jest stare, ale Nie rozumiałem odpowiedzi Antaka i nie chciałem korzystać z biblioteki, więc opracowałem własne rozwiązanie.

Rozwiązanie zakłada, że ​​rekordy to tablica 2D, rekordy mają tę samą długość, a wszystkie pola są ciągami.

Powiązane problemy