PlotlyView from dict

Hi,

Just like Bart in the post before I still get an empty graph when trying to plot anything other then a simple list like in the example from the tutorial. I’ve tried to plot a single column dict as well as a pandas serie.

Could you give an example?

Thanks!

Bart

Hi Bart,

Can you share a code snippet of your implementation? It is quite difficult to troubleshoot this without a code reference.

Regards,

Raoul

Hi Raoul,

This is a piece of the code. However, I’m not sure if I have anything in the dict as I can not really verifiy that anywhere. When I click the download button, it should update the HiddenField and give an output to the plotly graph.

def download_spreadsheet(self, params, **kwargs):
    path_file_4 = File.from_path(Path(__file__).parent / "df_failures.csv")
    content4 = path_file_4.getvalue_binary()  # 
    s4=str(content4,'utf-8')
    data4 = StringIO(s4) 
    df_failures = pd.read_csv(data4, sep=";")
    
    serialized_df_failures = df_failures['TBF'].to_dict()
    serialized_df_types = df_failures['Type of Technical Object'].to_dict()
    #serialized_df_failures = {'key1': 1, 'key2': 3, 'key3': 2}
    data_file = File.from_data(content4)
    #df_failures.to_dict()#func(orders=params.downloads.calculation_sheet.orders, 
                  #flocs=params.downloads.calculation_sheet.flocs,
                  #fail_rates=params.downloads.calculation_sheet.fail_rates)
    return ViktorResult(set_params_result=SetParamsResult({'downloads': {'calculation_sheet': {'failures': serialized_df_failures}}}),
                        download_result=DownloadResult(file_content=data_file, file_name="my_file.txt"))

@Parametrization
class CalculationParametrization(Parametrization):
downloads = Tab(‘Downloads’)
downloads.calculation_sheet = Section(‘Updated PoFs’)
downloads.calculation_sheet.btn = DownloadButton(‘Download’, ‘download_spreadsheet’)
downloads.calculation_sheet.failures = HiddenField(“failures”, name=‘failures’)

@PlotlyView('Plotly view', duration_guess=1)
def get_plotly_view(self, params, **kwargs):
    y_values = pd.DataFrame.from_dict(params.failures, orient='index', dtype=float, columns=['TBF'])
    fig = go.Figure(
        data=[go.Bar(y=y_values)],
        layout=go.Layout(title=go.layout.Title(text="A Figure Specified By A Graph Object"))
        )
    return PlotlyResult(fig.to_json())

Hi Bart,

Thank you!

I see that you are combining a DownloadResult and a SetParamsResult, this is not possible, only single results are supported (see the ViktorResult documentation):

Currently, the serialisation only accounts for the following (backward compatible) combinations:

  • a single optimization result
  • a single set_params result
  • a single download result

So the values are not set on the HiddenField, which cause the graph to be empty.


Furthermore, it is not required to convert a File to a StringIO as read_csv can also use a buffer as input, e.g.:

path_file_4 = File.from_path(Path(__file__).parent / "df_failures.csv")
with path_file_4.open() as f:
    df_failures = pd.read_csv(f, sep=";")

Assuming the methods in your post are on the same controller you can add the parsing of the csv to the get_plotly_view method and select a column of the dataframe for the x and y values of the bar chart (e.g. y=df["TBT"]). You can find examples from the plotly documentation here: Bar charts in Python. If you only specify a y-value the x-values will be the indices of the corresponding y-values.


Some last remarks:

path_file_4 = File.from_path(Path(__file__).parent / "df_failures.csv")

Please note that in your example you are now using a csv that is hard-coded in your app-code, is that desired?

However, I’m not sure if I have anything in the dict as I can not really verifiy that anywhere

You can add a print-statement to inspect the content of the dictionary or the dataframe in the terminal.

Hopefully this helps!

Regards,

Raoul

Hi Raoul,

Thank you very much for the tips! I didn’t know that the print statement works. That’s a great tip!

Also the charts works when I parse the CSV directly in the Plotly controller. However I need a way to read and write tables and use them in different decorators. I was hoping the HiddenField could store my different dicts, but I understand now that I can not store an entire dict in a HiddenField. Could I use a writable file object for that?

Thanks!

Bart

It is possible to store an dictionary on a HiddenField, it is just not possible to combine this action together with a DownloadResult / DownloadButton. If you want to update your parametrization from the user interface you should use a SetParamsButton, see the documentation here: User input, fields and buttons - Set params using a button | VIKTOR Documentation.

If parsing of the csv’s is cheap (time-wise) it is not really a problem to repeat the parsing on each controller method to reduce complexity. If the user uploads the csv’s you can also choose to parse and store the result with a ParamsFromFile decorator (Managing files - Uploading files | VIKTOR Documentation).

Hi Raoul,

Thanks! I can see I store a dict in the HiddenField. However the HiddenField seems to remain emtpy when I print the param.failures in the PlotlyView.

def download_spreadsheet(self, params, **kwargs):

    path_file_4 = File.from_path(Path(__file__).parent / "df_failures.csv")
    with path_file_4.open() as f:
        df_failures = pd.read_csv(f, sep=";")
    
    serialized_df_failures = df_failures['TBF'].to_dict()
    print(serialized_df_failures)
    return SetParamsResult({'downloads': {'calculation_sheet': {'failures': serialized_df_failures}}})

The serialized_df_failures shows a dict.

@PlotlyView('Plotly view', duration_guess=1)
def get_plotly_view(self, params, **kwargs):
    y_values = params.failures
    print(y_values)        
    fig = go.Figure(
        data=[go.Bar(y=y_values)],
        layout=go.Layout(title=go.layout.Title(text="A Figure Specified By A Graph Object"))
        )
    return PlotlyResult(fig.to_json())

The y_values is empty.

Any idea?

ps. I will use ParamsFromFile in the future when I have the calculations running.

Hi Raoul,

Do you have a suggestion for this problem? I’m a little stuck at the moment.

Thanks!

Bart

I think you set the wrong params in download_spreadsheet, as you explicitly set the name argument on HiddenField (thus overwriting the default location in params). Replacing the SetParamsResult with the following should solve your problem:

return SetParamsResult({'failures': serialized_df_failures})

Hi Bas,

When I try to use the results for a plotly graph I try to print the values there but the parameter gives ‘None’ and is empty. Any idea?

@PlotlyView('Plotly view', duration_guess=1)
def get_plotly_view(self, params, **kwargs):
    y_values = params.failures 
    print(y_values)

    fig = go.Figure(
        data=[go.Bar(y=y_values)],
        layout=go.Layout(title=go.layout.Title(text="A Figure Specified By A Graph Object"))
        )
    return PlotlyResult(fig.to_json())

I did use this time by the way, but no difference :slight_smile:

return SetParamsResult({'failures': serialized_df_failures})

Based on your other post, the method download_spreadsheet returns a SetParamsResult, but I suspect is called by a DownloadButton. You would need a SetParamsButton instead.

1 Like

Perfect! Works like a charm!

Thanks, Bas

1 Like