Spaces:
Running
Running
import pandas as pd | |
import streamlit as st | |
from pandas.api.types import ( | |
is_categorical_dtype, | |
is_datetime64_any_dtype, | |
is_numeric_dtype, | |
is_object_dtype, | |
) | |
def make_hashable(x): | |
""" | |
Converte recursivamente listas (e, se necessário, dicionários) em tipos hashable. | |
""" | |
if isinstance(x, list): | |
return tuple(make_hashable(e) for e in x) | |
# Se precisar, trate dicionários também: | |
if isinstance(x, dict): | |
return tuple(sorted((k, make_hashable(v)) for k, v in x.items())) | |
return x | |
def flatten_unique_values(series: pd.Series) -> list: | |
""" | |
Achata os valores únicos de uma série. | |
Se um valor for uma tupla, extrai cada item individualmente. | |
""" | |
unique_values_set = set() | |
for val in series.dropna().unique(): | |
# Se o valor for uma tupla, adicione cada item separadamente | |
if isinstance(val, tuple): | |
unique_values_set.update(val) | |
else: | |
unique_values_set.add(val) | |
return list(unique_values_set) | |
def filter_dataframe(df: pd.DataFrame, ignore_unique_limit = ["Alvo ou depósito"]) -> pd.DataFrame: | |
df = df.copy() | |
# Primeiro, converta todos os valores da DataFrame para hashable | |
for col in df.columns: | |
df[col] = df[col].apply(make_hashable) | |
# Tenta converter strings para datetime e remover fuso horário | |
for col in df.columns: | |
if is_object_dtype(df[col]): | |
try: | |
df[col] = pd.to_datetime(df[col], format="%d-%m-%Y") | |
except Exception: | |
pass | |
if is_datetime64_any_dtype(df[col]): | |
df[col] = df[col].dt.tz_localize(None) | |
modification_container = st.container() | |
with modification_container: | |
to_filter_columns = st.multiselect( | |
"Filtrar por valor", | |
[column for column in df.columns if column != 'id'], | |
placeholder="Selecione um ou mais itens para filtrar" | |
) | |
for column in to_filter_columns: | |
left, right = st.columns((1, 20)) | |
left.write("↳") | |
# Para colunas categóricas ou com poucos valores únicos, use multiselect | |
if is_categorical_dtype(df[column]) or df[column].nunique() < 100 or column in ignore_unique_limit: | |
raw_unique_values = df[column].dropna().unique() | |
# Verifica se há valores do tipo tupla (decorrentes de listas convertidas) | |
if any(isinstance(val, tuple) for val in raw_unique_values): | |
unique_values = flatten_unique_values(df[column]) | |
is_flattened = True | |
else: | |
unique_values = list(raw_unique_values) | |
is_flattened = False | |
user_cat_input = right.multiselect( | |
f"Valores para {column}", | |
unique_values, | |
default=[], # Sem valores pré-selecionados | |
placeholder="Escolha uma opção" | |
) | |
if user_cat_input: # Filtrar apenas se houver seleção | |
if is_flattened: | |
df = df[df[column].apply( | |
lambda x: any(item in x for item in user_cat_input) if isinstance(x, tuple) else x in user_cat_input | |
)] | |
else: | |
df = df[df[column].isin(user_cat_input)] | |
elif is_numeric_dtype(df[column]): | |
_min = float(df[column].min()) | |
_max = float(df[column].max()) | |
step = (_max - _min) / 100 | |
user_num_input = right.slider( | |
f"Valores para {column}", | |
min_value=_min, | |
max_value=_max, | |
value=(_min, _max), | |
step=step, | |
) | |
df = df[df[column].between(*user_num_input)] | |
elif is_datetime64_any_dtype(df[column]): | |
user_date_input = right.date_input( | |
f"Valores para {column}", | |
value=( | |
df[column].min(), | |
df[column].max(), | |
), | |
format="YYYY-MM-DD", | |
) | |
if isinstance(user_date_input, tuple) and len(user_date_input) == 2: | |
start_date, end_date = map(pd.to_datetime, user_date_input) | |
df = df[df[column].between(start_date, end_date)] | |
else: | |
# Para colunas de texto | |
raw_unique_values = df[column].dropna().unique() | |
if any(isinstance(val, tuple) for val in raw_unique_values): | |
unique_values = flatten_unique_values(df[column]) | |
is_flattened = True | |
else: | |
unique_values = list(raw_unique_values) | |
is_flattened = False | |
if len(unique_values) < 100: | |
user_text_input = right.multiselect( | |
f"Valores para {column}", | |
unique_values, | |
default=[], | |
placeholder="Escolha uma opção", | |
) | |
if user_text_input: | |
if is_flattened: | |
df = df[df[column].apply( | |
lambda x: any(item in x for item in user_text_input) if isinstance(x, tuple) else x in user_text_input | |
)] | |
else: | |
df = df[df[column].isin(user_text_input)] | |
else: | |
user_text_input = right.text_input( | |
f"Substring ou regex em {column}", | |
help=""" | |
**Pesquise palavras ou padrões usando regex:** | |
- **Múltiplos termos:** `maçã|banana` (busca "maçã" ou "banana"). | |
- **Início da palavra:** `^carro` (encontra "carro", "carroça", etc.). | |
- **Fim da palavra:** `casa$` (encontra "minha casa", "tua casa", etc.). | |
- **Números:** `\d+` (encontra qualquer número, como "123", "2024"). | |
- **Número específico:** `123` (encontra exatamente o número "123"). | |
- **Palavras e números:** `carro|123` (encontra "carro" ou "123"). | |
Deixe vazio para não filtrar. | |
""" | |
) | |
if user_text_input: | |
df = df[df[column].astype(str).str.contains(user_text_input, na=False)] | |
return df | |