def create_figure(y_column, x_column): df_cars = load_data() source = ColumnDataSource(data=df_cars) hover = HoverTool(tooltips=[ ("Modell", "@Modell"), ("price", "@price"), ]) p = figure(title='Cars', tools=[hover]) #x_column = 'Tillverkningsår' #y_column = 'price' markers = ['circle'] p.scatter(x=x_column, y=y_column, source=source, legend="car type", marker=factor_mark('car type', markers=markers, factors=df_cars['car type'].unique()), color=factor_cmap('car type', palette='Category10_3', factors=df_cars['car type'].unique()), size=8, alpha=0.25) p.xaxis.axis_label = x_column p.yaxis.axis_label = y_column return p
def test_defaults(self) -> None: t = bt.factor_mark("foo", ["hex", "square"], ["foo", "bar"]) assert isinstance(t, Field) assert t.field == "foo" assert isinstance(t.transform, CategoricalMarkerMapper) assert t.transform.markers == ["hex", "square"] assert t.transform.factors == ["foo", "bar"] assert t.transform.start == 0 assert t.transform.end is None
def test_defaults(self): t = bt.factor_mark("foo", ["hex", "square"], ["foo", "bar"]) assert isinstance(t, dict) assert set(t) == {"field", "transform"} assert t['field'] == "foo" assert isinstance(t['transform'], CategoricalMarkerMapper) assert t['transform'].markers == ["hex", "square"] assert t['transform'].factors == ["foo", "bar"] assert t['transform'].start == 0 assert t['transform'].end is None
def test_basic(self) -> None: t = bt.factor_mark("foo", ["hex", "square"], ["foo", "bar"], start=1, end=2) assert isinstance(t, dict) assert set(t) == {"field", "transform"} assert t['field'] == "foo" assert isinstance(t['transform'], CategoricalMarkerMapper) assert t['transform'].markers == ["hex", "square"] assert t['transform'].factors == ["foo", "bar"] assert t['transform'].start == 1 assert t['transform'].end == 2
def test_basic(self) -> None: t = bt.factor_mark("foo", ["hex", "square"], ["foo", "bar"], start=1, end=2) assert isinstance(t, Field) assert t.field == "foo" assert isinstance(t.transform, CategoricalMarkerMapper) assert t.transform.markers == ["hex", "square"] assert t.transform.factors == ["foo", "bar"] assert t.transform.start == 1 assert t.transform.end == 2
def mask_rendering_kwargs(self): """ Get the marks and colors to use for the various point masks Returns ------- dict : Returns a dict for bokeh that describes the markers and pallete """ return { 'marker': bt.factor_mark('mask', self.MARKERS, self.MASK_TYPE), 'color': bt.factor_cmap('mask', self.PALETTE, self.MASK_TYPE) }
def mapping_marker(): SPECIES = ['setosa', 'versicolor', 'virginica'] MARKERS = ['hex', 'circle_x', 'triangle'] p = figure(title="Iris Morphology") p.xaxis.axis_label = 'Petal Length' p.yaxis.axis_label = 'Sepal Width' p.scatter("petal_length", "sepal_width", source=flowers, legend_field="species", fill_alpha=0.4, size=12, marker=factor_mark('species', MARKERS, SPECIES), color=factor_cmap('species', 'Category10_3', SPECIES)) show(p)
def create_sample_scatter(x_data, y_data, source, label, title='', x_axis_title='', y_axis_title=''): result_plot = figure(title=title, tools=tools_list, tooltips=custom_tooltip) result_plot.xaxis.axis_label = x_axis_title result_plot.yaxis.axis_label = y_axis_title for cat_filter in label['standard_label_list']: index_list = [] for i in range(len(source.data[label['real_label_list']])): if source.data[label['real_label_list']][i] == cat_filter: index_list.append(i) view = CDSView(source=source, filters=[IndexFilter(index_list)]) result_plot.scatter( x_data, y_data, source=source, fill_alpha=0.4, size=8, marker=factor_mark(label['real_label_list'], markers, label['standard_label_list']), color=factor_cmap(label['real_label_list'], 'Category10_8', label['standard_label_list']), # muted_color=factor_cmap(label['real_label_list'], 'Category10_8', # label['standard_label_list']), muted_alpha=0.1, view=view, legend=cat_filter) result_plot.legend.click_policy = "mute" # highlight x y axes result_plot.renderers.extend([vline, hline]) return result_plot
def Electron_Energy_Graph(conn): output_file( "Electron_Energy_Graph2.html" ) #???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ############################################################################ ############################# USER INPUTS ################################## # Decide what the default viewing option is going to be. (i.e. the fields to # be plotted on the x and y axis when the graph is opened). # NB: Have it set that if axis is 'adate' then will automatically update # to plot datetime. x_data1 = 'adate' y_data1 = '6fwhm' plot_title1 = 'Electron Energy' x_axis_title1 = x_data1 y_axis_title1 = y_data1 plot_size_height1 = 450 plot_size_width1 = 800 legend_location = 'bottom_left' hover_tool_fields = ['comments'] # Create a list of the plot parameters that will be used as input to a # function later. list_plot_parameters = [ x_data1, y_data1, plot_title1, x_axis_title1, y_axis_title1, plot_size_height1, plot_size_width1, legend_location ] # Define the fields that the legend will be based off. If there is only # one field then put it in both columns. color_column = 'machinename' custom_color_boolean = False custom_color_palette = [] marker_column = 'machinename' custom_marker_boolean = False custom_marker_palette = [] # From the legend defined above give the values that will be pre-ticked when # the plot is opened. NB: Bokeh will throw an error if one of these lists is # empty (i.e. =[]) If only using color or marker then set the color_to plot # and then enter the command: marker_to_plot = color_to_plot. color_to_plot = ['TrueBeam B', 'TrueBeam C'] marker_to_plot = ['option1', 'option2', 'option3'] marker_to_plot = color_to_plot ############################################################################ #################### CREATE THE DATA FOR THE GRAPH ######################### # Do this in a function so it can be used in an update callback later def Create_df(): # Use the connection passed to the function to read the data into a # dataframe via an SQL query. df = pd.read_sql('SELECT * FROM [eEnergyICP]', conn) # Delete empty rows where the data is very important to have df = df.dropna(subset=['protocol id'], how='any') # The format is complicated for this field but seems to be that the date is # always the first element and the machine is always the last regardless of # how many elements there are. # Seperate on the first '_' df_left = df['protocol id'].str.partition(sep='_') # Seperate on the last '_' df_right = df['protocol id'].str.rpartition(sep='_') # From these sperated dataframes add the appropriate columns back into # the main dataframe. df.loc[:, 'adate'] = df_left[0] df.loc[:, 'machinename'] = df_right[2] # Turn 'adate' into datetime. An annoying factor in the database is a # few entries with a different datetime format. In combination with the # dayfirst=True parameter to override the American date default the # to_datetime function seems to solve this. NB: Might be a little slow # without feeding it a specific format but unlikely to be an issue given # relatively small datasets. Possibly someway to feed multiple formats # but currently more effort than it's worth. df.loc[:, 'adate'] = pd.to_datetime(df.loc[:, 'adate'], dayfirst=True) # Drop any rows that aren't related to the Truebeams (ditches the old # uneeded data). Might be possible to put this in the SQL query but # difficult as machinename is embedded in the protocol ID. df = df[df['machinename'].isin( ['TrueBeam B', 'TrueBeam C', 'TrueBeam D', 'TrueBeam F'])] # Drop any columns where there is no data (likely because of the # dropping of the old linacs (e.g. data that used to be collected from # them that is no longer collected for the Truebeams)) df = df.dropna(axis='columns', how='all') return df df = Create_df() # Create a list of the fields using the dataframe. By doing it now before # the extra legend fields are added it's easy to limit what is displayed in # the select widgets. TableFields = (list(df.columns)) ############################################################################ ############################################################################ ############################################################################ ################ CREATE THE DATAFRAME FOR THE TOLERANCES ################### # If you want to add tolerances change the boolean to True and construct the # dataframe in the correct format. tolerance_boolean = True # The format of the dataframe should be the first line being the x_axis # (with some values taken from the main dataframe to get the right # formatting). The subsequent columns are the tolerances [low, high]. # NB: column names should match those from the main dataframe. if tolerance_boolean == True: df_tol1 = pd.DataFrame({ 'adate': [df['adate'].max(), df['adate'].max()], '6fwhm': [6, 10], '9fwhm': [9, 12] }) df_tol1 = pd.read_sql('SELECT * FROM [ElectronFWHMLimits]', conn) df_tol1 = df_tol1.set_index('class') df_tol1 = pd.DataFrame({ 'adate': [df['adate'].max(), df['adate'].max()], '6fwhm': [df_tol1.loc['TBUCLH', 'lower6'], df_tol1.loc['TBUCLH', 'upper6']], '9fwhm': [df_tol1.loc['TBUCLH', 'lower9'], df_tol1.loc['TBUCLH', 'upper9']], '12fwhm': [ df_tol1.loc['TBUCLH', 'lower12'], df_tol1.loc['TBUCLH', 'upper12'] ], '15fwhm': [ df_tol1.loc['TBUCLH', 'lower15'], df_tol1.loc['TBUCLH', 'upper15'] ] }) ############################################################################ ############################################################################ ############################################################################ ############################################################################ ''' This is the end of the user input section. If you don't need to make any other changes you can end here. ''' ########################################################################## ################### CREATE THE COLUMNS FOR THE LEGEND ###################### (color_list, color_palette, marker_list, marker_palette, df, add_legend_to_df) = Create_Legend(df, color_column, custom_color_boolean, custom_color_palette, marker_column, custom_marker_boolean, custom_marker_palette) ############################################################################ ############################################################################ ############################################################################ ################## FORMATTING AND CREATING A BASIC PLOT #################### ######### Make Dataset: # Run the Make_Dataset function to create a sub dataframe that the plot will # be made from. Sub_df1 = Make_Dataset(df, color_column, color_to_plot, marker_column, marker_to_plot, x_data1, y_data1) # Make the ColumnDataSource (when making convert dataframe to a dictionary, # which is helpful for the callback). src1 = ColumnDataSource(Sub_df1.to_dict(orient='list')) ######### Make Plot: # Create an empty plot (plot parameters will be applied later in a way that # can be manipulated in the callbacks) p1 = figure() p1.scatter( source=src1, x='x', y='y', fill_alpha=0.4, size=12, # NB: Always use legend_field for this not legend_group as the # former acts on the javascript side but the latter the Python # side. Therefore the former will update automatically when the # plot is changed with no need for a callback. legend_field='legend', marker=factor_mark('marker1', marker_palette, marker_list), color=factor_cmap('color1', color_palette, color_list)) ######### Add plot parameters: Define_Plot_Parameters(p1, list_plot_parameters) ############################################################################ ############################################################################ ############################################################################ ############################ ADD TOLERANCES ################################ # We defined the tolerances further up and now want to add the correct ones # to the plot. Only do this through if the boolean is set to True as # otherwise the user doesn't want tolerances. if tolerance_boolean == True: Sub_df1_tol1 = Make_Dataset_Tolerance(x_data1, y_data1, Sub_df1, df_tol1) src1_tol = ColumnDataSource(Sub_df1_tol1.to_dict(orient='list')) p1.line(source=src1_tol, x='x', y='y_low', color='firebrick') p1.line(source=src1_tol, x='x', y='y_high', color='firebrick') ############################################################################ ############################################################################ ############################################################################ ################## ADD MORE COMPLEX TOOLS TO THE PLOT ###################### ######## 1) # Create a hover tool and add it to the plot hover1 = HoverTool() if len(hover_tool_fields) < 11: kwargs = {} i = 0 for x in hover_tool_fields: i = i + 1 kwargs['Field' + str(i)] = x else: kwargs = {} msgbox('Too many fields selected to display on HoverTool ' \ '(Max = 10). Please reduce number of fields selected') Update_HoverTool(hover1, x_data1, y_data1, **kwargs) p1.add_tools(hover1) ############################################################################ ############################################################################ ############################################################################ ################# CREATE WIDGETS TO BE ADDED TO THE PLOT ################### ######## 1) # This select funtion will be used to create dropdown lists to change the # data plotted on the x/y-axis. select_xaxis, select_yaxis = Create_Select_Axis(TableFields, x_axis_title1, y_axis_title1) ######## 2) # This select widget will be used to create dropdown lists to change the # legend position. select_legend = Create_Select_Legend(legend_location) ######## 3) # These checkbox widgets will be used to create a tool to select the machine # and energy that are being plotted. checkbox_color, checkbox_marker = Create_Checkbox_Legend( df, color_column, color_to_plot, marker_column, marker_to_plot) ######## 4) # These checkbox widgets will be used to create a tool to select the machine # and energy that are being plotted. checkbox_hovertool = Create_Checkbox_HoverTool(TableFields, hover_tool_fields) ######## 5) # Make an 'Update Button' to requery the database and get up to date data. update_button = Button(label='Update', button_type='success') ######## 6) # Make a Range Button range_button = Button(label='Range', button_type='primary') ######## 7) # Make some titles for the checkboxes color_title = Div(text='<b>Machine Choice</b>') marker_title = Div(text='<b>Marker</b>') hover_title = Div(text='<b>Hovertool Fields</b>') ############################################################################ ############################################################################ ############################################################################ ########################### CREATE A LAYOUT ################################ # Create a layout to add widgets and arrange the display. if color_column == marker_column: layout_checkbox = column( [color_title, checkbox_color, hover_title, checkbox_hovertool]) else: layout_checkbox = column([ color_title, checkbox_color, marker_title, checkbox_marker, hover_title, checkbox_hovertool ]) button_row = row([update_button, range_button]) layout_plots = column( [button_row, select_xaxis, select_yaxis, select_legend, p1]) tab_layout = row([layout_plots, layout_checkbox]) ############################################################################ ############################################################################ ############################################################################ ####################### CREATE CALLBACK FUNCTIONS ########################## # Create a big callback that does most stuff def callback(attr, old, new): # Want to acquire the current values of all of the checkboxes and select # widgets to provide as inputs for the re-plot. color_to_plot = [ checkbox_color.labels[i] for i in checkbox_color.active ] if color_column != marker_column: marker_to_plot = [ checkbox_marker.labels[i] for i in checkbox_marker.active ] else: marker_to_plot = color_to_plot hovertool_to_plot = [ checkbox_hovertool.labels[i] for i in checkbox_hovertool.active ] plot1_xdata_to_plot = select_xaxis.value plot1_ydata_to_plot = select_yaxis.value legend_location = select_legend.value # Set the new axis titles x_axis_title1 = plot1_xdata_to_plot y_axis_title1 = plot1_ydata_to_plot # Use the pre-defined Make_Dataset function with these new inputs to # create new versions of the sub dataframes. Sub_df1 = Make_Dataset(df, color_column, color_to_plot, marker_column, marker_to_plot, plot1_xdata_to_plot, plot1_ydata_to_plot) # Use the pre-defined Define_Plot_Parameters function with these new # inputs to update the plot parameters. Define_Plot_Parameters(p1, [ plot1_xdata_to_plot, plot1_ydata_to_plot, plot_title1, x_axis_title1, y_axis_title1, plot_size_height1, plot_size_width1, legend_location ]) # Update the hovertool if len(hovertool_to_plot) < 11: kwargs = {} i = 0 for x in hovertool_to_plot: i = i + 1 kwargs['Field' + str(i)] = x else: kwargs = {} msgbox('Too many fields selected to display on HoverTool ' \ '(Max = 10). Please reduce number of fields selected') Update_HoverTool(hover1, plot1_xdata_to_plot, plot1_ydata_to_plot, **kwargs) # Use the pre-defined tolerances function with these new inputs to # make a new version of the tolerances sub dataframe. if tolerance_boolean == True: Sub_df1_tol1 = Make_Dataset_Tolerance(plot1_xdata_to_plot, plot1_ydata_to_plot, Sub_df1, df_tol1) # Update the ColumnDataSources. src1.data = Sub_df1.to_dict(orient='list') if tolerance_boolean == True: src1_tol.data = Sub_df1_tol1.to_dict(orient='list') return select_xaxis.on_change('value', callback) select_yaxis.on_change('value', callback) select_legend.on_change('value', callback) checkbox_color.on_change('active', callback) checkbox_marker.on_change('active', callback) checkbox_hovertool.on_change('active', callback) # Callback for the Update Button def callback_update(): # Make a new version of the dataframe using the original Create_df # function that connects to the database. df = Create_df() df = add_legend_to_df(df) color_to_plot = [ checkbox_color.labels[i] for i in checkbox_color.active ] if color_column != marker_column: marker_to_plot = [ checkbox_marker.labels[i] for i in checkbox_marker.active ] else: marker_to_plot = color_to_plot hovertool_to_plot = [ checkbox_hovertool.labels[i] for i in checkbox_hovertool.active ] plot1_xdata_to_plot = select_xaxis.value plot1_ydata_to_plot = select_yaxis.value x_axis_title1 = plot1_xdata_to_plot y_axis_title1 = plot1_ydata_to_plot legend_location = select_legend.value Sub_df1 = Make_Dataset(df, color_column, color_to_plot, marker_column, marker_to_plot, plot1_xdata_to_plot, plot1_ydata_to_plot) Define_Plot_Parameters(p1, [ plot1_xdata_to_plot, plot1_ydata_to_plot, plot_title1, x_axis_title1, y_axis_title1, plot_size_height1, plot_size_width1, legend_location ]) if len(hovertool_to_plot) < 11: kwargs = {} i = 0 for x in hovertool_to_plot: i = i + 1 kwargs['Field' + str(i)] = x else: kwargs = {} msgbox('Too many fields selected to display on HoverTool ' \ '(Max = 10). Please reduce number of fields selected') Update_HoverTool(hover1, plot1_xdata_to_plot, plot1_ydata_to_plot, **kwargs) if tolerance_boolean == True: Sub_df1_tol1 = Make_Dataset_Tolerance(plot1_xdata_to_plot, plot1_ydata_to_plot, Sub_df1, df_tol1) src1_tol.data = Sub_df1_tol1.to_dict(orient='list') src1.data = Sub_df1.to_dict(orient='list') return update_button.on_click(callback_update) # Callback for the Range Button def callback_range(): color_to_plot = [ checkbox_color.labels[i] for i in checkbox_color.active ] if color_column != marker_column: marker_to_plot = [ checkbox_marker.labels[i] for i in checkbox_marker.active ] else: marker_to_plot = color_to_plot plot1_xdata_to_plot = select_xaxis.value plot1_ydata_to_plot = select_yaxis.value # Use the pre-defined Make_Dataset function with these new inputs to # create new versions of the sub dataframes. Sub_df1 = Make_Dataset(df, color_column, color_to_plot, marker_column, marker_to_plot, plot1_xdata_to_plot, plot1_ydata_to_plot) x_data1 = select_xaxis.value y_data1 = select_yaxis.value if (x_data1 == 'adate') and ((y_data1 == '6fwhm') or (y_data1 == '9fwhm') or (y_data1 == '12fwhm') or (y_data1 == '15fwhm') or (y_data1 == '16fwhm')): p1.x_range.start = Sub_df1['x'].max() - timedelta(weeks=53) p1.x_range.end = Sub_df1['x'].max() + timedelta(weeks=2) if y_data1 == '6fwhm': p1.y_range.start = 9.6 p1.y_range.end = 10.3 elif y_data1 == '9fwhm': p1.y_range.start = 12.6 p1.y_range.end = 13.32 elif y_data1 == '12fwhm': p1.y_range.start = 16.25 p1.y_range.end = 17.01 elif y_data1 == '15fwhm': p1.y_range.start = 19.4 p1.y_range.end = 20.16 elif y_data1 == '16fwhm': p1.y_range.start = 19.5 p1.y_range.end = 19.9 return range_button.on_click(callback_range) ############################################################################ ############################################################################ ############################################################################ ####################### RETURN TO THE MAIN SCRIPT ########################## return Panel(child=tab_layout, title='Electron Energy')
# SquareX, Triangle, X] TOOLTIPS_SCATTER = [ ("(Fare,AGE)", "$x, $y"), ] # Set the Title p = figure(title = "Titanic Passenger Age & Fare by Survial Type", tooltips=TOOLTIPS_SCATTER) # Construnct the colours p.scatter("Fare", "Age", source=titanic_df, legend="Survived", fill_alpha=0.3, size=12, marker=factor_mark('Survived', MARKERS, FATE), color=factor_cmap('Survived', palette=['#3a6587', '#aeb3b7'], factors=FATE)) #Set the axis labels p.xaxis.axis_label = 'Fare (In Pounds)' p.yaxis.axis_label = 'Age (In Years)' # Remove the Grid lines p.xgrid.grid_line_color = None p.ygrid.grid_line_color = None # change just some things about the x-axis p.xaxis.axis_line_width = 2 p.xaxis.major_label_text_color = "black" p.xaxis.axis_line_color = "#aeb3b7"
def main(assignment_dir: str, log_dirs: List[str], plotfile: str, csv: str): """This script generates a HTML file containing a plot of the coverage for each sample after they have been through the QC pipeline.\n LOG_DIRS: Director(y/ies) containing the subsampling log files. (Coverage is extracted from these) ), """ logfiles = [] for d in log_dirs: logfiles.extend(Path(d).rglob("*.log")) data = defaultdict(dict) for file in logfiles: tech = "illumina" if "illumina" in file.parts[2] else "nanopore" sample = file.with_suffix("").name site = file.parts[-2] covg = ripgrep_extract_covg(file) data[sample].update({f"{tech}_covg": covg, "site": site}) assignment_files = Path(assignment_dir).rglob("*.csv") for file in assignment_files: fields = file.read_text().split("\n")[1].split(",") sample = file.name.split(".")[0] data[sample]["lineage"] = fields[1] df = pd.DataFrame(data).T df.index.name = "sample" df.to_csv(csv) cds = ColumnDataSource(df) sites = list(set(df["site"])) lineages = list(set(df["lineage"])) tooltips = [ ("index", "@sample"), ("Illumina", "@illumina_covg"), ("Nanopore", "@nanopore_covg"), ("site", "@site"), ("lineage", "@lineage"), ] title = "Sample coverage for different technologies after quality control" palette = Set2[len(lineages)] legend_var = "lineage" # inline effectively allows the plot to work offline output_file(plotfile, title=title, mode="inline") p = figure( tools=TOOLS, height=HEIGHT, width=WIDTH, tooltips=tooltips, active_drag="box_zoom", active_scroll="wheel_zoom", title=title, ) p.yaxis.axis_label = "Nanopore Coverage" p.xaxis.axis_label = "Illumina Coverage" legend = Legend( click_policy="hide", location="top_left", title=legend_var.capitalize(), background_fill_alpha=0.1, ) p.add_layout(legend) p.scatter( source=cds, y="nanopore_covg", x="illumina_covg", size=10, fill_alpha=0.4, marker=factor_mark("site", MARKERS, sites), color=factor_cmap("lineage", palette, lineages), legend_field=legend_var, ) save(p)
def Electron_Energy_Graph_Old(conn): ############################################################################ #################### CREATE THE DATA FOR THE GRAPH ######################### output_file( "Electron_Output_Graph.html" ) #???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? # Use the connection passed to the function to read the data into a # dataframe via an SQL query. df = pd.read_sql('SELECT * FROM [eEnergyICP]', conn) print(df) # Delete cells where 'protocol id' is empty df = df.dropna(subset=['protocol id']) # With any luck this can be removed after chatting to AW and MB ????????????????????????????????????????????????????????????????????????????????? # Get the date and machinename from the protocol id' field # Seperate on the first '_' df_left = df['protocol id'].str.partition(sep='_') # Seperate on the last '_' df_right = df['protocol id'].str.rpartition(sep='_') # From these sperated dataframes add the appropriate columns back into the # main dataframe. df.loc[:, 'adate'] = df_left[0] df.loc[:, 'machinename'] = df_right[2] # Turn 'adate' into datetime. Problems with this function as it assumes american date formats over british. ????????????????????????????????????????????????????????????????????????????????? # Talk to AW and MB about getting date from other tables in the database and pulling them into the query. ??????????????????????????????????????????????????????????????????????????????????? # This way the date should be in a set format that the datetime function can be told, which should resolve this issue. ?????????????????????????????????????????????????????????????????????? # # Need to turn the date fields into a Dateime object (either 'adate' # (protons) or the newly created 'adate' (photons)). The date field should # always be named 'adate' for consistency. df.loc[:, 'adate'] = pd.to_datetime(df.loc[:, 'adate']) # When starting a new graph can be useful to print the dataframe after any # manipulations to make sure the code has done what you expected. print(df) # Create a list of the fields using the dataframe TableFields = (list(df.columns)) ############################################################################ ############################################################################ ############################################################################ ################ CREATE THE DATAFRAME FOR THE TOLERANCES ################### # The purpose of this plot is generally to be as general as possible but # there are only a few parameters that will have defined tolerances. # Therefore the tolerance section can be a bit more specific and a dataframe # containing tolereances can be manually created for many cases and # extracted from the database in others (in a manner similar to above but # calling from a different table/query with the SQL statement) # # The format of the dataframe should be the first line being the x_axis # (with some values taken from the main dataframe to get the right # formatting). The subsequent columns are the tolerances [low, high]. # NB: column names should match those from the main dataframe. df_tol1 = pd.read_sql('SELECT * FROM [ElectronFWHMLimits]', conn) print(df_tol1) df_tol1 = df_tol1.set_index('class') print(df_tol1) df_tol_TB = pd.DataFrame({ 'adate': [df['adate'].max(), df['adate'].max()], '6fwhm': [df_tol1.loc['TBUCLH', 'lower6'], df_tol1.loc['TBUCLH', 'upper6']], '9fwhm': [df_tol1.loc['TBUCLH', 'lower9'], df_tol1.loc['TBUCLH', 'upper9']], '12fwhm': [df_tol1.loc['TBUCLH', 'lower12'], df_tol1.loc['TBUCLH', 'upper12']], '15fwhm': [df_tol1.loc['TBUCLH', 'lower15'], df_tol1.loc['TBUCLH', 'upper15']] }) print(df_tol_TB) df_tol_Classic = pd.DataFrame({ 'adate': [df['adate'].max(), df['adate'].max()], '6fwhm': [df_tol1.loc['Classic', 'lower6'], df_tol1.loc['Classic', 'upper6']], '9fwhm': [df_tol1.loc['Classic', 'lower9'], df_tol1.loc['Classic', 'upper9']], '12fwhm': [df_tol1.loc['Classic', 'lower12'], df_tol1.loc['Classic', 'upper12']], '16fwhm': [df_tol1.loc['Classic', 'lower16'], df_tol1.loc['Classic', 'upper16']], '20fwhm': [df_tol1.loc['Classic', 'lower20'], df_tol1.loc['Classic', 'upper20']] }) print(df_tol_Classic) ############################################################################ ############################################################################ ########################################################################## ################### CREATE THE COLUMNS FOR THE LEGEND ###################### # NB: The following section has been designed to be as general as possible # but in reality it might be preferable to more manually choose the markers # and colors based on optimising the most likey things to be plotted. # # This code is a way of creating a legend with markers based on one # parameter (e.g. machine name) and color on another parameter (e.g. energy) ######### Colors: # Create a sorted list of the unique values in a dataframe column that the # colors will be based on. list_forcolor = sorted(df['machinename'].unique().tolist()) # If the length of the list is <9 then we can use the colorblind palette, # which contains 8 colors. This should be the default for accessability # reasons unless there are compeling reasons otherwise. if len(list_forcolor) < 9: color_palette = Colorblind[len(list_forcolor)] # If not <9 then we can use the much larger Turbo palette which contains # 256 colors. Will check if there are more than 256 options though and # throw an error if so. elif len(list_forcolor) > 256: print( 'Error - Name of Function: >256 unique energies in database ' \ 'causing failure of the turbo color palette function (only ' \ '256 availible colors.' ) exit() # If it passes the check then use the built in turbo function that splits # the turbo palette into roughly equal sections based on a supplied integer # number. else: color_palette = turbo(len(list_forcolor)) ######### Markers: # Doesn't seem to be a way to create a simple list of all the Bokeh marker # options so just do this manually. May want to re-order to improve # visibility of commonly used options. markers = [ 'asterisk', 'circle', 'circle_cross', 'circle_x', 'cross', 'dash', 'diamond', 'diamond_cross', 'hex', 'inverted_triangle', 'square', 'square_cross', 'square_x', 'triangle', 'x' ] # Create a sorted list of the unique values in a dataframe column that the # markers will be based on. list_formarker = sorted(df['machinename'].unique().tolist()) # Check that there are enough markers to give a unique marker to each option # but otherwise throw an error. if len(list_formarker) > len(markers): print( 'Error - Name of Function: Not enough markers to assign a ' \ 'unique marker to each option.' ) exit() ######### Legend Key: # Create a function that will be used to run through the dataframe looking # at the energy and machine column and creating a new column that will have # values for both seperated by a '_', stored as a string. def add_legend(row): return str(str(row['machinename'])) # Run the function and also copy the other columns into new columns so that # when ther are renamed to 'x' and 'y' later they are still availible for # the legend if needed. df.loc[:, 'legend'] = df.apply(lambda row: add_legend(row), axis=1) df.loc[:, 'machinename1'] = df.loc[:, 'machinename'] print(df) ############################################################################ ############################################################################ ############################################################################ ################## FORMATTING AND CREATING A BASIC PLOT #################### ############################################################################ ############################# USER INPUTS ################################## # Decide what the default viewing option is going to be. (i.e. the fields to # be plotted on the x and y axis when the graph is opened, the plot size # etc.). # From the legend defined above give the values that will be pre-ticked when # the plot is opened color_to_plot = ['TrueBeam B', 'TrueBeam C'] marker_to_plot = color_to_plot # Decide on what data to plot on the x/y axis when opened. x_data1 = 'adate' y_data1 = '6fwhm' # Decide what the plot formatting will be, inluding the plot title, axis # titles and the size of the plot. plot_title1 = 'Electron Energy' x_axis_title1 = x_data1 y_axis_title1 = y_data1 plot_size_height1 = 450 plot_size_width1 = 800 legend_location = 'bottom_left' # Create a list of the plot parameters that will be used as input to a # function later. list_plot_parameters = [ x_data1, y_data1, plot_title1, x_axis_title1, y_axis_title1, plot_size_height1, plot_size_width1, legend_location ] ############################################################################ ############################################################################ ############################################################################ ########################### CREATE THE PLOT ################################ # Create the actual ploy. Generally it's a good idea to do this by defining # functions as they can then be used in the callbacks later without having # a lot of redundant very similar code. ######### Make Dataset: # Define a make dataset function that can be used now but also called later # in the callback functions to save re-writing similar code later. def make_dataset(color_to_plot, marker_to_plot, x_data1, y_data1): # Create a sub dataframe Sub_df1 = df.copy() # Delete any rows in the sub-dataframes that do not exist in the # checkboxes/default user choices. (e.g. if you've selected 6MV in the # checkbox this will remove any rows that have something other than 6MV) Sub_df1 = Sub_df1[Sub_df1['machinename'].isin(color_to_plot)] Sub_df1 = Sub_df1[Sub_df1['machinename'].isin(marker_to_plot)] # Search for the columns with the x_data and y_data names and replace # them with 'x' and 'y'. Unless plotting the same data on both in which # case add an extra column for 'y' that's a copy of 'x' if x_data1 == y_data1: Sub_df1.rename(columns={x_data1: 'x'}, inplace=True) Sub_df1.loc[:, 'y'] = Sub_df1.loc[:, 'x'] else: Sub_df1.rename(columns={x_data1: 'x'}, inplace=True) Sub_df1.rename(columns={y_data1: 'y'}, inplace=True) # Return the newly created Sub_df1 return Sub_df1 # Run the make_dataset function to create a sub dataframe that the plot will # be made from. Sub_df1 = make_dataset(color_to_plot, marker_to_plot, x_data1, y_data1) # Create a Column Data Source. This is important as it is the data format # needed for Bokeh. When making this it is useful to convert the dataframe # into a dictionary, which seems to help with the callback function (see # 'Common Issues' for details). src1 = ColumnDataSource(Sub_df1.to_dict(orient='list')) ######### Make Plot: # Create an empty plot (plot parameters will be applied later in a way that # can be manipulated in the callbacks) p1 = figure() # Create a scatter plot. p1.scatter( # source = The ColumnDataSource made above. source=src1, # x/y = 'x'/'y' which are fields that were renamed as such in # the make_dataset function x='x', y='y', # Some general parameters about marker size. These seem like # reasonable values but possible could alter these in a # callback? fill_alpha=0.4, size=12, # Create the legend using the created fields added in the legend # section. Use the factor_mark and factor_cmap functions to # match the colors/markers to the right lists. # NB: Always use legend_field for this not legend_group as the # former acts on the javascript side but the latter the Python # side. Therefore the former will update automatically when the # plot is changed with no need for a callback. legend_field='legend', marker=factor_mark('machinename1', markers, list_formarker), color=factor_cmap('machinename1', color_palette, list_forcolor)) ######### Add plot parameters: # Define a define plot parameters factor that can be used now but also # called later in the callback functions. def define_plot_parameters(list): # Input is a List of format: # list_plot_parameters = [ x_data1, y_data1, # plot_title1, x_axis_title1, y_axis_title1, # plot_size_height1, plot_size_width1, # legend_location ] # The parameters have to be controlled like this in a callback to allow # for them to be adjusted. Otherwise the plot parameters are not # interactive. # Yes! - p1.xaxis.axis_label = 'X_axis_title' # No! - p1 = figure(x_axis_label = 'X_axis_title') p1.title.text = list[2] p1.xaxis.axis_label = list[3] p1.yaxis.axis_label = list[4] p1.plot_height = list[5] p1.plot_width = list[6] p1.legend.location = list[7] # If the user wants to plot an axis as datetime then the axis needs to # be reformatted. Will do this by checking if the x_data1/y_data1 is # =='adate'. # NB: This only works if 'adate' is used as the name for the date column # and also that this is the only date column. if list[0] == 'adate': p1.xaxis.formatter = DatetimeTickFormatter(days=['%d/%m', '%a%d']) else: p1.xaxis.formatter = BasicTickFormatter() if list[1] == 'adate': p1.yaxis.formatter = DatetimeTickFormatter(days=['%d/%m', '%a%d']) else: p1.yaxis.formatter = BasicTickFormatter() return # Run the define_plot_parameters function to format the plot. define_plot_parameters(list_plot_parameters) ############################################################################ ############################################################################ ############################################################################ ############################################################################ ############################################################################ ############################ ADD TOLERANCES ################################ # We defined the tolerances further up and now want to add the correct ones # to the plot (having created the plot above). Again this will be done with # functions and in a way that the functions can be used in the callbacks # later. # # NB: At the moment this is still a bit of a work in progress and shows the # way to add line tolerances. Another option might be to add colorblocks # using varea and/or varea_stack. # # NB: Also this funcion assumes that tolerances will all be against one # x_axis value (e.g. date). This is probably the majority of use cases but # probably relatively trivial to add further toleraces against other x_axis # data. # Create a function that will create a dataframe that can be used to supply # a plot of two tolerance lines. This will including 'appearing' and # 'disappearing' depending on whether tolerances are defined or not. def tolerances(x_data1, y_data1, Sub_df1, df_tol1): # Get a list of the column headers from the tolerance table defined # earlier. headers1 = df_tol1.columns.values.tolist() # Check if the xdata is what is in the df_tol1 as the x_axis (if not no # point plotting tolerances as all tolerances are vs this tolerance). if x_data1 != headers1[0]: # x_data1 doesn't match so going to output something that should # basically just not plot but also won't throw the viewing range. data = { 'x': [Sub_df1['x'].max(), Sub_df1['x'].max()], 'y_low': [Sub_df1['y'].max(), Sub_df1['y'].max()], 'y_high': [Sub_df1['y'].max(), Sub_df1['y'].max()] } Sub_df1_tol1 = pd.DataFrame(data) return Sub_df1_tol1 # Otherwise we have the right x_data1 so now just check if it's datetime # or not. if x_data1 == 'adate': # It has the format 'adate' so should be datetime. So find the max # min dates in the Sub_df1 and add a couple of weeks either side so # that it plots the full range (plus a little bit for visualisation # reasons). max_x = Sub_df1['x'].max() + pd.DateOffset(weeks=2) min_x = Sub_df1['x'].min() + pd.DateOffset(weeks=-2) else: # If it's not datetime then just add about 5% of the range to # either side to make the plot look nicer. # NB: This has not been checked extensively as most tolerances are # vs. time. max_x = Sub_df1['x'].max() min_x = Sub_df1['x'].min() range = max_x - min_x max_x = max_x + (range / 20) min_x = min_x - (range / 20) # Used the x part so now remove the element from the list. This will # help for the small case where x_data1 == ydata1. headers1.remove(x_data1) if y_data1 in headers1: # If y_data1 is in the list then loop through to find out where and # get the data from the tolerance dataframe. for x in headers1: if y_data1 == x: # When the loop has found where it is then can output a # dataframe of the form: # x = [far left of plot, far right of plot] # y_low = [low_tolerance, low_tolerance] # y_high = [high_tolerance, high_tolerance] data = { 'x': [min_x, max_x], 'y_low': [df_tol1[x][0], df_tol1[x][0]], 'y_high': [df_tol1[x][1], df_tol1[x][1]] } Sub_df1_tol1 = pd.DataFrame(data) else: # If y_data1 is not in the headers1 list then there are no # tolerances to plot so going to output something that should # basically just not plot but also won't throw the viewing range. data = { 'x': [Sub_df1['x'].max(), Sub_df1['x'].max()], 'y_low': [Sub_df1['y'].max(), Sub_df1['y'].max()], 'y_high': [Sub_df1['y'].max(), Sub_df1['y'].max()] } Sub_df1_tol1 = pd.DataFrame(data) return Sub_df1_tol1 return Sub_df1_tol1 def choose_tolerances(x_data1, y_data1, Sub_df1, color_to_plot): if any(item in color_to_plot for item in ['TrueBeam B', 'TrueBeam C', 'TrueBeam D', 'TrueBeam F']): # If this is true then will need to run the df_tol_TB tolerances Sub_df1_tol_TB = tolerances(x_data1, y_data1, Sub_df1, df_tol_TB) else: data = { 'x': [Sub_df1['x'].max(), Sub_df1['x'].max()], 'y_low': [Sub_df1['y'].max(), Sub_df1['y'].max()], 'y_high': [Sub_df1['y'].max(), Sub_df1['y'].max()] } Sub_df1_tol_TB = pd.DataFrame(data) if any(item in color_to_plot for item in ['Linac B', 'Linac C', 'Linac D', 'Linac E']): # If this is true then will need to run the df_tol_TB tolerances Sub_df1_tol_Classic = tolerances(x_data1, y_data1, Sub_df1, df_tol_Classic) else: data = { 'x': [Sub_df1['x'].max(), Sub_df1['x'].max()], 'y_low': [Sub_df1['y'].max(), Sub_df1['y'].max()], 'y_high': [Sub_df1['y'].max(), Sub_df1['y'].max()] } Sub_df1_tol_Classic = pd.DataFrame(data) return Sub_df1_tol_TB, Sub_df1_tol_Classic # Run the tolerances function to output the new dataframe Sub_df1_tol_TB, Sub_df1_tol_Classic = choose_tolerances( x_data1, y_data1, Sub_df1, color_to_plot) # Turn the dataframe into a new ColumnDataSource (again turning it into a # dictionary) src1_tol_TB = ColumnDataSource(Sub_df1_tol_TB.to_dict(orient='list')) src1_tol_Classic = ColumnDataSource( Sub_df1_tol_Classic.to_dict(orient='list')) # Add two lines to the plot using the new ColumnDataSource as the source, # one line for low tolerance and one line for high. p1.line(source=src1_tol_TB, x='x', y='y_low', color='firebrick') p1.line(source=src1_tol_TB, x='x', y='y_high', color='firebrick') p1.line(source=src1_tol_Classic, x='x', y='y_low', color='hotpink') p1.line(source=src1_tol_Classic, x='x', y='y_high', color='hotpink') ############################################################################ ############################################################################ ############################################################################ ################## ADD MORE COMPLEX TOOLS TO THE PLOT ###################### # Create tools here that will allow for some manipulation or inspection of # plotted data. # # As an example a 'HoverTool' will be added to the plot. # # Other useful tools and details of the syntax can be found here: # https://docs.bokeh.org/en/latest/docs/user_guide/tools.html # Create the hover tool (see website above for syntax/details). # This example creates a hover tool that displays: # Date: The value of the data-point as measued on the x-axis # (formatted for datetime) # Y-Axis: The value of the data-point as measued on the y-axis # (x,y): The x and y co-ordinates in plot space # Chamber Comb.: The data stored under the 'Chamber' column for that # data-point. # Comments: The data stored under the 'comments' column for that # data-point. hover = HoverTool(tooltips=[('Date', '@x{%F}'), ('Y-Axis', '@y'), ('(x,y)', '($x, $y)'), ('Chamber Comb.', '@Chamber'), ('Comments', '@comments')], formatters={'x': 'datetime'}) # Add the newly created tool to the plot. p1.add_tools(hover) ############################################################################ ############################################################################ ############################################################################ ################# CREATE WIDGETS TO BE ADDED TO THE PLOT ################### # Create widgets here that will allow for some manipulation of the plotted # data. These widgets provide an interactive ability that can alter the data # that is plotted, provide update fnctions and call other programmatic # functions. This is done either using built in Bokeh functionality or # using more powerful but complex python and javascript based callbacks. # # As an example some 'Select' widgets, 'Checkbox' widgets and 'RangeSliders' # will be added to the plot. # # Other useful widgets and details of the syntax can be found here: # https://docs.bokeh.org/en/latest/docs/user_guide/interaction/widgets.html ######## 1) # Create the select widget (see website above for syntax/details). This # widget will be used for the callback example later to change data plotted # on the x/y-axis. # This example creates a select tool that displays: # Dropdown list containing a list of every field that was downloaded from # the database. # NB: When making a list it may be worth manually creating it to limit # it to the fields that can be plotted (e.g. not including fields # like 'Comments'). This will shorten the dropdown list but you # should err on the side of inclusion to make the final plot as # flexible as possible. # # Create a list of the availible options menu_axis = [] for field in TableFields: menu_axis.append(field) menu_axis = sorted(menu_axis) # Select tool needs inputs for the title, a starting value and the just # created list to supply the available options. select_xaxis = Select(title='X-Axis Fields Available:', value=x_axis_title1, options=menu_axis) select_yaxis = Select(title='Y-Axis Fields Available:', value=y_axis_title1, options=menu_axis) ######## 2) # This select widget will be made in the same way and used to create a # dropdown list to change the legend position. # # Create a list of the availible options menu_legend = [ 'top_left', 'top_center', 'top_right', 'center_left', 'center', 'center_right', 'bottom_left', 'bottom_center', 'bottom_right' ] # Create the select tool as above select_legend = Select(title='Legend Position', value=legend_location, options=menu_legend) ######## 3) # These checkbox widgets will be used to create a tool to select the # values that are being plotted from the fields that the legend is based on. # # NB: There is some built in Bokeh functionality for interavtive legends # that can fulfill some of the same goals where the number of options is # limited to something that can display on a reasonably sized legend. May # be a better and more robust solution where possible. # Create a list of all unique names in the column chosen to be matched to # markers (sorted). options_marker = sorted(df['machinename'].unique().tolist()) # Create an index list for all of the values that should be pre-ticked. index_marker = [ i for i in range(len(options_marker)) if options_marker[i] in marker_to_plot ] # Create the checkbox, providing the list of availible options and a list # of what should be active (pre-ticked). checkbox_marker = CheckboxGroup(labels=options_marker, active=index_marker, visible=False) # Do the same for the column that was matched to colors. options_color = sorted(df['machinename'].unique().tolist()) index_color = [ i for i in range(len(options_color)) if options_color[i] in color_to_plot ] checkbox_color = CheckboxGroup(labels=options_color, active=index_color) ######## 4) # Make some range sliders that will be used to manipulate the x-axis and # y-axis range. # Most of the manipulation will be done using a later function but will need # to create the bare minimum rangeslider first that can later be manipulated # (This seems to be the minimum number of parameters needed to create these # widgets). Note that a RangeSliders AND a DateRangeSlider needs to be # created for each axis. range_slider_x = RangeSlider(title='X-Axis Range', start=0, end=1, value=(0, 1), step=0.1) range_slider_y = RangeSlider(title='Y-Axis Range', start=0, end=1, value=(0, 1), step=0.1) range_slider_xdate = DateRangeSlider(title='X-Axis Range (Date)', start=date(2017, 1, 1), end=date(2017, 1, 2), value=(date(2017, 1, 1), date(2017, 1, 2)), step=1) range_slider_ydate = DateRangeSlider(title='Y-Axis Range (Date)', start=date(2017, 1, 1), end=date(2017, 1, 2), value=(date(2017, 1, 1), date(2017, 1, 2)), step=1) # Define the function that will be used now and also in the callbacks later. # This will allow the range_sliders to adjust to match any changes in the # data being plotted on the x/y axis. def range_slider(x_data1, y_data1, Sub_df1): # Start with the y-axis. # First need to check if 'adate' and if so edit the date range slider # but otherwise edit the normal slider. if y_data1 == 'adate': # Set the start, end and value fields to the full range. range_slider_ydate.start = Sub_df1['y'].min() range_slider_ydate.end = Sub_df1['y'].max() range_slider_ydate.value = (Sub_df1['y'].min(), Sub_df1['y'].max()) # Step to 1 works for DateRangeSlider range_slider_ydate.step = 1 # Make the DateRangeSlider visible and hide the normal RangeSlider range_slider_ydate.visible = True range_slider_y.visible = False else: # Set the start, end and value fields to the full range. range_slider_y.start = Sub_df1['y'].min() range_slider_y.end = Sub_df1['y'].max() range_slider_y.value = (Sub_df1['y'].min(), Sub_df1['y'].max()) # Step to range/10000 should give sufficient granularity range_slider_y.step = (Sub_df1['y'].max() - Sub_df1['y'].min()) / 100000 # Make the normal RangeSlider visible and hide the DateRangeSlider range_slider_y.visible = True range_slider_ydate.visible = False # Do the same for the x-axis if x_data1 == 'adate': range_slider_xdate.start = Sub_df1['x'].min() range_slider_xdate.end = Sub_df1['x'].max() range_slider_xdate.value = (Sub_df1['x'].min(), Sub_df1['x'].max()) range_slider_xdate.step = 1 range_slider_xdate.visible = True range_slider_x.visible = False else: range_slider_x.start = Sub_df1['x'].min() range_slider_x.end = Sub_df1['x'].max() range_slider_x.value = (Sub_df1['x'].min(), Sub_df1['x'].max()) range_slider_x.step = (Sub_df1['x'].max() - Sub_df1['x'].min()) / 100000 range_slider_x.visible = True range_slider_xdate.visible = False return # Run the function. range_slider(x_data1, y_data1, Sub_df1) ############################################################################ ############################################################################ ############################################################################ ########################### CREATE A LAYOUT ################################ # Create a layout to add widgets and arrange the display. This simple layout # displays the select widgets above the plot with the checkboxes to the # right (one above the other). # # More details can be found at: # https://docs.bokeh.org/en/latest/docs/user_guide/layout.html # # NB: More work to do here to make plots responsive to browser window size # (e.g. using sizing_mode = scale_both) but need to invstigate with/without # remote desktops. layout_checkbox = column([checkbox_marker, checkbox_color]) layout_plots = column([ select_xaxis, select_yaxis, select_legend, range_slider_x, range_slider_y, range_slider_xdate, range_slider_ydate, p1 ]) tab_layout = row([layout_plots, layout_checkbox]) ############################################################################ ############################################################################ ############################################################################ ####################### CREATE CALLBACK FUNCTIONS ########################## # CAVEAT: Callback functions are very complex and below is my (CB) rough # understanding of how they function based mainly on experience/trial and # error while writting these functions for other graphs. It should be taken # as a starting point but not as a definitive user guide. # # Callback functions are very powerful and can be based off of javascript or # python. The example presented here uses python but in future a javascript # copy should also be added. ######## 1) # This callback is designed to take inputs from the select and checkbox # widgets update the graph to plot the new data requested by the user. # # Syntax: # attr = The value passed from the on_change function before the callback # was named (e.g. in this example attr = 'value') # old = The value of the widget before it was changed (I.e. If a select # widget is changed from 'Output' to 'T/P Correction', then # old = 'Output' # new = The value of the widget after it was changed (I.e. If a select # widget is changed from 'Output' to 'T/P Correction', then # old = 'T/P Correction' # # NB: In general seen little need to use these inputs as you can generally # access the value of the widgets directly which seems to be more powerful # and flexible # # First define the callback function. def callback(attr, old, new): # Want to acquire the current values of all of the checkboxes and select # widgets to provide as inputs for the re-plot. For the checkboxes this # means itterating through the active list and outputting the labels # that are active color_to_plot = [ checkbox_color.labels[i] for i in checkbox_color.active ] marker_to_plot = color_to_plot plot1_xdata_to_plot = select_xaxis.value plot1_ydata_to_plot = select_yaxis.value legend_location = select_legend.value # Use the pre-defined make_dataset function with these new inputs to # create a new version of the sub dataframe. Sub_df1 = make_dataset(color_to_plot, marker_to_plot, plot1_xdata_to_plot, plot1_ydata_to_plot) # Use the pre-defined define_plot_parameters function with these new # inputs to update the plot parameters. x_axis_title1 = plot1_xdata_to_plot y_axis_title1 = plot1_ydata_to_plot define_plot_parameters([ plot1_xdata_to_plot, plot1_ydata_to_plot, plot_title1, x_axis_title1, y_axis_title1, plot_size_height1, plot_size_width1, legend_location ]) # Use the pre-defined tolerances function with these new inputs to # make a new version of the tolerances sub dataframe. Sub_df1_tol_TB, Sub_df1_tol_Classic = choose_tolerances( plot1_xdata_to_plot, plot1_ydata_to_plot, Sub_df1, color_to_plot) # Use the pre-defined range_slider function with these new inputs to # update the range sliders (this will make sure that the range sliders # start/end etc. match up with what's being plotted, as well as # displaying/hiding the RangeSlider/DateRangeSlider as needed range_slider(plot1_xdata_to_plot, plot1_ydata_to_plot, Sub_df1) # Update the ColumnDataSources using the newly created dataframes. The # plots look to these as the source so this changes what is being # plotted. src1.data = Sub_df1.to_dict(orient='list') src1_tol_TB.data = Sub_df1_tol_TB.to_dict(orient='list') src1_tol_Classic.data = Sub_df1_tol_Classic.to_dict(orient='list') return # Use the on_change function to call the now defined callback function # whenever the user changes the value in the widget. # NB: Other functions such as on_click are availible for other widgets. # Syntax: # First argument is passed to the callback as attr (see callback section # above) # Second argument is the name of the callback function to be called. select_xaxis.on_change('value', callback) select_yaxis.on_change('value', callback) select_legend.on_change('value', callback) checkbox_color.on_change('active', callback) checkbox_marker.on_change('active', callback) ######## 2) # This callback is designed to take inputs from the range sliders to change # visible range def callback_range(attr, old, new): # Check what is currently being plotted. Need this to know whether to # look for the values from the DateRangeSlider or the RangeSlider plot1_xdata_to_plot = select_xaxis.value plot1_ydata_to_plot = select_yaxis.value # Start with the x-axis if plot1_xdata_to_plot == 'adate': # If it's 'adate' then need to look at the DateRangeSlider and # update the start and end values of the range using the values from # the slider. # NB: range_slider.value = left_value, right_value p1.x_range.start, p1.x_range.end = range_slider_xdate.value else: # If it's not 'adate' then need to look at the normal RangeSlider p1.x_range.start, p1.x_range.end = range_slider_x.value # Do the same for the y-axis if plot1_ydata_to_plot == 'adate': p1.y_range.start, p1.y_range.end = range_slider_ydate.value else: p1.y_range.start, p1.y_range.end = range_slider_y.value return # Use the on_change function to call the now defined callback function # whenever the user changes the value in the widget. range_slider_x.on_change('value', callback_range) range_slider_y.on_change('value', callback_range) range_slider_xdate.on_change('value', callback_range) range_slider_ydate.on_change('value', callback_range) ############################################################################ ############################################################################ ############################################################################ ####################### RETURN TO THE MAIN SCRIPT ########################## # Now that the script is finished and the plot created we can return to the # main script. # # To pass back the data for the tab we need to return a Panel with: # child = layout (the one that we made earlier with the widget and plot) # title = 'Something that makes sense as a tab label for the user' return Panel(child=tab_layout, title='Electron Energy')
markers = ['hex', 'cross', 'triangle'] scatter_plot_subgroups = figure(plot_width=600, plot_height=400, title ='Iris', x_axis_label='petalLength', y_axis_label='petalWidth') scatter_plot_subgroups.scatter(x='petalLength', y='petalWidth', source=iris, legend='species', fill_alpha=0.5, size=15, color=factor_cmap(field_name='species', palette='Dark2_3', factors=species), marker=factor_mark('species', markers, species) ) # move legend scatter_plot_subgroups.legend.location = 'top_left' show(scatter_plot_subgroups) In [73]: # scatter_plot_subgroups.scatter? Subplots In [74]: from bokeh.layouts import gridplot output_notebook() # data subplot_x1 = cars['Acceleration']; subplot_y1 = cars['Miles_per_Gallon']
def make_summary_plot(source, table_source, pars_dict): tools = "pan,wheel_zoom,box_zoom,box_select,tap,hover,reset,crosshair" pars = [ 'M1_init', 'M2_init', 'P_init', 'q_init', 'product', 'stability', 'termination_code' ] basic_tooltip = [(p, '@' + p) for p in pars] PRODUCTS = ['HB', 'He-WD', 'CE', 'UK', 'failed', 'sdO', 'sdB', 'sdA'] + \ ['stable', 'CE', 'contact', 'merger'] +\ ['single-lined', 'composite'] MARKERS = ['square', 'triangle', 'asterisk', 'asterisk', 'diamond', 'circle', 'circle', 'circle'] + \ ['circle', 'diamond', 'square', 'triangle', ] +\ ['circle', 'circle'] COLORS = ['red', 'green', 'purple', 'purple', 'gray', 'green', 'blue', 'orange'] + \ ['red', 'green', 'blue', 'gray'] +\ ['gray', 'blue'] SIZES = [7, 7, 7, 7, 15, 7] v_func = """ const norm = new Float64Array(xs.length) for (let i = 0; i < xs.length; i++) { if (xs[i] == 'sdB' || xs[i] == 'Fail' || xs[i] == 'CE') { norm[i] = 15 } else { norm[i] = 7 } } return norm """ size_transform = mpl.CustomJSTransform(v_func=v_func) # Left Figure p1 = figure(x_axis_label=pars_dict['x1'], y_axis_label=pars_dict['y1'], active_drag='box_select', tools=tools, tooltips=basic_tooltip) p1.scatter( x="x1", y="y1", source=source, fill_alpha=0.4, size=transform('z1', size_transform), color=factor_cmap('z1', COLORS, PRODUCTS), marker=factor_mark('z1', MARKERS, PRODUCTS), ) # legend_group="z1", # Right Figure p2 = figure(x_axis_label=pars_dict['x2'], y_axis_label=pars_dict['y2'], active_drag='box_select', tools=tools, tooltips=basic_tooltip) p2.scatter( x="x2", y="y2", source=source, fill_alpha=0.4, size=transform('z2', size_transform), color=factor_cmap('z2', COLORS, PRODUCTS), marker=factor_mark('z2', MARKERS, PRODUCTS), ) # legend_group="z2", # color_bar2 = mpl.ColorBar(color_mapper=color_mapper, location=(0,0), title=pars_dict['color2'], title_text_font_size='12pt') # p2.add_layout(color_bar2, 'right') plot = gridplot([[p1, p2]]) # add interaction when selecting a model callback = CustomJS(args=dict(summary_source=source, table_source=table_source), code=""" selected_indices = summary_source.selected.indices; console.log(summary_source.selected.indices[0]); console.log(summary_source); if (summary_source.selected.indices.length > 0){ var data = summary_source.data; var ind = summary_source.selected.indices[0]; var parameters = table_source.data['parameters'] var values = table_source.data['values'] parameters.forEach(function (par, index) { values[index] = data[par][ind]; }); //table_source.data['parameters'] = x; table_source.data['values'] = values; table_source.change.emit(); } """) p1.js_on_event('tap', callback) p2.js_on_event('tap', callback) return plot, p1, p2
y_axis_type="log", plot_height=400, plot_width=700, tools=[ HoverTool(tooltips=[( 'Country', '@country'), ('Confirmed Cases', '@total_cases'), ('Date', '@date_formatted')]) ]) p.scatter("date", "total_cases", source=source, fill_alpha=0.4, size=6, marker=factor_mark('country', MARKERS, COUNTRIES), color=factor_cmap('country', 'Category10_5', COUNTRIES)) output_file("COVID19_1.html", title="Total Number of Confirmed COVID-19 Cases") show(p) # %% #Plotting log-scale number of cases for the selected countries. Hover-tool and series-hiding on. from bokeh.plotting import figure, show, output_file from bokeh.models import ColumnDataSource, CDSView, GroupFilter, HoverTool source = ColumnDataSource(covid) source.add(covid['date'].apply(lambda d: d.strftime('%Y-%m-%d')), 'date_formatted')
x = 'x', y = 'y', # Some general parameters about marker size. These seem like # reasonable values but possible could alter these in a # callback? fill_alpha = 0.4, size = 12, # Create the legend using the created fields added in the legend # section. Use the factor_mark and factor_cmap functions to # match the colors/markers to the right lists. # NB: Always use legend_field for this not legend_group as the # former acts on the javascript side but the latter the Python # side. Therefore the former will update automatically when the # plot is changed with no need for a callback. legend_field = 'legend', marker = factor_mark('marker1', marker_palette, marker_list), color = factor_cmap('color1', color_palette, color_list) ) ######### Add plot parameters: # Run the Define_Plot_Parameters function to format the plot. Takes the plot # and the pre-defined list of default plot parameters as inputs. Define_Plot_Parameters(p1, list_plot_parameters) ############################################################################ ############################################################################
FATE = ['Died', 'Lived'] MARKERS = ['cross', 'circle'] # Set the Title a and size of plot p = figure(plot_height=600, plot_width=971, title="Titanic Passenger Age & Fare by Survial Type") # Construnct the colours p.scatter("Fare", "Age", source=titanic_df, legend="Survived", fill_alpha=0.3, size=12, marker=factor_mark('Survived', MARKERS, FATE), color=factor_cmap('Survived', ['#3a6587', '#aeb3b7'], FATE)) # Set axis labels p.xaxis.axis_label = 'Fare (In Pounds)' p.yaxis.axis_label = 'Age (In Years)' # Remove the Grid lines p.xgrid.grid_line_color = None p.ygrid.grid_line_color = None # change just some things about the x-axis p.xaxis.axis_line_width = 2 p.xaxis.major_label_text_color = "black" p.xaxis.axis_line_color = "#aeb3b7"
from bokeh.plotting import figure, show, output_file from bokeh.sampledata.iris import flowers from bokeh.transform import factor_cmap, factor_mark SPECIES = ['setosa', 'versicolor', 'virginica'] MARKERS = ['hex', 'circle_x', 'triangle'] p = figure(title = "Iris Morphology", background_fill_color="#fafafa") p.xaxis.axis_label = 'Petal Length' p.yaxis.axis_label = 'Sepal Width' p.scatter("petal_length", "sepal_width", source=flowers, legend="species", fill_alpha=0.4, size=12, marker=factor_mark('species', MARKERS, SPECIES), color=factor_cmap('species', 'Category10_3', SPECIES)) output_file("marker_map.html") show(p)
def photometry_plot(obj_id, user, width=600, device="browser"): """Create object photometry scatter plot. Parameters ---------- obj_id : str ID of Obj to be plotted. Returns ------- dict Returns Bokeh JSON embedding for the desired plot. """ data = pd.read_sql( DBSession() .query( Photometry, Telescope.nickname.label("telescope"), Instrument.name.label("instrument"), ) .join(Instrument, Instrument.id == Photometry.instrument_id) .join(Telescope, Telescope.id == Instrument.telescope_id) .filter(Photometry.obj_id == obj_id) .filter( Photometry.groups.any(Group.id.in_([g.id for g in user.accessible_groups])) ) .statement, DBSession().bind, ) if data.empty: return None, None, None # get spectra to annotate on phot plots spectra = ( Spectrum.query_records_accessible_by(user) .filter(Spectrum.obj_id == obj_id) .all() ) data['color'] = [get_color(f) for f in data['filter']] # get marker for each unique instrument instruments = list(data.instrument.unique()) markers = [] for i, inst in enumerate(instruments): markers.append(phot_markers[i % len(phot_markers)]) filters = list(set(data['filter'])) colors = [get_color(f) for f in filters] color_mapper = CategoricalColorMapper(factors=filters, palette=colors) color_dict = {'field': 'filter', 'transform': color_mapper} labels = [] for i, datarow in data.iterrows(): label = f'{datarow["instrument"]}/{datarow["filter"]}' if datarow['origin'] is not None: label += f'/{datarow["origin"]}' labels.append(label) data['label'] = labels data['zp'] = PHOT_ZP data['magsys'] = 'ab' data['alpha'] = 1.0 data['lim_mag'] = ( -2.5 * np.log10(data['fluxerr'] * PHOT_DETECTION_THRESHOLD) + data['zp'] ) # Passing a dictionary to a bokeh datasource causes the frontend to die, # deleting the dictionary column fixes that del data['original_user_data'] # keep track of things that are only upper limits data['hasflux'] = ~data['flux'].isna() # calculate the magnitudes - a photometry point is considered "significant" # or "detected" (and thus can be represented by a magnitude) if its snr # is above PHOT_DETECTION_THRESHOLD obsind = data['hasflux'] & ( data['flux'].fillna(0.0) / data['fluxerr'] >= PHOT_DETECTION_THRESHOLD ) data.loc[~obsind, 'mag'] = None data.loc[obsind, 'mag'] = -2.5 * np.log10(data[obsind]['flux']) + PHOT_ZP # calculate the magnitude errors using standard error propagation formulae # https://en.wikipedia.org/wiki/Propagation_of_uncertainty#Example_formulae data.loc[~obsind, 'magerr'] = None coeff = 2.5 / np.log(10) magerrs = np.abs(coeff * data[obsind]['fluxerr'] / data[obsind]['flux']) data.loc[obsind, 'magerr'] = magerrs data['obs'] = obsind data['stacked'] = False split = data.groupby('label', sort=False) finite = np.isfinite(data['flux']) fdata = data[finite] lower = np.min(fdata['flux']) * 0.95 upper = np.max(fdata['flux']) * 1.05 xmin = data['mjd'].min() - 2 xmax = data['mjd'].max() + 2 # Layout parameters based on device type active_drag = None if "mobile" in device or "tablet" in device else "box_zoom" tools = ( 'box_zoom,pan,reset' if "mobile" in device or "tablet" in device else "box_zoom,wheel_zoom,pan,reset,save" ) legend_loc = "below" if "mobile" in device or "tablet" in device else "right" legend_orientation = ( "vertical" if device in ["browser", "mobile_portrait"] else "horizontal" ) # Compute a plot component height based on rough number of legend rows added below the plot # Values are based on default sizing of bokeh components and an estimate of how many # legend items would fit on the average device screen. Note that the legend items per # row is computed more exactly later once labels are extracted from the data (with the # add_plot_legend() function). # # The height is manually computed like this instead of using built in aspect_ratio/sizing options # because with the new Interactive Legend approach (instead of the legacy CheckboxLegendGroup), the # Legend component is considered part of the plot and plays into the sizing computations. Since the # number of items in the legend can alter the needed heights of the plot, using built-in Bokeh options # for sizing does not allow for keeping the actual graph part of the plot at a consistent aspect ratio. # # For the frame width, by default we take the desired plot width minus 64 for the y-axis/label taking # up horizontal space frame_width = width - 64 if device == "mobile_portrait": legend_items_per_row = 1 legend_row_height = 24 aspect_ratio = 1 elif device == "mobile_landscape": legend_items_per_row = 4 legend_row_height = 50 aspect_ratio = 1.8 elif device == "tablet_portrait": legend_items_per_row = 5 legend_row_height = 50 aspect_ratio = 1.5 elif device == "tablet_landscape": legend_items_per_row = 7 legend_row_height = 50 aspect_ratio = 1.8 elif device == "browser": # Width minus some base width for the legend, which is only a column to the right # for browser mode frame_width = width - 200 height = ( 500 if device == "browser" else math.floor(width / aspect_ratio) + legend_row_height * int(len(split) / legend_items_per_row) + 30 # 30 is the height of the toolbar ) plot = figure( frame_width=frame_width, height=height, active_drag=active_drag, tools=tools, toolbar_location='above', toolbar_sticky=True, y_range=(lower, upper), min_border_right=16, x_axis_location='above', sizing_mode="stretch_width", ) plot.xaxis.axis_label = 'MJD' now = Time.now().mjd plot.extra_x_ranges = {"Days Ago": Range1d(start=now - xmin, end=now - xmax)} plot.add_layout(LinearAxis(x_range_name="Days Ago", axis_label="Days Ago"), 'below') imhover = HoverTool(tooltips=tooltip_format) imhover.renderers = [] plot.add_tools(imhover) model_dict = {} legend_items = [] for i, (label, sdf) in enumerate(split): renderers = [] # for the flux plot, we only show things that have a flux value df = sdf[sdf['hasflux']] key = f'obs{i}' model_dict[key] = plot.scatter( x='mjd', y='flux', color='color', marker=factor_mark('instrument', markers, instruments), fill_color=color_dict, alpha='alpha', source=ColumnDataSource(df), ) renderers.append(model_dict[key]) imhover.renderers.append(model_dict[key]) key = f'bin{i}' model_dict[key] = plot.scatter( x='mjd', y='flux', color='color', marker=factor_mark('instrument', markers, instruments), fill_color=color_dict, source=ColumnDataSource( data=dict( mjd=[], flux=[], fluxerr=[], filter=[], color=[], lim_mag=[], mag=[], magerr=[], stacked=[], instrument=[], ) ), ) renderers.append(model_dict[key]) imhover.renderers.append(model_dict[key]) key = 'obserr' + str(i) y_err_x = [] y_err_y = [] for d, ro in df.iterrows(): px = ro['mjd'] py = ro['flux'] err = ro['fluxerr'] y_err_x.append((px, px)) y_err_y.append((py - err, py + err)) model_dict[key] = plot.multi_line( xs='xs', ys='ys', color='color', alpha='alpha', source=ColumnDataSource( data=dict( xs=y_err_x, ys=y_err_y, color=df['color'], alpha=[1.0] * len(df) ) ), ) renderers.append(model_dict[key]) key = f'binerr{i}' model_dict[key] = plot.multi_line( xs='xs', ys='ys', color='color', # legend_label=label, source=ColumnDataSource(data=dict(xs=[], ys=[], color=[])), ) renderers.append(model_dict[key]) legend_items.append(LegendItem(label=label, renderers=renderers)) if device == "mobile_portrait": plot.xaxis.ticker.desired_num_ticks = 5 plot.yaxis.axis_label = 'Flux (μJy)' plot.toolbar.logo = None add_plot_legend(plot, legend_items, width, legend_orientation, legend_loc) slider = Slider( start=0.0, end=15.0, value=0.0, step=1.0, title='Binsize (days)', max_width=350, margin=(4, 10, 0, 10), ) callback = CustomJS( args={'slider': slider, 'n_labels': len(split), **model_dict}, code=open( os.path.join(os.path.dirname(__file__), '../static/js/plotjs', 'stackf.js') ) .read() .replace('default_zp', str(PHOT_ZP)) .replace('detect_thresh', str(PHOT_DETECTION_THRESHOLD)), ) slider.js_on_change('value', callback) # Mark the first and last detections detection_dates = data[data['hasflux']]['mjd'] if len(detection_dates) > 0: first = round(detection_dates.min(), 6) last = round(detection_dates.max(), 6) first_color = "#34b4eb" last_color = "#8992f5" midpoint = (upper + lower) / 2 line_top = 5 * upper - 4 * midpoint line_bottom = 5 * lower - 4 * midpoint y = np.linspace(line_bottom, line_top, num=5000) first_r = plot.line( x=np.full(5000, first), y=y, line_alpha=0.5, line_color=first_color, line_width=2, ) plot.add_tools( HoverTool( tooltips=[("First detection", f'{first}')], renderers=[first_r], ) ) last_r = plot.line( x=np.full(5000, last), y=y, line_alpha=0.5, line_color=last_color, line_width=2, ) plot.add_tools( HoverTool( tooltips=[("Last detection", f'{last}')], renderers=[last_r], ) ) # Mark when spectra were taken annotate_spec(plot, spectra, lower, upper) layout = column(slider, plot, width=width, height=height) p1 = Panel(child=layout, title='Flux') # now make the mag light curve ymax = ( np.nanmax( ( np.nanmax(data.loc[obsind, 'mag']) if any(obsind) else np.nan, np.nanmax(data.loc[~obsind, 'lim_mag']) if any(~obsind) else np.nan, ) ) + 0.1 ) ymin = ( np.nanmin( ( np.nanmin(data.loc[obsind, 'mag']) if any(obsind) else np.nan, np.nanmin(data.loc[~obsind, 'lim_mag']) if any(~obsind) else np.nan, ) ) - 0.1 ) plot = figure( frame_width=frame_width, height=height, active_drag=active_drag, tools=tools, y_range=(ymax, ymin), x_range=(xmin, xmax), toolbar_location='above', toolbar_sticky=True, x_axis_location='above', sizing_mode="stretch_width", ) plot.xaxis.axis_label = 'MJD' now = Time.now().mjd plot.extra_x_ranges = {"Days Ago": Range1d(start=now - xmin, end=now - xmax)} plot.add_layout(LinearAxis(x_range_name="Days Ago", axis_label="Days Ago"), 'below') obj = DBSession().query(Obj).get(obj_id) if obj.dm is not None: plot.extra_y_ranges = { "Absolute Mag": Range1d(start=ymax - obj.dm, end=ymin - obj.dm) } plot.add_layout( LinearAxis(y_range_name="Absolute Mag", axis_label="m - DM"), 'right' ) # Mark the first and last detections again detection_dates = data[obsind]['mjd'] if len(detection_dates) > 0: first = round(detection_dates.min(), 6) last = round(detection_dates.max(), 6) midpoint = (ymax + ymin) / 2 line_top = 5 * ymax - 4 * midpoint line_bottom = 5 * ymin - 4 * midpoint y = np.linspace(line_bottom, line_top, num=5000) first_r = plot.line( x=np.full(5000, first), y=y, line_alpha=0.5, line_color=first_color, line_width=2, ) plot.add_tools( HoverTool( tooltips=[("First detection", f'{first}')], renderers=[first_r], ) ) last_r = plot.line( x=np.full(5000, last), y=y, line_alpha=0.5, line_color=last_color, line_width=2, ) plot.add_tools( HoverTool( tooltips=[("Last detection", f'{last}')], renderers=[last_r], point_policy='follow_mouse', ) ) # Mark when spectra were taken annotate_spec(plot, spectra, ymax, ymin) imhover = HoverTool(tooltips=tooltip_format) imhover.renderers = [] plot.add_tools(imhover) model_dict = {} # Legend items are individually stored instead of being applied # directly when plotting so that they can be separated into multiple # Legend() components if needed (to simulate horizontal row wrapping). # This is necessary because Bokeh does not support row wrapping with # horizontally-oriented legends out-of-the-box. legend_items = [] for i, (label, df) in enumerate(split): renderers = [] key = f'obs{i}' model_dict[key] = plot.scatter( x='mjd', y='mag', color='color', marker=factor_mark('instrument', markers, instruments), fill_color=color_dict, alpha='alpha', source=ColumnDataSource(df[df['obs']]), ) renderers.append(model_dict[key]) imhover.renderers.append(model_dict[key]) unobs_source = df[~df['obs']].copy() unobs_source.loc[:, 'alpha'] = 0.8 key = f'unobs{i}' model_dict[key] = plot.scatter( x='mjd', y='lim_mag', color=color_dict, marker='inverted_triangle', fill_color='white', line_color='color', alpha='alpha', source=ColumnDataSource(unobs_source), ) renderers.append(model_dict[key]) imhover.renderers.append(model_dict[key]) key = f'bin{i}' model_dict[key] = plot.scatter( x='mjd', y='mag', color=color_dict, marker=factor_mark('instrument', markers, instruments), fill_color='color', source=ColumnDataSource( data=dict( mjd=[], flux=[], fluxerr=[], filter=[], color=[], lim_mag=[], mag=[], magerr=[], instrument=[], stacked=[], ) ), ) renderers.append(model_dict[key]) imhover.renderers.append(model_dict[key]) key = 'obserr' + str(i) y_err_x = [] y_err_y = [] for d, ro in df[df['obs']].iterrows(): px = ro['mjd'] py = ro['mag'] err = ro['magerr'] y_err_x.append((px, px)) y_err_y.append((py - err, py + err)) model_dict[key] = plot.multi_line( xs='xs', ys='ys', color='color', alpha='alpha', source=ColumnDataSource( data=dict( xs=y_err_x, ys=y_err_y, color=df[df['obs']]['color'], alpha=[1.0] * len(df[df['obs']]), ) ), ) renderers.append(model_dict[key]) key = f'binerr{i}' model_dict[key] = plot.multi_line( xs='xs', ys='ys', color='color', source=ColumnDataSource(data=dict(xs=[], ys=[], color=[])), ) renderers.append(model_dict[key]) key = f'unobsbin{i}' model_dict[key] = plot.scatter( x='mjd', y='lim_mag', color='color', marker='inverted_triangle', fill_color='white', line_color=color_dict, alpha=0.8, source=ColumnDataSource( data=dict( mjd=[], flux=[], fluxerr=[], filter=[], color=[], lim_mag=[], mag=[], magerr=[], instrument=[], stacked=[], ) ), ) imhover.renderers.append(model_dict[key]) renderers.append(model_dict[key]) key = f'all{i}' model_dict[key] = ColumnDataSource(df) key = f'bold{i}' model_dict[key] = ColumnDataSource( df[ [ 'mjd', 'flux', 'fluxerr', 'mag', 'magerr', 'filter', 'zp', 'magsys', 'lim_mag', 'stacked', ] ] ) legend_items.append(LegendItem(label=label, renderers=renderers)) add_plot_legend(plot, legend_items, width, legend_orientation, legend_loc) plot.yaxis.axis_label = 'AB mag' plot.toolbar.logo = None slider = Slider( start=0.0, end=15.0, value=0.0, step=1.0, title='Binsize (days)', max_width=350, margin=(4, 10, 0, 10), ) button = Button(label="Export Bold Light Curve to CSV") button.js_on_click( CustomJS( args={'slider': slider, 'n_labels': len(split), **model_dict}, code=open( os.path.join( os.path.dirname(__file__), '../static/js/plotjs', "download.js" ) ) .read() .replace('objname', obj_id) .replace('default_zp', str(PHOT_ZP)), ) ) # Don't need to expose CSV download on mobile top_layout = ( slider if "mobile" in device or "tablet" in device else row(slider, button) ) callback = CustomJS( args={'slider': slider, 'n_labels': len(split), **model_dict}, code=open( os.path.join(os.path.dirname(__file__), '../static/js/plotjs', 'stackm.js') ) .read() .replace('default_zp', str(PHOT_ZP)) .replace('detect_thresh', str(PHOT_DETECTION_THRESHOLD)), ) slider.js_on_change('value', callback) layout = column(top_layout, plot, width=width, height=height) p2 = Panel(child=layout, title='Mag') # now make period plot # get periods from annotations annotation_list = obj.get_annotations_readable_by(user) period_labels = [] period_list = [] for an in annotation_list: if 'period' in an.data: period_list.append(an.data['period']) period_labels.append(an.origin + ": %.9f" % an.data['period']) if len(period_list) > 0: period = period_list[0] else: period = None # don't generate if no period annotated if period is not None: # bokeh figure for period plotting period_plot = figure( frame_width=frame_width, height=height, active_drag=active_drag, tools=tools, y_range=(ymax, ymin), x_range=(-0.01, 2.01), # initially one phase toolbar_location='above', toolbar_sticky=False, x_axis_location='below', sizing_mode="stretch_width", ) # axis labels period_plot.xaxis.axis_label = 'phase' period_plot.yaxis.axis_label = 'mag' period_plot.toolbar.logo = None # do we have a distance modulus (dm)? obj = DBSession().query(Obj).get(obj_id) if obj.dm is not None: period_plot.extra_y_ranges = { "Absolute Mag": Range1d(start=ymax - obj.dm, end=ymin - obj.dm) } period_plot.add_layout( LinearAxis(y_range_name="Absolute Mag", axis_label="m - DM"), 'right' ) # initiate hover tool period_imhover = HoverTool(tooltips=tooltip_format) period_imhover.renderers = [] period_plot.add_tools(period_imhover) # initiate period radio buttons period_selection = RadioGroup(labels=period_labels, active=0) phase_selection = RadioGroup(labels=["One phase", "Two phases"], active=1) # store all the plot data period_model_dict = {} # iterate over each filter legend_items = [] for i, (label, df) in enumerate(split): renderers = [] # fold x-axis on period in days df['mjd_folda'] = (df['mjd'] % period) / period df['mjd_foldb'] = df['mjd_folda'] + 1.0 # phase plotting for ph in ['a', 'b']: key = 'fold' + ph + f'{i}' period_model_dict[key] = period_plot.scatter( x='mjd_fold' + ph, y='mag', color='color', marker=factor_mark('instrument', markers, instruments), fill_color=color_dict, alpha='alpha', # visible=('a' in ph), source=ColumnDataSource(df[df['obs']]), # only visible data ) # add to hover tool period_imhover.renderers.append(period_model_dict[key]) renderers.append(period_model_dict[key]) # errorbars for phases key = 'fold' + ph + f'err{i}' y_err_x = [] y_err_y = [] # get each visible error value for d, ro in df[df['obs']].iterrows(): px = ro['mjd_fold' + ph] py = ro['mag'] err = ro['magerr'] # set up error tuples y_err_x.append((px, px)) y_err_y.append((py - err, py + err)) # plot phase errors period_model_dict[key] = period_plot.multi_line( xs='xs', ys='ys', color='color', alpha='alpha', # visible=('a' in ph), source=ColumnDataSource( data=dict( xs=y_err_x, ys=y_err_y, color=df[df['obs']]['color'], alpha=[1.0] * len(df[df['obs']]), ) ), ) renderers.append(period_model_dict[key]) legend_items.append(LegendItem(label=label, renderers=renderers)) add_plot_legend( period_plot, legend_items, width, legend_orientation, legend_loc ) # set up period adjustment text box period_title = Div(text="Period (days): ") period_textinput = TextInput(value=str(period if period is not None else 0.0)) period_textinput.js_on_change( 'value', CustomJS( args={ 'textinput': period_textinput, 'numphases': phase_selection, 'n_labels': len(split), 'p': period_plot, **period_model_dict, }, code=open( os.path.join( os.path.dirname(__file__), '../static/js/plotjs', 'foldphase.js' ) ).read(), ), ) # a way to modify the period period_double_button = Button(label="*2", width=30) period_double_button.js_on_click( CustomJS( args={'textinput': period_textinput}, code=""" const period = parseFloat(textinput.value); textinput.value = parseFloat(2.*period).toFixed(9); """, ) ) period_halve_button = Button(label="/2", width=30) period_halve_button.js_on_click( CustomJS( args={'textinput': period_textinput}, code=""" const period = parseFloat(textinput.value); textinput.value = parseFloat(period/2.).toFixed(9); """, ) ) # a way to select the period period_selection.js_on_click( CustomJS( args={'textinput': period_textinput, 'periods': period_list}, code=""" textinput.value = parseFloat(periods[this.active]).toFixed(9); """, ) ) phase_selection.js_on_click( CustomJS( args={ 'textinput': period_textinput, 'numphases': phase_selection, 'n_labels': len(split), 'p': period_plot, **period_model_dict, }, code=open( os.path.join( os.path.dirname(__file__), '../static/js/plotjs', 'foldphase.js' ) ).read(), ) ) # layout if device == "mobile_portrait": period_controls = column( row( period_title, period_textinput, period_double_button, period_halve_button, width=width, sizing_mode="scale_width", ), phase_selection, period_selection, width=width, ) # Add extra height to plot based on period control components added # 18 is the height of each period selection radio option (per default font size) # and the 130 encompasses the other components which are consistent no matter # the data size. height += 130 + 18 * len(period_labels) else: period_controls = column( row( period_title, period_textinput, period_double_button, period_halve_button, phase_selection, width=width, sizing_mode="scale_width", ), period_selection, margin=10, ) # Add extra height to plot based on period control components added # Numbers are derived in similar manner to the "mobile_portrait" case above height += 90 + 18 * len(period_labels) period_layout = column(period_plot, period_controls, width=width, height=height) # Period panel p3 = Panel(child=period_layout, title='Period') # tabs for mag, flux, period tabs = Tabs(tabs=[p2, p1, p3], width=width, height=height, sizing_mode='fixed') else: # tabs for mag, flux tabs = Tabs(tabs=[p2, p1], width=width, height=height + 90, sizing_mode='fixed') return bokeh_embed.json_item(tabs)
("BowlerType", "@BowlerType"), ] # Set the Title p = figure(title = "Top Test Bowlers by Decade (1877-1919)", tooltips=TOOLTIPS_SCATTER, x_range=(0, 30), y_range=(0, 200), plot_width=1280, plot_height=800) p.title.text_font_size = '24pt' # Construnct the colours p.scatter("Average", "Wickets", source=titanic_df, legend="RightHand", fill_alpha=0.9, size=24, marker=factor_mark('RightHand', MARKERS, FATE), color=factor_cmap('RightHand', palette=['#aeb3b7', '#3a6587'], factors=FATE)) #Set the axis labels p.xaxis.axis_label = 'Career Bowling Average' p.yaxis.axis_label = 'Total Career Wickets' # Remove the Grid lines p.xgrid.grid_line_color = None p.ygrid.grid_line_color = None # change just some things about the x-axis p.xaxis.axis_line_width = 2 p.xaxis.major_label_text_color = "black" p.xaxis.axis_line_color = "#aeb3b7"
def get_total_infected_people_figure(colors): with open(SOURCES_FILE_PATH) as f: source_data = json.load(f) df = pd.DataFrame(source_data['image']) df = pd.DataFrame(df.stack(), columns=['rate']).reset_index() mapper = LinearColorMapper(palette=colors, low=df.rate.min(), high=df.rate.max()) TOOLS = "hover,save,pan,box_zoom,reset,wheel_zoom" # Create a figure with a datetime type x-axis fig = figure(title=source_data['name'], plot_height=900, plot_width=900, x_axis_label=source_data['x'], y_axis_label=source_data['y'], x_minor_ticks=5, y_range=(0, 50), x_range=(0, 50), y_minor_ticks=5, y_scale=Scale(), toolbar_location='below', tools=TOOLS, tooltips=[]) rects = fig.rect(x='level_0', y='level_1', width=1, height=1, source=df, fill_color={ 'field': 'rate', 'transform': mapper }, line_color=None) stars = fig.scatter("petal_length", "sepal_width", source=flowers, legend_field="species", fill_alpha=0.4, size=12, marker=factor_mark('species', MARKERS, SPECIES), color=factor_cmap('species', 'Category10_3', SPECIES)) g1_hover = bkm.HoverTool(renderers=[rects], tooltips=[('rects', 'rects')]) g2_hover = bkm.HoverTool(renderers=[stars], tooltips=[('stars', 'stars')]) fig.add_tools(g1_hover, g2_hover) color_bar = ColorBar(color_mapper=mapper, major_label_text_font_size="5pt", ticker=BasicTicker(desired_num_ticks=len(colors)), formatter=PrintfTickFormatter(format="%d%%"), label_standoff=6, border_line_color=None, location=(0, 0)) tap_tool_1 = TapTool(behavior='inspect', callback=CustomJS(args=dict(), code=get_callback_function('red')), renderers=[rects]) tap_tool_2 = TapTool( behavior='inspect', callback=CustomJS(args=dict(), code=get_callback_function('violet')), renderers=[stars]) fig.add_tools(tap_tool_1, tap_tool_2) fig.add_layout(color_bar, 'right') return fig
def Gulmay_Output_Graph(conn): output_file("PDD_Graph.html") #???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ''' In order to make a simple plot easy to create we're going to define all of the user inputs up front. Then pull the data out of the database and add a simple tolerance dataframe if necessary. For a first plot this may be all that is needed but to make a more complex plot the writer may need to add details further down. Remember that: * The dataframe will not have capital letters in the field names due to a quirk of the read_sql function. * The date field MUST be called 'adate' and of the format 'datetime' in order for parts of the code to function correctly ''' ############################################################################ ############################################################################ ############################################################################ ############################# USER INPUTS ################################## # Decide what the default viewing option is going to be. (i.e. the fields to # be plotted on the x and y axis when the graph is opened, the plot size # etc.). # # Decide on what data to plot on the x/y axis when opened (enter the field # names here) x_data1 = 'adate' y_data1 = 'output' # Decide what the plot formatting will be, inluding the plot title, axis # titles and the size of the plot. A good generic start point is to use the # field names as the axis titles but future plots could potentially use a # look up table to give more user friendly titles. plot_title1 = 'Gulmay Output' x_axis_title1 = x_data1 y_axis_title1 = y_data1 plot_size_height1 = 450 plot_size_width1 = 800 legend_location = 'bottom_left' # Set the fields that will display in the hovertool in addition to the x and # y fields (NB: Can have a maximum of 10 options here). (Useful example # defaults would be the comments field or maybe chamber/electrometer?) hover_tool_fields = ['comments', 'input by', 'checked by'] # Create a list of the plot parameters that will be used as input to a # function later. list_plot_parameters = [x_data1, y_data1, plot_title1, x_axis_title1, y_axis_title1, plot_size_height1, plot_size_width1, legend_location] # Define the fields that the legend will be based off. If there is only # one field then put it in both columns. # # If the default options in the function are not acceptable then change the # boolean to True and then set the palette to the color and marker palettes # that you want (they will be mapped against a 'sorted' list of the unique # values from the fields). color_column = 'energy' custom_color_boolean = False custom_color_palette = [] marker_column = 'energy' custom_marker_boolean = False custom_marker_palette = [] # From the legend defined above give the values that will be pre-ticked when # the plot is opened. NB: Bokeh will throw an error if one of these lists is # empty (i.e. =[]) If only using color or marker then set the color_to plot # and then enter the command: color_to_plot = marker_to_plot. # color_to_plot = ['Gulmay'] marker_to_plot = ['100 kV', '150 kV', '220 kV'] color_to_plot = marker_to_plot ############################################################################ #################### CREATE THE DATA FOR THE GRAPH ######################### # Do this in a function so it can be used in an update callback later def Create_df(): # Use the connection passed to the function to read the data into a # dataframe via an SQL query. df = pd.read_sql('select [gulmay session ID], [output], ' \ '[chamber and electrometer], [Chamber factor], [Dose rate], ' \ '[energy], [T/P factor], [Temp], [Press], [Comments], ' \ '[Input by], [Checked by] from [gulmay output]', conn) # Delete empty rows where the data is very important to have df = df.dropna(subset=['gulmay session id']) df = df.dropna(subset=['energy']) # The format is complicated for this field but seems to be that the date is # always the first element and the machine is always the last regardless of # how many elements there are. # Seperate on the first '_' df_left = df['gulmay session id'].str.partition(sep = '_') # Seperate on the last '_' df_right = df['gulmay session id'].str.rpartition(sep = '_') # From these sperated dataframes add the appropriate columns back into # the main dataframe. df.loc[:,'adate'] = df_left[0] df.loc[:,'machinename'] = df_right[2] # Turn 'adate' into datetime. df.loc[:,'adate'] = pd.to_datetime(df.loc[:,'adate'], dayfirst=True) # Drop any columns where there is no data df = df.dropna(axis='columns', how='all') return df df = Create_df() # Create a list of the fields using the dataframe. By doing it now before # the extra legend fields are added it's easy to limit what is displayed in # the select widgets. TableFields = (list(df.columns)) ############################################################################ ############################################################################ ############################################################################ ################ CREATE THE DATAFRAME FOR THE TOLERANCES ################### # If you want to add tolerances change the boolean to True and construct the # dataframe in the correct format. tolerance_boolean = True # The format of the dataframe should be the first line being the x_axis # (with some values taken from the main dataframe to get the right # formatting). The subsequent columns are the tolerances [low, high]. # NB: column names should match those from the main dataframe. if tolerance_boolean == True: df_tol1 = pd.DataFrame({'adate':[df['adate'].max(), df['adate'].max()], 'output':[97, +103]}) ############################################################################ ############################################################################ ############################################################################ ############################################################################ ''' This is the end of the user input section. If you don't need to make any other changes you can end here. ''' ########################################################################## ################### CREATE THE COLUMNS FOR THE LEGEND ###################### (color_list, color_palette, marker_list, marker_palette, df, add_legend_to_df) = Create_Legend(df, color_column, custom_color_boolean, custom_color_palette, marker_column, custom_marker_boolean, custom_marker_palette) ############################################################################ ############################################################################ ############################################################################ ################## FORMATTING AND CREATING A BASIC PLOT #################### ######### Make Dataset: # Run the Make_Dataset function to create a sub dataframe that the plot will # be made from. Sub_df1 = Make_Dataset( df, color_column, color_to_plot, marker_column, marker_to_plot, x_data1, y_data1 ) # Make the ColumnDataSource (when making convert dataframe to a dictionary, # which is helpful for the callback). src1 = ColumnDataSource(Sub_df1.to_dict(orient='list')) ######### Make Plot: # Create an empty plot (plot parameters will be applied later in a way that # can be manipulated in the callbacks) p1 = figure() # Create a scatter plot. p1.scatter( source = src1, x = 'x', y = 'y', fill_alpha = 0.4, size = 12, # NB: Always use legend_field for this not legend_group as the # former acts on the javascript side but the latter the Python # side. Therefore the former will update automatically. legend_field = 'legend', marker = factor_mark('marker1', marker_palette, marker_list), color = factor_cmap('color1', color_palette, color_list) ) # Run the Define_Plot_Parameters function to format the plot. Define_Plot_Parameters(p1, list_plot_parameters) ############################################################################ ############################################################################ ############################################################################ ############################ ADD TOLERANCES ################################ # We defined the tolerances further up and now want to add the correct ones # to the plot. Only do this through if the boolean is set to True as # otherwise the user doesn't want tolerances. if tolerance_boolean == True: Sub_df1_tol1 = Make_Dataset_Tolerance(x_data1, y_data1, Sub_df1, df_tol1) src1_tol = ColumnDataSource(Sub_df1_tol1.to_dict(orient='list')) # Add two lines to the plot using the new ColumnDataSource as the # source, one line for low tolerance and one line for high. p1.line(source = src1_tol, x = 'x', y = 'y_low', color = 'firebrick') p1.line(source = src1_tol, x = 'x', y = 'y_high', color = 'firebrick') ############################################################################ ############################################################################ ############################################################################ ################## ADD MORE COMPLEX TOOLS TO THE PLOT ###################### ######## 1) # Create a hover tool and add it to the plot hover1 = HoverTool() if len(hover_tool_fields) < 11: kwargs = {} i = 0 for x in hover_tool_fields: i = i+1 kwargs['Field'+str(i)] = x else: kwargs = {} msgbox('Too many fields selected to display on HoverTool ' \ '(Max = 10). Please reduce number of fields selected') Update_HoverTool(hover1, x_data1, y_data1, **kwargs) p1.add_tools(hover1) ############################################################################ ############################################################################ ############################################################################ ################# CREATE WIDGETS TO BE ADDED TO THE PLOT ################### ######## 1) # This funtion from Universal.py will be used to create dropdown lists to # change the data plotted on the x/y-axis. select_xaxis, select_yaxis = Create_Select_Axis(TableFields, x_axis_title1, y_axis_title1) ######## 2) # This function from Universal.py will be used to create a dropdown list to # change the legend position. select_legend = Create_Select_Legend(legend_location) ######## 3) # This funtion from Universal.py will be used to create checkbox widgets to # change the data being plotted from the fields that the legend is based on. checkbox_color, checkbox_marker = Create_Checkbox_Legend(df, color_column, color_to_plot, marker_column, marker_to_plot) ######## 4) # This funtion from Universal.py will be used to create a checkbox widget # to change the fields included in the hovertool. checkbox_hovertool = Create_Checkbox_HoverTool(TableFields, hover_tool_fields) ######## 5) # Make an 'Update Button' to requery the database and get up to date data. update_button = Button(label='Update', button_type='success') ######## 6) # Make a Range Button range_button = Button(label='Range', button_type='primary') ######## 7) # Make some titles for the checkboxes color_title = Div(text='<b>Energy</b>') marker_title = Div(text='<b>Energ</b>') hover_title = Div(text='<b>Hovertool Fields</b>') ############################################################################ ############################################################################ ############################################################################ ########################### CREATE A LAYOUT ################################ # Create a layout where the widgets will be added and any scaling applied. if color_column == marker_column: layout_checkbox = column([color_title, checkbox_color, hover_title, checkbox_hovertool]) else: layout_checkbox = column([color_title, checkbox_color, marker_title, checkbox_marker, hover_title, checkbox_hovertool]) button_row = row([update_button, range_button]) layout_plots = column([ button_row, select_xaxis, select_yaxis, select_legend,p1]) tab_layout = row([layout_plots, layout_checkbox]) ############################################################################ ############################################################################ ############################################################################ ####################### CREATE CALLBACK FUNCTIONS ########################## # Create a big callback that does most stuff def callback(attr, old, new): # Want to acquire the current values of all of the checkboxes and select # widgets to provide as inputs for the re-plot. color_to_plot = [checkbox_color.labels[i] for i in checkbox_color.active] if color_column != marker_column: marker_to_plot = [checkbox_marker.labels[i] for i in checkbox_marker.active] else: marker_to_plot = color_to_plot hovertool_to_plot = [checkbox_hovertool.labels[i] for i in checkbox_hovertool.active] plot1_xdata_to_plot = select_xaxis.value plot1_ydata_to_plot = select_yaxis.value legend_location = select_legend.value # Set the new axis titles from the values just acquired. x_axis_title1 = plot1_xdata_to_plot y_axis_title1 = plot1_ydata_to_plot # Use the pre-defined Make_Dataset function with these new inputs to # create new versions of the sub dataframes. Sub_df1 = Make_Dataset( df, color_column, color_to_plot, marker_column, marker_to_plot, plot1_xdata_to_plot, plot1_ydata_to_plot) # Use the pre-defined Define_Plot_Parameters function with these new # inputs to update the plot parameters. Define_Plot_Parameters(p1, [plot1_xdata_to_plot, plot1_ydata_to_plot, plot_title1, x_axis_title1, y_axis_title1, plot_size_height1, plot_size_width1, legend_location]) # Update the hovertool if len(hovertool_to_plot) < 11: kwargs = {} i = 0 for x in hovertool_to_plot: i = i+1 kwargs['Field'+str(i)] = x else: kwargs = {} msgbox('Too many fields selected to display on HoverTool ' \ '(Max = 10). Please reduce number of fields selected') Update_HoverTool(hover1, plot1_xdata_to_plot, plot1_ydata_to_plot, **kwargs) # Use the pre-defined tolerances function with these new inputs to # make a new version of the tolerances sub dataframe. if tolerance_boolean == True: Sub_df1_tol1 = Make_Dataset_Tolerance(plot1_xdata_to_plot, plot1_ydata_to_plot, Sub_df1, df_tol1) src1_tol.data = Sub_df1_tol1.to_dict(orient='list') # Update the ColumnDataSources src1.data = Sub_df1.to_dict(orient='list') return select_xaxis.on_change('value', callback) select_yaxis.on_change('value', callback) select_legend.on_change('value', callback) checkbox_color.on_change('active', callback) checkbox_marker.on_change('active', callback) checkbox_hovertool.on_change('active', callback) ######## 2) # This callback is designed to update the plotted data with new values from # the database def callback_update(): # Make a new version of the dataframe using the original Create_df # function that connects to the database. df = Create_df() df = add_legend_to_df(df) # The rest of this callback is a copy from the original callback above. color_to_plot = [checkbox_color.labels[i] for i in checkbox_color.active] if color_column != marker_column: marker_to_plot = [checkbox_marker.labels[i] for i in checkbox_marker.active] else: marker_to_plot = color_to_plot hovertool_to_plot = [checkbox_hovertool.labels[i] for i in checkbox_hovertool.active] plot1_xdata_to_plot = select_xaxis.value plot1_ydata_to_plot = select_yaxis.value legend_location = select_legend.value # Set the new axis titles from the values just acquired. x_axis_title1 = plot1_xdata_to_plot y_axis_title1 = plot1_ydata_to_plot Sub_df1 = Make_Dataset( df, color_column, color_to_plot, marker_column, marker_to_plot, plot1_xdata_to_plot, plot1_ydata_to_plot) Define_Plot_Parameters(p1, [plot1_xdata_to_plot, plot1_ydata_to_plot, plot_title1, x_axis_title1, y_axis_title1, plot_size_height1, plot_size_width1, legend_location]) if len(hovertool_to_plot) < 11: kwargs = {} i = 0 for x in hovertool_to_plot: i = i+1 kwargs['Field'+str(i)] = x else: kwargs = {} msgbox('Too many fields selected to display on HoverTool ' \ '(Max = 10). Please reduce number of fields selected') Update_HoverTool(hover1, plot1_xdata_to_plot, plot1_ydata_to_plot, **kwargs) if tolerance_boolean == True: Sub_df1_tol1 = Make_Dataset_Tolerance(plot1_xdata_to_plot, plot1_ydata_to_plot, Sub_df1, df_tol1) src1_tol.data = Sub_df1_tol1.to_dict(orient='list') src1.data = Sub_df1.to_dict(orient='list') return update_button.on_click(callback_update) # Callback for the Range Button def callback_range(): color_to_plot = [ checkbox_color.labels[i] for i in checkbox_color.active] if color_column != marker_column: marker_to_plot = [ checkbox_marker.labels[i] for i in checkbox_marker.active] else: marker_to_plot = color_to_plot plot1_xdata_to_plot = select_xaxis.value plot1_ydata_to_plot = select_yaxis.value # Use the pre-defined Make_Dataset function with these new inputs to # create new versions of the sub dataframes. Sub_df1 = Make_Dataset( df, color_column, color_to_plot, marker_column, marker_to_plot, plot1_xdata_to_plot, plot1_ydata_to_plot) if (plot1_xdata_to_plot == 'adate') and (plot1_ydata_to_plot == 'output'): p1.x_range.start = Sub_df1['x'].max() - timedelta(weeks=53) p1.x_range.end = Sub_df1['x'].max() + timedelta(weeks=2) if plot1_ydata_to_plot == 'output': p1.y_range.start = 95 p1.y_range.end = 105 return range_button.on_click(callback_range) ############################################################################ ############################################################################ ############################################################################ ####################### RETURN TO THE MAIN SCRIPT ########################## return Panel(child = tab_layout, title = 'Gulmay Output')
def plot_scatter(label=None, water=None, stakeholders=None, costs=None, potAll=None): # needs some heavy refactoring... data_path = os.path.join(os.getcwd(), '..', 'input_files', 'input', 'measures') data_fname = 'stats_measures.csv' df = pd.read_csv(os.path.join(data_path, data_fname)) fill_alpha = 0.7 line_width = 1 # Plain adding a column with marker sizes marker_size = len(df) * [10] df['marker_size'] = marker_size # Add the user defined measure, in case if not (label is None and water is None and stakeholders is None and costs is None and potAll is None): colour = 'gold' marker = 'hex' marker_size = 20 row = pd.DataFrame([[ label, water, colour, costs, potAll, stakeholders, marker, marker_size ]], columns=[ 'labels', 'dwl_Qref', 'colour', 'cost_sum', 'FI', 'nr_stakeholders', 'marker', 'marker_size' ]) df = df.append(row) subplot_width = 275 subplot_height = subplot_width min_border = 0 delta_offset_left = 50 categories = df['labels'] markers = df['marker'] marker_sizes = df['marker_size'] pot_ymin = 60 pot_ymax = 180 colours = df['colour'] y = 'nr_stakeholders' toolset = ['pan', 'box_zoom', 'wheel_zoom', 'zoom_in', 'zoom_out', 'reset'] subfig11 = figure(plot_width=subplot_width + delta_offset_left, plot_height=subplot_height, min_border_left=min_border, min_border_bottom=min_border, toolbar_location='above', tools=toolset) x = 'dwl_Qref' v1 = 'dwl_Qref' v2 = 'nr_stakeholders' pp = pareto_points(df[[v1, v2]]) subfig11.line(pp[v1], pp[v2], line_width=20, color='gray', line_alpha=0.25) scatter11 = subfig11.scatter(x, y, source=df, size='marker_size', marker=factor_mark('labels', markers, categories), color=factor_cmap('labels', colours, categories), fill_alpha=fill_alpha, line_width=line_width) subfig11.yaxis.axis_label = 'No. of stakeholders (-)' subfig11.add_tools( HoverTool(tooltips=[('', '@labels')], renderers=[scatter11])) y = 'FI' subfig21 = figure(plot_width=subplot_width + delta_offset_left, plot_height=subplot_height, min_border_left=min_border, min_border_bottom=min_border, tools=toolset, toolbar_location=None, x_range=subfig11.x_range) x = 'dwl_Qref' v1 = 'dwl_Qref' v2 = 'FI' pp = pareto_points(pd.concat([df[['dwl_Qref']], -df[['FI']]], axis=1)) subfig21.line(pp[v1], -pp[v2], line_width=20, color='gray', line_alpha=0.25) scatter21 = subfig21.scatter(x, y, source=df, size='marker_size', marker=factor_mark('labels', markers, categories), color=factor_cmap('labels', colours, categories), fill_alpha=fill_alpha, line_width=line_width) subfig21.yaxis.axis_label = 'PotAll (-)' subfig21.y_range = Range1d(pot_ymax, pot_ymin) subfig21.add_tools( HoverTool(tooltips=[('', '@labels')], renderers=[scatter21])) subfig22 = figure(plot_width=subplot_width, plot_height=subplot_height, min_border_left=min_border, min_border_bottom=min_border, tools=toolset, toolbar_location=None, y_range=subfig21.y_range) x = 'nr_stakeholders' v1 = 'nr_stakeholders' v2 = 'FI' pp = pareto_points( pd.concat([df[['nr_stakeholders']], -df[['FI']]], axis=1)) subfig22.line(pp[v1], -pp[v2], line_width=20, color='gray', line_alpha=0.25) scatter22 = subfig22.scatter(x, y, source=df, size='marker_size', marker=factor_mark('labels', markers, categories), color=factor_cmap('labels', colours, categories), fill_alpha=fill_alpha, line_width=line_width) subfig22.yaxis.major_label_text_font_size = '0pt' subfig22.add_tools( HoverTool(tooltips=[('', '@labels')], renderers=[scatter22])) y = 'cost_sum' subfig31 = figure(plot_width=subplot_width + delta_offset_left, plot_height=subplot_height, min_border_left=min_border, min_border_bottom=min_border, tools=toolset, toolbar_location=None, x_range=subfig11.x_range) x = 'dwl_Qref' v1 = 'dwl_Qref' v2 = 'cost_sum' pp = pareto_points(df[[v1, v2]]) subfig31.line(pp[v1], pp[v2], line_width=20, color='gray', line_alpha=0.25) scatter31 = subfig31.scatter(x, y, source=df, size='marker_size', marker=factor_mark('labels', markers, categories), color=factor_cmap('labels', colours, categories), fill_alpha=fill_alpha, line_width=line_width) subfig31.yaxis.axis_label = 'Implementation costs (\u20AC)' subfig31.xaxis.axis_label = 'Water level lowering (m)' subfig31.add_tools( HoverTool(tooltips=[('', '@labels')], renderers=[scatter31])) subfig32 = figure(plot_width=subplot_width, plot_height=subplot_height, min_border_left=min_border, min_border_bottom=min_border, tools=toolset, toolbar_location=None, x_range=subfig22.x_range, y_range=subfig31.y_range) x = 'nr_stakeholders' v1 = 'nr_stakeholders' v2 = 'cost_sum' subfig32.circle(0, 0, line_width=20, fill_color='gray', color='gray', line_alpha=0.25) scatter32 = subfig32.scatter(x, y, source=df, size='marker_size', marker=factor_mark('labels', markers, categories), color=factor_cmap('labels', colours, categories), fill_alpha=fill_alpha, line_width=line_width) subfig32.yaxis.major_label_text_font_size = '0pt' subfig32.xaxis.axis_label = 'No. of stakeholders (-)' subfig32.add_tools( HoverTool(tooltips=[('', '@labels')], renderers=[scatter32])) subfig33 = figure(plot_width=subplot_width, plot_height=subplot_height, min_border_left=min_border, min_border_bottom=min_border, tools=toolset, toolbar_location=None, y_range=subfig31.y_range) x = 'FI' v1 = 'FI' v2 = 'cost_sum' pp = pareto_points( pd.concat([-df[[v1]], df[[v2]]], axis=1)) # pd.concat([df[[v1], -df[[v2]]], axis=1)) #df[[v1, v2]]) subfig33.line(-pp[v1], pp[v2], line_width=20, color='gray', line_alpha=0.25) scatter33 = subfig33.scatter(x, y, source=df, size='marker_size', marker=factor_mark('labels', markers, categories), color=factor_cmap('labels', colours, categories), fill_alpha=fill_alpha, line_width=line_width) subfig33.yaxis.major_label_text_font_size = '0pt' subfig33.x_range = Range1d(pot_ymax, pot_ymin) subfig33.xaxis.axis_label = 'PotAll (-)' subfig33.add_tools( HoverTool(tooltips=[('', '@labels')], renderers=[scatter33])) matrix = gridplot([[subfig11, None, None], [subfig21, subfig22, None], [subfig31, subfig32, subfig33]], toolbar_location='above') show(matrix)
items = [] cmap = viridis(len(species)) counter = 0 for index, animal in enumerate(species): source = ColumnDataSource( dataframe.loc[dataframe['AnimalGroupParent'] == animal]) scatters[animal] = m.scatter(x='x_mercator', y='y_mercator', source=source, fill_alpha=0.3, size=15, color=cmap[index], marker=factor_mark('AnimalGroupParent', markers, species), visible=False, muted_alpha=0.00) items.append((animal, [scatters[animal]])) hover = m.select(dict(type=HoverTool)) hover.tooltips = [ ("Animal", "@AnimalGroupParent"), ] hover.mode = 'mouse' legend = Legend(items=items) m.add_layout(legend) m.legend.location = 'top_left' m.legend.click_policy = 'hide' m.legend.label_text_font_size = "10px"
from bokeh.plotting import figure, show from bokeh.sampledata.iris import flowers from bokeh.transform import factor_cmap, factor_mark SPECIES = ['setosa', 'versicolor', 'virginica'] MARKERS = ['hex', 'circle_x', 'triangle'] p = figure(title="Iris Morphology") p.xaxis.axis_label = 'Petal Length' p.yaxis.axis_label = 'Sepal Width' p.scatter("petal_length", "sepal_width", source=flowers, legend_field="species", fill_alpha=0.4, size=12, marker=factor_mark('species', MARKERS, SPECIES), color=factor_cmap('species', 'Category10_3', SPECIES)) show(p)
def make_Gaia_CM_diagram(source, table_source): tools = "pan,wheel_zoom,box_zoom,box_select,tap,hover,reset,crosshair" pars = [ 'M1_init', 'M2_init', 'P_init', 'q_init', 'product', 'stability', 'termination_code' ] basic_tooltip = [(p, '@' + p) for p in pars] PRODUCTS = ['HB', 'He-WD', 'CE', 'UK', 'failed', 'sdB', 'sdA'] + \ ['stable', 'CE', 'contact', 'merger'] MARKERS = ['square', 'triangle', 'asterisk', 'asterisk', 'diamond', 'circle', 'circle'] + \ ['circle', 'diamond', 'square', 'triangle', ] COLORS = ['red', 'green', 'purple', 'purple', 'gray', 'blue', 'orange'] + \ ['red', 'green', 'blue', 'gray'] SIZES = [7, 7, 7, 7, 15, 7] v_func = """ const norm = new Float64Array(xs.length) for (let i = 0; i < xs.length; i++) { if (xs[i] == 'sdB' || xs[i] == 'Fail' || xs[i] == 'CE') { norm[i] = 15 } else { norm[i] = 7 } } return norm """ size_transform = mpl.CustomJSTransform(v_func=v_func) # Left Figure booleans = [ True if val != 0 else False for val in source.data['G_HeCoreBurning'] ] view1 = CDSView(source=source, filters=[BooleanFilter(booleans)]) p1 = figure(x_axis_label='Gaia BP-RP', y_axis_label='Gaia G mag', active_drag='box_select', tools=tools, tooltips=basic_tooltip, title="HeCoreBurning", y_range=(6, -5)) p1.circle(hiparcos['bp_rp'], hiparcos['M_g'], size=1, color='gray') p1.scatter(x="BP-RP_HeCoreBurning", y="G_HeCoreBurning", source=source, fill_alpha=0.4, size=transform('z1', size_transform), color=factor_cmap('z1', COLORS, PRODUCTS), marker=factor_mark('z1', MARKERS, PRODUCTS), view=view1) # Right Figure booleans = [ True if val != 0 else False for val in source.data['G_MLstart'] ] view2 = CDSView(source=source, filters=[BooleanFilter(booleans)]) p2 = figure(x_axis_label='Gaia BP-RP', y_axis_label='Gaia G mag', active_drag='box_select', tools=tools, tooltips=basic_tooltip, title="ML start", y_range=(6, -5)) p2.circle(hiparcos['bp_rp'], hiparcos['M_g'], size=1, color='gray') p2.scatter(x="BP-RP_MLstart", y="G_MLstart", source=source, fill_alpha=0.4, size=transform('z1', size_transform), color=factor_cmap('z1', COLORS, PRODUCTS), marker=factor_mark('z1', MARKERS, PRODUCTS), view=view2) plot = gridplot([[p1, p2]]) # add interaction when selecting a model callback = CustomJS(args=dict(summary_source=source, table_source=table_source), code=""" selected_indices = summary_source.selected.indices; console.log(summary_source.selected.indices[0]); console.log(summary_source); if (summary_source.selected.indices.length > 0){ var data = summary_source.data; var ind = summary_source.selected.indices[0]; var parameters = table_source.data['parameters'] var values = table_source.data['values'] parameters.forEach(function (par, index) { values[index] = data[par][ind]; }); //table_source.data['parameters'] = x; table_source.data['values'] = values; table_source.change.emit(); } """) p1.js_on_event('tap', callback) p2.js_on_event('tap', callback) return plot, p1, p2
def make_comparison_plot(source, pars_dict, titles=['', '']): tools = "pan,wheel_zoom,box_zoom,box_select,tap,hover,reset,crosshair" pars = [ 'M1_init', 'M2_init', 'P_init', 'q_init', 'product', 'stability', 'termination_code' ] basic_tooltip = [(p, '@' + p) for p in pars] PRODUCTS = ['HB', 'He-WD', 'CE', 'UK', 'failed', 'sdB', 'sdA'] MARKERS = [ 'square', 'triangle', 'asterisk', 'asterisk', 'diamond', 'circle', 'circle' ] COLORS = ['red', 'green', 'purple', 'purple', 'gray', 'blue', 'orange'] SIZES = [7, 7, 7, 7, 15, 7] v_func = """ const norm = new Float64Array(xs.length) for (let i = 0; i < xs.length; i++) { if (xs[i] == 'sdB' || xs[i] == 'Fail') { norm[i] = 15 } else { norm[i] = 7 } } return norm """ size_transform = mpl.CustomJSTransform(v_func=v_func) # Left Figure p1 = figure(x_axis_label=pars_dict['x1'], y_axis_label=pars_dict['y1'], active_drag='box_select', tools=tools, title=titles[0]) # , tooltips=basic_tooltip) p1.scatter( x="x1", y="y1", source=source, fill_alpha=0.4, size=transform('product_1', size_transform), color=factor_cmap('product_1', COLORS, PRODUCTS), marker=factor_mark('product_1', MARKERS, PRODUCTS), ) # Right Figure p2 = figure(x_axis_label=pars_dict['x2'], y_axis_label=pars_dict['y2'], active_drag='box_select', tools=tools, title=titles[1]) # , tooltips=basic_tooltip) p2.scatter( x="x2", y="y2", source=source, fill_alpha=0.4, size=transform('product_2', size_transform), color=factor_cmap('product_2', COLORS, PRODUCTS), marker=factor_mark('product_2', MARKERS, PRODUCTS), ) # add interaction when selecting a model callback = CustomJS(args=dict(summary_source=source), code=""" selected_indices = summary_source.selected.indices; """) p1.js_on_event('tap', callback) p2.js_on_event('tap', callback) plot = gridplot([[p1, p2]]) return plot, p1, p2
def comps_tab(comps): df = comps df['Close Date'] = pd.to_datetime(df['Close Date']) df = df.drop(columns=['index']) df['Size'] = 24 df['Color'] = "#31AADE" df['xs'] = 'Close Date' df['ys'] = 'Finished Lot Value' SIZES = list(range(12, 36, 3)) COLORS = Spectral6 N_SIZES = len(SIZES) N_COLORS = len(COLORS) MARKERSOURCE = list(df['Site Condition'].unique()) MARKERS = ['hex', 'circle_x', 'triangle', 'square'] source = ColumnDataSource(df) columns = sorted(df.columns) discrete = [x for x in columns if df[x].dtype == object] continuous = [x for x in columns if x not in discrete] x_axis_select = Select(title='X-Axis', value='Close Date', options=columns) y_axis_select = Select(title='Y-Axis', value='Finished Lot Value', options=columns) size = Select(title='Size', value='None', options=['None'] + continuous) color = Select(title='Color', value='None', options=['None'] + continuous) kw = dict() x_title = x_axis_select.value.title() y_title = y_axis_select.value.title() df['xs'] = df[x_axis_select.value].values df['ys'] = df[y_axis_select.value].values x_title = x_axis_select.value.title() y_title = y_axis_select.value.title() kw = dict() if x_axis_select.value in discrete: kw['x_range'] = sorted(set(df['xs'])) if y_axis_select.value in discrete: kw['y_range'] = sorted(set(df['ys'])) kw['title'] = "%s vs %s" % (x_title, y_title) if x_axis_select.value in discrete: p.xaxis.major_label_orientation = pd.np.pi / 4 df['Size'] = 24 if size.value != 'None': if len(set(df[size.value])) > N_SIZES: groups = pd.qcut(df[size.value].values, N_SIZES, duplicates='drop') else: groups = pd.Categorical(df[size.value]) df['Size'] = [SIZES[xx] for xx in groups.codes] df['Color'] = "#31AADE" if color.value != 'None': if len(set(df[color.value])) > N_COLORS: groups = pd.qcut(df[color.value].values, N_COLORS, duplicates='drop') else: groups = pd.Categorical(df[color.value]) df['Color'] = [COLORS[xx] for xx in groups.codes] p = figure(plot_height=500, plot_width=800, tools='pan,box_zoom,hover,reset', **kw) p.xaxis.axis_label = x_title p.yaxis.axis_label = y_title map_options = GMapOptions(lat=df.Lat.mean(), lng=df.Long.mean(), map_type="roadmap", zoom=8) pmap = gmap(Google_API, map_options, plot_width=360, plot_height=400, title="CMA Map", toolbar_location="above") pmap.circle(x="Long", y="Lat", size=15, fill_color='Color', fill_alpha=0.25, line_color='black', line_width=.08, source=source) callback = CustomJS(code=""" var tooltips = document.getElementsByClassName("bk-tooltip"); for (var i = 0, len = tooltips.length; i < len; i ++) { tooltips[i].style.top = "10px"; // unset what bokeh.js sets tooltips[i].style.left = "800px"; tooltips[i].style.bottom = ""; tooltips[i].style.right = ""; } """) p.scatter(x='xs', y='ys', color='Color', size='Size', line_color="white", alpha=0.6, marker=factor_mark('Site Condition', MARKERS, MARKERSOURCE), hover_color='white', hover_alpha=0.5, source=source) hover = HoverTool(tooltips=[(x_title, '@xs'), (y_title, '@ys'), ('Name', '@Neighborhood'), ('FLV', '@{Finished Lot Value}{$0,0}'), ('Community Count', '@{Community Count}'), ('Market_Tier', '@{Market Tier}'), ('Close Date', '@DateString'), ('Lot Count', '@{Lot Count}'), ('Site_Condition', '@{Site Condition}'), ('Seller', '@Seller'), ('Entitlements', '@Entitlements'), ('Market', '@Market')], callback=callback) def select_df(): # filter df here based on widget inputs selected = df # (df['Square Footage (1)'] <= float(sf_slider.value[1])) return selected def update(): df = select_df() df['xs'] = df[x_axis_select.value].values df['ys'] = df[y_axis_select.value].values x_title = x_axis_select.value.title() y_title = y_axis_select.value.title() kw = dict() if x_axis_select.value in discrete: kw['x_range'] = sorted(set(df['xs'])) if y_axis_select.value in discrete: kw['y_range'] = sorted(set(df['ys'])) kw['title'] = "%s vs %s" % (x_title, y_title) if x_axis_select.value in discrete: p.xaxis.major_label_orientation = pd.np.pi / 4 df['Size'] = 24 if size.value != 'None': if len(set(df[size.value])) > N_SIZES: groups = pd.qcut(df[size.value].values, N_SIZES, duplicates='drop') else: groups = pd.Categorical(df[size.value]) df['Size'] = [SIZES[xx] for xx in groups.codes] df['Color'] = "#31AADE" if color.value != 'None': if len(set(df[color.value])) > N_COLORS: groups = pd.qcut(df[color.value].values, N_COLORS, duplicates='drop') else: groups = pd.Categorical(df[color.value]) df['Color'] = [COLORS[xx] for xx in groups.codes] source = df return source controls = [x_axis_select, y_axis_select, size, color] for control in controls: control.on_change('value', lambda attr, old, new: update()) button = Button(label="Download", button_type="success") button.callback = df.to_csv( os.path.abspath( os.path.join(os.path.dirname(__file__), '..', 'data', 'Downloads', 'sales_comps_' + str(date) + '.csv'))) table = DataTable(source=source, editable=True, height=600, width=1400, fit_columns=True, scroll_to_selection=True) widgets = column([*controls, button], width=180, height=250) widgets.sizing_mode = "fixed" # Make a tab with the layout p.add_tools(hover) l = layout([[column([widgets]), p, pmap], table], sizing_mode='fixed') # l = layout(table) update() tab = Panel(child=l, title='Comps') return tab
def Photon_Output_Graph(conn): output_file( "Photon_Output_Graph.html" ) #???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ############################################################################ ############################# USER INPUTS ################################## # Decide what the default viewing option is going to be. (i.e. the fields to # be plotted on the x and y axis when the graph is opened). # NB: Have it set that if axis is 'adate' then will automatically update # to plot datetime. x_data1 = 'adate' y_data1 = 'graph % diff in output' plot_title1 = 'Photon Output Results' x_axis_title1 = x_data1 y_axis_title1 = y_data1 plot_size_height1 = 450 plot_size_width1 = 800 legend_location = 'bottom_left' hover_tool_fields = ['chamber and electrometer', 'comments'] # Create a list of the plot parameters that will be used as input to a # function later. list_plot_parameters = [ x_data1, y_data1, plot_title1, x_axis_title1, y_axis_title1, plot_size_height1, plot_size_width1, legend_location ] # Define the fields that the legend will be based off. If there is only # one field then put it in both columns. color_column = 'energy' custom_color_boolean = True custom_color_palette = [ '#FF0000', 'black', 'yellow', 'purple', '#008F8F', '#FF00FF', 'white' ] marker_column = 'machinename' custom_marker_boolean = True custom_marker_palette = [ 'circle_x', 'square', 'square_x', 'diamond', 'hex', 'x', 'circle_cross', 'square_cross', 'diamond_cross', 'dash', 'cross', 'inverted_triangle', 'circle', 'triangle', 'asterisk' ] # From the legend defined above give the values that will be pre-ticked when # the plot is opened. NB: Bokeh will throw an error if one of these lists is # empty (i.e. =[]) If only using color or marker then set the color_to plot # and then enter the command: marker_to_plot = color_to_plot. color_to_plot = ['6MV', '10MV'] marker_to_plot = ['TrueBeam B', 'TrueBeam C', 'TrueBeam D'] ############################################################################ #################### CREATE THE DATA FOR THE GRAPH ######################### # Do this in a function so it can be used in an update callback later def Create_df(): # Use the connection passed to the function to read the data into a # dataframe via an SQL query. df = pd.read_sql( 'SELECT [Protocol ID], [Energy], ' \ '[chamber and electrometer], [Chamber factor], ' \ '[Gantry angle], [Temp], [Press], [T/P factor], ' \ '[output], [QI], [Comments], ' \ '[Graph % Diff in output], [Graph % diff in QI] ' \ 'FROM [phcal_Graph] ' \ , conn ) # Delete empty rows where the data is very important to have df = df.dropna(subset=['protocol id'], how='any') df = df.dropna(subset=['energy'], how='any') # The format is complicated for this field but seems to be that the date is # always the first element and the machine is always the last regardless of # how many elements there are. # Seperate on the first '_' df_left = df['protocol id'].str.partition(sep='_') # Seperate on the last '_' df_right = df['protocol id'].str.rpartition(sep='_') # From these sperated dataframes add the appropriate columns back into the # main dataframe. df.loc[:, 'adate'] = df_left[0] df.loc[:, 'machinename'] = df_right[2] # Turn 'adate' into datetime. df.loc[:, 'adate'] = pd.to_datetime(df.loc[:, 'adate'], dayfirst=True) # Drop any rows that aren't related to the Truebeams (ditches the old # uneeded data). Might be possible to put this in the SQL query but # difficult as machinename is embedded in the protocol ID. df = df[df['machinename'].isin( ['TrueBeam B', 'TrueBeam C', 'TrueBeam D', 'TrueBeam F'])] # Drop any columns where there is no data (likely because of the # dropping of the old linacs (e.g. data that used to be collected from # them that is no longer collected for the Truebeams)) df = df.dropna(axis='columns', how='all') return df df = Create_df() # Create a list of the fields using the dataframe. By doing it now before # the extra legend fields are added it's easy to limit what is displayed in # the select widgets. TableFields = (list(df.columns)) ############################################################################ ############################################################################ ############################################################################ ################ CREATE THE DATAFRAME FOR THE TOLERANCES ################### # If you want to add tolerances change the boolean to True and construct the # dataframe in the correct format. tolerance_boolean = True # The format of the dataframe should be the first line being the x_axis # (with some values taken from the main dataframe to get the right # formatting). The subsequent columns are the tolerances [low, high]. # NB: column names should match those from the main dataframe. if tolerance_boolean == True: df_tol1 = pd.DataFrame({ 'adate': [df['adate'].max(), df['adate'].max()], 'output': [98, 102], 'graph % diff in output': [-2, 2] }) # Added a seperate qi tolerance as multiple energes can appear # simultaneously so need an special tolerance function to deal with # this. df_tol1_qi = pd.DataFrame({ 'adate': [df['adate'].max(), df['adate'].max()], 'qi_6MV': [0.64, 0.68], 'qi_6XFFF': [0.61, 0.65], 'qi_10MV': [0.71, 0.75], 'qi_10XFFF': [0.68, 0.72] }) def special_tolerance(color_to_plot, x_data1, y_data1, Sub_df1, df_tol1_qi): energy_list = ['6MV', '6XFFF', '10MV', '10XFFF'] data = {} if (x_data1 != 'adate') or (y_data1 != 'qi'): for x in range(0, len(energy_list)): data.update({ 'x_' + energy_list[x]: [Sub_df1['x'].max(), Sub_df1['x'].max()], 'y_low_' + energy_list[x]: [Sub_df1['y'].max(), Sub_df1['y'].max()], 'y_high_' + energy_list[x]: [Sub_df1['y'].max(), Sub_df1['y'].max()] }) else: # Get a list of the column headers headers1 = df_tol1_qi.columns.values.tolist() # Check if the xdata is what is in the df_tol1 as the x_axis (if not no # point plotting tolerances as all tolerances are vs this column). max_x = Sub_df1['x'].max() + pd.DateOffset(weeks=2) min_x = Sub_df1['x'].min() + pd.DateOffset(weeks=-2) for x in range(0, len(energy_list)): if energy_list[x] in color_to_plot: data.update({ 'x_' + energy_list[x]: [min_x, max_x], 'y_low_' + energy_list[x]: [ df_tol1_qi['qi_' + energy_list[x]][0], df_tol1_qi['qi_' + energy_list[x]][0] ], 'y_high_' + energy_list[x]: [ df_tol1_qi['qi_' + energy_list[x]][1], df_tol1_qi['qi_' + energy_list[x]][1] ] }) else: data.update({ 'x_' + energy_list[x]: [Sub_df1['x'].max(), Sub_df1['x'].max()], 'y_low_' + energy_list[x]: [Sub_df1['y'].max(), Sub_df1['y'].max()], 'y_high_' + energy_list[x]: [Sub_df1['y'].max(), Sub_df1['y'].max()] }) Sub_df1_tol1_qi = pd.DataFrame(data) return Sub_df1_tol1_qi ############################################################################ ############################################################################ ############################################################################ ############################################################################ ''' This is the end of the user input section. If you don't need to make any other changes you can end here. ''' ############################################################################ ################### CREATE THE COLUMNS FOR THE LEGEND ###################### (color_list, color_palette, marker_list, marker_palette, df, add_legend_to_df) = Create_Legend(df, color_column, custom_color_boolean, custom_color_palette, marker_column, custom_marker_boolean, custom_marker_palette) ############################################################################ ############################################################################ ############################################################################ ################## FORMATTING AND CREATING A BASIC PLOT #################### ######### Make Dataset: # Run the Make_Dataset function to create two sub dataframs that the plots # will be made from. Sub_df1 = Make_Dataset(df, color_column, color_to_plot, marker_column, marker_to_plot, x_data1, y_data1) # Make the ColumnDataSource (when making convert dataframe to a dictionary, # which is helpful for the callback). src1 = ColumnDataSource(Sub_df1.to_dict(orient='list')) ######### Make Plot: # Create an empty plot (plot parameters will be applied later in a way that # can be manipulated in the callbacks) p1 = figure() p1.scatter( source=src1, x='x', y='y', fill_alpha=0.4, size=12, # NB: Always use legend_field for this not legend_group as the # former acts on the javascript side but the latter the Python # side. Therefore the former will update automatically. legend_field='legend', marker=factor_mark('marker1', marker_palette, marker_list), color=factor_cmap('color1', color_palette, color_list)) # Run the Define_Plot_Parameters function to set the plot parameters Define_Plot_Parameters(p1, list_plot_parameters) ############################################################################ ############################################################################ ############################################################################ ############################ ADD TOLERANCES ################################ # We defined the tolerances further up and now want to add the correct ones # to the plot. Done in such a way that they are updated with the callbacks # later. if tolerance_boolean == True: Sub_df1_tol1 = Make_Dataset_Tolerance(x_data1, y_data1, Sub_df1, df_tol1) src1_tol = ColumnDataSource(Sub_df1_tol1.to_dict(orient='list')) p1.line(source=src1_tol, x='x', y='y_low', color='firebrick') p1.line(source=src1_tol, x='x', y='y_high', color='firebrick') Sub_df1_tol1_qi = special_tolerance(color_to_plot, x_data1, y_data1, Sub_df1, df_tol1_qi) src1_tol_qi = ColumnDataSource(Sub_df1_tol1_qi.to_dict(orient='list')) p1.line(source=src1_tol_qi, x='x_6MV', y='y_low_6MV', color='yellow') p1.line(source=src1_tol_qi, x='x_6MV', y='y_high_6MV', color='yellow') p1.line(source=src1_tol_qi, x='x_6XFFF', y='y_low_6XFFF', color='mediumorchid') p1.line(source=src1_tol_qi, x='x_6XFFF', y='y_high_6XFFF', color='mediumorchid') p1.line(source=src1_tol_qi, x='x_10MV', y='y_low_10MV', color='firebrick') p1.line(source=src1_tol_qi, x='x_10MV', y='y_high_10MV', color='firebrick') p1.line(source=src1_tol_qi, x='x_10XFFF', y='y_low_10XFFF', color='black') p1.line(source=src1_tol_qi, x='x_10XFFF', y='y_high_10XFFF', color='black') ############################################################################ ############################################################################ ############################################################################ ################## ADD MORE COMPLEX TOOLS TO THE PLOT ###################### ######## 1) # Create a hover tool and add it to the plot hover1 = HoverTool() if len(hover_tool_fields) < 11: kwargs = {} i = 0 for x in hover_tool_fields: i = i + 1 kwargs['Field' + str(i)] = x else: kwargs = {} msgbox('Too many fields selected to display on HoverTool ' \ '(Max = 10). Please reduce number of fields selected') Update_HoverTool(hover1, x_data1, y_data1, **kwargs) p1.add_tools(hover1) ############################################################################ ############################################################################ ############################################################################ ################# CREATE WIDGETS TO BE ADDED TO THE PLOT ################### ######## 1) # This select funtion will be used to create dropdown lists to change the # data plotted on the x/y-axis. select_xaxis, select_yaxis = Create_Select_Axis(TableFields, x_axis_title1, y_axis_title1) ######## 2) # This select widget will be used to create dropdown lists to change the # legend position. select_legend = Create_Select_Legend(legend_location) ######## 3) # These checkbox widgets will be used to create a tool to select the machine # and energy that are being plotted. checkbox_color, checkbox_marker = Create_Checkbox_Legend( df, color_column, color_to_plot, marker_column, marker_to_plot) ######## 4) # These checkbox widgets will be used to create a tool to select the machine # and energy that are being plotted. checkbox_hovertool = Create_Checkbox_HoverTool(TableFields, hover_tool_fields) ######## 5) # Make an 'Update Button' to requery the database and get up to date data. update_button = Button(label='Update', button_type='success') ######## 6) # Make a Range Button range_button = Button(label='Range', button_type='primary') ######## 7) # Make some titles for the checkboxes color_title = Div(text='<b>Energy Choice</b>') marker_title = Div(text='<b>Machine Choice</b>') hover_title = Div(text='<b>Hovertool Fields</b>') ############################################################################ ############################################################################ ############################################################################ ############################ CREATE A LAYOUT ############################### # Create a layout where the widgets will be added and any scaling applied. if color_column == marker_column: layout_checkbox = column( [color_title, checkbox_color, hover_title, checkbox_hovertool]) else: layout_checkbox = column([ color_title, checkbox_color, marker_title, checkbox_marker, hover_title, checkbox_hovertool ]) button_row = row([update_button, range_button]) layout_plots = column( [button_row, select_xaxis, select_yaxis, select_legend, p1]) tab_layout = row([layout_plots, layout_checkbox]) ############################################################################ ############################################################################ ############################################################################ ####################### CREATE CALLBACK FUNCTIONS ########################## # Create a big callback that does most stuff def callback(attr, old, new): # Want to acquire the current values of all of the checkboxes and select # widgets to provide as inputs for the re-plot. color_to_plot = [ checkbox_color.labels[i] for i in checkbox_color.active ] if color_column != marker_column: marker_to_plot = [ checkbox_marker.labels[i] for i in checkbox_marker.active ] else: marker_to_plot = color_to_plot hovertool_to_plot = [ checkbox_hovertool.labels[i] for i in checkbox_hovertool.active ] plot1_xdata_to_plot = select_xaxis.value plot1_ydata_to_plot = select_yaxis.value legend_location = select_legend.value # Set the new axis titles x_axis_title1 = plot1_xdata_to_plot y_axis_title1 = plot1_ydata_to_plot # Use the pre-defined Make_Dataset function with these new inputs to # create new versions of the sub dataframes. Sub_df1 = Make_Dataset(df, color_column, color_to_plot, marker_column, marker_to_plot, plot1_xdata_to_plot, plot1_ydata_to_plot) # Use the pre-defined Define_Plot_Parameters function with these new # inputs to update the plot. Define_Plot_Parameters(p1, [ plot1_xdata_to_plot, plot1_ydata_to_plot, plot_title1, x_axis_title1, y_axis_title1, plot_size_height1, plot_size_width1, legend_location ]) # Update the hovertool if len(hovertool_to_plot) < 11: kwargs = {} i = 0 for x in hovertool_to_plot: i = i + 1 kwargs['Field' + str(i)] = x else: kwargs = {} msgbox('Too many fields selected to display on HoverTool ' \ '(Max = 10). Please reduce number of fields selected') Update_HoverTool(hover1, plot1_xdata_to_plot, plot1_ydata_to_plot, **kwargs) print(hover1.tooltips) print(p1.hover.tooltips) print(hover1) print(p1.hover) # Use the pre-defined Make_Dataset_Tolerance function with these new # inputs to update the tolerances. if tolerance_boolean == True: Sub_df1_tol1 = Make_Dataset_Tolerance(plot1_xdata_to_plot, plot1_ydata_to_plot, Sub_df1, df_tol1) Sub_df1_tol1_qi = special_tolerance(color_to_plot, plot1_xdata_to_plot, plot1_ydata_to_plot, Sub_df1, df_tol1_qi) # Update the ColumnDataSources. src1.data = Sub_df1.to_dict(orient='list') if tolerance_boolean == True: src1_tol.data = Sub_df1_tol1.to_dict(orient='list') src1_tol_qi.data = Sub_df1_tol1_qi.to_dict(orient='list') return select_xaxis.on_change('value', callback) select_yaxis.on_change('value', callback) select_legend.on_change('value', callback) checkbox_color.on_change('active', callback) checkbox_marker.on_change('active', callback) checkbox_hovertool.on_change('active', callback) # Callback for the Update Button def callback_update(): # Make a new version of the dataframe using the original Create_df # function that connects to the database. df = Create_df() df = add_legend_to_df(df) color_to_plot = [ checkbox_color.labels[i] for i in checkbox_color.active ] if color_column != marker_column: marker_to_plot = [ checkbox_marker.labels[i] for i in checkbox_marker.active ] else: marker_to_plot = color_to_plot hovertool_to_plot = [ checkbox_hovertool.labels[i] for i in checkbox_hovertool.active ] plot1_xdata_to_plot = select_xaxis.value plot1_ydata_to_plot = select_yaxis.value x_axis_title1 = plot1_xdata_to_plot y_axis_title1 = plot1_ydata_to_plot legend_location = select_legend.value Sub_df1 = Make_Dataset(df, color_column, color_to_plot, marker_column, marker_to_plot, plot1_xdata_to_plot, plot1_ydata_to_plot) Define_Plot_Parameters(p1, [ plot1_xdata_to_plot, plot1_ydata_to_plot, plot_title1, x_axis_title1, y_axis_title1, plot_size_height1, plot_size_width1, legend_location ]) if len(hovertool_to_plot) < 11: kwargs = {} i = 0 for x in hovertool_to_plot: i = i + 1 kwargs['Field' + str(i)] = x else: kwargs = {} msgbox('Too many fields selected to display on HoverTool ' \ '(Max = 10). Please reduce number of fields selected') Update_HoverTool(hover1, plot1_xdata_to_plot, plot1_ydata_to_plot, **kwargs) if tolerance_boolean == True: Sub_df1_tol1 = Make_Dataset_Tolerance(plot1_xdata_to_plot, plot1_ydata_to_plot, Sub_df1, df_tol1) Sub_df1_tol1_qi = special_tolerance(color_to_plot, plot1_xdata_to_plot, plot1_ydata_to_plot, Sub_df1, df_tol1_qi) src1_tol.data = Sub_df1_tol1.to_dict(orient='list') src1_tol_qi.data = Sub_df1_tol1_qi.to_dict(orient='list') src1.data = Sub_df1.to_dict(orient='list') return update_button.on_click(callback_update) # Callback for the Range Button def callback_range(): color_to_plot = [ checkbox_color.labels[i] for i in checkbox_color.active ] if color_column != marker_column: marker_to_plot = [ checkbox_marker.labels[i] for i in checkbox_marker.active ] else: marker_to_plot = color_to_plot plot1_xdata_to_plot = select_xaxis.value plot1_ydata_to_plot = select_yaxis.value # Use the pre-defined Make_Dataset function with these new inputs to # create new versions of the sub dataframes. Sub_df1 = Make_Dataset(df, color_column, color_to_plot, marker_column, marker_to_plot, plot1_xdata_to_plot, plot1_ydata_to_plot) if (plot1_xdata_to_plot == 'adate') and ( (plot1_ydata_to_plot == 'graph % diff in output') or (plot1_ydata_to_plot == 'output') or plot1_ydata_to_plot == 'qi'): p1.x_range.start = Sub_df1['x'].max() - timedelta(weeks=53) p1.x_range.end = Sub_df1['x'].max() + timedelta(weeks=2) if plot1_ydata_to_plot == 'output': p1.y_range.start = 97 p1.y_range.end = 103 elif plot1_ydata_to_plot == 'graph % diff in output': p1.y_range.start = -3 p1.y_range.end = 3 elif plot1_ydata_to_plot == 'qi': p1.y_range.start = 0.55 p1.y_range.end = 0.8 return range_button.on_click(callback_range) ############################################################################ ############################################################################ ############################################################################ ####################### RETURN TO THE MAIN SCRIPT ########################## return Panel(child=tab_layout, title='Photon Output')