如何用Python实现随minute_play列变化的动态2D直方图热力图
实现随minute_play动态变化的2D直方图(热力图)
我已编写如下可生成静态2D直方图(热力图)的Python代码,但需要创建随
minute_play列动态变化的热力图,请问该如何实现?附上数据样例图、静态输出图及现有代码:multicolor = ["#ffffff","#e2eeff", "#c3d8f9","#80bdff", "#42b0f5", "#a0e242", "#d1cb1c", "#a06a20", "#872a10", "#630d02", "#000000"] bins = 100 densities,_,_ = np.histogram2d(heatmap_df.params_pos_x, heatmap_df.params_pos_y, bins=bins) percentiles = [0,1,10,20,30,40,50,60,70,80,99,100] bounds = [] for i in percentiles: bounds.append(np.percentile(np.unique(densities),i)) cmaplist = multicolor cmap = mpl.colors.LinearSegmentedColormap.from_list( 'Custom cmap', cmaplist, len(percentiles)) norm = mpl.colors.BoundaryNorm(bounds, cmap.N) plt.hist2d(heatmap_df.params_pos_x, heatmap_df.params_pos_y, bins = bins, norm = norm, cmap = cmap) cbar = plt.colorbar(ticks = bounds) plt.show()我曾尝试参考Plotly的热力图动画教程,但不清楚如何将现有代码迁移适配,恳请指导。
解决方案:用Plotly实现按minute_play分帧的动态热力图
核心思路
- 按
minute_play字段分组,为每个时间切片计算独立的2D直方图数据 - 复用你定义的自定义色阶和分位数边界,确保所有帧的颜色标尺统一,避免视觉混乱
- 基于Plotly的
Figure和Frame对象构建可交互的动画组件
完整适配代码
import numpy as np import plotly.graph_objects as go from matplotlib import colors as mpl_colors # 复用原配置:自定义色阶、分位数参数 multicolor = ["#ffffff","#e2eeff", "#c3d8f9","#80bdff", "#42b0f5", "#a0e242", "#d1cb1c", "#a06a20", "#872a10", "#630d02", "#000000"] bins = 100 percentiles = [0,1,10,20,30,40,50,60,70,80,99,100] # 预先计算全局分位数边界(用全量数据保证所有帧颜色标尺一致) full_densities, x_edges, y_edges = np.histogram2d( heatmap_df.params_pos_x, heatmap_df.params_pos_y, bins=bins ) bounds = [np.percentile(np.unique(full_densities), p) for p in percentiles] # 按minute_play分组生成动画帧 frames = [] unique_minutes = sorted(heatmap_df['minute_play'].unique()) # 排序保证时间顺序 for minute in unique_minutes: # 筛选当前分钟的数据子集 subset = heatmap_df[heatmap_df['minute_play'] == minute] # 计算当前分钟的2D直方图(复用全局bins边缘,保证坐标对齐) densities, _, _ = np.histogram2d( subset.params_pos_x, subset.params_pos_y, bins=[x_edges, y_edges] ) # 添加动画帧 frames.append(go.Frame( data=[go.Heatmap( z=densities.T, # Plotly的z轴需转置以匹配numpy输出格式 x=x_edges, y=y_edges, colorscale=multicolor, zmin=bounds[0], zmax=bounds[-1], colorbar=dict(tickvals=bounds) )], name=str(minute) )) # 创建初始图(用第一个分钟的数据) first_minute = unique_minutes[0] first_subset = heatmap_df[heatmap_df['minute_play'] == first_minute] first_densities, _, _ = np.histogram2d( first_subset.params_pos_x, first_subset.params_pos_y, bins=[x_edges, y_edges] ) fig = go.Figure( data=[go.Heatmap( z=first_densities.T, x=x_edges, y=y_edges, colorscale=multicolor, zmin=bounds[0], zmax=bounds[-1], colorbar=dict(tickvals=bounds, ticktext=[str(p) for p in percentiles]) )], frames=frames, layout=go.Layout( title='随比赛分钟变化的位置热力图', xaxis_title='X坐标', yaxis_title='Y坐标', # 添加播放/暂停控制按钮 updatemenus=[dict( type='buttons', showactive=True, buttons=[dict( label='播放', method='animate', args=[None, dict(frame=dict(duration=500, redraw=True), fromcurrent=True)] ), dict( label='暂停', method='animate', args=[[None], dict(frame=dict(duration=0, redraw=False), mode='immediate')] )] )], # 添加分钟选择滑块 sliders=[dict( steps=[dict( method='animate', args=[[str(minute)], dict(mode='immediate', frame=dict(duration=300))] ) for minute in unique_minutes], currentvalue=dict(prefix='当前分钟: ') )] ) ) fig.show()
关键适配说明
- 统一颜色标尺:用全量数据计算分位数边界,避免不同时间片的颜色标尺波动,确保跨帧对比的一致性
- 坐标对齐:复用全局计算的
x_edges和y_edges,保证所有帧的热力图网格完全重合 - 色阶无缝迁移:直接将原自定义色阶列表传入Plotly的
colorscale参数,完美保留原配色风格 - 交互控制:内置播放/暂停按钮和分钟滑块,支持自动播放和手动切换时间片
内容的提问来源于stack exchange,提问作者Quang Dang Hong
相关产品推荐
相关产品推荐

