Jupyter笔记本电脑并排显示两个熊猫桌


94

我有两个熊猫数据框,我想在Jupyter笔记本中显示它们。

做类似的事情:

display(df1)
display(df2)

将它们显示为一个接一个:

在此处输入图片说明

我想在第一个数据框的右边添加第二个数据框。有一个类似的问题,但似乎有人对将它们合并到一个显示它们之间差异的数据框中感到满意。

这对我不起作用。在我的情况下,数据帧可以表示完全不同的(不可比较的元素),并且它们的大小可以不同。因此,我的主要目标是节省空间。


我发布了杰克·范德普拉斯的解决方案。漂亮干净的代码。
私人

Answers:


85

您可以覆盖输出代码的CSS。flex-direction: column默认情况下使用。尝试将其更改为row。这是一个例子:

import pandas as pd
import numpy as np
from IPython.display import display, HTML

CSS = """
.output {
    flex-direction: row;
}
"""

HTML('<style>{}</style>'.format(CSS))

Jupyter图片

当然,您可以根据需要进一步自定义CSS。

如果只希望定位一个单元格的输出,请尝试使用:nth-child()选择器。例如,此代码将只修改笔记本中第5个单元格的输出的CSS:

CSS = """
div.cell:nth-child(5) .output {
    flex-direction: row;
}
"""

5
此解决方案会影响所有单元,我如何仅对一个单元执行此操作?
jrovegno

2
@jrovegno我更新了我的答案,以包含您要求的信息。
扎拉克

1
@ntg您需要确保该行HTML('<style>{}</style>'.format(CSS))是单元格中的最后一行(并且不要忘记使用nth-child选择器)。但是,这可能会导致格式问题,因此您的解决方案更好。(+1)
扎拉克

1
@zarak Thanx表示好心的词:)在您的解决方案中,可以使用display(HTML('<style> {} </ style>'。format(CSS)))代替HTML('<style> {} </样式>'。format(CSS))。然后它可以在任何地方。我仍然有第n个单元格的问题(这意味着,如果我复制粘贴,n可能会改变)
ntg

4
HTML('<style>.output {flex-direction: row;}</style>')为了简单起见
Thomas Matthew

114

我最终写了一个可以做到这一点的函数:

from IPython.display import display_html
def display_side_by_side(*args):
    html_str=''
    for df in args:
        html_str+=df.to_html()
    display_html(html_str.replace('table','table style="display:inline"'),raw=True)

用法示例:

df1 = pd.DataFrame(np.arange(12).reshape((3,4)),columns=['A','B','C','D',])
df2 = pd.DataFrame(np.arange(16).reshape((4,4)),columns=['A','B','C','D',])
display_side_by_side(df1,df2,df1)

在此处输入图片说明


真的很棒,谢谢。您认为将数据帧名称添加到每个输出上方有多么容易,否则您会觉得呢?
Ricky McMaster

1
会有两个问题:1.知道数据帧的名称超出了恕我直言stackoverflow.com/questions/2749796/…但可以执行stackoverflow.com/questions/218616/…或将它们作为参数传递)2.您会需要额外的html及其开放式/由您决定要怎么做...这是该部分外观的基本示例: i.stack.imgur.com/mIVsD.png
ntg

感谢您的回答,我以与您上次评论中所述类似的方式向其添加了标头
安东尼·哈奇金斯

惊人的答案。这也是我在寻找的东西。我仍在学习解决方法,因此我想知道:1)为什么使用*args而不是df?是因为您可以使用进行多个输入*args吗?2)您的函数的哪一部分使第二个及随后的df添加到第一个df的右侧而不是它的下方?是这个'table style="display:inline"'部分吗?再次感谢
Bowen Liu

1
感谢您的出色解决方案!如果要在显示数据框之前对它们进行样式设置,则输入将为Stylers,而不是DataFrames。在这种情况下,请使用html_str+=df.render()代替html_str+=df.to_html()
马丁·贝克尔

35

pandas 0.17.1DataFrames的可视化开始,可以使用熊猫样式化方法直接进行修改

要并排显示两个DataFrame,您必须使用ntg answer中建议set_table_attributes的参数。这将返回两个对象。要显示对齐的数据框,只需通过IPython 的方法传递其合并的HTML表示。"style='display:inline'"Stylerdisplay_html

使用此方法也更容易添加其他样式选项。以下是如何添加标题,按要求在这里

import numpy as np
import pandas as pd   
from IPython.display import display_html 

df1 = pd.DataFrame(np.arange(12).reshape((3,4)),columns=['A','B','C','D',])
df2 = pd.DataFrame(np.arange(16).reshape((4,4)),columns=['A','B','C','D',])

df1_styler = df1.style.set_table_attributes("style='display:inline'").set_caption('Caption table 1')
df2_styler = df2.style.set_table_attributes("style='display:inline'").set_caption('Caption table 2')

display_html(df1_styler._repr_html_()+df2_styler._repr_html_(), raw=True)

带有标题的对齐的数据框pandas styler


15

结合使用gibbone(设置样式和标题)和stevi(添加空间)的方法,我制作了函数版本,该函数将pandas数据帧并排输出为表格:

from IPython.core.display import display, HTML

def display_side_by_side(dfs:list, captions:list):
    """Display tables side by side to save vertical space
    Input:
        dfs: list of pandas.DataFrame
        captions: list of table captions
    """
    output = ""
    combined = dict(zip(captions, dfs))
    for caption, df in combined.items():
        output += df.style.set_table_attributes("style='display:inline'").set_caption(caption)._repr_html_()
        output += "\xa0\xa0\xa0"
    display(HTML(output))

用法:

display_side_by_side([df1, df2, df3], ['caption1', 'caption2', 'caption3'])

输出:

在此处输入图片说明


11

这是前几天我遇到的杰克·范德普拉斯(Jake Vanderplas)的解决方案:

import numpy as np
import pandas as pd

class display(object):
    """Display HTML representation of multiple objects"""
    template = """<div style="float: left; padding: 10px;">
    <p style='font-family:"Courier New", Courier, monospace'>{0}</p>{1}
    </div>"""

    def __init__(self, *args):
        self.args = args

    def _repr_html_(self):
        return '\n'.join(self.template.format(a, eval(a)._repr_html_())
                     for a in self.args)

    def __repr__(self):
       return '\n\n'.join(a + '\n' + repr(eval(a))
                       for a in self.args)

图片来源:https : //github.com/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/03.08-Aggregation-and-Grouping.ipynb


1
你能解释这个答案吗?Jake VanderPlas尚未在他的网站上解释过。这是唯一在顶部显示数据集名称的解决方案。
加乌拉夫·辛哈尔

你想知道什么?
私人

可能是所有功能的描述/它们如何工作,如何称呼等等,以便新手python程序员可以正确理解它。
加拉夫·辛哈尔

10

我的解决方案仅用HTML构建表,而没有任何CSS hack并输出它:

import pandas as pd
from IPython.display import display,HTML

def multi_column_df_display(list_dfs, cols=3):
    html_table = "<table style='width:100%; border:0px'>{content}</table>"
    html_row = "<tr style='border:0px'>{content}</tr>"
    html_cell = "<td style='width:{width}%;vertical-align:top;border:0px'>{{content}}</td>"
    html_cell = html_cell.format(width=100/cols)

    cells = [ html_cell.format(content=df.to_html()) for df in list_dfs ]
    cells += (cols - (len(list_dfs)%cols)) * [html_cell.format(content="")] # pad
    rows = [ html_row.format(content="".join(cells[i:i+cols])) for i in range(0,len(cells),cols)]
    display(HTML(html_table.format(content="".join(rows))))

list_dfs = []
list_dfs.append( pd.DataFrame(2*[{"x":"hello"}]) )
list_dfs.append( pd.DataFrame(2*[{"x":"world"}]) )
multi_column_df_display(2*list_dfs)

输出量


9

这会在@nts的答案中添加标题:

from IPython.display import display_html

def mydisplay(dfs, names=[]):
    html_str = ''
    if names:
        html_str += ('<tr>' + 
                     ''.join(f'<td style="text-align:center">{name}</td>' for name in names) + 
                     '</tr>')
    html_str += ('<tr>' + 
                 ''.join(f'<td style="vertical-align:top"> {df.to_html(index=False)}</td>' 
                         for df in dfs) + 
                 '</tr>')
    html_str = f'<table>{html_str}</table>'
    html_str = html_str.replace('table','table style="display:inline"')
    display_html(html_str, raw=True)

在此处输入图片说明


这似乎很有用,但给我一个问题。对于mydisplay((df1,df2))仅给出df.to_html(index=False) df.to_html(index=False)而不是数据帧内容。另外,在f'string'处还有额外的'}'符号。

有点无关,但是可以修改您的功能,以便隐藏单元格输出的代码吗?
alpenmilch411

1
@ alpenmilch411参见“隐藏输入”扩展名
Antony Hatchkins '18

知道如何在其中添加“ max_rows”吗?
Tickon

2

我最终使用了HBOX

import ipywidgets as ipyw

def get_html_table(target_df, title):
    df_style = target_df.style.set_table_attributes("style='border:2px solid;font-size:10px;margin:10px'").set_caption(title)
    return df_style._repr_html_()

df_2_html_table = get_html_table(df_2, 'Data from Google Sheet')
df_4_html_table = get_html_table(df_4, 'Data from Jira')
ipyw.HBox((ipyw.HTML(df_2_html_table),ipyw.HTML(df_4_html_table)))

2

Gibbone的答案对我有用!如果要在表之间留出更多空间,请转到他建议的代码,并将其添加"\xa0\xa0\xa0"到以下代码行中。

display_html(df1_styler._repr_html_()+"\xa0\xa0\xa0"+df2_styler._repr_html_(), raw=True)

2

我决定在Yasin的优雅答案中添加一些额外的功能,在其中可以选择列数行数。然后将所有多余的df添加到底部。另外,可以选择填充网格的顺序(只需根据需要将填充关键字更改为“ cols”或“ rows”)

import pandas as pd
from IPython.display import display,HTML

def grid_df_display(list_dfs, rows = 2, cols=3, fill = 'cols'):
    html_table = "<table style='width:100%; border:0px'>{content}</table>"
    html_row = "<tr style='border:0px'>{content}</tr>"
    html_cell = "<td style='width:{width}%;vertical-align:top;border:0px'>{{content}}</td>"
    html_cell = html_cell.format(width=100/cols)

    cells = [ html_cell.format(content=df.to_html()) for df in list_dfs[:rows*cols] ]
    cells += cols * [html_cell.format(content="")] # pad

    if fill == 'rows': #fill in rows first (first row: 0,1,2,... col-1)
        grid = [ html_row.format(content="".join(cells[i:i+cols])) for i in range(0,rows*cols,cols)]

    if fill == 'cols': #fill columns first (first column: 0,1,2,..., rows-1)
        grid = [ html_row.format(content="".join(cells[i:rows*cols:rows])) for i in range(0,rows)]

    display(HTML(html_table.format(content="".join(grid))))

    #add extra dfs to bottom
    [display(list_dfs[i]) for i in range(rows*cols,len(list_dfs))]

list_dfs = []
list_dfs.extend((pd.DataFrame(2*[{"x":"hello"}]), 
             pd.DataFrame(2*[{"x":"world"}]), 
             pd.DataFrame(2*[{"x":"gdbye"}])))

grid_df_display(3*list_dfs)

测试输出


0

扩展antony的答案如果您想将表的可视化限制为每行一些块,请使用maxTables变量。在此处输入图片说明

def mydisplay(dfs, names=[]):

    count = 0
    maxTables = 6

    if not names:
        names = [x for x in range(len(dfs))]

    html_str = ''
    html_th = ''
    html_td = ''

    for df, name in zip(dfs, names):
        if count <= (maxTables):
            html_th += (''.join(f'<th style="text-align:center">{name}</th>'))
            html_td += (''.join(f'<td style="vertical-align:top"> {df.to_html(index=False)}</td>'))
            count += 1
        else:
            html_str += f'<tr>{html_th}</tr><tr>{html_td}</tr>'
            html_th = f'<th style="text-align:center">{name}</th>'
            html_td = f'<td style="vertical-align:top"> {df.to_html(index=False)}</td>'
            count = 0


    if count != 0:
        html_str += f'<tr>{html_th}</tr><tr>{html_td}</tr>'


    html_str += f'<table>{html_str}</table>'
    html_str = html_str.replace('table','table style="display:inline"')
    display_html(html_str, raw=True)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.