Skip to content

Instantly share code, notes, and snippets.

@nvictus
Forked from nokados/pandas_jupyter_paginator.py
Last active January 22, 2020 11:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nvictus/6eeed1bca9084bd0b67aa3fbe52ebcd7 to your computer and use it in GitHub Desktop.
Save nvictus/6eeed1bca9084bd0b67aa3fbe52ebcd7 to your computer and use it in GitHub Desktop.
Paginator for pandas.DataFrame in Jupyter Notebook
"""
Forked from: https://gist.github.com/nokados/e8f0a64b55099f2f07a50f2b090c91c7
Changes
* Added slider control to scroll through pages of really large dataframes.
* Reduce flicker by making events trigger widget element updates instead of
clearing output and re-rendering.
* Add support for dataframe CSS styling.
* Register custom pandas accessor
"""
from IPython.core.display import display, HTML, clear_output
from ipywidgets import widgets
from pandas.api.extensions import register_dataframe_accessor
import math
class Paginator:
def __init__(self, df, pagesize=15, start_page=0):
self.df = df
self.pagesize = pagesize
self.page = start_page
self.max_pages = math.ceil(df.shape[0] / self.pagesize)
self.btn_next = widgets.Button(description='Next')
self.btn_next.on_click(self._next_page)
self.btn_prev = widgets.Button(description='Prev')
self.btn_prev.on_click(self._prev_page)
self.label = widgets.Label(
'PAGE {}/{}'.format(self.page + 1, self.max_pages)
)
self.slider = widgets.IntSlider(
value=1,
max=self.max_pages,
min=start_page+1,
step=1,
)
self.slider.observe(self._handle_slider_change, names='value')
self._styles = []
self.control = widgets.HBox((
self.btn_prev,
self.label,
self.btn_next,
self.slider,
))
self.content = widgets.HTML(self._render_table(self.get_page()))
def get_page(self):
return self.df.iloc[
self.page * self.pagesize: (self.page + 1) * self.pagesize
]
@property
def style(self):
"""
Get the current page's styler and use it to set the styles to be
applied when each page is rendered.
Examples
--------
>>> p = Paginator(df)
>>> p.style = p.style.bar()
"""
return self.get_page().style
@style.setter
def style(self, styler):
self._styles = styler.export()
self._update()
def show(self):
"""
Display the widget.
"""
clear_output()
display(self.control)
display(self.content)
display(self.control)
return self
def _render_table(self, df):
try:
html = (
df.style
.use(self._styles)
.set_table_attributes('class="rendered_html"')
.render()
)
except ValueError as e:
# Sometimes styling fails (e.g. non-unique indexes).
# See <https://pandas.pydata.org/pandas-docs/stable/user_guide/
# style.html#Limitations>.
# Fall back on default repr.
html = df._repr_html_()
return html
def _update(self):
self.label.value = 'PAGE {}/{}'.format(self.page + 1, self.max_pages)
self.content.value = self._render_table(self.get_page())
def _handle_slider_change(self, change):
self.page = change['new'] - 1
self._update()
def _next_page(self, b):
self.page = min(self.max_pages - 1, self.page + 1)
self.slider.value = self.page + 1
self._update()
def _prev_page(self, b):
self.page = max(0, self.page - 1)
self.slider.value = self.page + 1
self._update()
@register_dataframe_accessor("paginate")
class PaginateAccessor:
def __init__(self, pandas_obj):
self._obj = pandas_obj
def __call__(self):
return Paginator(self._obj).show()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment