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

如何在Matplotlib/Seaborn条形图添加次坐标轴并使点对齐对应条形

Got it, let's tackle this problem step by step. The key challenges here are aligning the median points perfectly with their corresponding bars and setting up the secondary axis correctly. Here's how you can modify your function to achieve exactly what you want:

Modified Function Code

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np

df = pd.DataFrame({
    'Student' : ['Alice', 'Bob', 'Chris', 'Dave', 'Edna', 'Frank'], 
    'Education' : ['HS', 'HS', 'HS', 'College', 'College', 'HS' ], 
    'Household Size': [4, 4, 3, 3, 3, 6 ], 
    'Gender' : ['F', 'M', 'M', 'M', 'F', 'M' ]
})

def MakePercentageFrequencyTable(dataFrame, primaryDimension, secondaryDimension=None, extraAggregatedField=None):
    # Calculate percentage frequency table
    if secondaryDimension is not None:
        # Compute percentages grouped by secondary + primary dimensions
        primaryDimensionPercent = (
            dataFrame.groupby([secondaryDimension, primaryDimension])
            .size()
            .groupby(level=0)
            .apply(lambda x: 100 * x / x.sum())
            .rename('percentage')
            .reset_index()
        )
        # Create grouped bar plot with dodge enabled for easy alignment
        g = sns.catplot(
            x="percentage", 
            y=secondaryDimension, 
            hue=primaryDimension, 
            kind='bar', 
            data=primaryDimensionPercent,
            dodge=True
        )
        ax = g.axes[0, 0]  # Extract the axis from the FacetGrid
    else:
        # Original logic for no secondary dimension
        primaryDimensionPercent = (
            dataFrame[primaryDimension].value_counts(normalize=True)
            .rename('percentage')
            .mul(100)
            .reset_index(drop=False)
        )
        g = sns.catplot(x="percentage", y='index', kind='bar', data=primaryDimensionPercent)
        ax = g.axes[0, 0]
    
    # Handle extra aggregated field for median points and secondary axis
    if extraAggregatedField is not None and secondaryDimension is not None:
        # Calculate median for each subgroup
        median_data = (
            dataFrame.groupby([secondaryDimension, primaryDimension])[extraAggregatedField]
            .median()
            .reset_index()
        )
        
        # Create top-aligned secondary axis
        ax_top = ax.twiny()
        ax_top.set_xlabel(f'{extraAggregatedField} Median')
        
        # Get bar position details for alignment
        y_category_positions = ax.get_yticks()
        bar_width = [bar.get_width() for bar in ax.patches][0]
        dodge_offset = bar_width * 0.4  # Matches seaborn's default dodge spacing
        
        # Plot median points aligned to bar centers
        for idx, row in median_data.iterrows():
            # Find y-axis position for the current secondary category
            y_pos = np.where(ax.get_yticklabels() == row[secondaryDimension])[0][0]
            # Find hue index to adjust horizontal position
            hue_idx = g.hue_order.index(row[primaryDimension])
            # Calculate exact center of the target bar
            bar_center_x = ax.patches[idx].get_x() + bar_width / 2
            # Plot point on secondary axis
            ax_top.scatter(row[extraAggregatedField], y_pos, color='red', zorder=5)
            # Add value label for clarity
            ax_top.text(row[extraAggregatedField], y_pos, f'{row[extraAggregatedField]}', 
                       ha='center', va='center', fontweight='bold')
        
        # Adjust axis limits to fit all elements
        ax_top.set_xlim(median_data[extraAggregatedField].min() - 0.5, median_data[extraAggregatedField].max() + 0.5)
        ax.set_xlim(0, primaryDimensionPercent['percentage'].max() + 5)
    
    # Clean up layout
    plt.tight_layout()
    return g

# Example call with all required parameters
MakePercentageFrequencyTable(
    dataFrame=df,
    primaryDimension='Education', 
    secondaryDimension='Gender',
    extraAggregatedField='Household Size'
)

Key Details & Explanations

  • Median Calculation: We group by both the primary and secondary dimensions to compute the exact median for each subgroup (e.g., Female/College, Male/HS).
  • Precise Alignment:
    • We extract the y-position of each secondary category (like Female/Male on the y-axis) and the width of individual bars.
    • Using Seaborn's default dodge spacing, we calculate the center of each bar to place the median point perfectly on top.
  • Secondary Axis:
    • ax.twiny() creates a top axis that shares the same y-axis as the bar plot, so points stay aligned vertically.
    • We adjust the secondary axis limits based on the median values to ensure all points are visible and properly spaced.
  • Visibility: Setting zorder=5 ensures the red median points appear above the bars, making them easy to spot.

What You'll See

When you run the example:

  • The original percentage bars for each Gender/Education subgroup are displayed.
  • A top axis labeled "Household Size Median" shows the scale for median values.
  • Red points (with value labels) are aligned exactly to the center of their corresponding bars, showing the median household size for each subgroup (e.g., 3 for Female/College, 4 for Male/HS).

内容的提问来源于stack exchange,提问作者dnb

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 13:02:51