How do I plot multiple X or Y axes in matplotlib?

前端 未结 2 543
后悔当初
后悔当初 2020-12-23 21:35

I\'m currently using matplotlib to plot a measurement against 2 or 3 other measurements (sometimes categorical) on the x-axis. Currently, I am grouping the data on the x-axi

相关标签:
2条回答
  • 2020-12-23 22:27

    Joe's example is good. I'll throw mine in too. I was working on it a few hours ago, but then had to run off to a meeting. It steals from here.

    import matplotlib.pyplot as plt
    import matplotlib.ticker as ticker
    
    ## the following two functions override the default behavior or twiny()
    def make_patch_spines_invisible(ax):
        ax.set_frame_on(True)
        ax.patch.set_visible(False)
        for sp in ax.spines.itervalues():
            sp.set_visible(False)
    
    def make_spine_invisible(ax, direction):
        if direction in ["right", "left"]:
            ax.yaxis.set_ticks_position(direction)
            ax.yaxis.set_label_position(direction)
        elif direction in ["top", "bottom"]:
            ax.xaxis.set_ticks_position(direction)
            ax.xaxis.set_label_position(direction)
        else:
            raise ValueError("Unknown Direction : %s" % (direction,))
    
        ax.spines[direction].set_visible(True)
    
    data = (('A',0.01),('A',0.02),('B',0.10),('B',0.20)) # fake data
    
    fig = plt.figure(1)
    sb = fig.add_subplot(111)
    sb.xaxis.set_major_locator(ticker.FixedLocator([0,1,2,3]))
    
    sb.plot([i[1] for i in data],"*",markersize=10)
    sb.set_xlabel("dose")
    
    plt.subplots_adjust(bottom=0.17) # make room on bottom
    
    par2 = sb.twiny() # create a second axes
    par2.spines["bottom"].set_position(("axes", -.1)) # move it down
    
    ## override the default behavior for a twiny axis
    make_patch_spines_invisible(par2) 
    make_spine_invisible(par2, "bottom")
    par2.set_xlabel("treatment")
    
    par2.plot([i[1] for i in data],"*",markersize=10) #redraw to put twiny on same scale
    par2.xaxis.set_major_locator(ticker.FixedLocator([0,1,2,3]))
    par2.xaxis.set_ticklabels([i[0] for i in data])
    
    plt.show()
    

    Produces:

    alt text

    0 讨论(0)
  • 2020-12-23 22:36

    First off, cool question! It's definitely possible with matplotlib >= 1.0.0. (The new spines functionality allows it)

    It requires a fair bit of voodoo, though... My example is far from perfect, but hopefully it makes some sense:

    import numpy as np
    import matplotlib.pyplot as plt
    import matplotlib as mpl
    
    def main():
        #-- Generate some data ----------------------------------------------------
        nx = 10
        x = np.linspace(0, 2*np.pi, 10)
        y = 2 * np.sin(x)
    
        groups = [('GroupA', (x[0], x[nx//3])),
                  ('GroupB', (x[-2*nx//3], x[2*nx//3])),
                  ('GroupC', (x[-nx//3], x[-1]))]
    
        #-- Plot the results ------------------------------------------------------
        fig = plt.figure()
        ax = fig.add_subplot(111)
    
        # Give ourselves a bit more room at the bottom
        plt.subplots_adjust(bottom=0.2)
    
        ax.plot(x,y, 'k^')
    
        # Drop the bottom spine by 40 pts
        ax.spines['bottom'].set_position(('outward', 40))
    
        # Make a second bottom spine in the position of the original bottom spine
        make_second_bottom_spine(label='Treatment')
    
        # Annotate the groups
        for name, xspan in groups:
            annotate_group(name, xspan)
    
        plt.xlabel('Dose')
        plt.ylabel('Response')
        plt.title('Experimental Data')
    
        plt.show()
    
    
    def annotate_group(name, xspan, ax=None):
        """Annotates a span of the x-axis"""
        def annotate(ax, name, left, right, y, pad):
            arrow = ax.annotate(name,
                    xy=(left, y), xycoords='data',
                    xytext=(right, y-pad), textcoords='data',
                    annotation_clip=False, verticalalignment='top',
                    horizontalalignment='center', linespacing=2.0,
                    arrowprops=dict(arrowstyle='-', shrinkA=0, shrinkB=0,
                            connectionstyle='angle,angleB=90,angleA=0,rad=5')
                    )
            return arrow
        if ax is None:
            ax = plt.gca()
        ymin = ax.get_ylim()[0]
        ypad = 0.01 * np.ptp(ax.get_ylim())
        xcenter = np.mean(xspan)
        left_arrow = annotate(ax, name, xspan[0], xcenter, ymin, ypad)
        right_arrow = annotate(ax, name, xspan[1], xcenter, ymin, ypad)
        return left_arrow, right_arrow
    
    def make_second_bottom_spine(ax=None, label=None, offset=0, labeloffset=20):
        """Makes a second bottom spine"""
        if ax is None:
            ax = plt.gca()
        second_bottom = mpl.spines.Spine(ax, 'bottom', ax.spines['bottom']._path)
        second_bottom.set_position(('outward', offset))
        ax.spines['second_bottom'] = second_bottom
    
        if label is not None:
            # Make a new xlabel
            ax.annotate(label, 
                    xy=(0.5, 0), xycoords='axes fraction', 
                    xytext=(0, -labeloffset), textcoords='offset points', 
                    verticalalignment='top', horizontalalignment='center')
    
    if __name__ == '__main__':
        main()
    

    Two bottom spines in a matplotlib plot

    0 讨论(0)
提交回复
热议问题