【问题标题】:Mark specific value in Boxplot using Pandas使用 Pandas 在 Boxplot 中标记特定值
【发布时间】:2018-05-28 14:00:53
【问题描述】:

我已经使用 pandas 数据框创建了一个箱线图,现在我想在同一个图中用“X”标记特定值(希望是红色的!)。

一些数据:

df = pd.DataFrame(
[
[2, 4, 5, 6, 1],
[4, 5, 6, 7, 2],
[5, 4, 5, 5, 1],
[10, 4, 7, 8, 2],
[9, 3, 4, 6, 2],
[3, 3, 4, 4, 1]
], columns=['a1', 'a2', 'a3', 'a4', 'b'])

mark_values = pd.DataFrame(
[
[2,1],
[8.25,2]
], columns=['a1', 'b'])

df_long = pd.melt(df, "b", var_name="a", value_name="c")
g = sns.boxplot(x='c', y='a', hue='b', data=df_long, 
palette=sns.color_palette("Blues_d"), orient='h')
sns.despine(left=True)

这会生成一个箱线图。我现在想将标记添加为红十字,例如将类别 a1、子组 1 标记为“4”处的 X,以及子组 2 标记为“8.25”处的 X 等等,但仍然保留我漂亮的箱线图。

应该按照上面定义的数据框mark_values 定义和存储要标记的值。如示例:

mark_values

Out[1]: 
     a1  b
0  4.00  1
1  8.25  2

有什么简单的解决办法吗?

谢谢

【问题讨论】:

    标签: python pandas seaborn boxplot


    【解决方案1】:

    首先,我想定义mark_values 以包含一个指定应标记哪个"a" 的列是有意义的,例如要标记"a1",请在a 列中输入1。

          c  a  b
    0  2.00  1  1
    1  8.25  1  2
    

    然后您可以绘制带有“x”作为标记的散点图,其中散点坐标是水平方向的 c 列,垂直方向由

    y = (a-1)+(b-1.5)*0.4
    

    解释一下:

    • a 从 1 开始,但第一个类别在 0 处绘制,
    • 此处所有 b 值的平均值为 1.5。
    • 一半的条宽是0.4

    总共给出:

    import matplotlib.pyplot as plt
    import pandas as pd
    import seaborn as sns
    
    
    df = pd.DataFrame( [[2, 4, 5, 6, 1],
                        [4, 5, 6, 7, 2],
                        [5, 4, 5, 5, 1],
                        [10, 4, 7, 8, 2],
                        [9, 3, 4, 6, 2],
                        [3, 3, 4, 4, 1]], 
                    columns=['a1', 'a2', 'a3', 'a4', 'b'])
    
    mark_values = pd.DataFrame( [ [2,1,1], [8.25,1,2], [4,3,2] ], columns=['c',"a",'b'])
    print mark_values
    df_long = pd.melt(df, "b", var_name="a", value_name="c")
    
    ax = sns.boxplot(x='c', y='a', hue='b', data=df_long, 
                    palette=sns.color_palette("Blues_d"), orient='h')
    sns.despine(left=True)
    
    y = (mark_values["a"].values - 1)+(mark_values["b"].values-1.5)*0.4
    ax.scatter(mark_values["c"].values, y, marker="x", c="red", s=400, lw=6)
    
    plt.show()
    

    【讨论】:

      【解决方案2】:

      由于 Seaborn 是使用 matplotlib 构建的,因此您可以使用 text

      import pandas as pd
      import seaborn as sns
      
      df = pd.DataFrame(
      [
      [2, 4, 5, 6, 1],
      [4, 5, 6, 7, 2],
      [5, 4, 5, 5, 1],
      [10, 4, 7, 8, 2],
      [9, 3, 4, 6, 2],
      [3, 3, 4, 4, 1]
      ], columns=['a1', 'a2', 'a3', 'a4', 'b'])
      
      mark_values = pd.DataFrame(
      [
      [2,1],
      [8.25,2]
      ], columns=['a1', 'b'])
      
      df_long = pd.melt(df, "b", var_name="a", value_name="c")
      g = sns.boxplot(x='c', y='a', hue='b', data=df_long, 
      palette=sns.color_palette("Blues_d"), orient='h')
      sns.despine(left=True)
      g.text(4,0.1,'X', fontsize=50, color='red')
      g.text(8.25,.5,'X', fontsize=50, color='red')
      

      X 轴只是来自c 的值。但是您也可以使用get_ylim() 来获得您想要的输出。您还可以使用np.linspace 来获取均匀间隔的值:

      import numpy as np
      print(g.get_ylim())
      print(str(g.get_ylim()[0]) + ' is the low value')
      print(str(g.get_ylim()[1]) + ' is the high value')
      print(np.linspace(g.get_ylim()[0], g.get_ylim()[1], 4))
      

      另请注意,“X”的左下角将位于 X 轴和 Y 轴的确切交点处。所以 50 的字体大小使它看起来太大了,看起来 X 是“关闭”的。您可能需要使用这些值,以便“X”在正确的位置。但从你的问题来看,我不确定你想要 X 多大。

      看看这里的区别。似乎-.08.1 对 30 的字体大小进行了很好的调整。绿色的“X”正在使用这些调整后的值。

      g.text(4,2.1666,'X', fontsize=30, color='red')
      g.text(4 - (4*.08) ,2.1666 + (2.1666 * .1),'X', fontsize=30, color='green')
      

      【讨论】:

      • 是的,这可行,但你不会很具体地知道实际标记的位置...假设我想要在“a3”上标记,那么我需要猜测我的y 值是。对吗?
      • @gussilago,检查我的编辑。你可以喜欢get_ylim()。例如将Y 值除以类别数,即可得到图上点的估计值。可能有更优雅的方法,但我的测试可以正常工作。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-05-11
      • 2018-06-10
      • 2017-10-17
      • 2019-10-19
      • 1970-01-01
      • 2021-03-06
      • 2017-11-15
      相关资源
      最近更新 更多