面向对象的matplotlib使用神秘地产生没有胡须或中线的箱形图

时间:2012-01-17 02:17:30

标签: python matplotlib boxplot

我正在尝试使用Python和matplotlib来定义一个自定义类 产生一个复杂的数字。但是,我很难得到 箱图可以正确打印 - 它们不会出现胡须或者出现 标记中值的线条。我无法嵌入示例图片,但您可以see one here

我的自定义类定义如下:

import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib.ticker import FixedLocator
from matplotlib.gridspec import GridSpec
from matplotlib.figure import Figure
from matplotlib.backends.backend_svg import FigureCanvasSVG as FigureCanvas
import numpy as np
import scipy as sp
import scipy.optimize

class DotDashHist(Figure):
    """A Tufte-style dot-dash plot with histograms along the x- and y-axes."""
    def __init__(self, the_vals):
        # Actually inherit all the attributes and methods of parent class
        super(DotDashHist, self).__init__()
        # Process incoming data
        self.vals = the_vals
        self.xvals, self.yvals = zip(*self.vals)
        self.xvals_uniq = list(set(self.xvals))
        self.yvals_uniq = list(set(self.yvals))
        self.xmax = float(max(self.xvals_uniq))
        self.xpadding = float(self.xmax / 50)
        self.ymax = float(max(self.yvals_uniq))
        self.ypadding = float(self.ymax / 50)
        self.xlims = [-1 * self.xpadding, self.xmax + self.xpadding]
        self.ylims = [-1 * self.ypadding, self.ymax + self.ypadding]
        self.lims = [-1 * self.xpadding, self.xmax + self.xpadding,
                     -1 * self.ypadding, self.ymax + self.ypadding]
        # Set some matplotlib default behavior
        mpl.rcParams['backend'] = 'SVG'
        mpl.rcParams['lines.antialiased'] = True
        mpl.rcParams['font.family'] = 'sans-serif'
        mpl.rcParams['font.sans-serif'] = 'Gill Sans MT Pro, Lucida Grande, Helvetica, sans-serif'
        mpl.rcParams['axes.titlesize'] = 'large'
        mpl.rcParams['axes.labelsize'] = 'xx-small'
        mpl.rcParams['xtick.major.size'] = 2
        mpl.rcParams['xtick.minor.size'] = 0.5
        mpl.rcParams['xtick.labelsize'] = 'xx-small'
        mpl.rcParams['ytick.major.size'] = 2
        mpl.rcParams['ytick.minor.size'] = 0.5
        mpl.rcParams['ytick.labelsize'] = 'xx-small'
    def _makeskel(self):
        # Set up the framework in which the figure will be drawn
        # Define the canvas for the figure
        self.canvas = FigureCanvas(self)
        self.set_canvas(self.canvas)
        # Place subplots on a 6x6 grid
        gs = GridSpec(6,6)
        # Add the main subplot, override weird axis and tick defaults
        self.main = self.add_subplot(gs[1:, :-1])
        self.main.set_frame_on(False)
        self.main.get_xaxis().tick_bottom()
        self.main.get_yaxis().tick_left()
        self.main.axis(self.lims)
        # Add the x-value histogram, override weird axis and tick defaults
        self.xhist = self.add_subplot(gs[0, :-1])
        self.xhist.set_xticks([])
        self.xhist.set_yticks([])
        self.xhist.set_frame_on(False)
        self.xhist.get_xaxis().tick_bottom()
        self.xhist.get_yaxis().tick_left()
        self.xhist.set_xlim(self.xlims)
        # Add the y-value histogram, override weird axis and tick defaults
        self.yhist = self.add_subplot(gs[1:, -1])
        self.yhist.set_xticks([])
        self.yhist.set_yticks([])
        self.yhist.set_frame_on(False)
        self.yhist.get_xaxis().tick_bottom()
        self.yhist.get_yaxis().tick_left()
        self.yhist.set_ylim(self.ylims)
    def _makehist(self):
        # Draw the x- and y-value histograms
        self.xhist.hist(self.xvals, normed=1, bins=min([50, self.xmax + 1]), 
                        range=[0, self.xmax + self.xpadding])
        self.yhist.hist(self.yvals, normed=1, bins=min([50, self.ymax + 1]),
                        range=[0, self.ymax + self.ypadding], 
                        orientation='horizontal')
    def makebox(self):
        self._makeskel()
        self._makehist()
        # Aggregate to make boxplots
        box_dict = {}
        for point in self.vals:
            if point[0] <= self.xmax and point[1] <= self.ymax:
                box_dict.setdefault(round(float(point[0]), 0), 
                        []).append(point[1])
        self.main.boxplot(box_dict.values(), positions=box_dict.keys(), 
                whis=1.0, sym='ro')
        self.main.set_xticks(np.arange(0, self.xmax + 1, 12))
        self.main.xaxis.set_minor_locator(FixedLocator(self.xvals_uniq))
        self.main.yaxis.set_minor_locator(FixedLocator(self.yvals_uniq))

此测试代码显示问题:

from numpy.random import randn
import mycustomfigures as hf
test_x = np.arange(0, 25, 0.01)
test_y = test_x + randn(2500)
test_data = zip(test_x, test_y)
test_fig = hf.DotDashHist(test_data)
test_fig.makebox()
test_fig.suptitle('Test Figure')
test_fig.savefig('testing.svg')

我定义DotDashHist的方式有什么问题?我可以使用MATLAB风格的状态语法生成胡须箱图,但是这种方法在绘制多个数字时会生成大量代码。

1 个答案:

答案 0 :(得分:3)

胡须在我原来的情节中,它们只是被你绘制的异常值所掩盖。

无论如何,我会继续这样做:

import collections
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
import numpy as np

def main():
    x = np.arange(0, 25, 0.01)
    y = x + np.random.randn(x.size)
    plot = DotDashHist(figsize=(10, 8))
    plot.plot(x, y, whis=1.0, sym='r.')
    plot.title('This is a Test')
    plt.show()

class DotDashHist(object):
    def __init__(self, **kwargs):
        self.fig = plt.figure(**kwargs)
        gs = GridSpec(6, 6)
        self.ax = self.fig.add_subplot(gs[1:, :-1])
        self.topax = self.fig.add_subplot(gs[0, :-1], sharex=self.ax)
        self.rightax = self.fig.add_subplot(gs[1:, -1], sharey=self.ax)
        for ax in [self.topax, self.rightax]:
            ax.set_axis_off()

    def plot(self, x, y, **kwargs):
        _, _, self.topbars = self.topax.hist(x, normed=1, bins=50)
        _, _, self.rightbars = self.rightax.hist(y, normed=1, bins=50,
                                                 orientation='horizontal')
        boxes = collections.defaultdict(list)
        for X, Y in zip(x, y):
            boxes[int(X)].append(Y)

        kwargs.pop('positions', None)
        self.boxes = self.ax.boxplot(boxes.values(), **kwargs)

    def title(self, *args, **kwargs):
        self.topax.set_title(*args, **kwargs)

if __name__ == '__main__':
    main()

enter image description here