Skip to content

Commit

Permalink
v0.1.2 - Add DataFrame support
Browse files Browse the repository at this point in the history
  • Loading branch information
cedricvlt committed Sep 30, 2023
1 parent 4c7e176 commit 52cf644
Show file tree
Hide file tree
Showing 7 changed files with 372 additions and 151 deletions.
59 changes: 45 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Based on [react-awesome-query-builder](https://github.com/ukrbublik/react-awesom

Check out [live demo](https://condition-tree-demo.streamlit.app/) !

This component allows users to build complex condition trees that can be used, for example, to filter a dataframe or build a query.
This component allows users to build complex condition trees that can be used to filter a dataframe or build a query.

<img src="preview.jpg" width="500" alt="preview">

Expand Down Expand Up @@ -33,11 +33,37 @@ This component allows users to build complex condition trees that can be used, f

## Basic usage

### Filter a dataframe

```python
import pandas as pd
from streamlit_condition_tree import condition_tree, config_from_dataframe

# Initial dataframe
df = pd.DataFrame({
'First Name': ['Georges', 'Alfred'],
'Age': [45, 98],
'Favorite Color': ['Green', 'Red'],
'Like Tomatoes': [True, False]
})

# Basic field configuration from dataframe
config = config_from_dataframe(df)

# Condition tree
query_string = condition_tree(config)

# Filtered dataframe
df = df.query(query_string)
```

### Build a query

```python
import streamlit as st
from streamlit_condition_tree import condition_tree


# Build a custom configuration
config = {
'fields': {
'name': {
Expand All @@ -58,11 +84,13 @@ config = {
}
}

# Condition tree
return_val = condition_tree(
config,
return_type='sql'
)

# Generated SQL
st.write(return_val)
```

Expand All @@ -72,18 +100,22 @@ st.write(return_val)

```python
def condition_tree(
config: Dict
return_type: str
tree: Dict
min_height: int
placeholder: str
config: dict,
return_type: str,
tree: dict,
min_height: int,
placeholder: str,
key: str
)
```

- **config**: Python dictionary that resembles the JSON counterpart of
the React component [config](https://github.com/ukrbublik/react-awesome-query-builder/blob/master/CONFIG.adoc).
*Note*: Javascript functions (ex: validators) are not yet supported.
- **config**: Python dictionary (mostly used to define the fields) that resembles the JSON counterpart of
the React component.

A basic configuration can be built from a DataFrame with `config_from_dataframe`.
For a more advanced configuration, see the component [doc](https://github.com/ukrbublik/react-awesome-query-builder/blob/master/CONFIG.adoc)
and [demo](https://ukrbublik.github.io/react-awesome-query-builder/).
*Note*: Javascript functions (ex: validators) are not yet supported.


- **return_type**: Format of the returned value :
Expand All @@ -92,9 +124,9 @@ def condition_tree(
- sql
- spel
- elasticSearch
- jsonLogic
Default : queryString
- jsonLogic

Default : queryString (can be used to filter a pandas DataFrame using DataFrame.query)


- **tree**: Input condition tree (see section below)
Expand Down Expand Up @@ -125,5 +157,4 @@ It can be loaded as an input tree through the `tree` parameter.


## Potential future improvements
- **Dataframe filtering support**: automatically build config from dataframe and return a query string adapted to `pandas.DataFrame.query`
- **Javascript support**: allow injection of javascript code in the configuration (e.g. validators)
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

setuptools.setup(
name="streamlit-condition-tree",
version="0.1.1",
version="0.1.2",
author="Cédric Villette",
author_email="[email protected]",
description="Condition Tree Builder for Streamlit",
Expand Down
56 changes: 56 additions & 0 deletions streamlit_condition_tree/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,43 @@
_component_func = components.declare_component("streamlit_condition_tree", path=build_dir)


type_mapper = {
'b': 'boolean',
'i': 'number',
'u': 'number',
'f': 'number',
'c': '',
'm': '',
'M': 'datetime',
'O': 'text',
'S': 'text',
'U': 'text',
'V': ''
}


def config_from_dataframe(dataframe):
"""Return a basic configuration from dataframe columns"""

fields = {}
for col_name, col_dtype in zip(dataframe.columns, dataframe.dtypes):
col_type = 'select' if col_dtype == 'category' else type_mapper[col_dtype.kind]

if col_type:
col_config = {
'label': col_name,
'type': col_type
}
if col_type == 'select':
categories = dataframe[col_name].cat.categories
col_config['fieldSettings'] = {
'listValues': [{'value': c, 'title': c} for c in categories]
}
fields[f'{col_name}'] = col_config

return {'fields': fields}


def condition_tree(config: dict,
return_type: str = 'queryString',
tree: dict = None,
Expand All @@ -32,12 +69,16 @@ def condition_tree(config: dict,
Format in which output should be returned to streamlit.
Possible values : queryString | mongodb | sql | spel |
elasticSearch | jsonLogic.
Default : queryString (compatible with DataFrame.query)
tree: dict or None
Input condition tree
Default: None
min_height: int
Minimum height of the component frame
Default: 400
placeholder: str
Text displayed when the condition tree is empty
Default: empty
key: str or None
An optional key that uniquely identifies this component. If this is
None, and the component's arguments are changed, the component will
Expand All @@ -51,6 +92,16 @@ def condition_tree(config: dict,
"""

if return_type == 'queryString':
# Add backticks to fields with space in their name
fields = {}
for field_name, field_config in config['fields'].items():
if ' ' in field_name:
field_name = f'`{field_name}`'
fields[field_name] = field_config

config['fields'] = fields

output_tree, component_value = _component_func(
config=config,
return_type=return_type,
Expand All @@ -60,6 +111,11 @@ def condition_tree(config: dict,
placeholder=placeholder,
default=['', '']
)

if return_type == 'queryString' and not component_value:
# Default string that returns all the values in DataFrame.query
component_value = 'index in index'

st.session_state[key] = output_tree

return component_value
40 changes: 18 additions & 22 deletions streamlit_condition_tree/example.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,26 @@
import numpy as np
import pandas as pd
import streamlit as st
from streamlit_condition_tree import condition_tree
from streamlit_condition_tree import condition_tree, config_from_dataframe

df = pd.read_csv(
'https://media.githubusercontent.com/media/datablist/sample-csv-files/main/files/people/people-100.csv',
index_col=0,
parse_dates=['Date of birth'],
date_format='%Y-%m-%d')
df['Age'] = ((pd.Timestamp.today() - df['Date of birth']).dt.days / 365).astype(int)
df['Sex'] = pd.Categorical(df['Sex'])
df['Likes tomatoes'] = np.random.randint(2, size=df.shape[0]).astype(bool)

config = {
'fields': {
'name': {
'label': 'Name',
'type': 'text',
},
'qty': {
'label': 'Age',
'type': 'number',
'fieldSettings': {
'min': 0
},
},
'like_tomatoes': {
'label': 'Likes tomatoes',
'type': 'boolean',
}
}
}
st.dataframe(df)

config = config_from_dataframe(df)

return_val = condition_tree(
config,
return_type='sql'
)

st.write(return_val)
st.code(return_val)

df = df.query(return_val)
st.dataframe(df)
2 changes: 1 addition & 1 deletion streamlit_condition_tree/frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "streamlit-condition-tree",
"version": "0.1.1",
"version": "0.1.2",
"private": true,
"dependencies": {
"@fontsource/source-sans-pro": "^5.0.8",
Expand Down
Loading

0 comments on commit 52cf644

Please sign in to comment.