DEV Community

A0mineTV
A0mineTV

Posted on

Interactive DataFrame Management with Streamlit Fragments 🚀

Introduction

Streamlit is a powerful Python framework designed to create interactive web applications for data analysis and visualization effortlessly. With the addition of the @st.fragment decorator, Streamlit allows developers to modularize their applications more effectively, improving performance and maintainability.

In this article, we'll explore how to create an interactive DataFrame management tool using the @st.fragment decorator. This tool will enable users to:

  1. View a DataFrame.
  2. Add new rows to the DataFrame.
  3. Edit existing rows in the DataFrame.
  4. Delete rows from the DataFrame.

Why Use @st.fragment?

The @st.fragment decorator provides a mechanism to encapsulate specific portions of your Streamlit app, minimizing unnecessary re-runs and improving performance. This feature is particularly useful in applications where distinct components interact independently but still share a global state.

By using @st.fragment, you:

  • Optimize Performance: Only the fragments involved in an interaction are refreshed, reducing the computational overhead.
  • Enhance Code Modularity: You can isolate different functionalities into separate fragments, making your codebase easier to maintain and debug.
  • Improve User Experience: Smooth and responsive interactions for your users without reloading unrelated components.

Full Code Example

Below is the complete code for the interactive DataFrame management tool:

import streamlit as st
import pandas as pd

# Initialize session state if not already set
if "data" not in st.session_state:
    st.session_state.data = pd.DataFrame({
        "Nom": ["Alice", "Bob", "Charlie"],
        "Âge": [25, 30, 35],
        "Ville": ["Paris", "Londres", "Berlin"]
    })

@st.fragment
def display_dataframe_fragment():
    """
    Fragment to display the current DataFrame.
    """
    st.subheader('Current DataFrame')
    st.dataframe(st.session_state.data)

@st.fragment
def add_row_fragment():
    """
    Fragment to add a new row to the DataFrame.
    """
    st.subheader('Add a Row')
    with st.form(key="add_row_form"):
        name = st.text_input('Name')
        age = st.number_input('Age', min_value=0, max_value=120, step=1)
        city = st.text_input('City')
        submitted = st.form_submit_button("Add")
        if submitted:
            if name and city:
                new_row = {"Nom": name, "Âge": age, "Ville": city}
                st.session_state.data = pd.concat(
                    [st.session_state.data, pd.DataFrame([new_row])], ignore_index=True
                )
                st.success('Row added successfully.')
                st.rerun()
            else:
                st.error('Please fill in all required fields.')

@st.fragment
def edit_row_fragment():
    """
    Fragment to edit an existing row in the DataFrame.
    """
    st.subheader('Edit a Row')
    if not st.session_state.data.empty:
        row_index = st.selectbox(
            'Select a row to edit (by index)',
            st.session_state.data.index
        )
        row_data = st.session_state.data.iloc[row_index]
        with st.form(key='edit_row_form'):
            name = st.text_input('Name', value=row_data['Nom'])
            age = st.number_input('Age', min_value=0, max_value=120, step=1, value=row_data["Âge"])
            city = st.text_input('City', value=row_data['Ville'])
            submitted = st.form_submit_button('Edit')
            if submitted:
                st.session_state.data.loc[row_index] = [name, age, city]
                st.success(f'Row {row_index} edited successfully.')
                st.rerun()
    else:
        st.warning('No data available for editing.')

@st.fragment
def delete_row_fragment():
    """
    Fragment to delete a row from the DataFrame.
    """
    st.subheader('Delete a Row')
    if not st.session_state.data.empty:
        row_index = st.selectbox(
            "Select a row to delete (by index)",
            st.session_state.data.index
        )
        if st.button('Delete'):
            st.session_state.data = st.session_state.data.drop(row_index).reset_index(drop=True)
            st.success(f'Row {row_index} deleted successfully.')
            st.rerun()
    else:
        st.warning('No data available for deletion.')

# Main interface
st.title("Interactive DataFrame Management")

# Call the fragments
display_dataframe_fragment()
add_row_fragment()
edit_row_fragment()
delete_row_fragment()
Enter fullscreen mode Exit fullscreen mode

Detailed Explanation of the Code

1. Session State Initialization

The DataFrame is stored in st.session_state to persist its state across user interactions. If the app is run for the first time or reloaded, st.session_state.data is initialized with some default data.

if "data" not in st.session_state:
    st.session_state.data = pd.DataFrame({
        "Nom": ["Alice", "Bob", "Charlie"],
        "Âge": [25, 30, 35],
        "Ville": ["Paris", "Londres", "Berlin"]
    })
Enter fullscreen mode Exit fullscreen mode

2. Fragment: Display DataFrame

The display_dataframe_fragment uses st.dataframe() to display the current state of the DataFrame. This fragment ensures that the displayed data is always up-to-date.

@st.fragment
def display_dataframe_fragment():
    st.subheader('Current DataFrame')
    st.dataframe(st.session_state.data)
Enter fullscreen mode Exit fullscreen mode

3. Fragment: Add a Row

The add_row_fragment uses a form to gather user input for adding a new row. When the form is submitted, the new data is concatenated to the existing DataFrame, and the app is refreshed using st.rerun().

@st.fragment
def add_row_fragment():
    st.subheader('Add a Row')
    with st.form(key="add_row_form"):
        name = st.text_input('Name')
        age = st.number_input('Age', min_value=0, max_value=120, step=1)
        city = st.text_input('City')
        submitted = st.form_submit_button("Add")
        if submitted:
            if name and city:
                new_row = {"Nom": name, "Âge": age, "Ville": city}
                st.session_state.data = pd.concat(
                    [st.session_state.data, pd.DataFrame([new_row])], ignore_index=True
                )
                st.success('Row added successfully.')
                st.rerun()
Enter fullscreen mode Exit fullscreen mode

4. Fragment: Edit a Row

The edit_row_fragment allows users to select a row by its index and update its values through a form. The changes are applied to the DataFrame, and the app is refreshed.

@st.fragment
def edit_row_fragment():
    st.subheader('Edit a Row')
    if not st.session_state.data.empty:
        row_index = st.selectbox(
            'Select a row to edit (by index)',
            st.session_state.data.index
        )
        row_data = st.session_state.data.iloc[row_index]
        with st.form(key='edit_row_form'):
            name = st.text_input('Name', value=row_data['Nom'])
            age = st.number_input('Age', min_value=0, max_value=120, step=1, value=row_data["Âge"])
            city = st.text_input('City', value=row_data['Ville'])
            submitted = st.form_submit_button('Edit')
            if submitted:
                st.session_state.data.loc[row_index] = [name, age, city]
                st.success(f'Row {row_index} edited successfully.')
                st.rerun()
Enter fullscreen mode Exit fullscreen mode

5. Fragment: Delete a Row

The delete_row_fragment enables row deletion based on a selected index. After deletion, the DataFrame is updated, and the app refreshes to reflect the changes.

@st.fragment
def delete_row_fragment():
    st.subheader('Delete a Row')
    if not st.session_state.data.empty:
        row_index = st.selectbox(
            "Select a row to delete (by index)",
            st.session_state.data.index
        )
        if st.button('Delete'):
            st.session_state.data = st.session_state.data.drop(row_index).reset_index(drop=True)
            st.success(f'Row {row_index} deleted successfully.')
            st.rerun()
Enter fullscreen mode Exit fullscreen mode

Conclusion

By leveraging the @st.fragment decorator, Streamlit applications can become more modular, efficient, and responsive. This example demonstrates a simple yet effective way to manage a DataFrame interactively, but the same principles can be applied to other use cases involving user interactions and dynamic updates.

Let us know in the comments how you plan to use @st.fragment in your projects, or share any feedback or improvements! 🚀


Happy coding! 🎉

Top comments (0)