Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sparklines ? #118

Closed
dcowden opened this issue Jul 29, 2022 · 4 comments
Closed

Sparklines ? #118

dcowden opened this issue Jul 29, 2022 · 4 comments

Comments

@dcowden
Copy link

dcowden commented Jul 29, 2022

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:

image

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:
image

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!

@GGA-PERSO
Copy link

GGA-PERSO commented Jul 29, 2022

@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 :
image

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')	

@dcowden
Copy link
Author

dcowden commented Jul 29, 2022

@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

@dcowden
Copy link
Author

dcowden commented Jul 29, 2022

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.

@uros-r
Copy link

uros-r commented Sep 25, 2022

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

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants