Source code for jarvis.analysis.periodic.ptable

"""Module for showing periodic table chemical trends."""

from bokeh.models import (
    ColumnDataSource,
    LinearColorMapper,
    LogColorMapper,
    ColorBar,
    BasicTicker,
)
from bokeh.plotting import figure
from bokeh.io import output_file, save, show
from bokeh.sampledata.periodic_table import elements
from bokeh.transform import dodge
from matplotlib.colors import Normalize, LogNorm, to_hex
from matplotlib.cm import plasma, ScalarMappable

# , inferno, magma, viridis, ScalarMappable

# Reuires bokeh installation https://docs.bokeh.org/en/latest/


[docs]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
""" if __name__ == "__main__": x = plot_ptable_trend() from bokeh.io import export_svgs # For svg x.output_backend = "svg" export_svgs(x, filename="plot.svg") """