【问题标题】:How to add axis offset in matplotlib plot?如何在 matplotlib 图中添加轴偏移?
【发布时间】:2018-05-06 03:27:22
【问题描述】:

我在 seaborn 中在同一张图上绘制了几个点图。 x 轴是有序的,而不是数字的;每个点图的序数值相同。我想将每个图移到一边,pointplot(dodge=...) 参数在单个图中的多行内执行的方式,但在这种情况下,对于绘制在彼此之上的多个不同的图。我该怎么做?

理想情况下,我想要一种适用于任何 matplotlib 情节的技术,而不仅仅是专门针对 seaborn 的。向数据添加偏移量并不容易,因为数据不是数字。

显示图重叠并使其难以阅读的示例(每个图中的闪避都可以)

import pandas as pd
import seaborn as sns

df1 = pd.DataFrame({'x':list('ffffssss'), 'y':[1,2,3,4,5,6,7,8], 'h':list('abababab')})
df2 = df1.copy()
df2['y'] = df2['y']+0.5
sns.pointplot(data=df1, x='x', y='y', hue='h', ci='sd', errwidth=2, capsize=0.05, dodge=0.1, markers='<')
sns.pointplot(data=df2, x='x', y='y', hue='h', ci='sd', errwidth=2, capsize=0.05, dodge=0.1, markers='>')

我可以使用 seaborn 以外的其他东西,但自动置信度/误差条非常方便,所以我更愿意在这里坚持使用 seaborn。

【问题讨论】:

    标签: python matplotlib seaborn


    【解决方案1】:

    首先回答最一般的情况。 可以通过将图中的艺术家移动一些量来实现闪避。将点用作该班次的单位可能很有用。例如。您可能希望将绘图上的标记移动 5 个点。
    这种转变可以通过向艺术家的数据转换添加翻译来实现。这里我建议ScaledTranslation

    现在为了保持这一点最通用,可以编写一个函数,该函数将绘图方法、轴和数据作为输入,此外还可以应用一些闪避,例如

    draw_dodge(ax.errorbar, X, y, yerr =y/4., ax=ax, dodge=d, marker="d" )
    

    完整的功能代码:

    import matplotlib.pyplot as plt
    from matplotlib import transforms
    import numpy as np
    import pandas as pd
    
    
    def draw_dodge(*args, **kwargs):
        func = args[0]
        dodge = kwargs.pop("dodge", 0)
        ax = kwargs.pop("ax", plt.gca())
        trans = ax.transData  + transforms.ScaledTranslation(dodge/72., 0,
                                       ax.figure.dpi_scale_trans)
        artist = func(*args[1:], **kwargs)
        def iterate(artist):
            if hasattr(artist, '__iter__'):
                for obj in artist:
                    iterate(obj)
            else:
                artist.set_transform(trans)
        iterate(artist)
        return artist
    
    X = ["a", "b"]
    Y = np.array([[1,2],[2,2],[3,2],[1,4]])
    
    Dodge = np.arange(len(Y),dtype=float)*10
    Dodge -= Dodge.mean()
    
    fig, ax = plt.subplots()
    
    for y,d in zip(Y,Dodge):
        draw_dodge(ax.errorbar, X, y, yerr =y/4., ax=ax, dodge=d, marker="d" )
    
    ax.margins(x=0.4)
    plt.show()
    

    您可以将其与ax.plotax.scatter 等一起使用。但不能与任何 seaborn 函数一起使用,因为它们不会返回任何有用的艺术家。


    现在对于有问题的情况,剩下的问题是以有用的格式获取数据。一种选择如下。

    df1 = pd.DataFrame({'x':list('ffffssss'), 
                        'y':[1,2,3,4,5,6,7,8], 
                        'h':list('abababab')})
    df2 = df1.copy()
    df2['y'] = df2['y']+0.5
    
    N = len(np.unique(df1["x"].values))*len([df1,df2])
    Dodge = np.linspace(-N,N,N)/N*10
    
    
    fig, ax = plt.subplots()
    k = 0
    for df in [df1,df2]:
        for (n, grp) in df.groupby("h"):
            x = grp.groupby("x").mean()
            std = grp.groupby("x").std()
            draw_dodge(ax.errorbar, x.index, x.values, 
                       yerr =std.values.flatten(), ax=ax, 
                       dodge=Dodge[k], marker="o", label=n)
            k+=1
    
    ax.legend()        
    ax.margins(x=0.4)
    plt.show()
    

    【讨论】:

    • 谢谢你,这个工作和非常好的通用代码。对于任何想要使用它的人,只需注意一点,它在 matplotlib == 2.2.2 中可以正常工作,但在 matplotlib == 2.0.0 中不能正常工作(ValueError:无法将字符串转换为浮点数:'a')。我没有进一步调查,所以我不知道最低要求的版本是什么。
    【解决方案2】:

    您可以使用 linspace 轻松地将图表移动到您希望它们开始和结束的位置。该功能还可以很容易地缩放图形,使它们在视觉上具有相同的宽度

    import numpy as np
    import matplotlib.pyplot as plt
    
    import numpy as np
    import matplotlib.pyplot as plt
    
    start_offset = 3
    end_offset = start_offset
    y1 = np.random.randint(0, 10, 20) ##y1 has 20 random ints from 0 to 10
    y2 = np.random.randint(0, 10, 10) ##y2 has 10 random ints from 0 to 10
    x1 = np.linspace(0, 20, y1.size) ##create a number of steps from 0 to 20 equal to y1 array size-1
    x2 = np.linspace(0, 20, y2.size)
    plt.plot(x1, y1)
    plt.plot(x2, y2)
    plt.show()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-08-05
      • 2019-01-24
      • 1970-01-01
      • 2012-09-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多