McUtils.Plots

A plotting framework that builds off of matplotlib, but potentially could use a different backend. The design is intended to mirror the Graphics framework in Mathematica and where possible option names have been chosen to be the same. Difficulties with matplotlib prevent a perfect mirror but the design is consistent. There are a few primary divisions:

  1. Graphics/Graphics3D/GraphicsGrid provide basic access to matplotlib.figure and matplotlib.axes they also hold a GraphicsPropertyManager/GraphicsPropertyManager3D that manages all properties (image_size, axes_label, ticks_style, etc.). The full lists can be found on the relevant reference pages and are bound as properties on the Graphics/Graphics3D instances.
  2. Plot/Plot3D and everything in the Plots subpackage provide concrete instances of common plots with nice names/consistent with Mathematica for discoverability but primarily fall back onto matplotlib built-in methods and then allow for restyling/data reuse, etc.
  3. Primitives provide direct access to the shapes that are actually plotted on screen (i.e. matplotlib.Patch objects) in a convenient way to add on to existing plots
  4. Styling provides access to theme management/construction

Image/animation support and other back end support for 3D graphics (VTK) are provided at the experimental level.

Members

Examples

Before we can run our examples we should get a bit of setup out of the way. Since these examples were harvested from the unit tests not all pieces will be necessary for all situations.

All tests are wrapped in a test class

class PlotsTests(TestCase):
    def tearDownClass(cls):
        import matplotlib.pyplot as plt
    def result_file(self, fname):
        if not os.path.isdir(os.path.join(TestManager.test_dir, "test_results")):
            os.mkdir(os.path.join(TestManager.test_dir, "test_results"))
        return os.path.join(TestManager.test_dir, "test_results", fname)

Plot

    def test_Plot(self):
        grid = np.linspace(0, 2*np.pi, 100)
        plot = Plot(grid, np.sin(grid))
        plot.show()

        # plot.savefig(self.result_file("test_Plot.png"))
        plot.close()

Plot3D

    def test_Plot3D(self):
        import matplotlib.cm as colormaps

        f = lambda pt: np.sin(pt[0]) + np.cos(pt[1])
        plot = Plot3D(f, np.arange(0, 2 * np.pi, .1), np.arange(0, 2 * np.pi, .1),
                      plot_style={
                          "cmap": colormaps.get_cmap('viridis')
                      },
                      axes_labels=['dogs', 'cats',
                                   Styled('rats', color='red')
                                   ],
                      plot_label='my super cool 3D plot',
                      plot_range=[(-5, 5)] * 3,
                      plot_legend='i lik turtle',
                      colorbar=True
                      )
        plot.savefig(self.result_file("test_Plot3D.png"))
        plot.close()

GraphicsGrid

    def test_GraphicsGrid(self):

        main = GraphicsGrid(ncols=3, nrows=1)
        grid = np.linspace(0, 2 * np.pi, 100)
        grid_2D = np.meshgrid(grid, grid)
        main[0, 0] = ContourPlot(grid_2D[1], grid_2D[0], np.sin(grid_2D[0]), figure=main[0, 0])
        main[0, 1] = ContourPlot(grid_2D[1], grid_2D[0], np.sin(grid_2D[0]) * np.cos(grid_2D[1]), figure=main[0, 1])
        main[0, 2] = ContourPlot(grid_2D[1], grid_2D[0], np.cos(grid_2D[1]), figure=main[0, 2])
        # main.show()

        main.savefig(self.result_file("test_GraphicsGrid.png"))
        main.close()

PlotStyling

    def test_PlotStyling(self):
        grid = np.linspace(0, 2 * np.pi, 100)
        # file = '~/Desktop/y.png'
        plot = Plot(grid, np.sin(grid),
                    aspect_ratio=1.3,
                    theme='dark_background',
                    ticks_style={'color':'red', 'labelcolor':'red'},
                    plot_label='bleh',
                    padding=((30, 0), (20, 20))
                    )
        # plot.savefig(file)
        # plot = Image.from_file(file)
        plot.show()
        # plot.savefig(self.result_file("test_PlotStyling.png"))
        plot.close()

PlotGridStyling

    def test_PlotGridStyling(self):
        main = GraphicsGrid(ncols=3, nrows=1, theme='Solarize_Light2', figure_label='my beuatufil triptych',
                            padding=((50, 10), (50, 65)),
                            spacings=[50, 0])
        grid = np.linspace(0, 2 * np.pi, 100)
        grid_2D = np.meshgrid(grid, grid)
        x = grid_2D[1]; y = grid_2D[0]
        main[0, 0] = ContourPlot(x, y, np.sin(y), plot_label='$sin(x)$',
                                 axes_labels=[None, "cats (cc)"],
                                 figure=main[0, 0]
                                 )
        main[0, 1] = ContourPlot(x, y, np.sin(x) * np.cos(y),
                                 plot_label='$sin(x)cos(y)$',
                                 axes_labels=[Styled("dogs (arb.)", {'color': 'red'}), None],
                                 figure=main[0, 1])
        main[0, 2] = ContourPlot(x, y, np.cos(y), plot_label='$cos(y)$', figure=main[0, 2])
        main.colorbar = {"graphics": main[0, 1].graphics}

        main.show()
        # main.savefig(self.result_file("test_PlotGridStyling.png"))
        main.close()

Scatter

    def test_Scatter(self):
        pts = np.random.rand(50, 2)
        plot = ScatterPlot(*pts.T,
                           aspect_ratio=2,
                           image_size=250
                           )
        plot.show()
        # plot.savefig(self.result_file("test_Scatter.pdf"), format='pdf')
        plot.close()

ListContourPlot

    def test_ListContourPlot(self):
        pts = np.pi*np.random.rand(150, 2)
        sins = np.sin(pts[:, 0])
        coses = np.cos(pts[:, 1])
        ptss = np.concatenate((pts, np.reshape(sins*coses, sins.shape + (1,))), axis=1)
        plot = ListContourPlot(ptss)

        plot.savefig(self.result_file("test_ListContourPlot.png"))
        plot.close()

ListTriPlot3D

    def test_ListTriPlot3D(self):
        pts = np.pi*np.random.rand(150, 2)
        sins = np.sin(pts[:, 0])
        coses = np.cos(pts[:, 1])
        ptss = np.concatenate((pts, np.reshape(sins*coses, sins.shape + (1,))), axis=1)
        plot = ListTriPlot3D(ptss)

        plot.savefig(self.result_file("test_ListTriPlot3D.png"))
        plot.close()

ListTriDensityPlot

    def test_ListTriDensityPlot(self):
        pts = np.pi*np.random.rand(150, 2)
        sins = np.sin(pts[:, 0])
        coses = np.cos(pts[:, 1])
        ptss = np.concatenate((pts, np.reshape(sins*coses, sins.shape + (1,))), axis=1)
        plot = ListTriDensityPlot(ptss)

        plot.savefig(self.result_file("test_ListTriDensityPlot.png"))
        plot.close()

ListTriContourPlot

    def test_ListTriContourPlot(self):
        pts = np.pi*np.random.rand(150, 2)
        sins = np.sin(pts[:, 0])
        coses = np.cos(pts[:, 1])
        ptss = np.concatenate((pts, np.reshape(sins*coses, sins.shape + (1,))), axis=1)
        plot = ListTriContourPlot(ptss)
        plot.add_colorbar()

        plot.savefig(self.result_file("test_ListTriContourPlot.png"))
        plot.close()

Animation

    def test_Animation(self):
        "Currently broken"
        def get_data(*args):
            pts = np.pi*np.random.normal(scale = .25, size=(10550, 2))
            sins = np.sin(pts[:, 0])
            coses = np.cos(pts[:, 1])
            ptss = np.concatenate((pts, np.reshape(sins*coses, sins.shape + (1,))), axis=1)
            return (ptss, )
        plot = ListTriContourPlot(*get_data(),
                                  animate = get_data,
                                  plot_range = [
                                      [-np.pi, np.pi],
                                      [-np.pi, np.pi]
                                  ]
                                  )

        plot.show()

        plot.savefig(self.result_file("test_ListTriContourPlot.gif"))
        plot.close()

X3D

    def test_X3D(self):
        plot = Graphics3D(backend="x3d", image_size=[1500, 500], background='white')
        Sphere(color='red').plot(plot)
        plot.show()

ColorPalettes

    def test_ColorPalettes(self):
        rgb_code = [200, 10, 25]
        huh = ColorPalette.color_convert(rgb_code, 'rgb', 'xyz')
        og = ColorPalette.color_convert(huh, 'xyz', 'rgb')
        print(huh, og)

        huh = ColorPalette.color_convert(rgb_code, 'rgb', 'hsl')
        og = ColorPalette.color_convert(huh, 'hsl', 'rgb')
        print(huh, og, ColorPalette.rgb_code(og))

        huh = ColorPalette.color_convert(rgb_code, 'rgb', 'hsv')
        og = ColorPalette.color_convert(huh, 'hsv', 'rgb')
        print(huh, og, ColorPalette.rgb_code(og))

        huh = ColorPalette.color_convert(rgb_code, 'rgb', 'lab')
        og = ColorPalette.color_convert(huh, 'lab', 'rgb')
        print(huh, og, ColorPalette.rgb_code(og))

        print(ColorPalette("pastel").blend(.2))
        # return

        grid = np.linspace(0, 2 * np.pi, 200)
        # base_fig = None
        # for i in range(6):
        #     base_fig = Plot(
        #         grid,
        #         i + np.sin((i + 1) * grid),
        #         figure=base_fig,
        #         style_list={'color': ColorPalette("pastel")}
        #     )

        palette_base = [
                    "#3a2652", "#dcca00", "#a15547",
                    "#009b5d", "#14013d",
                    "#8d0001", "#494947"
                ]
        lighter_palette = [
            ColorPalette.color_lighten(c, .2, shift=True)
            for c in palette_base
        ]
        lighter_palette_hsl = [
            ColorPalette.color_lighten(c, .2, modification_space='hsl', shift=True)
            for c in palette_base
        ]

        for n,p in {
            'starters':'starters',
            'base':palette_base,
            'lighter':lighter_palette,
            'lighter_hsl':lighter_palette_hsl,
        }.items():
            base_fig = None
            print(ColorPalette(p).get_colorblindness_test_url())
            for i in range(6):
                base_fig = Plot(
                    grid,
                    i + np.sin((i + 1) * grid),
                    figure=base_fig,
                    style_list={'color': ColorPalette(p)},
                    plot_label=n
                )

ColorMaps

    def test_ColorMaps(self):
        print(
            ColorPalette('pastel').blend([
                0, .2, .5, 1.1
            ])
        )

        print(
            ColorPalette('pastel')([0, .2, .5, 1.1])
        )

        grid_x = np.linspace(0, 2*np.pi, 100)
        grid_y = np.linspace(0, 2*np.pi, 100)
        mg = np.meshgrid(grid_x, grid_y)
        grid_z = np.sum(np.meshgrid(np.sin(grid_x), np.cos(grid_y)), axis=0)
        ContourPlot(
            *mg,
            grid_z,
            cmap=ColorPalette('starters').as_colormap(
                levels=np.linspace(0, 1, 20)**2,
                cmap_type='interpolated'
            ),
            levels=50,
            colorbar=True
        ).show()

PropertySetting

    def test_PropertySetting(self):
        StickPlot(
            np.linspace(0, 2*np.pi, 35),
            np.sin(np.linspace(0, 2*np.pi, 35)),
            color='red',
            label='spec_1',
            plot_legend=True
        ).show()

PlotDelayed

    def test_PlotDelayed(self):
        p = Plot(background = 'black')
        for i, c in enumerate(('red', 'white', 'blue')):
            p.plot(np.sin, [-2 + 4/3*i, -2 + 4/3*(i+1)], color = c)
        # p.show()

        p.savefig(self.result_file("test_PlotDelayed.gif"))
        p.close()

Plot3DDelayed

    def test_Plot3DDelayed(self):
        p = Plot3D(background = 'black')
        for i, c in enumerate(('red', 'white', 'blue')):
            p.plot(
                lambda g: (
                    np.sin(g.T[0]) + np.cos(g.T[1])
                ),
                [-2 + 4/3*i, -2 + 4/3*(i+1)],
                [-2 + 4/3*i, -2 + 4/3*(i+1)],
                color = c)
        # p.show()

        p.savefig(self.result_file("test_Plot3DDelayed.gif"))
        p.close()

Feedback

Examples

Templates

Documentation