Das Jupyter-Notizbuch zeigt zwei Pandas-Tische nebeneinander an

93

Ich habe zwei Pandas-Datenrahmen und möchte sie im Jupyter-Notizbuch anzeigen.

So etwas tun wie:

display(df1)
display(df2)

Zeigt sie untereinander:

Geben Sie hier die Bildbeschreibung ein

Ich hätte gerne einen zweiten Datenrahmen rechts vom ersten. Es gibt eine ähnliche Frage , aber es sieht so aus, als ob eine Person damit zufrieden ist, sie in einem Datenrahmen zusammenzuführen, um den Unterschied zwischen ihnen zu zeigen.

Das wird bei mir nicht funktionieren. In meinem Fall können Datenrahmen völlig unterschiedliche (nicht vergleichbare) Elemente darstellen und ihre Größe kann unterschiedlich sein. Mein Hauptziel ist es daher, Platz zu sparen.

Salvador Dali
quelle
Ich habe die Lösung von Jake Vanderplas veröffentlicht. Schöner sauberer Code.
Privat

Antworten:

83

Sie können das CSS des Ausgabecodes überschreiben. Es flex-direction: columnwird standardmäßig verwendet. Versuchen Sie es rowstattdessen zu ändern . Hier ist ein Beispiel:

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 Bild

Sie können das CSS natürlich weiter anpassen, wie Sie möchten.

Wenn Sie nur auf die Ausgabe einer Zelle abzielen möchten, verwenden Sie den :nth-child()Selektor. Mit diesem Code wird beispielsweise das CSS der Ausgabe nur der 5. Zelle im Notizbuch geändert:

CSS = """
div.cell:nth-child(5) .output {
    flex-direction: row;
}
"""
Zarak
quelle
5
Diese Lösung betrifft alle Zellen. Wie kann ich dies nur für eine Zelle tun?
Jrovegno
2
@jrovegno Ich habe meine Antwort aktualisiert, um die von Ihnen angeforderten Informationen aufzunehmen.
Zarak
1
@ntg Sie müssen sicherstellen, dass die Zeile HTML('<style>{}</style>'.format(CSS))die letzte Zeile in der Zelle ist (und vergessen Sie nicht, den n-ten untergeordneten Selektor zu verwenden). Dies kann jedoch zu Problemen bei der Formatierung führen, sodass Ihre Lösung besser ist. (+1)
Zarak
1
@zarak Danke für die freundlichen Worte :) In Ihrer Lösung können Sie anstelle von HTML ('<style> {} </) (HTML (' <style> {} </ style> '.format (CSS))) anzeigen lassen Stil> '. Format (CSS)). Dann kann es an jedem Ort sein. Ich hatte immer noch das Problem mit der n-ten Zelle (was bedeutet, wenn ich kopiere und
einfüge
3
HTML('<style>.output {flex-direction: row;}</style>')der Einfachheit halber
Thomas Matthew
114

Am Ende habe ich eine Funktion geschrieben, die dies kann:

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)

Anwendungsbeispiel:

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)

Geben Sie hier die Bildbeschreibung ein

ntg
quelle
Das ist wirklich toll, danke. Wie einfach oder auf andere Weise wäre es Ihrer Meinung nach, den Datenrahmennamen über jeder Ausgabe hinzuzufügen?
Ricky McMaster
1
Es würde zwei Probleme geben: 1. Das Wissen um die Namen der Datenrahmen liegt außerhalb des Gültigkeitsbereichs von stackoverflow.com/questions/2749796/… , kann jedoch stackoverflow.com/questions/218616/… ausführen oder sie als Parameter übergeben.) 2. Sie würde extra HTML brauchen und es ist offen / bis zu Ihnen, was zu tun ist ... hier ist ein Basisbeispiel,
ntg
Vielen Dank für Ihre Antwort. Ich habe Kopfzeilen hinzugefügt , ähnlich wie Sie es in Ihrem letzten Kommentar beschrieben haben.
Antony Hatchkins
Erstaunliche Antwort. Das ist es, wonach ich auch suche. Ich lerne mich immer noch darum herum, also möchte ich wissen: 1) Warum hast du *argsstatt nur verwendet df? Liegt es daran, dass Sie mehrere Eingaben mit haben können *args? 2) Welcher Teil Ihrer Funktion bewirkt, dass der 2. und der nachfolgende df rechts vom ersten anstatt darunter hinzugefügt werden? Ist es das 'table style="display:inline"'Teil?
Bowen Liu
1
Vielen Dank für Ihre großartige Lösung! Wenn Sie Ihre Datenrahmen vor der Anzeige formatieren möchten, lautet die Eingabe Stylers und nicht DataFrames. In diesem Fall verwenden Sie html_str+=df.render()anstelle von html_str+=df.to_html().
Martin Becker
34

Ausgehend von pandas 0.17.1der Visualisierung von DataFrames können direkt mit Pandas-Styling-Methoden geändert werden

Um zwei DataFrames nebeneinander anzuzeigen, müssen Sie set_table_attributesdas "style='display:inline'"in ntg answer vorgeschlagene Argument verwenden . Dies gibt zwei StylerObjekte zurück. Um die ausgerichteten Datenrahmen anzuzeigen, übergeben Sie einfach ihre verknüpfte HTML-Darstellung durch diedisplay_html Methode von IPython.

Mit dieser Methode ist es auch einfacher, andere Styling-Optionen hinzuzufügen. So fügen Sie eine Beschriftung hinzu, wie hier angefordert :

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)

Ausgerichtete Datenrahmen Pandas Styler mit Beschriftung

Gibbone
quelle
15

Durch die Kombination der Ansätze von Gibbone (zum Festlegen von Stilen und Beschriftungen) und Stevi (Hinzufügen von Speicherplatz) habe ich meine Funktionsversion erstellt, die Pandas-Datenrahmen als Tabellen nebeneinander ausgibt:

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))

Verwendung:

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

Ausgabe:

Geben Sie hier die Bildbeschreibung ein

Anton Golubev
quelle
11

Hier ist die Lösung von Jake Vanderplas, auf die ich neulich gestoßen bin:

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)

Bildnachweis: https://github.com/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/03.08-Aggregation-and-Grouping.ipynb

Privat
quelle
1
Könnten Sie bitte diese Antwort erklären. Jake VanderPlas hat es auf seiner Website nicht erklärt. Dies ist die einzige Lösung, die den Dataset-Namen oben druckt.
Gaurav Singhal
Was möchtest du wissen?
Privat
Kann eine Beschreibung aller Funktionen sein / wie sie funktionieren, wie sie als so weiter bezeichnet werden ... damit neue Python-Programmierer sie richtig verstehen können.
Gaurav Singhal
9

Meine Lösung erstellt einfach eine Tabelle in HTML ohne CSS-Hacks und gibt sie aus:

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)

Ausgabe

Yasin Zähringer
quelle
9

Dies fügt der Antwort von @ nts Header hinzu:

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)

Geben Sie hier die Bildbeschreibung ein

Antony Hatchkins
quelle
Dies scheint sehr nützlich zu sein, gibt mir aber ein Problem. Denn mydisplay((df1,df2))gibt nur df.to_html(index=False) df.to_html(index=False)anstelle des Datenrahmeninhalts. Außerdem gibt es ein zusätzliches '}' Zeichen bei f'string '.
Etwas unabhängig, aber ist es möglich, Ihre Funktion so zu ändern, dass der Code für die Zellenausgabe ausgeblendet wird?
Alpenmilch411
1
@ Alpenmilch411 siehe "Eingabe verbergen" Erweiterung
Antony Hatchkins
Irgendeine Idee, wie man 'max_rows' dazu hinzufügt?
Tickon
2

Am Ende habe ich HBOX verwendet

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)))
Dinis Cruz
quelle
2

Gibbones Antwort hat bei mir funktioniert! Wenn Sie zusätzlichen Platz zwischen den Tabellen wünschen, gehen Sie zu dem von ihm vorgeschlagenen Code und fügen Sie diesen "\xa0\xa0\xa0"der folgenden Codezeile hinzu.

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

Ich beschloss, Yasins elegante Antwort um einige zusätzliche Funktionen zu erweitern, bei denen man sowohl die Anzahl der Spalten als auch die Zeilen auswählen kann . Alle zusätzlichen dfs werden dann unten hinzugefügt. Zusätzlich kann man wählen, in welcher Reihenfolge das Raster gefüllt werden soll (ändern Sie einfach das Füllschlüsselwort in "Spalten" oder "Zeilen", je nach Bedarf).

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)

Testausgabe

Martino Schröder
quelle
0

Erweiterung der Antwort von antony Wenn Sie die Visualisierung von Tabellen auf eine bestimmte Anzahl von Blöcken pro Zeile beschränken möchten, verwenden Sie die Variable maxTables.Geben Sie hier die Bildbeschreibung ein

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)
Arzanico
quelle