25

I am trying to format the output in an IPython notebook. I tried using the to_string function, and this neatly lets me eliminate the index column. But the textual data is right justified.

In [10]:

import pandas as pd
columns = ['Text', 'Value']
a = pd.DataFrame ({'Text': ['abcdef', 'x'], 'Value': [12.34, 4.2]})
print (a.to_string (index=False))

   Text  Value
 abcdef  12.34
      x   4.20

The same is true when just printing the dataframe.

In [12]:

print (a)

     Text  Value
0  abcdef  12.34
1       x   4.20

The justify argument in the to_string function, surprisingly, only justifies the column heading.

In [13]:

import pandas as pd
columns = ['Text', 'Value']
a = pd.DataFrame ({'Text': ['abcdef', 'x'], 'Value': [12.34, 4.2]})
print (a.to_string (justify='left', index=False))
Text     Value
 abcdef  12.34
      x   4.20

How can I control the justification settings for individual columns?

1
  • As a side note: this is not currently supported for html rendering of data frames either. Commented Nov 6, 2015 at 15:06

5 Answers 5

20

If you're willing to use another library, tabulate will do this -

$ pip install tabulate

and then

from tabulate import tabulate
df = pd.DataFrame ({'Text': ['abcdef', 'x'], 'Value': [12.34, 4.2]})
print(tabulate(df, showindex=False, headers=df.columns))

Text      Value
------  -------
abcdef    12.34
x          4.2

It has various other output formats also.

Sign up to request clarification or add additional context in comments.

Comments

20

You could use a['Text'].str.len().max() to compute the length of the longest string in a['Text'], and use that number, N, in a left-justified formatter '{:<Ns}'.format:

In [211]: print(a.to_string(formatters={'Text':'{{:<{}s}}'.format(a['Text'].str.len().max()).format}, index=False))
   Text  Value
 abcdef  12.34
 x        4.20

3 Comments

That is pretty close to what I want. It left aligns the data in the rows of that column, but leaves the column heading "outdented" by a character, at least in this case, when I also use the 'justify' option.
This is what I want -- thanks. But it's still pretty verbose. I think there should be a simpler way.
thanks, it's working but it still seems to append an extra space at the beginning.
7

I like @unutbu's answer (not requiring any additional dependencies). @JS.'s additions are a step in the direction (towards something re-usable).

Since the construction of the formatter dict is the difficult part, let's create a function which creates the formatter dict from a DataFrame and an optional list of columns to format.

def make_lalign_formatter(df, cols=None):
    """
    Construct formatter dict to left-align columns.

    Parameters
    ----------
    df : pandas.core.frame.DataFrame
        The DataFrame to format
    cols : None or iterable of strings, optional
        The columns of df to left-align. The default, cols=None, will
        left-align all the columns of dtype object

    Returns
    -------
    dict
        Formatter dictionary

    """
    if cols is None:
       cols = df.columns[df.dtypes == 'object'] 

    return {col: f'{{:<{df[col].str.len().max()}s}}'.format for col in cols}

Let's create some example data to demonstrate using this function:

import pandas as pd

# Make some data
data = {'First': ['Tom', 'Dick', 'Harry'],
        'Last': ['Thumb', 'Whittington', 'Potter'],
        'Age': [183, 667, 23]}

# Make into a DataFrame
df = pd.DataFrame(data)

To align all the columns of type object in our DataFrame:

# Left align all columns
print(df.to_string(formatters=make_lalign_formatter(df), 
                   index=False,
                   justify='left'))

To align only the 'First' column:

# Left align 'First' column
print(df.to_string(formatters=make_lalign_formatter(df, cols=['First']), 
                   index=False,
                   justify='left'))

2 Comments

Nice function, i found it more handy and reusable though, Only one thing which i see missing is column names are still outdented.
Thanks @kulfi. Yes, I've just noticed this too. It's in using the justify='left' argument. I'm not sure if there is a fix for this issue. I haven't been able to find one.
4

This works on Python 3.7 (functools is a part of that release now)

# pylint: disable=C0103,C0200,R0205
from __future__ import print_function
import pandas as pd
import functools

@staticmethod
def displayDataFrame(dataframe, displayNumRows=True, displayIndex=True, leftJustify=True):
    # type: (pd.DataFrame, bool, bool, bool) -> None
    """
    :param dataframe: pandas DataFrame
    :param displayNumRows: If True, show the number or rows in the output.
    :param displayIndex: If True, then show the indexes
    :param leftJustify: If True, then use technique to format columns left justified.
    :return: None
    """

    if leftJustify:
        formatters = {}

        for columnName in list(dataframe.columns):
            columnType = type(columnName)  # The magic!!
            # print("{} =>  {}".format(columnName, columnType))
            if columnType == type(bool):
                form = "{{!s:<8}}".format()
            elif columnType == type(float):
                form = "{{!s:<5}}".format()
            else:
                max = dataframe[columnName].str.len().max()
                form = "{{:<{}s}}".format(max)

            formatters[columnName] = functools.partial(str.format, form)

        print(dataframe.to_string(index=displayIndex, formatters=formatters), end="\n\n")
    else:
        print(dataframe.to_string(index=displayIndex), end="\n\n")

    if displayNumRows:
        print("Num Rows: {}".format(len(dataframe)), end="\n\n")

Comments

3

I converted @unutbu's approach to a function so I could left-justify my dataframes.

my_df = pd.DataFrame({'StringVals': ["Text string One", "Text string Two", "Text string Three"]})

def left_justified(df):
    formatters = {}
    for li in list(df.columns):
        max = df[li].str.len().max()
        form = "{{:<{}s}}".format(max)
        formatters[li] = functools.partial(str.format, form)
    return df.to_string(formatters=formatters, index=False)

So now this:

print(my_df.to_string())

          StringVals
0    Text string One
1    Text string Two
2  Text string Three

becomes this:

print(left_justified(my_df))

StringVals
Text string One  
Text string Two  
Text string Three

Note, however, any non-string values in your dataframe will give you errors:

AttributeError: Can only use .str accessor with string values, which use np.object_ dtype in pandas

You'll have to pass different format strings to .to_string() if you want it to work with non-string values:

my_df2 = pd.DataFrame({'Booleans'  : [False, True, True],
                       'Floats'    : [1.0, 0.4, 1.5],           
                       'StringVals': ["Text string One", "Text string Two", "Text string Three"]})

FLOAT_COLUMNS = ('Floats',)
BOOLEAN_COLUMNS = ('Booleans',)

def left_justified2(df):
    formatters = {}

    # Pass a custom pattern to format(), based on
    # type of data
    for li in list(df.columns):
        if li in FLOAT_COLUMNS:
           form = "{{!s:<5}}".format()
        elif li in BOOLEAN_COLUMNS:
            form = "{{!s:<8}}".format()
        else:
            max = df[li].str.len().max()
            form = "{{:<{}s}}".format(max)
        formatters[li] = functools.partial(str.format, form)
    return df.to_string(formatters=formatters, index=False)

With floats and booleans:

print(left_justified2(my_df2))

Booleans Floats         StringVals
False     1.0    Text string One  
True      0.4    Text string Two  
True      1.5    Text string Three

Note this approach is a bit of a hack. Not only do you have to maintain column names in a separate lists, but you also have to best-guess at the data widths. Perhaps someone with better Pandas-Fu can demonstrate how to automate parsing the dataframe info to generate the formats automatically.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.