streamlit-aggrid
streamlit-aggrid copied to clipboard
Sparklines ?
Hi, thank you for the excellent python module!
I'm trying to use AGGrid's relatively new ( October 2021) Sparklines feature:
https://ag-grid.com/javascript-data-grid/sparklines-overview/
I am using these libs, which at the time of this writing are the latest:
streamlit 1.11.1
streamlit-aggrid 0.2.3.post2
I think this simple example should yeild a grid with sparklines in the history column:
import pandas as pd
import streamlit as st
from st_aggrid import AgGrid,GridUpdateMode, GridOptionsBuilder,DataReturnMode,JsCode
def compute_simple_data():
return pd.DataFrame({
'name': [ 'a','b'] ,
'history' : [ [1,2,0,0,1,0,2],[1,0,0,0,2,2,2,2,] ]})
simple_data = compute_simple_data()
gb = GridOptionsBuilder.from_dataframe(simple_data)
gb.configure_side_bar()
gb.configure_column('history', cellRenderer='agSparklineCellRenderer')
gridOptions = gb.build()
print(gridOptions)
print(simple_data.info())
g = AgGrid(
simple_data,
gridOptions=gridOptions,
allow_unsafe_jscode=True,
enable_enterprise_modules=True
)
But it renders an empty column, like this:
I have a feeling that this has to do with the format of the data. pandas reports the column as an object:
Data columns (total 2 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 name 2 non-null object
1 history 2 non-null object
dtypes: object(2)
memory usage: 160.0+ bytes
And Aggrid sparkline expects a numeric javascript array
I know that an attempt is being made to apply the agSparkLineCellRenderer because if we comment out the configure_column call, we get the expected result:
Using browser console, there are no javascript errors reported (other than the AGgrid license warning).
I also tried applying type='number' and type='numeric' in the column properties, with the same blank column behavior.
I suspect i'm missing a pretty simple data conversion issue here, or alternately it could be that the aggride version being used is older.
Help would be appreciated!
@dcowden
You can do it by JS injections (one for the data and one for the params of the sparkline)
sparkline_returned_data JS is attached to valueGetter & sparkline_params JS is attached to cellRendererParams when you configure History column
result :
My example (test.csv) is done on following data name;History a;[1,2,0,0,1,0,2] b;[1,0,0,0,2,2,2,2]
full example code :
sparkline_params = JsCode("""
function(params) {
return {
sparklineOptions: {
type: 'line',
line: {
stroke: 'rgb(124, 255, 178)',
strokeWidth: 2,
},
padding: {
top: 5,
bottom: 5,
},
marker: {
size: 3,
shape: 'diamond',
},
highlightStyle: {
size: 10,
},
}
};
};
""")
sparkline_returned_data = JsCode("""
function(params) {
value_to_be_parsed = params.data.History.replace(/['"]+/g, '').replace(/[\])}[{(]/g, '');
list_of_int = value_to_be_parsed.split(',').map(function(item) {return parseInt(item);});
return list_of_int;
}
""")
test_df = pd.read_csv('test.csv', sep=';')
gb_test = GridOptionsBuilder.from_dataframe(test_df)
gb_test.configure_column('History',valueGetter=**sparkline_returned_data**, cellRenderer='agSparklineCellRenderer', cellRendererParams=**sparkline_params**)
gridOptions_test = gb_test.build()
grid_response_test = AgGrid(test_df,
gridOptions = gridOptions_test,
height=200,
width='100%',
data_return_mode=DataReturnMode.FILTERED,
update_mode=GridUpdateMode.MODEL_CHANGED,
fit_columns_on_grid_load=True,
allow_unsafe_jscode=True,
enable_enterprise_modules=True,
reload_data=True,
key='select_grid_test',
theme='light')
@Hurikaine thank you very much! After digging around, I had learned that the solution might involve a valuegetter, because it appears that the data is a string by the time it gets to the javascript layer.
The above solution works, but I was hoping to avoid having to parse a number-list encoded as a string. A simple but unsafe eval() would probably also work.
I think this is the line of code that creates the problem is:
https://github.com/PablocFonseca/streamlit-aggrid/blob/main/st_aggrid/init.py#L55
Here, even if the dataframe has a column with a list of numbers, it is rendered as a string. It would be ideal if it was smarter, and tested for a list, rendering a list. instead of converting to a string, which then has to be undone on the client side
following up, this sample solution uses [dangerous] eval, but illustrates another working solution:
py
import pandas as pd
import streamlit as st
from st_aggrid import AgGrid,GridUpdateMode, GridOptionsBuilder,DataReturnMode,JsCode
def compute_simple_data():
return pd.DataFrame({
'name': [ 'a','b'] ,
'history' : [ [1,2,0,0,1,0,2],[1,0,0,0,2,2,2,2,] ]})
simple_data = compute_simple_data()
gb = GridOptionsBuilder.from_dataframe(simple_data)
gb.configure_side_bar()
gb.configure_column('history', cellRenderer='agSparklineCellRenderer')
gridOptions = gb.build()
gridOptions['columnDefs'].append({
'field': 'history2',
'cellRenderer': 'agSparklineCellRenderer',
'valueGetter' : 'eval(data.history)'
})
g = AgGrid(
simple_data,
gridOptions=gridOptions,
allow_unsafe_jscode=True,
enable_enterprise_modules=True
)
This is not a bug, but an enhancement.
The ideal behavior would be for __parse_row_data to detect lists, and convert them to a native list, instead of converting to a string and requiring lists to be re-parsed on the client side.
Actually the example in the question works for me with
streamlit 1.12.2
streamlit-aggrid 0.3.3
... without JS injection, evals, allow_unsafe_jscode
.
In other words, the following:
import pandas as pd
import streamlit as st
from st_aggrid import AgGrid, GridOptionsBuilder
simple_data = pd.DataFrame(
{
"name": ["a", "b"],
"history": [
[1, 2, 0, 0, 1, 0, 2],
[1, 0, 0, 0, 2, 2, 2, 2],
],
}
)
gb = GridOptionsBuilder.from_dataframe(simple_data)
gb.configure_side_bar()
gb.configure_column(
"history",
cellRenderer="agSparklineCellRenderer",
cellRendererParams={
"sparklineOptions": {
"type": "line",
"line": {"stroke": "#91cc75", "strokeWidth": 2},
}
},
)
gridOptions = gb.build()
g = AgGrid(
simple_data,
gridOptions=gridOptions,
)
... produces
