Search code examples
pythonjupyter-notebookipythonjupyteripywidgets

iPyWidget with date slider?


I am wondering if there's an easy way to build an iPyWidget with a datetime slider. Right now it is easy to slide over integer or floating point ranges (e.g. numbers 1-10, decimals 0.01, 0.02, ...).

I imagine you could convert dates to floats or integers, build some sort of slider using these, and then convert back to dates for the display labels on the slider. However, this seems clunky. Does anyone have a smoother solution?


Solution

  • I had the same issue recently. I had to write my own class to do a daterange picker. Here is my code:

    import pandas as pd
    import ipywidgets as widgets
    from IPython.display import display
    
    class DateRangePicker(object):
        def __init__(self,start,end,freq='D',fmt='%Y-%m-%d'):
            """
            Parameters
            ----------
            start : string or datetime-like
                Left bound of the period
            end : string or datetime-like
                Left bound of the period
            freq : string or pandas.DateOffset, default='D'
                Frequency strings can have multiples, e.g. '5H' 
            fmt : string, defauly = '%Y-%m-%d'
                Format to use to display the selected period
    
            """
            self.date_range=pd.date_range(start=start,end=end,freq=freq)
            options = [(item.strftime(fmt),item) for item in self.date_range]
            self.slider_start = widgets.SelectionSlider(
                description='start',
                options=options,
                continuous_update=False
            )
            self.slider_end = widgets.SelectionSlider(
                description='end',
                options=options,
                continuous_update=False,
                value=options[-1][1]
            )
    
            self.slider_start.on_trait_change(self.slider_start_changed, 'value')
            self.slider_end.on_trait_change(self.slider_end_changed, 'value')
    
            self.widget = widgets.Box(children=[self.slider_start,self.slider_end])
    
        def slider_start_changed(self,key,value):
            self.slider_end.value=max(self.slider_start.value,self.slider_end.value)
            self._observe(start=self.slider_start.value,end=self.slider_end.value)
    
        def slider_end_changed(self,key,value):
            self.slider_start.value=min(self.slider_start.value,self.slider_end.value)
            self._observe(start=self.slider_start.value,end=self.slider_end.value)
    
        def display(self):
            display(self.slider_start,self.slider_end)
    
        def _observe(self,**kwargs):
            if hasattr(self,'observe'):
                self.observe(**kwargs)
    
    def fct(start,end):
        print start,end
    

    Using it is relatively straightforward:

    w=DateRangePicker(start='2016-08-02',end="2016-09-02",freq='D',fmt='%Y-%m-%d')
    w.observe=fct
    w.display()
    

    Enjoy ;-)