tags:

views:

111

answers:

7

Hi,

I have data (numbers) saved in the following format (example):
234 127 34 23 45567
23 12 4 4 45
23456 2 1 444 567
... Is there any python-way method to line up the numbers and get them as

  
  234  127  34   23  45567  
   23   12   4    4     45  
23456    2   1  444    567  

(I cannot predict the column size).

A: 

look at the .rjust(n) method - left padding characters perhaps?

Ross
+1  A: 

Try this nice well documented example http://code.activestate.com/recipes/267662-table-indentation/

Rahul
+4  A: 

You need some way of finding the column size, maybe by reading all the data and finding the maximum width.

>>> line='234 127 34 23 45567'
>>> line.split()
['234', '127', '34', '23', '45567']
>>> max(map(len, line.split()))
5

Repeat over all lines, to find column size (e.g., 5). Constructing a formatted line with percent formatting is straightforward.

>>> colsize = 5
>>> ' '.join(('%*s' % (colsize, i) for i in line.split()))
'  234   127    34    23 45567'
>>> 
gimel
Didn't know about '%*s'. Good tip!
Jeff
+2  A: 
#!/usr/bin/env python

class ALIGN:
    LEFT, RIGHT = '-', ''

class Column(list):
    def __init__(self, name, data, align=ALIGN.RIGHT):
        list.__init__(self, data)
        self.name = name
        width = max(len(str(x)) for x in data + [name])
        self.format = ' %%%s%ds ' % (align, width)

class Table:
    def __init__(self, *columns):
        self.columns = columns
        self.length = max(len(x) for x in columns)
    def get_row(self, i=None):
        for x in self.columns:
            if i is None:
                yield x.format % x.name
            else:
                yield x.format % x[i]
    def get_rows(self):
        yield ' '.join(self.get_row(None))
        for i in range(0, self.length):
            yield ' '.join(self.get_row(i))

    def __str__(self):
        return '\n'.join(self.get_rows())   

For your example:

if __name__ == '__main__':
    print Table(
        Column("", [234, 32, 23456]),
        Column("", [127, 12, 2]),
        Column("", [34, 4, 1]),
        Column("", [23, 4, 444]),
        Column("", [45567, 45, 567])
    )

It will yield:

   234   127   34    23   45567 
    32    12    4     4      45 
 23456     2    1   444     567 

Adapted from http://code.activestate.com/recipes/577202-render-tables-for-text-interface/

The MYYN
That is incredibly heavyweight in view of some other solutions. What's the benefit of cooking up classes to handle this?
aaronasterling
+2  A: 
>>> rows = """234 127 34 23 45567
... 23 12 4 4 45
... 23456 2 1 444 567"""

first convert the rows into a 2d array (list of lists)

>>> arr=[x.split() for x in rows.split("\n")]

now compute the space each field will need to fit into

>>> widths = [max(map(len,(f[i] for f in tab))) for i in range(len(arr[0]))]

and pad each element to fit into that space

>>> [[k.rjust(widths[i]) for i,k in enumerate(j)] for j in arr]
[['  234', '127', '34', ' 23', '45567'], ['   23', ' 12', ' 4', '  4', '   45'], ['23456', '  2', ' 1', '444', '  567']]

finally join the array back into a string

>>> print "\n".join("  ".join(k.rjust(widths[i]) for i,k in enumerate(j)) for j in arr)
  234  127  34   23  45567
   23   12   4    4     45
23456    2   1  444    567
gnibbler
+1  A: 

Here is a simple, self-contained example that shows how to format variable column widths:

data = '''\
234 127 34 23 45567
23 12 4 4 45
23456 2 1 444 567'''

# Split input data by row and then on spaces
rows = [ line.strip().split(' ') for line in data.split('\n') ]

# Reorganize data by columns
cols = zip(*rows)

# Compute column widths by taking maximum length of values per column
col_widths = [ max(len(value) for value in col) for col in cols ]

# Create a suitable format string
format = ' '.join(['%%%ds' % width for width in col_widths ])

# Print each row using the computed format
for row in rows:
  print format % tuple(row)

which outputs:

  234 127 34  23 45567
   23  12  4   4    45
23456   2  1 444   567
Kevin Jacobs
A: 

For years I've been using a very powerful and easy to use module called TextFormatter by Hamish B Lawson for this sort of thing. Originally I got it from www.faqts.com but that's now gone, so I just did a Google search and found a copy of it here -- looks like it might be used in Zope.

Edit: I checked www.faqts.com using the Internet Archive Wayback Machine and there don't seem to be any licensing issues -- so here's the module with some tweaks by myself and others:

#===================================================================
#!/usr/bin/env python
# File:    TextFormatter2.py
# Author:  Hamish B Lawson
# Date:    19/11/1999
# from http://www.faqts.com/knowledge_base/view.phtml/aid/4517
"""
Here is TextFormatter, a simple module for formatting text into
columns of specified widths. It does multiline wrapping and supports
left, center and right alignment.

SKWM made filling & padding optional, tweaked some edge cases
"""

import string

left  = 0
center = centre = 1
right  = 2

class TextFormatter:

    """
    Formats text into columns.

    Constructor takes a list of dictionaries that each specify the
    properties for a column. Dictionary entries can be:

       width         the width within which the text will be wrapped
       alignment     left|center|right
       margin        amount of space to prefix in front of column

    The compose() method takes a list of strings and returns a formatted
    string consisting of each string wrapped within its respective column.

    Example:

            formatter = TextFormatter(
                    (
                            {'width': 10},
                            {'width': 12, 'margin': 4},
                            {'width': 20, 'margin': 8, 'alignment': right},
                    )
            )

            print formatter.compose(
                    (
                            "A rather short paragraph",
                            "Here is a paragraph containing a veryveryverylongwordindeed.",
                            "And now for something on the right-hand side.",
                    )
            )

    gives:

            A rather      Here is a                    And now for
            short         paragraph               something on the
            paragraph     containing a            right-hand side.
                                      veryveryvery
                                      longwordinde
                                      ed.

    """
    class Column:

        def __init__(self, width=75, alignment=left, margin=0, fill=1, pad=1):
            self.width = width
            self.alignment = alignment
            self.margin = margin
            self.fill = fill
            self.pad = pad
            self.lines = []

        def align(self, line):
            if self.alignment == center:
                return string.center(line, self.width)
            elif self.alignment == right:
                return string.rjust(line, self.width)
            else:
                if self.pad:
                    return string.ljust(line, self.width)
                else:
                    return line

        def wrap(self, text):
            self.lines = []
            words = []
            if self.fill:               # SKWM
                for word in string.split(text):
                    wordlen = len(word)
                    if wordlen <= self.width:  # fixed MRM
                        words.append(word)
                    else:
                        for i in range(0, wordlen, self.width):
                            words.append(word[i:i+self.width])
            else:
                for line in string.split(text,'\n'):
                    for word in string.split(line):
                        for i in range(0, len(word), self.width):
                            words.append(word[i:i+self.width])
                    words.append('\n')
                if words[-1] == '\n': words.pop() # remove trailing newline - this comment by MRM

            if words:
                current = words.pop(0)
                for word in words:
                    increment = 1 + len(word)
                    if word == '\n':
                        self.lines.append(self.align(current))
                        current = ''
                    elif len(current) + increment > self.width:
                        self.lines.append(self.align(current))
                        current = word
                    else:
                        if current:
                            current = current + ' ' + word
                        else:
                            current = word
                if current: self.lines.append(self.align(current))

        def getline(self, index):
            if index < len(self.lines):
                return ' '*self.margin + self.lines[index]
            else:
                if self.pad:
                    return ' ' * (self.margin + self.width)
                else:
                    return ''

        def numlines(self):
            return len(self.lines)

    def __init__(self, colspeclist):
        self.columns = []
        for colspec in colspeclist:
            self.columns.append(apply(TextFormatter.Column, (), colspec))

    def compose(self, textlist):
        numlines = 0
        textlist = list(textlist)
        if len(textlist) != len(self.columns):
            raise IndexError, "Number of text items does not match columns"
        for text, column in map(None, textlist, self.columns):
            column.wrap(text)
            numlines = max(numlines, column.numlines())
        complines = [''] * numlines
        for ln in range(numlines):
            for column in self.columns:
                complines[ln] = complines[ln] + column.getline(ln)
        return string.join(complines, '\n') + '\n'
        #return string.join(complines, '\n')


def test():
    formatter = TextFormatter(
            (
                    {'width': 10},
                    {'width': 12, 'margin': 4, 'fill': 0},
                    {'width': 20, 'margin': 8, 'alignment': right},
            )
    )

    result = formatter.compose(
            (
                    "A rather short paragraph",
                    "Here is\na paragraph containing a veryveryverylongwordindeed.",
                    "And now for something\non the right-hand side.",
            )
    )

    print result
    print
    for line in result.split('\n'):
        print repr(line+'\n')

if __name__ == '__main__':
    test()
martineau