Il taccuino Jupyter mostra due tavolini di panda fianco a fianco


94

Ho due dataframe panda e vorrei visualizzarli nel notebook Jupyter.

Fare qualcosa come:

display(df1)
display(df2)

Li mostra uno sotto l'altro:

inserisci qui la descrizione dell'immagine

Vorrei avere un secondo dataframe a destra del primo. C'è una domanda simile , ma sembra che una persona sia soddisfatta di unirli in un frame di dati per mostrare la differenza tra loro.

Questo non funzionerà per me. Nel mio caso i dataframe possono rappresentare completamente diversi (elementi non confrontabili) e la loro dimensione può essere diversa. Quindi il mio obiettivo principale è risparmiare spazio.


Ho pubblicato la soluzione di Jake Vanderplas. Bel codice pulito.
Privato

Risposte:


85

Potresti sovrascrivere il CSS del codice di output. Utilizza flex-direction: columnper impostazione predefinita. Prova a cambiarlo in row. Ecco un esempio:

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

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

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

Immagine di Jupyter

Ovviamente potresti personalizzare ulteriormente il CSS come desideri.

Se desideri scegliere come target solo l'output di una cella, prova a utilizzare il :nth-child()selettore. Ad esempio, questo codice modificherà il CSS dell'output solo della quinta cella del notebook:

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

5
Questa soluzione interessa tutte le celle, come posso farlo per una sola cella?
jrovegno

2
@jrovegno ho aggiornato la mia risposta per includere le informazioni che hai richiesto.
zarak

1
@ntg È necessario assicurarsi che la riga HTML('<style>{}</style>'.format(CSS))sia l'ultima riga nella cella (e non dimenticare di utilizzare il selettore n-esimo figlio). Tuttavia, ciò potrebbe causare problemi con la formattazione, quindi la tua soluzione è migliore. (+1)
zarak

1
@zarak Grazie per le parole gentili :) Nella tua soluzione, puoi avere display (HTML ('<style> {} </style>' .format (CSS))) invece di HTML ('<style> {} </ stile> '. formato (CSS)). Quindi può essere in qualsiasi posto. Ho ancora avuto il problema con l'ennesima cella (il che significa che se copio e incolla, n potrebbe cambiare)
ntg

4
HTML('<style>.output {flex-direction: row;}</style>')per semplicità
Thomas Matthew,

114

Ho finito per scrivere una funzione che può fare questo:

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)

Utilizzo di esempio:

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)

inserisci qui la descrizione dell'immagine


È davvero fantastico, grazie. Quanto sarebbe facile o meno aggiungere il nome del frame di dati sopra ogni output, pensi?
Ricky McMaster

1
Ci sarebbero due problemi: 1. conoscere i nomi dei dataframe non rientra nell'ambito imho stackoverflow.com/questions/2749796/… ma puoi eseguire stackoverflow.com/questions/218616/… , o passarli come parametri) 2. Tu avrebbe bisogno di html extra ed è aperto / a te cosa fare ... ecco un esempio base di come potrebbe apparire questa parte: i.stack.imgur.com/mIVsD.png
ntg

Grazie per la tua risposta, ho aggiunto intestazioni in un modo simile a quello che hai descritto nel tuo ultimo commento.
Antony Hatchkins

Risposta incredibile. Questo è anche quello che cerco. Sto ancora imparando a muovermi, quindi voglio sapere: 1) Perché hai usato *argsinvece di solo df? È perché puoi avere più input con *args? 2) Quale parte della tua funzione fa aggiungere il 2 ° e il successivo df a destra del primo invece che sotto? È la 'table style="display:inline"'parte? Grazie ancora
Bowen Liu

1
Grazie per la tua ottima soluzione! Se vuoi applicare uno stile ai tuoi dataframe prima di visualizzarli, l'input sarà Stylers, non DataFrames. In questo caso, usa html_str+=df.render()invece di html_str+=df.to_html().
Martin Becker

35

A partire dalla pandas 0.17.1visualizzazione dei DataFrame possono essere modificati direttamente con metodi di styling panda

Per visualizzare due DataFrame fianco a fianco è necessario utilizzare set_table_attributescon l'argomento "style='display:inline'"come suggerito nella risposta ntg . Ciò restituirà due Styleroggetti. Per visualizzare i dataframe allineati basta passare la loro rappresentazione HTML unita attraverso il filedisplay_html metodo da IPython.

Con questo metodo è anche più facile aggiungere altre opzioni di stile. Ecco come aggiungere una didascalia, come richiesto qui :

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)

allineato dataframes panda styler con didascalia


15

Combinando gli approcci di gibbone (per impostare stili e didascalie) e stevi (aggiungendo spazio) ho creato la mia versione di funzione, che produce frame di dati panda come tabelle fianco a fianco:

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

Utilizzo:

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

Produzione:

inserisci qui la descrizione dell'immagine


11

Ecco la soluzione di Jake Vanderplas che ho trovato proprio l'altro giorno:

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)

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


1
potresti spiegare questa risposta. Jake VanderPlas non lo ha spiegato sul suo sito web. Questa è l'unica soluzione che stampa il nome del set di dati in alto.
Gaurav Singhal

Cosa vuoi sapere?
Privato

Può essere una descrizione di tutte le funzioni / come funzionano, come vengono chiamate e così via ... in modo che i programmatori python principianti possano capirlo correttamente.
Gaurav Singhal

10

La mia soluzione crea semplicemente una tabella in HTML senza alcun hack CSS e la genera:

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)

Produzione


9

Questo aggiunge intestazioni alla risposta di @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)

inserisci qui la descrizione dell'immagine


Sembra molto utile, ma mi dà un problema. Per mydisplay((df1,df2))solo dà df.to_html(index=False) df.to_html(index=False)invece del contenuto del dataframe. Inoltre, c'è un segno "}" in più in f'string ".

Un po 'non correlato, ma è possibile modificare la funzione in modo che il codice per l'output della cella sia nascosto?
alpenmilch411

1
@ alpenmilch411 vedere l'estensione "Nascondi input"
Antony Hatchkins

Qualche idea su come aggiungere un "max_rows" a questo?
Tickon

2

Ho finito per usare 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

La risposta di Gibbone ha funzionato per me! Se vuoi più spazio tra le tabelle vai al codice che ha proposto e aggiungilo "\xa0\xa0\xa0"alla seguente riga di codice.

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

2

Ho deciso di aggiungere alcune funzionalità extra all'elegante risposta di Yasin, dove si può scegliere sia il numero di colonne colonne che di righe; eventuali DFS extra vengono quindi aggiunti in fondo. Inoltre si può scegliere in quale ordine riempire la griglia (cambiare semplicemente la parola chiave di riempimento in "cols" o "righe" secondo necessità)

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)

uscita di prova


0

Estensione della risposta di antony Se vuoi limitare la visualizzazione delle tabelle a un certo numero di blocchi per riga, usa la variabile maxTables.inserisci qui la descrizione dell'immagine

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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.