Matplotlib - Stepped histogram with already binned data

前端 未结 4 1653
温柔的废话
温柔的废话 2020-12-15 06:13

I am trying to get a histogram with already binned data. I have been trying to use bar() for this, but I can\'t seem to figure out how to make it a stepped hist

相关标签:
4条回答
  • 2020-12-15 06:48

    The simplest solution is to convert your binned dataset to an un-binned, weighted dataset (with number of elements == number of bins). The unbinned dataset would consist of data values equal to the bin centres and weights equal to the values in each bin. For example let's say that your binned data is,

    binedges = [0.0, 1.0, 2.0, 3.0]
    ybinned = [11., 22., 33.]
    

    The corresponding weighted dataset would be,

    y =       [0.5, 1.5, 2.5]
    weights = [11., 22., 33.]
    

    Note that the choice to use the bin centre is arbitrary, you could use any point within a bin. Once you have generated the un-binned dataset then you can use the normal matplotlib histogram plotting (i.e. Axes.hist).

    An example implementation in python follows:

    def plot_binned_data(axes, binedges, data,
                   *args, **kwargs):
        #The dataset values are the bin centres
        x = (binedges[1:] + binedges[:-1]) / 2.0
        #The weights are the y-values of the input binned data
        weights = data
        return axes.hist(x, bins=binedges, weights=weights,
                   *args, **kwargs)
    

    You can now have full access to all of the Axes.Histogram plotting options, including histtype="step" to create the stepped histogram that you wanted.

    An example using this function would be,

    import numpy
    import matplotlib.pyplot as plt
    
    #Create a dataset
    dataset = numpy.random.normal(size=100)
    #Bin the dataset
    binedges = numpy.linspace(-5.0, 5.0, num=10)
    y, binedges = numpy.histogram(dataset, binedges)
    
    #Plot the dataset
    fig = plt.figure()
    ax = fig.add_subplot(1, 1, 1)
    plot_binned_data(ax, binedges, y, histtype="step")
    plt.show()
    

    Hope that helps!

    0 讨论(0)
  • 2020-12-15 06:52

    For some reason, the last bin isn't properly closed when I try it. It is not visible from the previous answers if the last line is showed, so I decided to make my own function, which does what I want.

    def make_bar_contour_plot(ax,x_input,y_input):
    
        x = list(np.ravel(zip(x_input[:-1],x_input[:-1]+1)))[1:]
        x += [x[-1]+20] + [300] 
        y = list(np.ravel(zip(y_input,y_input))) +[0]
        ax.plot(x,y,ls='steps')
    
        return ax
    

    The 20 and 300 that are added are my binsize and ending value respectively, and need to be adjusted if anyone wants to use this. x_input and y_input are the returning values from np.histogram. My resulting plot (in blue the contour, plotted with above function. In red, the barplot of the same data):

    My result in contourplotting a histogram

    0 讨论(0)
  • 2020-12-15 06:53

    From the accompanying source at http://matplotlib.sourceforge.net/examples/pylab_examples/histogram_demo_extended.html

    here is how they drew that graph:

    [snip]

    and the bit you want appears to be

    pylab.hist(x, bins=bins, histtype='step')
                                ^
                            right here
    

    Edit: if you want to know how hist() works, look at the source - it's defined in matplotlib/axes.py starting at line 7407.

    Looking at line 7724,

    x = np.zeros( 2*len(bins), np.float )
    y = np.zeros( 2*len(bins), np.float )
    

    for N bars, bins is an numpy.ndarray of N+1 values, being the edges for each bar. They twin the values for each bar (this is what fraxel is doing with np.ravel below) and shift the datapoints half a bar left to center them

    x[0::2], x[1::2] = bins, bins
    x -= 0.5*(bins[1]-bins[0])
    

    set the height of each bar, twinned but offset by one (relative to the x values) to produce the step effect

    # n is an array of arrays containing the number of items per bar
    patches = []    # from line 7676
    for m, c in zip(n, color):
        y[1:-1:2], y[2::2] = m, m
        patches.append(self.fill(x, y, closed=False, edgecolor=c, fill=False))
    

    and the self.fill bit is what actually draws the lines.

    0 讨论(0)
  • 2020-12-15 06:55

    You could cheat, by offsetting your data and using plot instead:

    from matplotlib import pyplot
    import numpy as np
    
    #sample data:
    x = np.arange(30)
    y = np.cumsum(np.arange(30))
    #offset the x for horizontal, repeat the y for vertical:
    x = np.ravel(zip(x,x+1))
    y = np.ravel(zip(y,y))
    
    pyplot.plot(x,y)
    pyplot.savefig('plt.png')
    

    the plot:

    enter image description here

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