Bokeh绘图中为每个datapoint分配唯一颜色失败求助
问题:散点图数据点无法按type_id分配唯一颜色,全部显示最后一种颜色
场景与问题
需要为每个数据点根据type_id分配唯一颜色,但运行代码后所有数据点仅显示最后一种颜色。
原始代码
for index, row in df.iterrows(): print(row['type'],row['type_id']) ########################################################### color_index=df['type_id'].max() for index, row in df.iterrows(): color_index = row['type_id'] % len(Category20) # Extract the time values for this event type as a Pandas Series object time_series = df['time'] # Convert each Pandas Timestamp object to a Python datetime object, then to a Unix timestamp (seconds since 1970-01-01) timestamps = [t.to_pydatetime().timestamp() for t in time_series] # Compute the number of seconds since midnight for each timestamp by taking the modulo with 86400 (the number of seconds in a day) seconds_since_midnight = [ts % 86400 for ts in timestamps] # Create a ColumnDataSource object with the data for this event type source_data = dict(x=seconds_since_midnight,y=[0]*len(seconds_since_midnight),desc=time_series.dt.strftime('%Y-%m-%d %H:%M:%S')) for col_name in df.columns: if col_name.startswith('details'): source_data[col_name] = df[col_name].tolist() source = ColumnDataSource(data=source_data) # Add a scatter plot glyph to the figure using the data from this event type's ColumnDataSource object and assign it a color from the Category10 palette and increase its size to 10 pixels. p.scatter('x', 'y', source=source, legend_label=row['type'],color=Category20[20][row['type_id']], size=10) #legend_labels = [item.label['value'] for item in p.legend.items] # Define tooltips dictionary containing label-value pairs for each column starting with 'details' tooltips_dict = {'Time': '@desc'} limited_df = df[['details.start_url','details.username']] for col_name in limited_df.columns: split_col = col_name.rsplit('.', 1) new_col = split_col[1] if len(split_col) > 1 else col_name tooltips_dict[new_col] = f': @{{{col_name}}}' # Add hover tool that displays all columns starting with 'details' when hovering over their dots hover_tool = HoverTool(tooltips=[(label, value) for label,value in tooltips_dict.items()]) # Add hover tool and wheel zoom tool to our plot p.add_tools(hover_tool) # Remove tick lines on y-axis p.yaxis.minor_tick_line_color = None p.yaxis.major_tick_line_color = None p.xaxis.minor_tick_line_color = None p.xaxis.major_tick_line_color = None # Show plot in web browser p.y_range = Range1d(y_min - 0.5, y_max + 0.5) p.yaxis.major_label_text_font_size = '0pt' show(p, width=1000, height=1000)
运行前打印的type与type_id
session_created 1 leader_joined 2 control_gained 3 relocate_start 4 input_change 5 input_change 5 follower_joined 6 control_gained 3 control_gained 3 control_switch 7 host_change 8 control_gained 3 follower_joined 6 control_gained 3 click 9 input_change 5 input_change 5 session_end 10
现象
所有数据点仅显示最后一种颜色,效果图:
问题原因
- 重复绘制所有数据:循环遍历每行时,始终使用整个DataFrame的所有数据创建
source_data,每次调用p.scatter都会把所有点重新画一遍,最后一次循环的颜色会覆盖之前所有点。 - 调色板调用错误:
Category20[20]是无效写法,Category20本身就是长度为20的调色板数组,直接取索引即可。
修复方案
修改核心循环逻辑,按type_id分组处理,只绘制对应组的数据:
# 提前计算所有数据的时间转换结果,避免重复计算 df['seconds_since_midnight'] = df['time'].apply(lambda t: t.to_pydatetime().timestamp() % 86400) df['desc'] = df['time'].dt.strftime('%Y-%m-%d %H:%M:%S') # 按type_id分组,每组对应一种颜色 for type_id, group in df.groupby('type_id'): color_index = type_id % len(Category20) # 仅使用当前分组的数据构建数据源 source_data = { 'x': group['seconds_since_midnight'], 'y': [0] * len(group), 'desc': group['desc'] } # 添加details列数据 for col_name in df.columns: if col_name.startswith('details'): source_data[col_name] = group[col_name].tolist() source = ColumnDataSource(data=source_data) # 获取当前分组的事件类型名称 type_label = group['type'].iloc[0] # 绘制当前分组的点,使用对应颜色 p.scatter('x', 'y', source=source, legend_label=type_label, color=Category20[color_index], size=10) # 以下保留原有的tooltip、轴样式设置代码 tooltips_dict = {'Time': '@desc'} limited_df = df[['details.start_url','details.username']] for col_name in limited_df.columns: split_col = col_name.rsplit('.', 1) new_col = split_col[1] if len(split_col) > 1 else col_name tooltips_dict[new_col] = f': @{{{col_name}}}' hover_tool = HoverTool(tooltips=[(label, value) for label,value in tooltips_dict.items()]) p.add_tools(hover_tool) p.yaxis.minor_tick_line_color = None p.yaxis.major_tick_line_color = None p.xaxis.minor_tick_line_color = None p.xaxis.major_tick_line_color = None p.y_range = Range1d(y_min - 0.5, y_max + 0.5) p.yaxis.major_label_text_font_size = '0pt' show(p, width=1000, height=1000)
修复要点
- 提前计算时间转换结果,提升代码效率
- 使用
groupby('type_id')分组,避免重复绘制所有数据点 - 修正
Category20的调用方式,直接通过索引取对应颜色 - 每个分组仅处理自身数据,确保不同
type_id的点使用对应颜色绘制
内容的提问来源于stack exchange,提问作者Nerds
相关产品推荐
相关产品推荐

