Создать хороший вывод столбца в python
Я пытаюсь создать хороший список столбцов в python для использования с инструментами администрирования командной строки, которые я создаю.
В основном, я хочу список, как:
[['a', 'b', 'c'], ['aaaaaaaaaa', 'b', 'c'], ['a', 'bbbbbbbbbb', 'c']]
в свою очередь на:
a b c
aaaaaaaaaa b c
a bbbbbbbbbb c
использование простых вкладок не будет делать трюк здесь, потому что я не знаю самые длинные данные в каждой строке.
это то же самое поведение, что и "столбец-t" в Linux..
$ echo -e "a b cnaaaaaaaaaa b cna bbbbbbbbbb c"
a b c
aaaaaaaaaa b c
a bbbbbbbbbb c
$ echo -e "a b cnaaaaaaaaaa b cna bbbbbbbbbb c" | column -t
a b c
aaaaaaaaaa b c
a bbbbbbbbbb c
Я искал различные библиотеки python, чтобы сделать это, но не могу найти что-нибудь полезное.
10 ответов:
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что это делает, это вычислить самую длинную запись данных для определения ширины столбца, а затем использовать
.ljust()чтобы добавить необходимое дополнение при печати каждого столбца.
в Python 2.6+, следующие формат строки можно использовать для установки столбцов не менее 20 символов и выравнивания текста вправо.
>>> 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
Я пришел сюда с теми же требованиями, но ответы @lvc и @Preet кажутся более встроенными с тем, что
column -tпроизводит в том, что колонки имеют разную ширину:>>> 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
вы должны сделать это с 2 проходами:
- получите максимальную ширину каждого столбца.
- форматирование столбцов с использованием наших знаний о максимальной ширине с первого прохода с помощью
str.ljust()иstr.rjust()
транспонирование столбцов, как это работа для zip:
>>> a = [['a', 'b', 'c'], ['aaaaaaaaaa', 'b', 'c'], ['a', 'bbbbbbbbbb', 'c']] >>> list(zip(*a)) [('a', 'aaaaaaaaaa', 'a'), ('b', 'b', 'bbbbbbbbbb'), ('c', 'c', 'c')], чтобы найти необходимую длину каждого столбца, вы можете использовать
max:>>> trans_a = zip(*a) >>> [max(len(c) for c in b) for b in trans_a] [10, 10, 1]который вы можете использовать, с подходящим дополнением, чтобы построить строки для передачи в
>>> 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'
чтобы получить более причудливые таблицы, такие как
--------------------------------------------------- | First Name | Last Name | Age | Position | --------------------------------------------------- | John | Smith | 24 | Software | | | | | Engineer | --------------------------------------------------- | Mary | Brohowski | 23 | Sales | | | | | Manager | --------------------------------------------------- | Aristidis | Papageorgopoulos | 28 | Senior | | | | | Reseacher | ---------------------------------------------------вы можете использовать этот питон рецепт:
''' 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 | #---------------------------------------------The страница рецепта Python содержит несколько улучшений на нем.
pandasрешение на основе создания таблицы данных: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чтобы удалить значения Индекса и заголовка, чтобы создать вывод, что вы хотите, вы могли бы использовать
to_stringспособ:result = df.to_string(index=False, header=False) print(result) a b c aaaaaaaaaa b c a bbbbbbbbbb c
Я понимаю, что этот вопрос старый, но я не понял ответа Антака и не хотел использовать библиотеку, поэтому я свернул свое собственное решение.
решение предполагает, что записи представляют собой двумерный массив, записи имеют одинаковую длину, а поля-все строки.
def stringifyRecords(records): column_widths = [0] * len(records[0]) for record in records: for i, field in enumerate(record): width = len(field) if width > column_widths[i]: column_widths[i] = width s = "" for record in records: for column_width, field in zip(column_widths, record): s += field.ljust(column_width+1) s += "\n" return s
Я нашел этот ответ супер-полезным и элегантным, первоначально от здесь:
matrix = [["A", "B"], ["C", "D"]] print('\n'.join(['\t'.join([str(cell) for cell in row]) for row in matrix]))выход
A B C D
вот вариация ответа Шона чина. Ширина фиксируется для каждого столбца, а не для всех столбцов. Существует также граница под первой строкой и между столбцами. (icontract библиотека используется для принудительного исполнения контрактов.)
@icontract.pre( lambda table: not table or all(len(row) == len(table[0]) for row in table)) @icontract.post(lambda table, result: result == "" if not table else True) @icontract.post(lambda result: not result.endswith("\n")) def format_table(table: List[List[str]]) -> str: """ Format the table as equal-spaced columns. :param table: rows of cells :return: table as string """ cols = len(table[0]) col_widths = [max(len(row[i]) for row in table) for i in range(cols)] lines = [] # type: List[str] for i, row in enumerate(table): parts = [] # type: List[str] for cell, width in zip(row, col_widths): parts.append(cell.ljust(width)) line = " | ".join(parts) lines.append(line) if i == 0: border = [] # type: List[str] for width in col_widths: border.append("-" * width) lines.append("-+-".join(border)) result = "\n".join(lines) return resultвот пример:
>>> table = [['column 0', 'another column 1'], ['00', '01'], ['10', '11']] >>> result = packagery._format_table(table=table) >>> print(result) column 0 | another column 1 ---------+----------------- 00 | 01 10 | 11
Comments