如何在IPython笔记本中的pandas DataFrame列中左对齐文本?

25
我正在尝试在IPython笔记本中格式化输出。我尝试使用to_string函数,这使我可以整洁地消除索引列。但文本数据是右对齐的。
在[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

当仅打印数据框时,情况也是如此。

In [12]:

print (a)

     Text  Value
0  abcdef  12.34
1       x   4.20

to_string函数中的justify参数令人惊讶地只对列标题进行对齐。

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

我如何控制单个列的对齐设置?


作为一则附注:当前对于数据框的 HTML 渲染也不受支持 - kynan
5个回答

20

如果您愿意使用另一个库,tabulate可以完成此操作 -

$ pip 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

它还有其他多种输出格式。


19
你可以使用 a['Text'].str.len().max() 来计算 a['Text'] 中最长字符串的长度,并使用该数字 N 在左对齐格式化程序 '{:<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

1
这非常接近我想要的。它将该列的行中的数据左对齐,但至少在这种情况下,当我使用“对齐”选项时,它会使列标题“向外缩进”一个字符。 - Fred Mitchell
9
这就是我想要的,谢谢。但还是有点啰嗦。我认为应该有更简单的方法。 - gabe
谢谢,它能工作了,但似乎仍然在开头添加了一个额外的空格。 - Praneeth Kumar

6

我喜欢@unutbu的答案(不需要任何额外的依赖项)。@JS的补充是朝着可重复使用的方向迈出的一步。

由于构建格式化程序字典是困难的部分,让我们创建一个函数,该函数从DataFrame和一个可选的要格式化的列列表中创建格式化程序字典。

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}

让我们创建一些示例数据来演示如何使用此函数:

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)

为了对齐DataFrame中所有类型为object的列,可以使用以下代码:
# Left align all columns
print(df.to_string(formatters=make_lalign_formatter(df), 
                   index=False,
                   justify='left'))

仅对'First'列进行对齐:

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

1
不错的函数,我发现它更方便和可重用,但唯一缺少的是列名仍然是“outdented”。 - user2023
1
谢谢@kulfi。是的,我也刚刚注意到了。这是使用justify='left'参数时出现的问题。我不确定是否有解决此问题的方法。我还没有找到一个。 - jwalton

4
这适用于Python 3.7(functools现在是该版本的一部分)。
# 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")

3
我将@unutbu的方法转换为一个函数,这样我就可以左对齐我的数据框。
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)

现在是这样的:
print(my_df.to_string())

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

变成了这样:

print(left_justified(my_df))

StringVals
Text string One  
Text string Two  
Text string Three

请注意,但是您的数据框中任何非字符串值都会导致错误:
AttributeError: Can only use .str accessor with string values, which use np.object_ dtype in pandas
如果您想让它与非字符串值一起使用,您将不得不传递不同的格式字符串给.to_string():
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)

使用浮点数和布尔值:

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

请注意,这种方法有点“hacky”。不仅需要在单独的列表中维护列名,还需要最好猜测数据宽度。也许有更好的Pandas-Fu的人可以演示如何自动解析数据框信息以自动生成格式。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接