You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

判断是否为足球场图及Matplotlib实现方案(含数据示例)

问题

我拥有一个包含特定列的DataFrame,列名格式为MotivX_MotivatorY_InnerDriverZ_PR,其中"PR"代表Percentile Rank(百分位排名,取值0-100)。每个动机(如MotivA)包含四个激励因子,每个激励因子对应InnerDriverA_PR和InnerDriverB_PR两个数值。
我希望生成的图表样式参考示例图,请问该图表是否为"Football Field Chart"?如何用Matplotlib实现该图表?

附最小可复现代码:

import pandas as pd
import random
import matplotlib.pyplot as plt
import seaborn as sns

# Create example data
columns = [
    'MotivA_MotivatorA_InnerDriverA_PR',
    'MotivA_MotivatorA_InnerDriverB_PR',
    'MotivA_MotivatorB_InnerDriverA_PR',
    'MotivA_MotivatorB_InnerDriverB_PR',
    'MotivA_MotivatorC_InnerDriverA_PR',
    'MotivA_MotivatorC_InnerDriverB_PR',
    'MotivA_MotivatorD_InnerDriverA_PR',
    'MotivA_MotivatorD_InnerDriverB_PR',
    'MotivB_MotivatorA_InnerDriverA_PR',
    'MotivB_MotivatorA_InnerDriverB_PR',
    'MotivB_MotivatorB_InnerDriverA_PR',
    'MotivB_MotivatorB_InnerDriverB_PR',
    'MotivB_MotivatorC_InnerDriverA_PR',
    'MotivB_MotivatorC_InnerDriverB_PR',
    'MotivB_MotivatorD_InnerDriverA_PR',
    'MotivB_MotivatorD_InnerDriverB_PR'
]

df = pd.DataFrame(columns=columns)

for i in range(1, 6):  
    df.loc[f'Subject_{i}'] = [random.randint(0, 100) for _ in range(len(columns))]

#────────────────────────────────────────────────

def create_horizontal_bar_chart(df, proband):
    motives = sorted(set(col.split('_')[0] for col in df.columns))

    for motive in motives:
        columns = [col for col in df.columns if col.startswith(motive)]

        data = df.loc[proband, columns].reset_index()

        data['Motivator'] = data['index'].apply(lambda x: x.split('_')[1])
        data['InnerDriver'] = data['index'].apply(lambda x: x.split('_')[2])
        data['Value'] = data[proband]
        data = data.drop(['index', proband], axis=1)

        plt.figure(figsize=(10, 6))
        sns.barplot(x='Value', y='Motivator', hue='InnerDriver', data=data)
        plt.title(f'{proband} - {motive}')
        plt.show()

create_horizontal_bar_chart(df, 'Subject_1')

当前代码生成分组水平条形图,与目标图表效果差距较大。


解答

1. 图表类型判断

你提到的参考图表不是Football Field Chart。Football Field Chart主要用于展示多类别在不同分位数区间的分布(常见于金融估值场景),而你的目标图表属于带背景区间的并列横向条形图,核心是将每个激励因子对应的两个InnerDriver数值并列展示,同时通过背景色块区分百分位层级,直观体现数值所处的排名区间。

2. Matplotlib实现方案

以下是修改后的代码,可实现类似目标图表的效果:

import pandas as pd
import random
import matplotlib.pyplot as plt
import numpy as np

# 生成示例数据(复用原代码)
columns = [
    'MotivA_MotivatorA_InnerDriverA_PR',
    'MotivA_MotivatorA_InnerDriverB_PR',
    'MotivA_MotivatorB_InnerDriverA_PR',
    'MotivA_MotivatorB_InnerDriverB_PR',
    'MotivA_MotivatorC_InnerDriverA_PR',
    'MotivA_MotivatorC_InnerDriverB_PR',
    'MotivA_MotivatorD_InnerDriverA_PR',
    'MotivA_MotivatorD_InnerDriverB_PR',
    'MotivB_MotivatorA_InnerDriverA_PR',
    'MotivB_MotivatorA_InnerDriverB_PR',
    'MotivB_MotivatorB_InnerDriverA_PR',
    'MotivB_MotivatorB_InnerDriverB_PR',
    'MotivB_MotivatorC_InnerDriverA_PR',
    'MotivB_MotivatorC_InnerDriverB_PR',
    'MotivB_MotivatorD_InnerDriverA_PR',
    'MotivB_MotivatorD_InnerDriverB_PR'
]

df = pd.DataFrame(columns=columns)
for i in range(1, 6):  
    df.loc[f'Subject_{i}'] = [random.randint(0, 100) for _ in range(len(columns))]

def create_target_chart(df, proband):
    # 获取所有动机类别
    motives = sorted(set(col.split('_')[0] for col in df.columns))
    
    for motive in motives:
        # 筛选当前动机的列
        motive_cols = [col for col in df.columns if col.startswith(motive)]
        # 整理数据:拆分列名,提取数值
        data = df.loc[proband, motive_cols].reset_index()
        data[['Motiv', 'Motivator', 'InnerDriver', 'PR']] = data['index'].str.split('_', expand=True)
        data['Value'] = data[proband]
        data = data.drop(['index', proband, 'Motiv', 'PR'], axis=1)
        
        # 准备绘图数据:按Motivator分组,获取两个InnerDriver的数值
        motivators = sorted(data['Motivator'].unique())
        driver_a_vals = [data[(data['Motivator'] == m) & (data['InnerDriver'] == 'InnerDriverA')]['Value'].values[0] for m in motivators]
        driver_b_vals = [data[(data['Motivator'] == m) & (data['InnerDriver'] == 'InnerDriverB')]['Value'].values[0] for m in motivators]
        
        # 绘图设置
        fig, ax = plt.subplots(figsize=(12, 6))
        y_pos = np.arange(len(motivators))
        bar_width = 0.35
        
        # 绘制背景百分位区间色块
        ax.axvspan(0, 25, color='#f8d7da', alpha=0.5)
        ax.axvspan(25, 50, color='#fff3cd', alpha=0.5)
        ax.axvspan(50, 75, color='#d4edda', alpha=0.5)
        ax.axvspan(75, 100, color='#d1ecf1', alpha=0.5)
        
        # 绘制并列横向条形图
        bar1 = ax.barh(y_pos - bar_width/2, driver_a_vals, bar_width, label='InnerDriverA')
        bar2 = ax.barh(y_pos + bar_width/2, driver_b_vals, bar_width, label='InnerDriverB')
        
        # 添加数值标签
        for bar in bar1:
            width = bar.get_width()
            ax.text(width + 1, bar.get_y() + bar.get_height()/2, f'{int(width)}', va='center')
        for bar in bar2:
            width = bar.get_width()
            ax.text(width + 1, bar.get_y() + bar.get_height()/2, f'{int(width)}', va='center')
        
        # 设置坐标轴与标题
        ax.set_yticks(y_pos)
        ax.set_yticklabels(motivators)
        ax.set_xlabel('Percentile Rank (0-100)')
        ax.set_title(f'{proband} - {motive}')
        ax.set_xlim(0, 105)
        ax.legend()
        
        plt.tight_layout()
        plt.show()

# 生成Subject_1的图表
create_target_chart(df, 'Subject_1')

代码关键点说明

  • 数据整理:通过str.split拆分列名,提取出Motivator和InnerDriver维度,将数据转换为适合绘图的结构
  • 背景区间:用ax.axvspan绘制四个百分位区间(0-25、25-50、50-75、75-100)的色块,模拟参考图的层级背景
  • 并列条形:通过调整y轴位置实现两个InnerDriver的条形并列,同时添加数值标签增强可读性
  • 样式优化:设置x轴范围、调整布局,让图表更贴近目标效果

内容的提问来源于stack exchange,提问作者Chris B.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 01:47:10