In [ ]:
import panel as pn

from bokeh.sampledata.autompg import autompg
from io import StringIO

pn.extension(sizing_mode="stretch_width")
In [ ]:
years = pn.widgets.MultiChoice(
    name='Years', options=list(autompg.yr.unique()), margin=(0, 20, 0, 0)
)
mpg = pn.widgets.RangeSlider(
    name='Mile per Gallon', start=autompg.mpg.min(), end=autompg.mpg.max()
)

@pn.depends(years, mpg)
def filtered_mpg(yrs, mpg):
    df = autompg
    if years.value:
        df = autompg[autompg.yr.isin(yrs)]
    return df[(df.mpg >= mpg[0]) & (df.mpg <= mpg[1])]

@pn.depends(years, mpg)
def filtered_file(yr, mpg):
    df = filtered_mpg(yr, mpg)
    sio = StringIO()
    df.to_csv(sio)
    sio.seek(0)
    return sio

fd = pn.widgets.FileDownload(
    callback=filtered_file, filename='filtered_autompg.csv'
)

pn.Column(pn.Row(years, mpg), fd, pn.panel(filtered_mpg, width=600), width=600)

App

Lets wrap it into nice template that can be served via panel serve save_filtered_df.ipynb

In [ ]:
pn.template.FastListTemplate(
    site="Panel", 
    title="Save Filtered Dataframe", 
    main=[
        pn.Column(
            "This app demonstrates how to **filter and download a Pandas DataFrame**.",
            pn.pane.HTML("<div style='font-size: 100px;text-align: center'>🐼</div>", height=75, margin=(50,5,10,5)),
        ),
        pn.Column(years, mpg, fd), 
        pn.Column(filtered_mpg, height=600, scroll=True),
    ], main_max_width="768px",
).servable();