Hi everyone,
I have been working for a while on developing a website using Flask and recently discovered the fantastic tool Plotly. However, I am having some trouble implementing Plotly’s functionalities into my website.
Specifically, I want to use Plotly’s Lasso selection option to create a table with the selected data. I already have the code for this functionality, but I am unsure how to incorporate it into the website.
This is the code
In this part i prepare the data that is going to be analysed
import numpy as np
import plotly.graph_objs as go
from scipy.signal import find_peaks, peak_prominences, peak_widths
import numpy as np
import matplotlib.pyplot as plt
import scipy.optimize as sc
import plotly.graph_objects as go
from ipywidgets import VBox
# Function to read numeric data from a file
def read_numeric_data(file_path):
with open(file_path, 'r', encoding='latin1') as f:
data = []
live_time = None
real_time = None
for line in f:
if 'LIVE_TIME' in line:
live_time = line.strip()
elif 'REAL_TIME' in line:
real_time = line.strip()
elif all(char.isdigit() or char in ".-+e" for char in line.strip()):
row = [float(x) for x in line.split()]
data.extend(row)
return np.array(data), live_time, real_time
file_path = '/home/Manuel/Calabria/APP/uploads/uploaded_directory/Unknown/Cópia de Fundo_40kV.mcs'
data, _, _ = read_numeric_data(file_path)
And here i create the Plotly and data slection
import pandas as pd
a, b = -0.014213857884039616, 0.039500128118851285
# Constantes
a, b = -0.014213857884039616, 0.039500128118851285
value = 2048 # Ajusta esto según tu necesidad
# Generar los valores de 'X'
x = a + b * np.linspace(0, value, value)
# Supongamos que 'data' contiene tus valores de 'Y'
# Crear el DataFrame 'dt' con las columnas 'X' y 'Y'
dt = pd.DataFrame({'X (Kev)': x, 'Y': data/np.max(data)})
print(dt.columns)
# Function definition for plot_energy_spectrum
def plot_energy_spectrum(data, a, b,widht):
maxim = np.max(data)
workingarray = np.squeeze(data)
x = a + b * np.linspace(0, value, value)
# Finding peaks with prominence and other parameters
peaks, properties = find_peaks(workingarray / maxim, height=0.008, prominence=0.01, width=widht)
prominences = peak_prominences(workingarray / maxim, peaks)[0]
widths, width_heights, left_ips, right_ips = peak_widths(workingarray / maxim, peaks, rel_height=0.5)
peak_values = workingarray[peaks] / maxim
peak_indices = peaks
# Create traces for the plot
trace1 = go.Scatter(x=x, y=workingarray[:value] / maxim, mode='lines+markers', name='Normalized Data',
marker=dict(color='black'))
trace2 = go.Scatter(x=a + b * peak_indices, y=peak_values, mode='markers', name='Detected Peaks',
marker=dict(color='red', size=10))
# Create traces for the widths
width_traces = []
for i in range(len(peaks)):
width_traces.append(go.Scatter(
x=[x[int(left_ips[i])], x[int(right_ips[i])]],
y=[width_heights[i], width_heights[i]],
mode='lines',
name=f'Width of Peak {i+1}',
line=dict(color='blue', dash='dash')
))
# Create annotations for the peaks
annotations = []
for j in range(len(peak_indices)):
rescaled_width = a + b * widths[j]
annotations.append(dict(
x=a + b * peak_indices[j],
y=peak_values[j],
text=f"{a + b * peak_indices[j]:.2f} KeV<br>Width: {rescaled_width:.2f}",
showarrow=True,
arrowhead=2,
ax=0,
ay=-15
))
layout = go.Layout(
title='Calibrated Plot',
xaxis=dict(title='Energy (KeV)'),
yaxis=dict(title='Normalized data'),
showlegend=True,
annotations=annotations,
width=100,
height=800
)
fig = go.FigureWidget()
fig.add_trace(trace1)
fig.add_trace(trace2)
fig.add_traces(width_traces)
fig.update_layout(annotations=annotations)
return fig
widht=(0.05-a)/b
# Create plot
fig = plot_energy_spectrum(data, a, b,widht)
# Create a table figure widget
table = go.FigureWidget([go.Table(
header=dict(values=['X (Kev)', 'Y'],
fill_color='#C2D4FF',
align='left'),
cells=dict(values=[dt[col] for col in ['X (Kev)', 'Y']],
fill_color='#F5F8FF',
align='left'))
])
# Variable para guardar los datos seleccionados
selected_data = None
# Function to update the table based on scatter plot selection and save selected data
def selection_fn(trace, points, selector):
global selected_data
inds = points.point_inds
if inds:
selected_data = dt.iloc[inds].copy()
table.data[0].cells.values = [selected_data[col].tolist() for col in ['X (Kev)', 'Y']]
else:
selected_data = None
table.data[0].cells.values = [dt[col].tolist() for col in ['X (Kev)', 'Y']]
# Link the selection function to the scatter plot
fig.data[0].on_selection(selection_fn)
# Display the figure and table in a VBox
display(VBox([fig, table]))
print(selected_data)
Does anyone know where i need to look, in orther to learn how to implement that code properly in my website?
In my app, I created the corresponding app route and thought that using jsonify
from Flask could help, but it did not work.
The app route essentially gets some parameters that are used as thresholds for the find_peak
function, which I know works and generates a plot on the website. However, after trying to add the table, the plot no longer shows up.
@app.route('/noisydata', methods=['GET', 'POST'])
def noisydata():
global a, b, spectradata, energy_spectrum, dt # Assuming these are defined elsewhere
# Constantes
a, b = -0.014213857884039616, 0.039500128118851285
value = 2048 # Ajusta esto según tu necesidad
# Generar los valores de 'X'
x = a + b * np.linspace(0, value, value)
# Supongamos que 'data' contiene tus valores de 'Y'
# Crear el DataFrame 'dt' con las columnas 'X' y 'Y'
dt = pd.DataFrame({'X (Kev)': x, 'Y': spectradata/np.max(spectradata)})
req_data = request.get_json()
height = float(req_data['height'])
prominence = float(req_data['prominence'])
width = (float(req_data['width']) - a) / b
value = len(spectradata)
print('altura', height)
print('prominence', prominence)
print('ancho', width)
print(value)
fig, energy_spectrum = plot_noisy_spectrum(spectradata, a, b, height, prominence, width, value)
plot_html = fig.to_html(full_html=False)
# Create a table figure widget
table = go.FigureWidget([go.Table(
header=dict(values=['X (Kev)', 'Y'],
fill_color='#C2D4FF',
align='left'),
cells=dict(values=[dt[col] for col in ['X (Kev)', 'Y']],
fill_color='#F5F8FF',
align='left'))
])
# Variable to store selected data
selected_data = None
# Function to update the table based on scatter plot selection and save selected data
def selection_fn(trace, points, selector):
global selected_data
inds = points.point_inds
if inds:
selected_data = dt.iloc[inds].copy()
table.data[0].cells.values = [selected_data[col].tolist() for col in ['X (Kev)', 'Y']]
else:
selected_data = None
table.data[0].cells.values = [dt[col].tolist() for col in ['X (Kev)', 'Y']]
# Link the selection function to the scatter plot
fig.data[0].on_selection(selection_fn)
table_html = pio.to_html(table, full_html=False)
print(selected_data)
return jsonify(plot_html=plot_html, table_html=table_html)