Source code for mendeleev.vis.bokeh

from collections import OrderedDict

import pandas as pd
from pandas.api.types import is_float_dtype

from bokeh.plotting import figure
from bokeh.models import HoverTool, ColumnDataSource, FixedTicker

from .utils import colormap_column


[docs] def periodic_table_bokeh( elements: pd.DataFrame, attribute: str = "atomic_weight", cmap: str = "RdBu_r", colorby: str = "color", decimals: int = 3, height: int = 800, missing: str = "#ffffff", title: str = "Periodic Table", wide_layout: bool = False, width: int = 1200, ): """ Use Bokeh backend to plot the periodic table. Args: elements : Pandas DataFrame with the elements data. Needs to have `x` and `y` columns with coordianates for each tile. attribute : Name of the attribute to be displayed cmap : Colormap to use, see matplotlib colormaps colorby : Name of the column containig the colors decimals : Number of decimals to be displayed in the bottom row of each cell height : Height of the figure in pixels missing : Hex code of the color to be used for the missing values title : Title to appear above the periodic table wide_layout: wide layout variant of the periodic table width : Width of the figure in pixels """ if any(col not in elements.columns for col in ["x", "y"]): raise ValueError( "Coordinate columns named 'x' and 'y' are required " "in 'elements' DataFrame. Consider using " "'mendeleev.vis.utils.create_vis_dataframe' and try again." ) # additional columns for positioning of the text elements.loc[:, "y_anumber"] = elements["y"] - 0.3 elements.loc[:, "y_name"] = elements["y"] + 0.2 if attribute: elements.loc[elements[attribute].notnull(), "y_prop"] = ( elements.loc[elements[attribute].notnull(), "y"] + 0.35 ) else: elements.loc[:, "y_prop"] = elements["y"] + 0.35 ac = "display_attribute" if is_float_dtype(elements[attribute]): elements[ac] = elements[attribute].round(decimals=decimals) else: elements[ac] = elements[attribute] if colorby == "attribute": colored = colormap_column(elements, attribute, cmap=cmap, missing=missing) elements.loc[:, "attribute_color"] = colored colorby = "attribute_color" # bokeh configuration source = ColumnDataSource(data=elements) TOOLS = "hover,save,reset" fig = figure( title=title, tools=TOOLS, x_axis_location="above", x_range=(elements.x.min() - 0.5, elements.x.max() + 0.5), y_range=(elements.y.max() + 0.5, elements.y.min() - 0.5), width=width, height=height, toolbar_location="above", toolbar_sticky=False, ) fig.rect("x", "y", 0.9, 0.9, source=source, color=colorby, fill_alpha=0.6) # adjust the ticks and axis bounds fig.yaxis.bounds = (1, 7) fig.axis[1].ticker.num_minor_ticks = 0 if wide_layout: # Turn off tick labels fig.axis[0].major_label_text_font_size = "0pt" # Turn off tick marks fig.axis[0].major_tick_line_color = None # turn off major ticks fig.axis[0].ticker.num_minor_ticks = 0 # turn off minor ticks else: fig.axis[0].ticker = FixedTicker(ticks=list(range(1, 19))) text_props = { "source": source, "angle": 0, "color": "black", "text_align": "center", "text_baseline": "middle", } fig.text( x="x", y="y", text="symbol", text_font_style="bold", text_font_size="15pt", **text_props, ) fig.text( x="x", y="y_anumber", text="atomic_number", text_font_size="9pt", **text_props ) fig.text(x="x", y="y_name", text="name", text_font_size="6pt", **text_props) fig.text(x="x", y="y_prop", text=ac, text_font_size="7pt", **text_props) fig.grid.grid_line_color = None hover = fig.select(dict(type=HoverTool)) hover.tooltips = OrderedDict( [ ("name", "@name"), ("atomic number", "@atomic_number"), ("atomic weight", "@atomic_weight"), ("EN Pauling", "@en_pauling"), ("Electron affinity", "@electron_affinity"), ("CPK color", "$color[hex, swatch]:cpk_color"), ("Jmol color", "$color[hex, swatch]:jmol_color"), ("Molcas GV color", "$color[hex, swatch]:molcas_gv_color"), ("electronic configuration", "@electronic_configuration"), ] ) return fig