Okay so I’m actually a biologist so I do not know how to make the example minimal, but here is the data file and a reworked version of the code (I am also french so I had to translate the commentaries, I hope it is understandable).
Link to the datafile: https://drive.google.com/file/d/1NiYTbkoI66pXrsCCYIDm3uz5ugu2dFO9/view?usp=sharing
import math
import bokeh as bk
import pandas as pd
tmpdf = pd.read_csv('path/to/file.csv', sep=';')
df_replicate_mean = tmpdf.groupby(["condition_order", "condition", "time", "isotopologue"])['mean_enrichment'].mean()
df_replicate_std = tmpdf.groupby(["condition_order", "condition", "time", "isotopologue"])['mean_enrichment'].std()
mean_df = df_replicate_mean.to_frame()
std_df = df_replicate_std.to_frame()
#Here we put the means into a df and clean up a bit
mean_df_unstack = mean_df.unstack()
mean_df_unstack = mean_df_unstack.droplevel(level = 0)
mean_df_unstack.columns = mean_df_unstack.columns.droplevel(level = 0)
mean_df_unstack.columns = mean_df_unstack.columns.astype(str)
mean_df_unstack.index = ['{}_{}'.format(i, j) for i, j in mean_df_unstack.index] #We rebuild ID column
#Columns have the same values, so we just take the first
mean_series = mean_df_unstack.iloc[:, 0].copy()
#Here we put the Stds into a df and clean up a bit
std_df_unstack = std_df.unstack()
std_df_unstack = std_df_unstack.droplevel(level = 0)
std_df_unstack.columns = std_df_unstack.columns.droplevel(level = 0)
std_df_unstack.columns = std_df_unstack.columns.astype(str)
std_df_unstack.index = ['{}_{}'.format(i, j) for i, j in std_df_unstack.index]
#Columns have the same values, so we just take the first
std_series = std_df_unstack.iloc[:, 0].copy()
#We prepare tops and bottoms for the error bars
upper_series = mean_series.add(std_series, fill_value=0)
upper_list = upper_series.to_list()
lower_series = mean_series.sub(std_series, axis='index', fill_value=0)
lower_list = lower_series.to_list()
#We prepare data to be plotted
my_x_range = mean_series.index.tolist()
values = mean_series.to_list()
my_dict = dict(ID=my_x_range, tops=values)
source = bk.models.ColumnDataSource(dict(ID=my_x_range, tops=values))
print("base = {}".format(my_x_range))
print("upper = {}".format(upper_list))
print("lower = {}".format(lower_list))
whisker_dico = dict(base = my_x_range, upper = upper_list, lower = lower_list)
source_error = bk.models.ColumnDataSource(whisker_dico)
#Passons au plot
TOOLTIPS = [
("", "@ID"),
("value", "@tops"),
]
myplot = figure(plot_width=1400,
plot_height=800,
title='test',
y_axis_label="mean_enrichment",
tools="save, wheel_zoom, reset, hover",
tooltips = TOOLTIPS,
x_range=my_x_range)
myplot.vbar(width=0.9,
bottom=0,
top='tops',
x='ID',
source = source)
#Nous ajoutons les barres d'erreurs
myplot.add_layout(bk.models.Whisker(source = source_error,
base = "base",
upper = "upper",
lower = "lower",
level="overlay"))
myplot.xaxis.major_label_orientation = math.pi/4
show(myplot)