pandas - 在直方图的相对 bin 上叠加箱线图

标签 pandas matplotlib seaborn

以数据集'tip'为例

<表类="s-表"> <头> total_bill 提示 吸烟者 <日>日 <次>次 大小 <正文> 16.99 1.01 没有 太阳 晚餐 2 10.34 1.66 没有 太阳 晚餐 3 21.01 3.50 没有 太阳 晚餐 3 23.68 3.31 没有 太阳 晚餐 2 24.59 3.61 没有 太阳 晚餐 4

我想做的是表示变量“total_bill”的分布,并将其每个容器与链接的变量“tip”的分布相关联给它。在此示例中,此图旨在回答以下问题:“客户留下的小费随他们支付的账单的分布情况如何?”

我已经或多或少地实现了我想要获得的图表(但是有一个问题,最后我解释它是什么)。
而我采用的流程是这样的:

  1. 将“total_bill”分成多个箱子。

    tips['bins_total_bill'] = pd.cut(tips.total_bill, 10)
    tips.head()
    
    <表类="s-表"> <头> total_bill 提示 吸烟者 <日>日 <次>次 大小 bins_total_bill <正文> 16.99 1.01 没有 太阳 晚餐 2 (12.618, 17.392] 10.34 1.66 没有 太阳 晚餐 3 (7.844, 12.618] 21.01 3.50 没有 太阳 晚餐 3 (17.392, 22.166] 23.68 3.31 没有 太阳 晚餐 2 (22.166, 26.94] 24.59 3.61 没有 太阳 晚餐 4 (22.166, 26.94]
  2. 使用以下内容创建 pd.Series:
    索引:total_cost bins 的 pd.interval
    值:出现次数

    s = tips['bins_total_bill'].value_counts(sort=False)
    s
    
(3.022, 7.844]       7
(7.844, 12.618]     42
(12.618, 17.392]    68
(17.392, 22.166]    51
(22.166, 26.94]     31
(26.94, 31.714]     19
(31.714, 36.488]    12
(36.488, 41.262]     7
(41.262, 46.036]     3
(46.036, 50.81]      4
Name: bins_total_bill, dtype: int64
  1. 结合barplot和poxplot

    fig, ax1 = plt.subplots(dpi=200)
    ax2 = ax1.twinx()
    
    sns.barplot(ax=ax1, x = s.index, y = s.values)
    sns.boxplot(ax=ax2, x='bins_total_bill', y='tip', data=tips)
    sns.stripplot(ax=ax2, x='bins_total_bill', y='tip', data=tips, size=5, color="yellow", edgecolor='red', linewidth=0.3)
    
    #Title and axis labels
    ax1.tick_params(axis='x', rotation=90)
    ax1.set_ylabel('Number of bills')
    ax2.set_ylabel('Tips [$]')
    ax1.set_xlabel("Mid value of total_bill bins [$]")
    ax1.set_title("Tips ~ Total_bill distribution")
    
    #Reference lines average(tip) + add yticks + Legend
    avg_tip = np.mean(tips.tip)
    ax2.axhline(y=avg_tip, color='red', linestyle="--", label="avg tip")
    ax2.set_yticks(list(ax2.get_yticks() + avg_tip))
    ax2.legend(loc='best')
    
    #Set labels axis x
    ax1.set_xticklabels(list(map(lambda s: round(s.mid,2), s.index)))
    

    barplot and poxplot together

不得不说,这张图有问题!由于 x 轴是分类的,例如,我不能在“total_bill”的平均值处添加一条垂直线。

如何解决这个问题以获得正确的结果? 我也想知道是否有比我采用的方法更正确、更精简的方法。

最佳答案

我想到了这个方法,它比上一个更紧凑(它可能可以做得更好)并且克服了在x轴上缩放的问题。

  1. 我将“total_bill”拆分为 bin 并将该列添加到 Df

    tips['bins_total_bill'] = pd.cut(tips.total_bill, 10)
    
  2. 按之前创建的 bin 对列“tip”进行分组

    obj_gby_tips = tips.groupby('bins_total_bill')['tip']
    gby_tip = dict(list(obj_gby_tips))
    
  3. 创建字典:
    keys:每个bins间隔的中点
    values: gby tips for each interval

    mid_total_bill_bins = list(map(lambda bins: bins.mid, list(gby_tip.keys())))
    gby_tips = gby_tip.values()
    
    tip_gby_total_bill_bins = dict(zip(mid_total_bill_bins, gby_tips))
    
  4. 通过传递给箱线图的每个矩形来创建图表 每个相应箱子的质心

    fig, ax1 = plt.subplots(dpi=200)
    ax2 = ax1.twinx()
    
    bp_values = list(tip_gby_total_bill_bins.values())
    bp_pos = list(tip_gby_total_bill_bins.keys())
    
    l1 = sns.histplot(tips.total_bill, bins=10, ax=ax1)
    l2 = ax2.boxplot(bp_values, positions=bp_pos, manage_ticks=False, patch_artist=True, widths=2)
    
    #Average tips as hline
    avg_tip = np.mean(tips.tip)
    ax2.axhline(y=avg_tip, color='red', linestyle="--", label="avg tip")
    ax2.set_yticks(list(ax2.get_yticks() + avg_tip)) #add value of avg(tip) to y-axis
    
    #Average total_bill as vline
    avg_total_bill=np.mean(tips.total_bill)
    ax1.axvline(x=avg_total_bill, color='orange', linestyle="--", label="avg tot_bill")
    

然后是结果。

Tips ~ Total_bill distribution

关于pandas - 在直方图的相对 bin 上叠加箱线图,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70598044/

相关文章:

python - 将通过 pandas/pytables 编写的大型 hdf5 数据集转换为 vaex

python pandas 将 3 列数据对传递到循环和 MySQL 查询中

python - 使用 pandas 按列值分组条形图

python - 如何分别绘制多个图?

python - 大量数据的散点图

python - Seaborn 直方图与大数据

python - 使用滚动窗口从数据帧创建 "buffer"矩阵?

python - 使用 Pandas 标记每组的第 N 行

python - Matplotlib 动画 : how to dynamically extend x limits?

python - 如何为拆分的 y 轴制作 ax.twinx()?