Removing rows from dataframe with dynamic form

Hello brains trust,

Python 3.8 running locally
Streamlit 1.32.2
Pandas 2.0.3

Playing around with a dynamic form and the possibility of removing data using st.data_editor.

Most of the code is part of a larger project I am working on, however the row delete logic is from here.

I am getting a ‘ValueError: Need to specify at least one of ‘labels’, ‘index’ or ‘columns’’’ error, any ideas? I am fairly new to Streamlit, and Python in general. So could be something simple.


import streamlit as st
import pandas as pd

st.set_page_config(
    layout="wide")

# create form
def add_dfForm():
    row = pd.DataFrame({'Test_1': [Test_1],
                        'Test_2': [Test_2],
                        'Test_3': [Test_3]})
    st.session_state.data = pd.concat([st.session_state.data, row])

(placeholder_for_Test_1, placeholder_for_Test_2, placeholder_for_Test_3) = st.columns(3)

disable_1 = True
disable_2 = True
disable_3 = True

with placeholder_for_Test_1:
    Test_1 = st.selectbox('Test_1', options=[0, 1, 2, 3])
    if Test_1 == 0:
        disable_1 = True
    else:
        disable_1 = False
with placeholder_for_Test_2:
    Test_2 = st.selectbox('Test_2', options=[0, 1, 2, 3], disabled=disable_1)
    if Test_2 == 0:
        disable_2 = True
    else:
        disable_2 = False
with placeholder_for_Test_3:
    Test_3 = st.selectbox('Test_3', options=[0, 1, 2, 3], disabled=disable_2)
    if Test_3 == 0:
        disable_3 = True
    else:
        disable_3 = False

dfForm = st.form(key='dfForm')

with dfForm:
    placeholder_for_Test_1 = st.empty()
    placeholder_for_Test_2 = st.empty()
    placeholder_for_Test_3 = st.empty()
    add_button = st.form_submit_button("Add", on_click=add_dfForm, type="primary",
                                       disabled=disable_3)
# create user dataframe
if 'data' not in st.session_state:
    data = pd.DataFrame({'Test_1': [], 'Test_2': [], 'Test_3': []})
    st.session_state.data = data

def callback():
    edited_rows = st.session_state["data_editor"]["edited_rows"]
    rows_to_delete = []

    for idx, value in edited_rows.items():
        if value["x"] is True:
            rows_to_delete.append(idx)

    st.session_state["data"] = (
        st.session_state["data"].drop(axis=0, inplace=True).reset_index(drop=True)
    )

columns = st.session_state["data"].columns
column_config = {column: st.column_config.Column(disabled=True) for column in columns}

modified_df = st.session_state["data"].copy()
modified_df["x"] = False
# Make Delete be the first column
modified_df = modified_df[["x"] + modified_df.columns[:-1].tolist()]

st.data_editor(
    modified_df,
    key="data_editor",
    on_change=callback,
    hide_index=True,
    column_config=column_config,
)
1 Like

Your code works after two small tweaks:

st.session_state["data"].drop(rows_to_delete, axis=0).reset_index(drop=True)

instead of

st.session_state["data"].drop(axis=0, inplace=True).reset_index(drop=True)

You need to pass rows_to_delete so that the right rows get deleted, and remove inplace=True, because you want to return the newly edited dataframe.

The other key is to make sure the index gets set properly in your dataframe, because right now the index column is a bunch of 0’s

You can fix that by doing

st.session_state.data = pd.concat([st.session_state.data, row], ignore_index=True)

instead of

st.session_state.data = pd.concat([st.session_state.data, row])

The ignore_index makes pandas add the default value for index, which is 0, 1, 2, etc.

2 Likes

Genius, thanks @blackary. Makes complete sense, appreciate the feedback :slight_smile:

1 Like

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.