Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add filter in the graph

Code is below

from io import StringIO
text = '''Product,Count
Pen,10
Pencil,15
Book, 10'''
df = pd.read_csv(StringIO(text))
df.plot(x="Product", y="Count", kind="bar")

enter image description here

  • How to add filter in the graph itself that user has to privilege to select which product has to display in the graph and count also let's say if count > 11 then only Pencil has to appear.

  • Is there any alternate way also there to do this?

  • IF one column is date column can we do filtering with date column also

like image 879
aysh Avatar asked Dec 22 '22 17:12

aysh


1 Answers

matplotlib.widgets

As suggested in the comments, one way to do this is using the matplotlib.widgets and you can read more about them here, though for the actual implementation, I found most useful their examples of Sliders and Check buttons. Using your minimal example, the simplest adaptation I could come up with (that looks ok) would look like this:

import pandas as pd
from io import StringIO
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
from matplotlib.widgets import Slider, CheckButtons

text = '''Product,Count
Pen,10
Pencil,15
Book,10'''
df = pd.read_csv(StringIO(text))

fig, ax = plt.subplots()
gs = gridspec.GridSpec(
    nrows = 2,
    ncols = 2,
    figure = fig,
    wspace = 0.3,
    hspace = 0.6,
    height_ratios = [2,1]
    )
ax.set_position(gs[0,:].get_position(fig))

axMinCount = fig.add_subplot(gs[1,0])
axProducts = fig.add_subplot(gs[1,1])

labels = ('Pen', 'Pencil', 'Book')
minimum = 5
actives = [True, True, True]

df.loc[actives & (df['Count'] >= minimum)].plot(
    x = 'Product', y = 'Count', kind = 'bar', ax = ax, legend = False
    )

sMinCount = Slider(axMinCount, 'Min Count', 0, 20, valinit = minimum, valstep = 1)
cProducts = CheckButtons(axProducts, labels, actives)


def update(val):
    minimum = sMinCount.val
    df_filtered = df.loc[actives & (df['Count'] >= minimum)]
    if not df_filtered.empty:
        df_filtered.plot(
        x = 'Product', y = 'Count', kind = 'bar', ax = ax, legend = False
        )
    else:
        ax.cla()

def check(label):
    index = labels.index(label)
    actives[index] = not actives[index]
    df_filtered = df.loc[actives & (df['Count'] >= minimum)]
    if not df_filtered.empty:
        df_filtered.plot(
        x = 'Product', y = 'Count', kind = 'bar', ax = ax, legend = False
        )
    else:
        ax.cla()
    
sMinCount.on_changed(update)
cProducts.on_clicked(check)

plt.show()

With various filtering settings, the result looks like this:

enter image description here


ipywidgets (Jupyter notebook)

I'd suggest also trying ipywidgets, which have a much nicer user interface than matplotlib.widgets. You can read more about Using Interact. Using your minimal example:

import pandas as pd
from io import StringIO
from ipywidgets import interact

text = '''Product,Count
Pen,10
Pencil,15
Book,10'''
df = pd.read_csv(StringIO(text))

# This is a wrapper of the function that follows, providing the interactive input
@interact(MinCount = (0, 20, 1), pen = True, pencil = True, book = True)
# Note that in the core function below, you can set the starting values
def plotter_fun(MinCount = 0, pen = True, pencil = True, book = True):   
    # Filter the data using the interactive input
    df_filtered = df.loc[(pen, pencil, book) & (df['Count'] >= MinCount)]
    # If all data has been filtered out, announce it
    if df_filtered.empty:
        print('No data to show.')
    # Otherwise plot
    else:
        df_filtered.plot(x = 'Product', y = 'Count', kind = 'bar')

The result with various filtering settings looks as follows:

enter image description here

Of course, there are many options for configuring the layout etc.

This solution is designed to work primarily in Jupyter Notebook, though if you'd like to embed this functionality somewhere else, you can read about Embedding Jupyter Widgets in Other Contexts than the Notebook.

like image 108
Brunox13 Avatar answered Dec 29 '22 08:12

Brunox13