matplotlib 创建“足球场图”

gpfsuwkq  于 2023-06-06  发布在  其他
关注(0)|答案(1)|浏览(124)

我有一个DataFrame,由以下列组成:

VP-ID,
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,
...
MotivC_MotivatorA_InnerDriverA_PR,
MotivC_MotivatorA_InnerDriverB_PR,
MotivC_MotivatorB_InnerDriverA_PR,
MotivC_MotivatorB_InnerDriverB_PR,
MotivC_MotivatorC_InnerDriverA_PR,
MotivC_MotivatorC_InnerDriverB_PR,
MotivC_MotivatorD_InnerDriverA_PR,
MotivC_MotivatorD_InnerDriverB_PR.

后面的名称动机A等。当然是正确的术语(列名)。
这里,“PR”代表百分位数排名(0-100)。
一个图形表示一个动机,它由四个动机和两个变量组成,然后具有InnerDriverA_PR和InnerDriverB_PR的值。
最终结果应该如下所示:

这是“足球场图”吗
我如何用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')

然而,这创造了激励因素作为额外的酒吧,仍然远远没有达到我希望它,如在上面的例子。

7kjnsjlb

7kjnsjlb1#

这是脊柱图问题是它不会排列你的酒吧。要做到这一点,你需要有创造力:

import pandas as pd
import random
import matplotlib.pyplot as plt
import seaborn as sns
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):  # 5 Probanden
    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[df.index == proband].reset_index()

        # Rename the new column to "Subject"
        data = data.rename(columns = {"index": "Subject"})
        
        # Melt the dataframe
        data_melted = data.melt(id_vars=["Subject"], var_name="Motiv_Motivator_InnerDriver_PR", value_name="PR")
        
        # Create new columns from the "Motiv_Motivator_InnerDriver_PR" column
        data_melted[['Motiv', 'Motivator', 'InnerDriver', '_']] = data_melted['Motiv_Motivator_InnerDriver_PR'].str.split("_",expand=True)
        data_melted = data_melted[data_melted['Motiv'] == motive]
        
        # Drop unnecessary columns
        data_melted = data_melted.drop(columns=['Motiv_Motivator_InnerDriver_PR', '_'])
        
        # Reorder the columns
        data_melted = data_melted[['Subject', 'Motiv', 'Motivator', 'InnerDriver', 'PR']]
        
        # Pivot the table
        data_pivot = pd.pivot_table(data_melted, values='PR', index=['Subject', 'Motiv', 'Motivator'],
                            columns='InnerDriver', aggfunc='first').reset_index()

        data_pivot['InnerDriverA'] = -data_pivot['InnerDriverA']
        data_pivot = data_pivot.sort_values('Motivator', ascending=False).reset_index(drop=True)

        fig, ax = plt.subplots(figsize=(10, 8))
        # Stacked bar chart
        data_pivot.plot(kind='barh', x='Motivator', y=['InnerDriverA', 'InnerDriverB'], 
                        ax=ax, stacked=True, color='#5fba7d', alpha=0.5, legend=False)
        
        ax.set_xlabel('PR')
        ax.axvline(0, color='grey', linewidth=4) # Add a vertical line at x=0
        ax.set_xlim(-100, 100)  # set x limit as -100 to 100
        
        # Add horizontal grid lines every 25 units
        ax.set_xticks(range(-100, 101, 25))
        ax.grid(True, axis='x', linestyle='dotted')
        
        # Adjust the x-axis tick labels to display all values as positive
        ax.set_xticklabels([abs(x) for x in ax.get_xticks()], fontsize=16, color='white')
        
        
        # Add y-axis labels
        yticks = np.arange(len(data_pivot))
        yticklabels_left = [f'{motive}   InnerDriverA' for motive in data_pivot['Motivator']]
        yticklabels_right = ['InnerDriverB'] * len(data_pivot)
        ax.set_yticks(yticks)
        ax.set_yticklabels(yticklabels_left, va='center', ha='right', fontsize=14, color='black')
        
        # Calculate y-tick positions for right-side labels
        split = len(data_pivot)
        intervals = np.linspace(0, 1, split + 1)  # Split the number line into specified number of intervals
        yticks_right = (intervals[:-1] + intervals[1:]) / 2  # Compute the midpoints
        
        # Add right-side y-axis labels
        ax2 = ax.twinx()
        ax2.set_yticks(yticks_right)
        ax2.set_yticklabels(yticklabels_right, va='center', ha='left', fontsize=14, color='black')
        

        # Remove x and y tick marks
        ax.tick_params(axis='x', which='both', bottom=False, top=False)
        ax.tick_params(axis='y', which='both', left=False, right=False)
        ax2.tick_params(axis='y', which='both', left=False, right=False)

        # Remove border around the axes
        ax.spines['top'].set_visible(False)
        ax.spines['right'].set_visible(False)
        ax.spines['bottom'].set_visible(False)
        ax.spines['left'].set_visible(False)
        
        # Remove border around the axes
        ax2.spines['top'].set_visible(False)
        ax2.spines['right'].set_visible(False)
        ax2.spines['bottom'].set_visible(False)
        ax2.spines['left'].set_visible(False)

        
        # Add values inside the bars
        for i, row in data_pivot.iterrows():
            value_a = row['InnerDriverA']
            value_b = row['InnerDriverB']
            ax.text(value_a + 2, i, str(-value_a), va='center', ha='left', color='white', fontsize=18, fontweight='bold')
            ax.text(value_b - 2, i, str(value_b),va='center', ha='right', color='white', fontsize=18, fontweight='bold')

        # Create a rectangle to set the background for bottom x-axis tick labels
        rect = plt.Rectangle((-.05, -0.08), 1.10, 0.08, transform=ax.transAxes, color='grey', clip_on=False)
        ax.add_patch(rect)        
        
        plt.title(f'{proband} - {motive}')
        plt.show()

create_horizontal_bar_chart(df, 'Subject_1')

输出:

然后...

相关问题