74

I use pd.set_option("display.colheader_justify","right") to set the column header. But I can't find the option for data by pd.describe_option().

How to set the data within a dataframe display left or right alignment for each column? Or, is it possible to define a format template for the whole row data display?

5
  • 2
    I don't think this exists, by default data is right justified according to the header position there only exists options to format the header not the data as far as I can see Commented Dec 14, 2015 at 9:04
  • 1
    are you looking to achieve this in the notebook or in general? Commented Jan 5, 2016 at 14:10
  • I don't think this exists (I know about some formatting stuff for pivots and Pandas Pretty Tables module), but AFAIK these won't do what you want. If all you need is to print the data, you can use to_string(justify-'left') - but this would apply it for all columns. It also seemed that this would work only if your column name is large enough to actually force the entries in the column to be justified. If the column name is small and the entry is large, it won't have any justification effect. Commented Jul 25, 2016 at 16:00
  • Most of these answers don't work on the console, for strings, apparently only in Jupyter Notebook (as of pandas 0.25), or for numeric fields. Please make sure to state which. Commented Feb 9, 2020 at 23:00
  • 1
    This really seems like a missing feature in Pandas / Jupyter, the lengths and struggle involved just to persuade it to display text in a column in a pleasant and readable way. Commented Jan 29, 2024 at 9:22

12 Answers 12

40

If you want to change the display in a Jupyter Notebook, you can use the Style feature.

# Test data
df = DataFrame({'text': ['foo', 'bar'],
                 'number': [1, 2]})

df.style.set_properties(**{'text-align': 'right'})

enter image description here

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

5 Comments

Unfortunately this does not work for strings, as can be seen by doing df.style.set_properties(**{'text-align': 'left'}) with the above example and inspecting the text data column (it will remain right-aligned)
{'text-align': 'left'} seems to work for me now thanks - Pandas 0.22.0, Jupyter 4.4.0, Chrome 70.
If you want to align the column headers for a single dataframe you can do it by df.style.set_table_styles([ dict(selector='th', props=[('text-align', 'left')] ) ])
To apply to a subset of columns, you can use the subset parameter: .set_properties(subset=["col1", "col2"], **{'text-align': 'right'}).
I want to clarify that you have to reassign the df. The Style command is not done inplace. df = df.style.set_properties(**{'text-align': 'left'})
25

The answer given by @Romain is great but I would like to summarize some comments:

# Test data
df = DataFrame({'text': ['foo', 'bar'],'number': [1, 2]})

dfStyler = df.style.set_properties(**{'text-align': 'left'})
dfStyler.set_table_styles([dict(selector='th', props=[('text-align', 'left')])])

will align all table text and the column headers as well.

1 Comment

the text is only aligning to the left until 2048, then everything resumes to the right
17
pip3 install tabulate

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

This will automatically align pandas header and column data to good view format. Automatically align pandas dataframes columns data to left. Removes showing of the index in pandas dataframe. Puts ---- between the header and column data.

Comments

10

Since solutions using pandas.Styler don't work in console printing (at least for me), I came up with the following code using pandas 1.3.3 and an example dataframe, printing all string columns left aligned (w/o header):

    df = pd.DataFrame({'float': [0.123, 7],
                       'int': [3, 357676],
                       'str': ["hello world", "bye"],
                       'cat': pd.Series(["a", "bbb"], dtype="category"),
                       'bool': [True, False]
                       })

    formatters = {}
    for col in df.select_dtypes("object"):
        len_max = df[col].str.len().max()
        formatters[col] = lambda _: f"{_:<{len_max}s}"

    print(df.to_string(formatters=formatters))
       float     int          str  cat   bool
    0  0.123       3  hello world    a   True
    1  7.000  357676  bye          bbb  False

If you also want to align the header left, add justify='left'. For some reason the header is now one character too far to left for some columns, but not for all:

    print(df.to_string(formatters=formatters, justify="left"))
       float  int    str          cat   bool 
    0  0.123       3  hello world    a   True
    1  7.000  357676  bye          bbb  False

However applying this pattern to other dtypes fails (also for string columns). I have no idea why this occurs. Be aware that string conversion is added below via astype, also inside the f-string via the !s flag (see f-string documentation):

    formatters = {}
    for col in df.columns:
        len_max = df[col].astype(str).str.len().max()
        formatters[col] = lambda _: f"{_!s:<{len_max}s}"
        print(col, len_max)

    print(df.to_string(formatters=formatters))
      float    int          str    cat   bool
    0 0.123  3      hello world  a      True 
    1 7.0   357676        bye    bbb    False

3 Comments

formatters[col] = lambda _: f"{_:<{len_max}s}" evaluates len_max within the f-string not at the time of the function definition but later causing unexpected behavior and your confusion. A workaround this issue is to use: eval('lambda x: f"{x:<{'+str(max_len_col)+'}s}"') which puts the right max_len value into the f-string. Now any number of string containing columns can be justified.
Great answer. Would you be so kind explaining what this line is doing lambda _: f"{_!s:<{len_max}s}", in particular what does _!s mean? Thanks.
!s uses the __str__() representation of the value. I've added a link to the f-string docs, there you can find an explanation.
6

Instead of justifying all columns the same way, I had a need to justify some columns differently. Since there was no mention in this thread, I thought of reminding the presence of the subset option:

Styler.set_properties(subset=None, **kwargs)[source]

From the same example as the OP, one could left justify just the 'text' column:

df = pd.DataFrame({'text': ['foo', 'bar'],
             'number': [1, 2]})
dfStyler = df.style.set_properties(subset=['text'],**{'text-align': 'left'})

Comments

4

I wrapped @Hagbard's answer in a function to use it whenever I wish to display a pandas dataframe consisting English text on a notebook cell:

from pandas import DataFrame


def left_align(df: DataFrame):
    left_aligned_df = df.style.set_properties(**{'text-align': 'left'})
    left_aligned_df = left_aligned_df.set_table_styles(
        [dict(selector='th', props=[('text-align', 'left')])]
    )
    return left_aligned_df

To show a dataframe, I simply write this:

left_align(df.head())

enter image description here

Caution: For large datasets, it prints all the rows and columns of df without any abstraction, so Jupyter crashes! That's why I use it with .head() or .tail() or some other limit.)

Comments

2

you can control it by a new context:

with pd.option_context('display.colheader_justify','right'):
    ...

4 Comments

looks like center aligning headers is not supported, which is a pity
Is there any option for justifying cells?
No this only left-aligns the column-headers, not the columns themselves.
I think that aligning the cell contents is not supported by pandas for console output. All answers either externally pad the cell string values, or use styling specific to html, Jupyter notebooks, etc. Don't waste your time.
2

If you wanna align both text and header to the left for example you can use:

df.style.set_properties(**{'text-align': 'left'}).set_table_styles([ dict(selector='th', props=[('text-align', 'left')] ) ])

This first sets the text to the left and then the header.

2 Comments

This doesn't work on the console (as of pandas 0.25). Possibly only on Jupyter.
This doesn't work on the console, outside of Jupyter too.
1

In my situation, I have a class wrapper around my Pandas DataFrame. This allows me to left-justify the DataFrame's string output by customizing the wrapper's __str__() method.

Here's how I solved the problem for my application, based on Unutbu's answer to a similar question. The Pandas DataFrame is referenced by self.data:

def __str__(self):
    """
    Return the test stats report as a single string
    with left-justified columns.

    """
    # Columns containing boolean values need different format strings
    # to avoid 'ValueError: Invalid format specifier' exceptions.
    BOOL_COLUMNS = ['success',]

    formatters = {}
    for li in list(self.data.columns):
        if li in BOOL_COLUMNS:
            form = "{{!s:<5}}".format()
        else:
            max = self.data[li].str.len().max()
            form = "{{:<{}s}}".format(max)

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

    return self.data.to_string(formatters=formatters, index=False)

Comments

1

The solutions using style didn't work for me for left-aligning the text in the headers or cells, for displaying the table in jupyter. And I didn't want to install new libraries just for this

Instead you can get an html representation of the table, change that to specify that all should be left aligned, and display.

Here is a function to do exactly that:

import IPython.display

def display_left_aligned_df(df):
    html_table = df.to_html(notebook=True)
    html_table = html_table.replace('<td>', '<td style="text-align:left">').replace('<th>', '<th style="text-align:left">')
    IPython.display.display(IPython.display.HTML(html_table))

The notebook=True option means the html generated is intended for display in a notebook. So for example length of text in each cell is truncated.

This worked perfectly to generate a jupyter-style table for the dataframe, but left aligned.

Comments

0

Also looking to be able to print left-aligned tables to the console/terminal, I accomplished this with a somewhat modified approach from those given:

def whatever_scope():
    ...
    # Closure for the formatter. do whatever fancy formatting you want here.
    def leftalign(len):
        def format(v):
            return(f"{v:<{len}}")
        return(format)

    # create formatters for all columns
    formatters = {}
    for col in df.columns:
        maxlen = df[col].astype(str).str.len().max()
        formatters[col] = leftalign(maxlen)

    # use the formatters
    # remember that `justify` is just for column headers
    text_tbl = df.to_string(
        justify="left",
        formatters=formatters,
        index=False,
    )
    ...

Comments

0

To complement @Hagbard answer, I prefer to apply several styles by using Styler.pipe

def make_pretty(styler):
    styler.set_caption("caption")
    styler.format(subset=["number"], precision=2)
    styler.bar(subset=["number"], cmap="PuBu")
    styler.set_properties(**{'text-align': 'left'})
    styler.set_table_styles([dict(selector='th', props=[('text-align', 'left')])])
    return styler

df.head().style.pipe(make_pretty)

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.