Introduction to Interactive GUIs

Meerkat allows users to build interactive applications on top of Meerkat dataframes.

One of the main reasons for this functionality is because working with unstructured data frequently involves playing with it. And there’s no better way to do that than through a simple, interactive application. These applications can range from simple forms to gather user input inside Jupyter notebooks, to full-blown dashboards and web applications that are deployed to the cloud.

Underneath the hood, Meerkat relies on a FastAPI Python backend, coupled with Svelte for the frontend.

There are some key distinctions between Meerkat and other tools that allow users to build interactive applications:

  1. Compared to tools like Streamlit, Meerkat is much more focused on supporting data and machine learning use cases. It is likely to be a better fit for users that want to bring the full power of a frontend framework like Svelte to their data science workflows.

  2. Compared to tools like Gradio, Meerkat is much more focused on complex, reactive applications, rather than demos. It is likely to be a better fit for users that want to build full-blown applications that involve ML models, graphing and interactivity.

  3. Compared to Python-to-React compilation approaches like Pynecone, Meerkat is more opinionated about providing a solution for the average data science and machine learning user.

Most data science and machine learning workflows revolve around Python, and Meerkat brings the ability to build reactive data apps in Python to these users.

In addition, Meerkat embraces making it as easy as possible for users to write custom Svelte components if they desire. This can make it easy for users with only a passing knowledge of Javascript/HTML/CSS to build custom components without having to deal with the intricacies of the web stack.

A Simple Counter

Let’s start with a simple example. We’ll build a simple counter that increments by 1 every time the user clicks on it.

Import

First, we’ll import the Meerkat library.

import meerkat as mk

Store

Next, let’s create a Store object to keep track of the state of the counter. Stores in Meerkat are borrowed from their Svelte counterparts, and provide a way for users to create values that are synchronized between the GUI and the Python code.

# Initialize the counter to 0
counter = mk.gui.Store(0)

There are a couple of things to keep in mind when it comes to Stores.

  • If the Store value is manipulated on the frontend, the updated value will be reflected here.

  • If the Store value is set using the special .set() method here, the updated value will be reflected in the frontend.

Endpoint

Then, we’ll create an Endpoint in Meerkat, which is a Python function that can be called from the frontend. In this case, we’ll create an endpoint that increments the counter by 1.

@mk.gui.endpoint
def increment(counter: Store[int]):
    # Use the special .set() method to update the Store
    counter.set(counter + 1)

What’s great here is that Meerkat uses FastAPI under the hood to automatically setup an API endpoint for increment. This can then be called by other services as well!

Component

Next, we’ll want to assemble our GUI: we want a button that we can press to increment the counter, as well as a way to display the current count value.

# A button, which when clicked calls the `increment` endpoint with the `counter` argument filled in
button = mk.gui.Button(title="Increment", on_click=increment.partial(counter))
# Display the counter value
text = mk.gui.Text(data=counter)

Meerkat comes with a collection of prebuilt Components that can be used to assemble interfaces.

Interface

Finally, we can start the API and frontend servers, and launch the interface.

# Start the server
mk.gui.start()

# Launch the interface
mk.gui.Interface(
    # Put the components into a row layout for display
    component=mk.gui.RowLayout(
        components=[button, text]
    ),
).launch()

A Simple DataFrame Viewer

Next, let’s run through a simple example of how to build a DataFrame viewer, combining Meerkat’s DataFrame with interactivity. We’ll build a simple application that allows users to filter the imagenette dataset by label. We’ll display the filtered DataFrame in a Table view.

Import

As always, we first start by importing Meerkat.

import meerkat as mk

In addition to being a library for building interactive applications, Meerkat is also a library for working with data. It provides a DataFrame object that is similar to Pandas, but is designed to work with unstructured data.

This deep integration between the DataFrame and the interactive GUIs makes it easy to build applications that work with data, and we’re excited to see what users build with it!

Load DataFrame

We’ll start by loading a small image dataset called imagenette from Meerkat’s dataset registry. It’s a great image classification dataset for demos!

# Let's load the `imagenette` dataset using Meerkat's dataset registry
df: mk.DataFrame = mk.get("imagenette")

Choice Component

Next, we’ll create a Choice component that allows users to select a label to filter by.

# List of unique labels represented in the DataFrame
labels = list(df['label'].unique())

# Component that can display the labels as choices to select from
choices = mk.gui.Choice(choices=labels, value=labels[0], title="Choose a Label")

Note here that Choice.value is a Store. This means that when the user selects a new label in the GUI, the value will be updated automatically!

Reactive Function

Now, we’ll see another concept that Meerkat’s interactive GUIs are built on: reactive functions. These are Python functions that are automatically re-run when the inputs change. They make it easy to build reactive applications that are driven by data.

# Create a reactive function that returns a filtered view of the DataFrame
@mk.gui.reactive
def filter_by_label(df: mk.DataFrame, label: str):
    """If either the `df` or `label` argument changes, this function will be re-run."""
    return df[df['label'] == label]

# Run the reactive function in the `react` context manager to make it retrigger automatically
with mk.gui.react():
    # Reactively run filter by label with `df` and `choices.value`
    df_filtered = filter_by_label(df, choices.value)

By wiring up the choices.value to the label argument, we can automatically filter the DataFrame when the user selects a new label.

Table Component

Now, we can target the filtered DataFrame with a Table component to display it.

# Visualize the filtered_df
df_viz = mk.gui.Table(df=df_filtered)

Interface

And then finally, we can launch the interface!

mk.gui.start()
mk.gui.Interface(
    component=mk.gui.RowLayout(components=[choices, df_viz])
).launch()