"angle": 0, "color": "black", "text_align": "left", "text_baseline": "middle" } x = dodge("group", -0.4, range=p.x_range) y = dodge("period", 0.3, range=p.y_range) p.text(x=x, y="period", text="sym", text_font_style="bold", text_font_size="16pt", **text_props) p.text(x=x, y=y, text="atomic_number", text_font_size="11pt", **text_props) color_bar = ColorBar(color_mapper=color_mapper, ticker=BasicTicker(desired_num_ticks=10), border_line_color=None, label_standoff=cbar_standoff, location=(0, 0), orientation="vertical", scale_alpha=alpha, major_label_text_font_size=str(cbar_fontsize) + "pt") if cbar_height is not None: color_bar.height = cbar_height p.add_layout(color_bar, "right") p.grid.grid_line_color = None show(p)
def plot_ptable_trend( data_elements=["Rb", "S", "Se"], data_list=[10, 20, 30], input_file=None, output_html="ptable.html", bokeh_palette="Plasma256", cmap=plasma, log_scale=0, width=1050, alpha=0.65, cbar_height=520, cbar_font="14pt", save_plot=True, ): """ Generate periodic table chemical trends. Either provide a file or list of data_elements, &data_list. Note that Bokeh already provided a periodic table. This module will take your data to color code them. See an example: https://www.nature.com/articles/s41598-019-45028-y Fig. 3 Forked from https://github.com/arosen93/ptable_trends """ output_file(output_html) # Define number of and groups period_label = ["1", "2", "3", "4", "5", "6", "7"] group_range = [str(x) for x in range(1, 19)] if input_file is not None: data_elements = [] data_list = [] f = open(input_file, "r") lines = f.read().splitlines() f.close() for i in lines: data_elements.append(i.split()[0]) data_list.append(i.split()[1]) data = [float(i) for i in data_list] if len(data) != len(data_elements): raise ValueError("Unequal number of atomic elements and data points") # lanthanides = [x.lower() for x in elements["symbol"][56:70].tolist()] # actinides = [x.lower() for x in elements["symbol"][88:102].tolist()] period_label.append("blank") period_label.append("La") period_label.append("Ac") count = 0 for i in range(56, 70): elements.period[i] = "La" elements.group[i] = str(count + 4) count += 1 count = 0 for i in range(88, 102): elements.period[i] = "Ac" elements.group[i] = str(count + 4) count += 1 # Define matplotlib and bokeh color map if log_scale == 0: color_mapper = LinearColorMapper(palette=bokeh_palette, low=min(data), high=max(data)) norm = Normalize(vmin=min(data), vmax=max(data)) elif log_scale == 1: for i in range(len(data)): if data[i] < 0: raise ValueError("Entry for element " + data_elements[i] + " is negative but" " log-scale is selected") color_mapper = LogColorMapper(palette=bokeh_palette, low=min(data), high=max(data)) norm = LogNorm(vmin=min(data), vmax=max(data)) color_scale = ScalarMappable(norm=norm, cmap=cmap).to_rgba(data, alpha=None) # Define color for blank entries blank_color = "#c4c4c4" color_list = [] for i in range(len(elements)): color_list.append(blank_color) # Compare elements in dataset with elements in periodic table for i in range(len(data)): element_entry = elements.symbol[elements.symbol.str.lower() == data_elements[i].lower()] if not element_entry.empty: element_index = element_entry.index[0] else: print("WARNING: Invalid chemical symbol: " + data_elements[i]) if color_list[element_index] != blank_color: print("WARNING: Multiple entries for element " + data_elements[i]) color_list[element_index] = to_hex(color_scale[i]) # Define figure properties for visualizing data source = ColumnDataSource(data=dict( group=[str(x) for x in elements["group"]], period=[str(y) for y in elements["period"]], sym=elements["symbol"], atomic_number=elements["atomic number"], type_color=color_list, )) # Plot the periodic table p = figure(x_range=group_range, y_range=list(reversed(period_label)), tools="save") p.plot_width = width p.outline_line_color = None p.toolbar_location = "above" p.rect( "group", "period", 0.9, 0.9, source=source, alpha=alpha, color="type_color", ) p.axis.visible = False text_props = { "source": source, "angle": 0, "color": "black", "text_align": "left", "text_baseline": "middle", } x = dodge("group", -0.4, range=p.x_range) y = dodge("period", 0.3, range=p.y_range) p.text(x=x, y="period", text="sym", text_font_style="bold", text_font_size="15pt", **text_props) p.text(x=x, y=y, text="atomic_number", text_font_size="9pt", **text_props) color_bar = ColorBar( color_mapper=color_mapper, ticker=BasicTicker(desired_num_ticks=10), border_line_color=None, label_standoff=6, major_label_text_font_size=cbar_font, location=(0, 0), orientation="vertical", scale_alpha=alpha, width=8, ) if cbar_height is not None: color_bar.height = cbar_height p.add_layout(color_bar, "right") p.grid.grid_line_color = None if save_plot: save(p) else: show(p) return p
def scalar_map_2d(cells, values, clim=None, figure=None, delaunay=False, colorbar=True, colormap=None, unit=None, clabel=None, colorbar_figure=None, xlim=None, ylim=None, **kwargs): """ Plot an interactive 2D scalar map as a colourful image. Arguments: cells (Partition): spatial description of the cells values (pandas.Series or pandas.DataFrame): feature value at each cell/bin, encoded into a colour clim (2-element sequence): passed to :func:`~matplotlib.cm.ScalarMappable.set_clim` figure (bokeh.plotting.figure.Figure): figure handle delaunay (bool or dict): overlay the Delaunay graph; if ``dict``, options are passed to :func:`~tramway.plot.bokeh.plot_delaunay` colorbar (bool or str or dict): add a colour bar; if ``dict``, options are passed to :func:`~bokeh.models.ColorBar` unit/clabel (str): colorbar label, usually the unit of displayed feature colormap (str): colormap name; see also https://matplotlib.org/users/colormaps.html xlim (2-element sequence): lower and upper x-axis bounds ylim (2-element sequence): lower and upper y-axis bounds """ if isinstance(values, pd.DataFrame): feature_name = values.columns[0] values = values.iloc[:,0] # to Series else: feature_name = None if figure is None: assert False figure = plt.figure() polygons = [] xy = cells.tessellation.cell_centers if not xlim or not ylim: xy_min, _, xy_max, _ = mplt._bounding_box(cells, xy) if not xlim: xlim = (xy_min[0], xy_max[0]) if not ylim: ylim = (xy_min[1], xy_max[1]) ix = np.arange(xy.shape[0]) vertices, cell_vertices, Av = mplt.box_voronoi_2d(cells.tessellation, xlim, ylim) try: ok = 0 < cells.location_count except (KeyboardInterrupt, SystemExit): raise except: print(traceback.format_exc()) ok = np.ones(ix.size, dtype=bool) if cells.tessellation.cell_label is not None: ok = np.logical_and(ok, 0 < cells.tessellation.cell_label) map_defined = np.zeros_like(ok) map_defined[values.index] = True ok[np.logical_not(map_defined)] = False ok[ok] = np.logical_not(np.isnan(values.loc[ix[ok]].values)) for i in ix[ok]: vs = cell_vertices[i].tolist() # order the vertices so that they draw a polygon v0 = v = vs[0] vs = set(vs) _vertices = [] #vvs = [] # debug while True: _vertices.append(vertices[v]) #vvs.append(v) vs.remove(v) if not vs: break ws = set(Av.indices[Av.indptr[v]:Av.indptr[v+1]]) & vs if not ws: ws = set(Av.indices[Av.indptr[v0]:Av.indptr[v0+1]]) & vs if ws: _vertices = _vertices[::-1] else: #print((v, vs, vvs, [Av.indices[Av.indptr[v]:Av.indptr[v+1]] for v in vs])) warn('cannot find a path that connects all the vertices of a cell', RuntimeWarning) break v = ws.pop() # if _vertices: _vertices = np.vstack(_vertices) polygons.append((_vertices[:,0], _vertices[:,1])) scalar_map = values.loc[ix[ok]].values vmin, vmax = scalar_map.min(), scalar_map.max() clim = {} if clim is None else dict(vmin=clim[0], vmax=clim[1]) scalar_map = mpl.colors.Normalize(**clim)(scalar_map) if colormap: color_map = mpl.cm.get_cmap(colormap) else: color_map = mpl.cm.viridis colors = [ "#%02x%02x%02x" % (int(r), int(g), int(b)) for r, g, b, _ in 255*color_map(scalar_map) ] patch_kwargs = dict(fill_color=colors, line_width=0) figure.patches(*zip(*polygons), **patch_kwargs) if delaunay or isinstance(delaunay, dict): if not isinstance(delaunay, dict): delaunay = {} plot_delaunay(cells, figure=figure, **delaunay) figure.x_range = Range1d(*xlim) figure.y_range = Range1d(*ylim) if colorbar: low = clim.get('vmin', vmin) high = clim.get('vmax', vmax) color_map = 'Viridis256' if colormap is None else colormap color_map = LinearColorMapper(palette=color_map, low=low, high=high) color_bar = ColorBar(color_mapper=color_map, ticker=BasicTicker(), border_line_color=None, margin=0) color_bar.background_fill_color = None if unit is None: unit = clabel if colorbar_figure is None: if unit is not None: color_bar.title = unit figure.add_layout(color_bar, place='right') else: if unit is not None: colorbar_figure.title.text = unit colorbar_figure.title_location = 'right' colorbar_figure.title.align = 'center' color_bar.height = colorbar_figure.plot_height if isinstance(colorbar_figure.center[-1], ColorBar): colorbar_figure.center = colorbar_figure.center[:-1] colorbar_figure.add_layout(color_bar, place='center')
def add_colorbar(figure, colormap=None, low=0, high=1, unit=None, clabel=None, colorbar_figure=None): if colormap is None: color_map = "Viridis256" elif isinstance(colormap, str): if colormap.lower() in ( "greys", "inferno", "magma", "plasma", "viridis", "cividis", "turbo", ): color_map = colormap[0].upper() + colormap[1:].lower() + "256" else: color_map = mpl.cm.get_cmap(colormap) color_map = [ "#%02x%02x%02x" % (int(r), int(g), int(b)) for r, g, b, _ in 255 * color_map(np.linspace(0, 1, 256)) ] else: color_map = colormap try: color_map = LinearColorMapper(palette=color_map, low=low, high=high, name="color_mapper") except ValueError as e: try: import colorcet except ImportError: raise ValueError( "colormap not found; try installing the colorcet package" ) from e else: raise color_bar = ColorBar( color_mapper=color_map, ticker=BasicTicker(name="ticker"), border_line_color=None, margin=0, name="color_bar", ) color_bar.background_fill_color = None # glyph_renderers.append(color_bar) if unit is None: unit = clabel if colorbar_figure is None: if unit is not None: color_bar.title = unit figure.add_layout(color_bar, place="right") else: if unit is not None: colorbar_figure.title.text = unit colorbar_figure.title_location = "right" colorbar_figure.title.align = "center" color_bar.height = colorbar_figure.plot_height if isinstance(colorbar_figure.center[-1], ColorBar): colorbar_figure.center = colorbar_figure.center[:-1] colorbar_figure.add_layout(color_bar, place="center")
def get_periodic_table_plot(): """Plot the periodic table with colors (returns bokeh figure)""" global names symbols, atomic_numbers, groups, periods, group_range, period_label = initialize_periodic_table( ) # get ColumnDataSource for values on periodic table source_allEF = get_data_on_perdic_table(load_data=True) # EF=0 starting values source = source_allEF[0] # create figure plot = bkp.figure(x_range=group_range, y_range=list(reversed(period_label)), tools='pan,wheel_zoom,reset,save', title='Impurities in Sb2Te3 (6QL)') # set up hover tool hover = HoverTool() hover.tooltips = [ ("Element", "@sym" ), # things displayed by hover tool, needs to be in 'source' dict ("# impcalcs", "@num_impcalcs"), ("% magnetic", "@percent_mag"), ("<spin mom>", "@smom_mean (+/-@smom_std)"), ("<orb mom>", "@omom_mean (+/-@omom_std)"), ("<charge doping>", "@charge_doping_mean (+/-@charge_doping_std)"), ("<DOS in gap>", "@DOSinGap_mean (+/-@DOSinGap_std)"), ] # add hover tool to plot plot.tools.append(hover) plot.plot_width = width plot.min_width = width plot.max_width = width * 2 plot.sizing_mode = 'scale_both' plot.outline_line_color = None plot.toolbar_location = 'above' # coloured patches for the elements: rects = plot.rect('group', 'period', 0.9, 0.9, source=source, alpha=alpha, color='type_color') plot.axis.visible = False # show axis? text_props = { 'source': source, 'angle': 0, 'color': 'black', 'text_align': 'left', 'text_baseline': 'middle' } # add text for all pairs of (x,y)=(group,period) x = dodge("group", -0.4, range=plot.x_range) y = dodge("period", 0.3, range=plot.y_range) y2 = dodge("period", -0.3, range=plot.y_range) # to displat 'c_value' entry as well # here add the texts inside atom boxes plot.text(x=x, y='period', text='sym', text_font_style='bold', text_font_size='16pt', **text_props) plot.text(x=x, y=y, text='atomic_number', text_font_size='11pt', **text_props) txt = { 0: 'num_impcalcs', 1: 'rms_mean', 2: 'rms_std', 3: 'smom_mean', 4: 'smom_std', 5: 'omom_mean', 6: 'omom_std', 7: 'nummag', 8: 'percent_mag', 9: 'charge_doping_mean', 10: 'DOSinGap_mean' }[0] color_value = plot.text(x=x, y=y2, text=txt, text_font_size='8pt', name='color_value', **text_props) # uses y2 # deactivate grid plot.grid.grid_line_color = None # title of color bar names = [ '# imps', '<rms>', 'rms std', '<spin mom>', 'spin std', '<orb mom>', 'orbital mom std', '# magnetic', '% magnetic', '<Charge doping>', 'charge dop std', '<DOS in gap>', 'dos in gap std' ] title_name = names[0] # for log scale use this as ticker from bokeh.models import LogTicker, BasicTicker ticker = BasicTicker(desired_num_ticks=10) # add color bar color_bar = ColorBar(color_mapper=color_mapper_all[0], ticker=ticker, border_line_color=None, label_standoff=cbar_standoff, location=(0, 0), orientation='vertical', scale_alpha=alpha, major_label_text_font_size=str(cbar_fontsize) + 'pt', title=title_name, formatter=formatter_int, padding=20) if cbar_height is not None: color_bar.height = cbar_height plot.add_layout(color_bar, 'right') """ color_bar_plot = bk.figure(title="My color bar title", title_location="right", plot_width=100, min_width=100, sizing_mode='stretch_both', toolbar_location=None) color_bar_plot.add_layout(color_bar, 'right') color_bar_plot.title.align="center" color_bar_plot.title.text_font_size = '12pt' """ return plot, source, source_allEF, color_bar