使用Plotly Express facet_col绘制散点图颜色异常,问题出在哪?
Plotly分面散点图色阶不符合预期问题
问题场景
- 代码生成两类数据:负数据(z值约-100)集中在左下角,正数据(z值约+100)集中在右上角
- 使用
facet_col='type'拆分正负数据为两个子图后,负数据子图仍沿用全数据集的色阶范围(-100到+100,包含红、蓝),但预期负数据子图仅显示红色系色阶
使用Plotly版本:5.18.0
原代码
import pandas as pd import plotly.express as px import numpy as np import plotly print("plotly.__version__=", plotly.__version__) np.random.seed(1) # 第一组数据配置 mean0 = np.array([0., 0.]) cov0 = np.array([[1., 0.], [0., 1.]]) size0 = 10000 print("size0=", size0) # 第二组数据配置 mean1 = np.array([10., 10.]) cov1 = np.array([[.5, 0.], [0., .5]]) size1 = 100 # 生成第一组数据 vals0 = np.random.multivariate_normal(mean0, cov0, size0) # 添加z列 vals0 = np.append(vals0, [[-1] for x in range(size0)], axis=1) # 填充负z值 vals0[:, 2] = -100.0 + 0.2 * np.random.random(size0) # 生成第二组数据 vals1 = np.random.multivariate_normal(mean1, cov1, size1) # 添加z列 vals1 = np.append(vals1, [[-1] for x in range(size1)], axis=1) # 填充正z值 vals1[:, 2] = 100.0 - 0.2 * np.random.random(size1) # 合并数据 vals2 = np.append(vals0, vals1, axis=0) # 转为DataFrame df = pd.DataFrame(vals2, columns=['x', 'y', 'z']) df['type'] = df.z.apply(lambda z: 'negative' if z < 0 else "positive") fig1 = px.scatter(df, x='x', y='y', color='z', color_continuous_scale=["red", "blue", ]) fig2 = px.scatter(df, x='x', y='y', color='z', facet_col='type', color_continuous_scale=["red", "blue", ]) fig1.show() fig2.show()
原因分析
Plotly Express的分面功能默认共享全数据集的色阶范围,因此即使子图仅包含负数据,色阶仍会覆盖正数据的最大值,导致负数据子图出现蓝色系色阶。
解决方法
手动遍历每个分面子图,为其设置独立的色轴范围(对应子图自身的z值区间),同时保持色阶一致。
修改后的代码
import pandas as pd import plotly.express as px import numpy as np import plotly print("plotly.__version__=", plotly.__version__) np.random.seed(1) # 第一组数据配置 mean0 = np.array([0., 0.]) cov0 = np.array([[1., 0.], [0., 1.]]) size0 = 10000 print("size0=", size0) # 第二组数据配置 mean1 = np.array([10., 10.]) cov1 = np.array([[.5, 0.], [0., .5]]) size1 = 100 # 生成第一组数据 vals0 = np.random.multivariate_normal(mean0, cov0, size0) # 添加z列 vals0 = np.append(vals0, [[-1] for x in range(size0)], axis=1) # 填充负z值 vals0[:, 2] = -100.0 + 0.2 * np.random.random(size0) # 生成第二组数据 vals1 = np.random.multivariate_normal(mean1, cov1, size1) # 添加z列 vals1 = np.append(vals1, [[-1] for x in range(size1)], axis=1) # 填充正z值 vals1[:, 2] = 100.0 - 0.2 * np.random.random(size1) # 合并数据 vals2 = np.append(vals0, vals1, axis=0) # 转为DataFrame df = pd.DataFrame(vals2, columns=['x', 'y', 'z']) df['type'] = df.z.apply(lambda z: 'negative' if z < 0 else "positive") fig1 = px.scatter(df, x='x', y='y', color='z', color_continuous_scale=["red", "blue", ]) fig2 = px.scatter(df, x='x', y='y', color='z', facet_col='type', color_continuous_scale=["red", "blue", ]) # 遍历每个分面子图,设置独立色轴范围 for idx, facet_type in enumerate(df['type'].unique()): # 获取当前分面的z值区间 facet_data = df[df['type'] == facet_type] z_min, z_max = facet_data['z'].min(), facet_data['z'].max() # 配置对应色轴 fig2.update_layout({ f'coloraxis{idx+1}': { 'range': [z_min, z_max], 'colorscale': ["red", "blue"], 'colorbar': {'title': 'z'} } }) # 将子图数据关联到对应色轴 fig2.data[idx].update(coloraxis=f'coloraxis{idx+1}') fig1.show() fig2.show()
效果说明
修改后:
- 负数据子图色阶范围限定为自身z值区间(约-100到-99.8),仅显示红色系
- 正数据子图色阶范围限定为自身z值区间(约99.8到100),仅显示蓝色系
内容的提问来源于stack exchange,提问作者7824238
相关产品推荐
相关产品推荐

