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

如何根据KDE图y值调整子图高度及修复四分位线显示问题

解决方案:调整子图高度与修复四分位线显示问题

针对你提出的两个核心需求,我修改了代码并逐一解决,以下是完整的修正代码和关键改动说明:

完整修正代码

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib.ticker import NullFormatter
import numpy as np
import random

def plotverweildauernwithuG(df):
    def rand_float_range(start, end):
        return random.random() * (end - start) + start
    def find_nearest(array, value):
        array = np.asarray(array)
        idx = (np.abs(array - value)).argmin()
        return idx

    df = df[df['method']=='data']
    df['years'] = df['years'].astype('float64')
    sns.set()

    max_cs = df['cs'].max()
    # 先创建子图,暂不设置尺寸
    fig, ax = plt.subplots(nrows=max_cs, sharex=True)
    nrows = len(ax)
    ax[-1].set_xlabel('Verweildauer in Jahren')

    s = [(df["cs"] == t).sum() for t in range(1, max_cs+1)]
    s.insert(0, 0)

    # 第一步:先计算所有子图中KDE曲线的最大y值
    max_kde_y = 0
    kde_data = []  # 存储每个子图的KDE数据点,后续绘制四分位线用
    for i in range(1, max_cs+1):
        verweildauer_i = df[df['cs'] == i]['years']
        # 先绘制KDE获取最大y值,不立即调整图尺寸
        sns.kdeplot(verweildauer_i, ax=ax[i-1], shade=True, cumulative=False)
        data_x, data_y = ax[i-1].lines[0].get_data()
        kde_data.append((data_x, data_y))
        current_max = data_y.max()
        if current_max > max_kde_y:
            max_kde_y = current_max

    # 第二步:根据最大KDE y值调整整个图的高度
    # 每个子图需要的高度要兼顾KDE的最大y值和现有ylim范围(-0.5到1)
    # 这里设置基础高度+KDE峰值对应的额外高度,确保所有子图的KDE都完整显示
    base_height_per_subplot = 1.8  # 基础高度,适配散点和标签
    total_height = base_height_per_subplot * nrows + max_kde_y * 0.8  # 系数可根据实际效果调整
    fig.set_size_inches(10, total_height)
    plt.subplots_adjust(hspace=0)

    # 第三步:重新绘制所有元素并修复四分位线
    for i in range(1, max_cs+1):
        # 获取当前子图的KDE数据
        data_x, data_y = kde_data[i-1]
        # 重新获取数据并绘制散点
        verweildauer_i_nugw = df[(df['cs'] == i) & (df['uGW']==False)]['years']
        cs_bandwith_i_nugw = df[(df['cs'] == i) & (df['uGW']==False)]['cs_bandwith']
        verweildauer_i_ugw = df[(df['cs'] == i) & (df['uGW']==True)]['years']
        cs_bandwith_i_ugw = df[(df['cs'] == i) & (df['uGW']==True)]['cs_bandwith']

        ax[i-1].scatter(verweildauer_i_nugw, cs_bandwith_i_nugw - i - 0.5, c="blue", label='Verweildauer')
        ax[i-1].scatter(verweildauer_i_ugw, cs_bandwith_i_ugw - i - 0.5, c="red", label='Unterer Grenzwert Verweildauer')

        # 计算四分位数值
        quatrilesx = [
            df[df['cs']==i]["years"].describe()['25%'],
            df[df['cs']==i]["years"].describe()['50%'],
            df[df['cs']==i]["years"].describe()['75%']
        ]

        # 修复四分位线:基于子图y轴范围计算比例
        y_min, y_max = ax[i-1].get_ylim()  # 获取当前子图的y轴范围(-0.5, 1)
        y_range = y_max - y_min  # 范围长度为1.5
        for k in range(len(quatrilesx)):
            q_val = quatrilesx[k]
            # 找到KDE曲线上对应四分位x值的y值
            idx = find_nearest(data_x, q_val)
            kde_y_at_q = data_y[idx]
            # 将KDE的y值转换为子图y轴的比例(0-1)
            y_start = 0.05  # 四分位线从底部稍上方开始
            y_end = y_start + (kde_y_at_q / y_range) * 0.9  # 占子图高度的90%区间,避免超出
            ax[i-1].axvline(q_val, ymin=y_start, ymax=y_end, 
                           linestyle='dotted', color='black', linewidth=0.6, alpha=0.8)

        # 设置子图属性
        ax[i-1].set_ylim(-0.5, 1 + max_kde_y * 0.1)  # 稍微扩展y轴上限,确保KDE完整
        ax[i-1].set_ylabel('ZK ' + str(i))
        ax[i-1].set_yticks([])
        ax[i-1].axhline(0, linestyle='--', color='blue', linewidth=0.5, alpha=0.35)

        # 修复图例:避免重复创建
        handles, labels = ax[i-1].get_legend_handles_labels()
        # 去重(散点和KDE可能重复标签)
        unique_handles = []
        unique_labels = []
        for h, l in zip(handles, labels):
            if l not in unique_labels:
                unique_labels.append(l)
                unique_handles.append(h)
        ax[i-1].legend(unique_handles, unique_labels)

        # 添加右侧计数标签
        ax2 = ax[i-1].twinx()
        ax2.grid(False)
        ax2.set_ylabel("n = {}".format(s[i]), rotation=0, labelpad=25)
        ax2.set_yticks([])

    plt.show()

关键改动说明

1. 子图高度自适应KDE最大y值

  • 问题根源:原代码在循环中逐次调整图尺寸,且仅用单个KDE的y最大值作为高度,没有考虑子图数量和每个子图的基础空间需求,导致最后一个子图的KDE可能被截断。
  • 修复方式:
    • 先遍历所有子图,计算所有KDE曲线的最大y值max_kde_y。
    • 基于子图数量和最大KDE值计算总高度,确保每个子图有足够空间显示KDE曲线。
    • 扩展子图的y轴上限(1 + max_kde_y * 0.1),避免KDE曲线被ylim截断。

2. 修复四分位线不可见问题

  • 问题根源:原代码中ymin和ymax的计算逻辑错误(使用height/width的比例),导致四分位线的位置超出子图可视范围,或者长度为0。
  • 修复方式:
    • 基于子图实际的y轴范围(y_min到y_max)计算比例,将KDE的y值转换为0-1之间的相对位置。
    • 调整四分位线的起始位置(y_start=0.05),确保从子图底部可见,结束位置根据KDE的y值动态计算,保证线条长度与KDE高度匹配。
    • 优化图例去重,避免重复添加导致的显示混乱。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 17:37:52