Skip to content

Commit 52cf644

Browse files
committed
v0.1.2 - Add DataFrame support
1 parent 4c7e176 commit 52cf644

File tree

7 files changed

+372
-151
lines changed

7 files changed

+372
-151
lines changed

README.md

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ Based on [react-awesome-query-builder](https://github.com/ukrbublik/react-awesom
33

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

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

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

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

3434
## Basic usage
3535

36+
### Filter a dataframe
37+
38+
```python
39+
import pandas as pd
40+
from streamlit_condition_tree import condition_tree, config_from_dataframe
41+
42+
# Initial dataframe
43+
df = pd.DataFrame({
44+
'First Name': ['Georges', 'Alfred'],
45+
'Age': [45, 98],
46+
'Favorite Color': ['Green', 'Red'],
47+
'Like Tomatoes': [True, False]
48+
})
49+
50+
# Basic field configuration from dataframe
51+
config = config_from_dataframe(df)
52+
53+
# Condition tree
54+
query_string = condition_tree(config)
55+
56+
# Filtered dataframe
57+
df = df.query(query_string)
58+
```
59+
60+
### Build a query
61+
3662
```python
3763
import streamlit as st
3864
from streamlit_condition_tree import condition_tree
3965

40-
66+
# Build a custom configuration
4167
config = {
4268
'fields': {
4369
'name': {
@@ -58,11 +84,13 @@ config = {
5884
}
5985
}
6086

87+
# Condition tree
6188
return_val = condition_tree(
6289
config,
6390
return_type='sql'
6491
)
6592

93+
# Generated SQL
6694
st.write(return_val)
6795
```
6896

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

73101
```python
74102
def condition_tree(
75-
config: Dict
76-
return_type: str
77-
tree: Dict
78-
min_height: int
79-
placeholder: str
103+
config: dict,
104+
return_type: str,
105+
tree: dict,
106+
min_height: int,
107+
placeholder: str,
80108
key: str
81109
)
82110
```
83111

84-
- **config**: Python dictionary that resembles the JSON counterpart of
85-
the React component [config](https://github.com/ukrbublik/react-awesome-query-builder/blob/master/CONFIG.adoc).
86-
*Note*: Javascript functions (ex: validators) are not yet supported.
112+
- **config**: Python dictionary (mostly used to define the fields) that resembles the JSON counterpart of
113+
the React component.
114+
115+
A basic configuration can be built from a DataFrame with `config_from_dataframe`.
116+
For a more advanced configuration, see the component [doc](https://github.com/ukrbublik/react-awesome-query-builder/blob/master/CONFIG.adoc)
117+
and [demo](https://ukrbublik.github.io/react-awesome-query-builder/).
118+
*Note*: Javascript functions (ex: validators) are not yet supported.
87119

88120

89121
- **return_type**: Format of the returned value :
@@ -92,9 +124,9 @@ def condition_tree(
92124
- sql
93125
- spel
94126
- elasticSearch
95-
- jsonLogic
96-
97-
Default : queryString
127+
- jsonLogic
128+
129+
Default : queryString (can be used to filter a pandas DataFrame using DataFrame.query)
98130

99131

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

126158

127159
## Potential future improvements
128-
- **Dataframe filtering support**: automatically build config from dataframe and return a query string adapted to `pandas.DataFrame.query`
129160
- **Javascript support**: allow injection of javascript code in the configuration (e.g. validators)

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
setuptools.setup(
99
name="streamlit-condition-tree",
10-
version="0.1.1",
10+
version="0.1.2",
1111
author="Cédric Villette",
1212
author_email="[email protected]",
1313
description="Condition Tree Builder for Streamlit",

streamlit_condition_tree/__init__.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,43 @@
1515
_component_func = components.declare_component("streamlit_condition_tree", path=build_dir)
1616

1717

18+
type_mapper = {
19+
'b': 'boolean',
20+
'i': 'number',
21+
'u': 'number',
22+
'f': 'number',
23+
'c': '',
24+
'm': '',
25+
'M': 'datetime',
26+
'O': 'text',
27+
'S': 'text',
28+
'U': 'text',
29+
'V': ''
30+
}
31+
32+
33+
def config_from_dataframe(dataframe):
34+
"""Return a basic configuration from dataframe columns"""
35+
36+
fields = {}
37+
for col_name, col_dtype in zip(dataframe.columns, dataframe.dtypes):
38+
col_type = 'select' if col_dtype == 'category' else type_mapper[col_dtype.kind]
39+
40+
if col_type:
41+
col_config = {
42+
'label': col_name,
43+
'type': col_type
44+
}
45+
if col_type == 'select':
46+
categories = dataframe[col_name].cat.categories
47+
col_config['fieldSettings'] = {
48+
'listValues': [{'value': c, 'title': c} for c in categories]
49+
}
50+
fields[f'{col_name}'] = col_config
51+
52+
return {'fields': fields}
53+
54+
1855
def condition_tree(config: dict,
1956
return_type: str = 'queryString',
2057
tree: dict = None,
@@ -32,12 +69,16 @@ def condition_tree(config: dict,
3269
Format in which output should be returned to streamlit.
3370
Possible values : queryString | mongodb | sql | spel |
3471
elasticSearch | jsonLogic.
72+
Default : queryString (compatible with DataFrame.query)
3573
tree: dict or None
3674
Input condition tree
75+
Default: None
3776
min_height: int
3877
Minimum height of the component frame
78+
Default: 400
3979
placeholder: str
4080
Text displayed when the condition tree is empty
81+
Default: empty
4182
key: str or None
4283
An optional key that uniquely identifies this component. If this is
4384
None, and the component's arguments are changed, the component will
@@ -51,6 +92,16 @@ def condition_tree(config: dict,
5192
5293
"""
5394

95+
if return_type == 'queryString':
96+
# Add backticks to fields with space in their name
97+
fields = {}
98+
for field_name, field_config in config['fields'].items():
99+
if ' ' in field_name:
100+
field_name = f'`{field_name}`'
101+
fields[field_name] = field_config
102+
103+
config['fields'] = fields
104+
54105
output_tree, component_value = _component_func(
55106
config=config,
56107
return_type=return_type,
@@ -60,6 +111,11 @@ def condition_tree(config: dict,
60111
placeholder=placeholder,
61112
default=['', '']
62113
)
114+
115+
if return_type == 'queryString' and not component_value:
116+
# Default string that returns all the values in DataFrame.query
117+
component_value = 'index in index'
118+
63119
st.session_state[key] = output_tree
64120

65121
return component_value
Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,26 @@
1+
import numpy as np
2+
import pandas as pd
13
import streamlit as st
2-
from streamlit_condition_tree import condition_tree
4+
from streamlit_condition_tree import condition_tree, config_from_dataframe
35

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

5-
config = {
6-
'fields': {
7-
'name': {
8-
'label': 'Name',
9-
'type': 'text',
10-
},
11-
'qty': {
12-
'label': 'Age',
13-
'type': 'number',
14-
'fieldSettings': {
15-
'min': 0
16-
},
17-
},
18-
'like_tomatoes': {
19-
'label': 'Likes tomatoes',
20-
'type': 'boolean',
21-
}
22-
}
23-
}
15+
st.dataframe(df)
16+
17+
config = config_from_dataframe(df)
2418

2519
return_val = condition_tree(
2620
config,
27-
return_type='sql'
2821
)
2922

30-
st.write(return_val)
23+
st.code(return_val)
24+
25+
df = df.query(return_val)
26+
st.dataframe(df)

streamlit_condition_tree/frontend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "streamlit-condition-tree",
3-
"version": "0.1.1",
3+
"version": "0.1.2",
44
"private": true,
55
"dependencies": {
66
"@fontsource/source-sans-pro": "^5.0.8",

0 commit comments

Comments
 (0)