สมุดบันทึก Jupyter แสดงตารางแพนด้าสองตัวเคียงข้างกัน


96

ฉันมีดาต้าเฟรมของแพนด้าสองตัวและฉันต้องการแสดงในสมุดบันทึก Jupyter

ทำสิ่งที่ชอบ:

display(df1)
display(df2)

แสดงให้เห็นด้านล่าง:

ป้อนคำอธิบายภาพที่นี่

ฉันต้องการมีดาต้าเฟรมที่สองทางด้านขวาของอันแรก มีคำถามที่คล้ายกันแต่ดูเหมือนว่าจะมีคนพอใจกับการรวมเข้าด้วยกันในดาต้าเฟรมเดียวเพื่อแสดงความแตกต่างระหว่างกัน

สิ่งนี้จะไม่ได้ผลสำหรับฉัน ในกรณีของฉันดาต้าเฟรมอาจแสดงถึงความแตกต่างอย่างสิ้นเชิง (องค์ประกอบที่เทียบไม่ได้) และขนาดของมันอาจแตกต่าง ดังนั้นเป้าหมายหลักของฉันคือการประหยัดพื้นที่


ฉันโพสต์วิธีแก้ปัญหาของ Jake Vanderplas รหัสสะอาดดี
ส่วนตัว

คำตอบ:


90

คุณสามารถแทนที่ 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()ตัวเลือก ตัวอย่างเช่นโค้ดนี้จะแก้ไข CSS ของเอาต์พุตเฉพาะเซลล์ที่ 5 ในสมุดบันทึก:

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

5
วิธีนี้มีผลต่อเซลล์ทั้งหมดฉันจะทำสิ่งนี้สำหรับเซลล์เดียวได้อย่างไร
jrovegno

2
@jrovegno ฉันอัปเดตคำตอบเพื่อรวมข้อมูลที่คุณร้องขอ
zarak

1
@ntg คุณต้องแน่ใจว่าบรรทัดHTML('<style>{}</style>'.format(CSS))นั้นเป็นบรรทัดสุดท้ายในเซลล์ (และอย่าลืมใช้ตัวเลือกที่ n ลูกด้วย) อย่างไรก็ตามสิ่งนี้อาจทำให้เกิดปัญหากับการจัดรูปแบบดังนั้นวิธีแก้ปัญหาของคุณจึงดีกว่า (+1)
zarak

1
@zarak Thanx สำหรับคำพูดที่ดี :) ในโซลูชันของคุณคุณสามารถใช้ display (HTML ('<style> {} </style>' .format (CSS))) แทน HTML ('<style> {} </ style> '. รูปแบบ (CSS)) จากนั้นสามารถอยู่ที่ใดก็ได้ ฉันยังคงมีปัญหากับเซลล์ที่ n (หมายถึงถ้าฉันคัดลอกวาง n อาจเปลี่ยนไป)
ntg

4
HTML('<style>.output {flex-direction: row;}</style>')เพื่อความเรียบง่าย
Thomas Matthew

123

ฉันได้เขียนฟังก์ชันที่สามารถทำได้:

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. การรู้ชื่อของดาต้าเฟรมอยู่นอกขอบเขต imho stackoverflow.com/questions/2749796/…แต่สามารถทำstackoverflow.com/questions/218616/…หรือส่งเป็นพารามิเตอร์) 2. คุณ จะต้องมี html พิเศษและเปิดแล้ว / ขึ้นอยู่กับคุณว่าจะทำอย่างไร ... นี่คือตัวอย่างพื้นฐานของลักษณะส่วนนี้: i.stack.imgur.com/mIVsD.png
ntg

ขอบคุณสำหรับคำตอบเราได้เพิ่มส่วนหัวในลักษณะที่คล้ายกับที่คุณอธิบายไว้ในความคิดเห็นล่าสุดของคุณ
Antony Hatchkins

คำตอบที่น่าทึ่ง นี่คือสิ่งที่ฉันกำลังมองหาเช่นกัน ฉันยังคงเรียนรู้วิธีการอยู่รอบตัวฉันจึงอยากรู้ว่า: 1) ทำไมคุณถึงใช้*argsแทน just df? เป็นเพราะคุณสามารถใส่ได้หลายช่องด้วย*argsหรือไม่? 2) ส่วนใดของฟังก์ชันของคุณที่ทำให้ df ตัวที่ 2 และตามมาเพิ่มทางด้านขวาของฟังก์ชันแรกแทนที่จะอยู่ด้านล่าง มันคือ'table style="display:inline"'ส่วน? ขอบคุณอีกครั้ง
Bowen Liu

1
ขอบคุณสำหรับวิธีแก้ปัญหาที่ยอดเยี่ยม! หากคุณต้องการจัดรูปแบบดาต้าเฟรมของคุณก่อนที่จะแสดงอินพุตจะเป็นStylers ไม่ใช่DataFrames ในกรณีนี้ให้ใช้html_str+=df.render()แทนhtml_str+=df.to_html().
Martin Becker

37

เริ่มต้นจากpandas 0.17.1การแสดงภาพของ DataFrames สามารถแก้ไขได้โดยตรงด้วยวิธีการจัดแต่งทรงผมของแพนด้า

ในการแสดงทั้งสองฝั่ง DataFrames ข้างคุณต้องใช้set_table_attributesกับการโต้แย้ง"style='display:inline'"ตามที่แนะนำในคำตอบ NTG สิ่งนี้จะส่งคืนStylerวัตถุสองชิ้น ในการแสดง dataframes ที่จัดแนวเพียงแค่ส่งผ่านการแสดง HTML ที่รวมเข้าด้วยกันผ่านdisplay_htmlวิธีการจาก IPython

ด้วยวิธีนี้ยังง่ายกว่าในการเพิ่มตัวเลือกการจัดแต่งทรงผมอื่น ๆ วิธีการเพิ่มคำบรรยายตามที่ร้องขอที่นี่ :

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)

จัดรูปแบบดาต้าเฟรมดาต้าเฟรมพร้อมคำบรรยาย


18

การรวมวิธีการของ gibbone (เพื่อตั้งค่ารูปแบบและคำอธิบายภาพ) และ stevi (การเพิ่มช่องว่าง) ฉันได้สร้างฟังก์ชันเวอร์ชันของฉันขึ้นมาซึ่งแสดงผลลัพธ์ดาต้าเฟรมของแพนด้าเป็นตารางเคียงข้างกัน:

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 ไม่ได้อธิบายไว้ในเว็บไซต์ของเขา นี่เป็นโซลูชันเดียวที่พิมพ์ชื่อชุดข้อมูลที่ด้านบน
Gaurav Singhal

อยากรู้อะไร
ส่วนตัว

อาจเป็นรายละเอียดของฟังก์ชั่นทั้งหมด / ทำงานอย่างไรเรียกว่าอย่างไรเป็นต้น ... เพื่อให้นักเขียนโปรแกรม python มือใหม่เข้าใจได้อย่างถูกต้อง
Gaurav Singhal

10

โซลูชันของฉันเพียงแค่สร้างตารางใน HTML โดยไม่ต้องแฮ็ก CSS และส่งออก:

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)แทนเนื้อหา dataframe เท่านั้น นอกจากนี้ยังมีเครื่องหมาย "}" ที่ f'string "พิเศษอีกด้วย

ค่อนข้างไม่เกี่ยวข้อง แต่เป็นไปได้ไหมที่จะปรับเปลี่ยนฟังก์ชันของคุณเพื่อให้ซ่อนโค้ดสำหรับเอาต์พุตเซลล์
alpenmilch411

1
@ alpenmilch411 ดูส่วนขยาย "ซ่อนอินพุต"
Antony Hatchkins

มีความคิดอย่างไรที่จะเพิ่ม "max_rows" ลงในสิ่งนี้
Tickon

สิ่งนี้จะสูญเสียดัชนีหลายดัชนีเช่นกันเมื่อใช้กรอบข้อมูลที่จัดทำดัชนีหลายรายการ
Parthiban Rajendran

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 ซึ่งเราสามารถเลือกได้ทั้งจำนวนคอลัมน์และแถว จากนั้นเพิ่ม dfs พิเศษใด ๆ ที่ด้านล่าง นอกจากนี้ยังสามารถเลือกลำดับที่จะเติมตาราง (เพียงแค่เปลี่ยนคำสำคัญเติมเป็น 'cols' หรือ 'แถว' ตามต้องการ)

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)

ผลลัพธ์การทดสอบ


1

รหัส @zarak ค่อนข้างเล็ก แต่มีผลต่อรูปแบบของสมุดบันทึกทั้งหมด ตัวเลือกอื่น ๆ ค่อนข้างยุ่งสำหรับฉัน

ฉันได้เพิ่ม CSS ที่ชัดเจนลงในคำตอบนี้ซึ่งมีผลต่อเอาต์พุตของเซลล์ปัจจุบันเท่านั้น นอกจากนี้คุณยังสามารถเพิ่มอะไรก็ได้ที่ด้านล่างหรือด้านบนดาต้าเฟรม

from ipywidgets import widgets, Layout
from IPython import display
import pandas as pd
import numpy as np

# sample data
df1 = pd.DataFrame(np.random.randn(8, 3))
df2 = pd.DataFrame(np.random.randn(8, 3))

# create output widgets
widget1 = widgets.Output()
widget2 = widgets.Output()

# render in output widgets
with widget1:
    display.display(df1.style.set_caption('First dataframe'))
    df1.info()
with widget2:
    display.display(df2.style.set_caption('Second dataframe'))
    df1.info()


# add some CSS styles to distribute free space
box_layout = Layout(display='flex',
                    flex_flow='row',
                    justify_content='space-around',
                    width='auto'
                   )
    
# create Horisontal Box container
hbox = widgets.HBox([widget1, widget2], layout=box_layout)

# render hbox
hbox

ป้อนคำอธิบายภาพที่นี่


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)

สิ่งนี้จะสูญเสียดัชนีหลายดัชนีเมื่อใช้กับดาต้าเฟรมที่จัดทำดัชนีหลายตัว
Parthiban Rajendran
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.