Как создать динамически обновляемый виджет SelectMultiple в Python?

Я пытаюсь создать набор виджетов SelectMultiple, которые будут работать как фильтр для Pandas df.

Ниже используются в качестве примера данные. Идея состоит в том, что это отдельная комбинация уникальных значений, доступных во всех столбцах.

import pandas as pd
data = {
    'category1': ['A', 'A', 'B', 'B', 'C', 'B'],
    'category2': ['X', 'Y', 'X', 'Z', 'Y', 'K'],
    'country': ['USA', 'USA', 'UK', 'UK', 'Canada', 'Germany'],
    'department': ['Dept1', 'Dept2', 'Dept1', 'Dept2', 'Dept1', 'Dept4'],
    'category3': ['Cat1', 'Cat2', 'Cat1', 'Cat2', 'Cat1', 'Cat2'],
    'gender': ['Male', 'Female', 'Male', 'Female', 'Male', 'Male'],
    'brand': ['Brand1', 'Brand2', 'Brand3', 'Brand4', 'Brand5', 'Brand2']
}
df = pd.DataFrame(data)
категория1 категория2 страна отделение категория3 пол бренд 0 А Икс США Отдел 1 Кат1 Мужской Бренд1 1 А Да США Отдел 2 Кот2 Женский Бренд2 2 Б Икс Великобритания Отдел 1 Кат1 Мужской Бренд3 3 Б З Великобритания Отдел 2 Кот2 Женский Бренд4 4 С Да Канада Отдел 1 Кат1 Мужской Бренд5 5 Б К Германия Отдел 4 Кот2 Мужской Бренд2

Я хочу иметь односторонние зависимости (слева направо), то есть я всегда сначала выбираю category1. Этот выбор ограничивает все остальные варианты. Затем я выбираю category2 и так далее. На каждом шаге варианты выбора обновляются.

Вот что мне ближе всего к решению:

import ipywidgets as widgets
from IPython.display import display

def update_options(category1, category2, country, department, category3, gender):
    filtered_df = df.copy()
    if category1:
        filtered_df = filtered_df[filtered_df['category1'].isin(category1)]
    if category2:
        filtered_df = filtered_df[filtered_df['category2'].isin(category2)]
    if country:
        filtered_df = filtered_df[filtered_df['country'].isin(country)]
    if department:
        filtered_df = filtered_df[filtered_df['department'].isin(department)]
    if category3:
        filtered_df = filtered_df[filtered_df['category3'].isin(category3)]
    if gender:
        filtered_df = filtered_df[filtered_df['gender'].isin(gender)]
   
    category2_options = filtered_df['category2'].unique().tolist()
    country_options = filtered_df['country'].unique().tolist()
    department_options = filtered_df['department'].unique().tolist()
    category3_options = filtered_df['category3'].unique().tolist()
    gender_options = filtered_df['gender'].unique().tolist()
    brand_options = filtered_df['brand'].unique().tolist()
   
    # Update options for dropdowns
    category2_dropdown.options = category2_options
    country_dropdown.options = country_options
    department_dropdown.options = department_options
    category3_dropdown.options = category3_options
    gender_dropdown.options = gender_options
    brand_dropdown.options = brand_options

# Define initial options for category1 and category2
category1_options = df['category1'].unique().tolist()
category2_options = df['category2'].unique().tolist()

# Create dropdown widgets
category1_dropdown = widgets.SelectMultiple(options=category1_options, description='Category 1:')
category2_dropdown = widgets.SelectMultiple(options=category2_options, description='Category 2:')
country_dropdown = widgets.SelectMultiple(description='Country:')
department_dropdown = widgets.SelectMultiple(description='Department:')
category3_dropdown = widgets.SelectMultiple(description='Category 3:')
gender_dropdown = widgets.SelectMultiple(description='Gender:')
brand_dropdown = widgets.SelectMultiple(description='Brand:')

# Use interact to dynamically update options
widgets.interact(update_options,
                 category1=category1_dropdown,
                 category2=category2_dropdown,
                 country=country_dropdown,
                 department=department_dropdown,
                 category3=category3_dropdown,
                 gender=gender_dropdown)

# Display widgets
display(brand_dropdown) # the others are displayed with widgets.interact() above

Это работает только частично: варианты для последующих столбцов обновляются, но если их несколько, я не могу выбрать только один, а только все. Например, если я выберу category1=B, то в виджете category2 я правильно вижу варианты X Z K, но нажатие только на один не работает (он не выбирается), работает только перетаскивание всех них.

Кто-нибудь знает, чего мне не хватает?

Большое спасибо!

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
0
76
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Мне удалось сделать это правильно. Проблема заключалась в том, что виджеты путались, если все это было в одной функции (своего рода ссылка на цикл). Их нужно распределить вниз.

import pandas as pd
import ipywidgets as widgets
from IPython.display import display

# Sample DataFrame
data = {
    'category1': ['A', 'A', 'B', 'B', 'C'],
    'category2': ['X', 'Y', 'X', 'Z', 'Y'],
    'country': ['USA', 'USA', 'UK', 'UK', 'Canada'],
    'department': ['Dept1', 'Dept2', 'Dept1', 'Dept2', 'Dept1'],
    'category3': ['Cat1', 'Cat2', 'Cat1', 'Cat2', 'Cat1'],
    'gender': ['Male', 'Female', 'Male', 'Female', 'Male'],
    'brand': ['Brand1', 'Brand2', 'Brand3', 'Brand4', 'Brand5']
}

df = pd.DataFrame(data)

# Function to update options of category2_dropdown based on category1 selection
def update_category2_options(change):
    if change.new:        
        filtered_df = df[df['category1'].isin(change.new)]
        options = filtered_df['category2'].unique()
        category2_dropdown.options = options
    else:
        category2_dropdown.options = []

# Function to update options of country_dropdown based on category1 and category2 selections
def update_country_options(change):
    if change.new:
        filtered_df = df[df['category1'].isin(category1_dropdown.value)]
        if category2_dropdown.value:
            filtered_df = filtered_df[df['category2'].isin(category2_dropdown.value)]
        options = filtered_df['country'].unique()
        country_dropdown.options = options
    else:
        country_dropdown.options = []

# Function to update options of department_dropdown based on category1, category2, and country selections
def update_department_options(change):
    if change.new:
        filtered_df = df[df['category1'].isin(category1_dropdown.value)]
        if category2_dropdown.value:
            filtered_df = filtered_df[df['category2'].isin(category2_dropdown.value)]
        if country_dropdown.value:
            filtered_df = filtered_df[df['country'].isin(country_dropdown.value)]
        options = filtered_df['department'].unique()
        department_dropdown.options = options
    else:
        department_dropdown.options = []

# Function to update options of category3_dropdown based on category1, category2, country, and department selections
def update_category3_options(change):
    if change.new:
        filtered_df = df[df['category1'].isin(category1_dropdown.value)]
        if category2_dropdown.value:
            filtered_df = filtered_df[df['category2'].isin(category2_dropdown.value)]
        if country_dropdown.value:
            filtered_df = filtered_df[df['country'].isin(country_dropdown.value)]
        if department_dropdown.value:
            filtered_df = filtered_df[df['department'].isin(department_dropdown.value)]
        options =  filtered_df['category3'].unique()
        category3_dropdown.options = options
    else:
        category3_dropdown.options = []

# Function to update options of gender_dropdown based on category1, category2, country, department, and category3 selections
def update_gender_options(change):
    if change.new:
        filtered_df = df[df['category1'].isin(category1_dropdown.value)]
        if category2_dropdown.value:
            filtered_df = filtered_df[df['category2'].isin(category2_dropdown.value)]
        if country_dropdown.value:
            filtered_df = filtered_df[df['country'].isin(country_dropdown.value)]
        if department_dropdown.value:
            filtered_df = filtered_df[df['department'].isin(department_dropdown.value)]
        if category3_dropdown.value:
            filtered_df = filtered_df[df['category3'].isin(category3_dropdown.value)]
        options = filtered_df['gender'].unique()
        gender_dropdown.options = options
    else:
        gender_dropdown.options = []

def update_brand_options(change):
    if change.new:
        filtered_df = df[df['category1'].isin(category1_dropdown.value)]
        if category2_dropdown.value:
            filtered_df = filtered_df[df['category2'].isin(category2_dropdown.value)]
        if country_dropdown.value:
            filtered_df = filtered_df[df['country'].isin(country_dropdown.value)]
        if department_dropdown.value:
            filtered_df = filtered_df[df['department'].isin(department_dropdown.value)]
        if category3_dropdown.value:
            filtered_df = filtered_df[df['category3'].isin(category3_dropdown.value)]
        if gender_dropdown.value:
            filtered_df = filtered_df[df['gender'].isin(gender_dropdown.value)]
        options = filtered_df['brand'].unique()
        brand_dropdown.options = options
    else:
        brand_dropdown.options = []

# Create dropdown widgets
category1_dropdown = widgets.SelectMultiple(description='Category 1:', options=df['category1'].unique())
category2_dropdown = widgets.SelectMultiple(description='Category 2:', options=[])
country_dropdown = widgets.SelectMultiple(description='Country:', options=[])
department_dropdown = widgets.SelectMultiple(description='Department:', options=[])
category3_dropdown = widgets.SelectMultiple(description='Category 3:', options=[])
gender_dropdown = widgets.SelectMultiple(description='Gender:', options=[])
brand_dropdown = widgets.SelectMultiple(description='Brand:', options=[])

# Use observe to dynamically update options
category1_dropdown.observe(update_category2_options, names='value')
category1_dropdown.observe(update_country_options, names='value')
category1_dropdown.observe(update_department_options, names='value')
category1_dropdown.observe(update_category3_options, names='value')
category1_dropdown.observe(update_gender_options, names='value')
category1_dropdown.observe(update_brand_options, names='value')

category2_dropdown.observe(update_country_options, names='value')
category2_dropdown.observe(update_department_options, names='value')
category2_dropdown.observe(update_category3_options, names='value')
category2_dropdown.observe(update_gender_options, names='value')
category2_dropdown.observe(update_brand_options, names='value')

country_dropdown.observe(update_department_options, names='value')
country_dropdown.observe(update_category3_options, names='value')
country_dropdown.observe(update_gender_options, names='value')
country_dropdown.observe(update_brand_options, names='value')

department_dropdown.observe(update_category3_options, names='value')
department_dropdown.observe(update_gender_options, names='value')
department_dropdown.observe(update_brand_options, names='value')

category3_dropdown.observe(update_gender_options, names='value')
category3_dropdown.observe(update_brand_options, names='value')

gender_dropdown.observe(update_brand_options, names='value')

# Display widgets
display(category1_dropdown, category2_dropdown, country_dropdown, department_dropdown, category3_dropdown, gender_dropdown, brand_dropdown)

Другие вопросы по теме