├── .github └── workflows │ ├── cd.yml │ └── codecov.yml ├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.md ├── broken_python_snake.png ├── brokenaxes.py ├── datetime_example.png ├── docs ├── Makefile ├── requirements-doc.txt └── source │ ├── auto_examples │ ├── auto_examples_jupyter.zip │ ├── auto_examples_python.zip │ ├── images │ │ ├── sphx_glr_plot_logscales_001.png │ │ ├── sphx_glr_plot_subplots_001.png │ │ ├── sphx_glr_plot_usage_001.png │ │ └── thumb │ │ │ ├── sphx_glr_plot_logscales_thumb.png │ │ │ ├── sphx_glr_plot_subplots_thumb.png │ │ │ └── sphx_glr_plot_usage_thumb.png │ ├── index.rst │ ├── plot_logscales.ipynb │ ├── plot_logscales.py │ ├── plot_logscales.py.md5 │ ├── plot_logscales.rst │ ├── plot_subplots.ipynb │ ├── plot_subplots.py │ ├── plot_subplots.py.md5 │ ├── plot_subplots.rst │ ├── plot_usage.ipynb │ ├── plot_usage.py │ ├── plot_usage.py.md5 │ ├── plot_usage.rst │ └── sg_execution_times.rst │ ├── conf.py │ └── index.rst ├── example1.png ├── example2.png ├── example3.png ├── examples ├── README.txt ├── plot_different_scales.py ├── plot_logscales.py ├── plot_second_y_axis.py ├── plot_subplots.py └── plot_usage.py ├── requirements-test.txt ├── requirements.txt ├── setup.cfg ├── setup.py ├── test.py └── test_baseline ├── test_datetime.png ├── test_datetime_y.png ├── test_despine.png ├── test_draw_diags.png ├── test_fig_tight_layout.png ├── test_legend.png ├── test_lims_arrays.png ├── test_log.png ├── test_pass_fig.png ├── test_secondary_axes.png ├── test_set_spine_prop.png ├── test_set_title.png ├── test_standard.png ├── test_subplots.png └── test_text.png /.github/workflows/cd.yml: -------------------------------------------------------------------------------- 1 | name: Publish to PyPI 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | pypi-publish: 8 | name: Upload release to PyPI 9 | runs-on: ubuntu-latest 10 | environment: 11 | name: pypi 12 | url: https://pypi.org/p/brokenaxes 13 | permissions: 14 | id-token: write 15 | steps: 16 | - name: Check out repository 17 | uses: actions/checkout@v2 18 | 19 | - name: Set up Python 20 | uses: actions/setup-python@v2 21 | with: 22 | python-version: '3.10' # Specify the Python version you are using 23 | 24 | - name: Install dependencies 25 | run: | 26 | python -m pip install --upgrade pip 27 | pip install setuptools wheel twine 28 | 29 | - name: Build package 30 | run: | 31 | python setup.py sdist bdist_wheel 32 | 33 | - name: Publish package distributions to PyPI 34 | uses: pypa/gh-action-pypi-publish@release/v1 35 | -------------------------------------------------------------------------------- /.github/workflows/codecov.yml: -------------------------------------------------------------------------------- 1 | name: Python application cross-platform test 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ${{ matrix.os }} 8 | 9 | strategy: 10 | matrix: 11 | os: [ubuntu-latest, windows-latest, macos-latest] 12 | python-version: [3.8, 3.9, "3.10", "3.11", "3.12"] 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | 17 | - name: Set up Python ${{ matrix.python-version }} 18 | uses: actions/setup-python@v2 19 | with: 20 | python-version: ${{ matrix.python-version }} 21 | 22 | - name: Install dependencies 23 | run: | 24 | python -m pip install --upgrade pip 25 | pip install -r requirements-test.txt 26 | pip install -r requirements.txt 27 | 28 | - name: Test with pytest 29 | run: | 30 | pytest --cov=brokenaxes --cov-report=xml --mpl --mpl-baseline-path test_baseline test.py 31 | 32 | - name: Upload coverage to Codecov 33 | uses: codecov/codecov-action@v3 34 | if: matrix.python-version == '3.10' && matrix.os == 'ubuntu-latest' 35 | with: 36 | token: ${{ secrets.CODECOV_TOKEN }} 37 | fail_ci_if_error: true 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | env/ 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *,cover 48 | .hypothesis/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | 58 | # Flask stuff: 59 | instance/ 60 | .webassets-cache 61 | 62 | # Scrapy stuff: 63 | .scrapy 64 | 65 | # Sphinx documentation 66 | docs/_build/ 67 | build/ 68 | static/ 69 | templates/ 70 | 71 | # PyBuilder 72 | target/ 73 | 74 | # IPython Notebook 75 | .ipynb_checkpoints 76 | 77 | # pyenv 78 | .python-version 79 | 80 | # celery beat schedule file 81 | celerybeat-schedule 82 | 83 | # dotenv 84 | .env 85 | 86 | # virtualenv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | 93 | # Rope project settings 94 | .ropeproject 95 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Ben Dichter 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENCE README.md 2 | recursive-include examples *.py 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # brokenaxes 2 | 3 | ![brokenaxes logo. Reference: http://www.brianhensley.net/2012/02/python-controlling-spi-bus-on.html](https://raw.githubusercontent.com/bendichter/brokenaxes/master/broken_python_snake.png) 4 | 5 | brokenaxes makes matplotlib plots with breaks in the axes for showing data across a discontinuous range. 6 | 7 | [![PyPI](https://img.shields.io/pypi/v/brokenaxes.svg?style=plastic)](https://pypi.python.org/pypi/brokenaxes) 8 | [![PyPI - Downloads](https://img.shields.io/pypi/dm/brokenaxes.svg?color=purple&label=PyPi)](https://pypistats.org/packages/brokenaxes) 9 | [![codecov](https://codecov.io/gh/bendichter/brokenaxes/graph/badge.svg?token=emsyOdN4YD)](https://codecov.io/gh/bendichter/brokenaxes) 10 | 11 | ### Features 12 | * Break x and y axes. 13 | * Supports multiple breaks on a single axis. 14 | * Automatically scales axes according to relative ranges. 15 | * Plot multiple lines. 16 | * Legend with positioning relative to entire broken axes object 17 | * x and y label centered to entire plot 18 | * Make brokenaxes object a subplot itself with `matplotlib.GridSpec.subplot_spec`. 19 | * xlims and ylims may be `datetime.datetime` objects 20 | * Supports log scales. 21 | 22 | ## Installation 23 | I recommend the [Anaconda python distribution](http://continuum.io/downloads) and this package is available via pypi: 24 | ``` 25 | pip install brokenaxes 26 | ``` 27 | 28 | ## Usage 29 | ```python 30 | import matplotlib.pyplot as plt 31 | from brokenaxes import brokenaxes 32 | import numpy as np 33 | 34 | fig = plt.figure(figsize=(5, 2)) 35 | bax = brokenaxes(xlims=((0, .1), (.4, .7)), ylims=((-1, .7), (.79, 1)), hspace=.05) 36 | x = np.linspace(0, 1, 100) 37 | bax.plot(x, np.sin(10 * x), label='sin') 38 | bax.plot(x, np.cos(10 * x), label='cos') 39 | bax.legend(loc=3) 40 | bax.set_xlabel('time') 41 | bax.set_ylabel('value') 42 | ``` 43 | ![example1](https://raw.githubusercontent.com/bendichter/brokenaxes/master/example1.png) 44 | 45 | ### Create subplots 46 | 47 | ```python 48 | from brokenaxes import brokenaxes 49 | from matplotlib.gridspec import GridSpec 50 | import numpy as np 51 | 52 | sps1, sps2 = GridSpec(2,1) 53 | 54 | bax = brokenaxes(xlims=((.1, .3), (.7, .8)), subplot_spec=sps1) 55 | x = np.linspace(0, 1, 100) 56 | bax.plot(x, np.sin(x*30), ls=':', color='m') 57 | 58 | x = np.random.poisson(3, 1000) 59 | bax = brokenaxes(xlims=((0, 2.5), (3, 6)), subplot_spec=sps2) 60 | bax.hist(x, histtype='bar') 61 | ``` 62 | ![example2](https://raw.githubusercontent.com/bendichter/brokenaxes/master/example2.png) 63 | 64 | ### Log scales 65 | 66 | ```python 67 | import matplotlib.pyplot as plt 68 | from brokenaxes import brokenaxes 69 | import numpy as np 70 | 71 | fig = plt.figure(figsize=(5, 5)) 72 | bax = brokenaxes( 73 | xlims=((1, 500), (600, 10000)), 74 | ylims=((1, 500), (600, 10000)), 75 | hspace=.15, 76 | xscale='log', 77 | yscale='log', 78 | ) 79 | 80 | x = np.logspace(0.0, 4, 100) 81 | bax.loglog(x, x, label='$y=x=10^{0}$ to $10^{4}$') 82 | 83 | bax.legend(loc='best') 84 | bax.grid(axis='both', which='major', ls='-') 85 | bax.grid(axis='both', which='minor', ls='--', alpha=0.4) 86 | bax.set_xlabel('x') 87 | bax.set_ylabel('y') 88 | plt.show() 89 | ``` 90 | ![example3](https://raw.githubusercontent.com/bendichter/brokenaxes/master/example3.png) 91 | 92 | 93 | ### datetime 94 | ```python 95 | import matplotlib.pyplot as plt 96 | from brokenaxes import brokenaxes 97 | import numpy as np 98 | import datetime 99 | 100 | fig = plt.figure(figsize=(5, 5)) 101 | xx = [datetime.datetime(2020, 1, x) for x in range(1, 20)] 102 | 103 | yy = np.arange(1, 20) 104 | 105 | bax = brokenaxes( 106 | xlims=( 107 | ( 108 | datetime.datetime(2020, 1, 1), 109 | datetime.datetime(2020, 1, 3), 110 | ), 111 | ( 112 | datetime.datetime(2020, 1, 6), 113 | datetime.datetime(2020, 1, 20), 114 | ) 115 | ) 116 | ) 117 | 118 | bax.plot(xx, yy) 119 | 120 | fig.autofmt_xdate() 121 | [x.remove() for x in bax.diag_handles] 122 | bax.draw_diags() 123 | 124 | import matplotlib.dates as mdates 125 | for ax in bax.axs: 126 | ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%b-%d')) 127 | ``` 128 | 129 | ![datetime_example](https://raw.githubusercontent.com/bendichter/brokenaxes/master/datetime_example.png) 130 | 131 | ### text annotation 132 | ```python 133 | import matplotlib.pyplot as plt 134 | from brokenaxes import brokenaxes 135 | 136 | fig = plt.figure(figsize=(5, 5)) 137 | bax = brokenaxes( 138 | xlims=((0, 0.1), (0.4, 0.7)), ylims=((-1, 0.7), (0.79, 1)) 139 | ) 140 | bax.text(0.5, 0.5, "hello") 141 | ``` 142 | ![text_example](https://github.com/bendichter/brokenaxes/assets/844306/35d2026a-a9e6-49df-9bf5-199e9a43b447) 143 | 144 | ## How do I do more? 145 | You can customize brokenaxes outside of the supported features listed above. Brokenaxes works by creating a number of smaller axes objects, with the positions and sizes of those axes dictated by the data ranges used in the constructor. Those individual axes are stored as a list in `bax.axs`. Most customizations will require accessing those inner axes objects. (See the last two lines of [the datetime example](https://github.com/bendichter/brokenaxes#datetime)). There is also a larger invisible axes object, `bax.big_ax`, which spans the entire brokenaxes region and is used for things like x and y axis labels which span all of the smaller axes. 146 | 147 | 148 | ### Gallery 149 | If you make a plot with this tool that you are proud of, send me a png and code and I'll add it to the gallery! 150 | 151 | ### Life advice 152 | Please use this tool wisely. Any data visualization techique can be used to elucidate trends in the data, but can also be used to manipulate and mislead. The latter is particularly true for broken axes plots, so please try to use them responsibly. Other than that, this software is free to use. See the license file for details. 153 | 154 | ## Testing 155 | brokenaxes uses `pytest-mpl` to ensure that the plots are created correctly. 156 | 157 | To test that the plots are created correctly, run `pytest --mpl --mpl-baseline-path test_baseline test.py` from the root directory. 158 | 159 | To generate new test plots, run `pytest --mpl-generate-path test_baseline test.py` from the root directory. 160 | 161 | If you are running the tests on a headless server, you may need to set the MPLBACKEND environment variable to Agg. 162 | -------------------------------------------------------------------------------- /broken_python_snake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bendichter/brokenaxes/042e2facd48fb4966bc4cbe7c95532f4a33a544d/broken_python_snake.png -------------------------------------------------------------------------------- /brokenaxes.py: -------------------------------------------------------------------------------- 1 | from datetime import timedelta 2 | from typing import Optional, Tuple, List 3 | 4 | import matplotlib.pyplot as plt 5 | import numpy as np 6 | from matplotlib import gridspec 7 | from matplotlib import rcParams 8 | from matplotlib import ticker 9 | from matplotlib.figure import Figure 10 | 11 | __author__ = "Ben Dichter" 12 | 13 | 14 | class BrokenAxes: 15 | def __init__( 16 | self, 17 | xlims: Optional[Tuple[Tuple[float, float], ...]] = None, 18 | ylims: Optional[Tuple[Tuple[float, float], ...]] = None, 19 | d: float = 0.015, 20 | tilt: float = 45, 21 | subplot_spec: Optional[gridspec.GridSpec] = None, 22 | fig: Optional[Figure] = None, 23 | despine: bool = True, 24 | xscale: Optional[str] = None, 25 | yscale: Optional[str] = None, 26 | diag_color: str = "k", 27 | height_ratios: Optional[List[int]] = None, 28 | width_ratios: Optional[List[int]] = None, 29 | *args, 30 | **kwargs 31 | ): 32 | """ 33 | Initializes a grid of axes that simulate a single broken axis. 34 | 35 | Parameters 36 | ---------- 37 | xlims : tuple of tuples, optional 38 | X-axis limits for each subplot. If `None`, the x-axis is not broken. 39 | ylims : tuple of tuples, optional 40 | Y-axis limits for each subplot. If `None`, the y-axis is not broken. 41 | d : float, default=0.015 42 | Length of diagonal split mark used to indicate broken axes. 43 | tilt : float, default=45 44 | Angle of diagonal split mark. 45 | subplot_spec : Gridspec.subplot_spec, optional 46 | Defines a subplot. If `None`, a new subplot specification is created. 47 | fig : Figure, optional 48 | The figure object. If `None`, uses the current figure. 49 | despine : bool, optional 50 | If `True`, removes the right and top spines. 51 | xscale : {'linear', 'log'}, optional 52 | Scaling for the x-axis; 'log' or 'linear'. 53 | yscale : {'linear', 'log'}, optional 54 | Scaling for the y-axis; 'log' or 'linear'. 55 | diag_color : str, optional 56 | Color of the diagonal lines indicating breaks, default is 'k'. 57 | height_ratios : list of int, optional 58 | Height ratios of the subplots. If `None`, the height ratios are determined 59 | using the `ylims` parameter. 60 | width_ratios : list of int, optional 61 | Width ratios of the subplots. If `None`, the width ratios are determined 62 | using the `xlims` parameter. 63 | 64 | Notes 65 | ----- 66 | This class facilitates creating plots with discontinuities in either the x or y axis, 67 | by arranging multiple subplots as a single cohesive plot with clear visual indicators 68 | for the discontinuities. 69 | """ 70 | 71 | self._spines = None 72 | self.diag_color: str = diag_color 73 | self.despine: bool = despine 74 | self.d: float = d 75 | self.tilt: float = tilt 76 | self.fig: Figure = fig if fig is not None else plt.gcf() 77 | self.diag_handles: List = [] 78 | 79 | width_ratios = width_ratios if width_ratios is not None else self._calculate_ratios(xlims, xscale) 80 | height_ratios = height_ratios if height_ratios is not None else self._calculate_ratios(ylims, yscale)[::-1] 81 | 82 | ncols, nrows = len(width_ratios), len(height_ratios) 83 | 84 | kwargs.update( 85 | ncols=ncols, 86 | nrows=nrows, 87 | height_ratios=height_ratios, 88 | width_ratios=width_ratios, 89 | ) 90 | if subplot_spec: 91 | gs = gridspec.GridSpecFromSubplotSpec( 92 | subplot_spec=subplot_spec, *args, **kwargs 93 | ) 94 | self.big_ax = plt.Subplot(self.fig, subplot_spec) 95 | else: 96 | gs = gridspec.GridSpec(*args, **kwargs) 97 | self.big_ax = plt.Subplot(self.fig, gridspec.GridSpec(1, 1)[0]) 98 | 99 | [sp.set_visible(False) for sp in self.big_ax.spines.values()] 100 | self.big_ax.set_xticks([]) 101 | self.big_ax.set_yticks([]) 102 | self.big_ax.patch.set_facecolor("none") 103 | 104 | self.axs = [] 105 | for igs in gs: 106 | ax = plt.Subplot(self.fig, igs) 107 | self.fig.add_subplot(ax) 108 | self.axs.append(ax) 109 | self.fig.add_subplot(self.big_ax) 110 | 111 | # get last axs row and first col 112 | self.last_row = [] 113 | self.first_col = [] 114 | for ax in self.axs: 115 | if ax.get_subplotspec().is_last_row(): 116 | self.last_row.append(ax) 117 | if ax.get_subplotspec().is_first_col(): 118 | self.first_col.append(ax) 119 | 120 | # Set common x/y lim for ax in the same col/row 121 | # and share x and y between them 122 | for i, ax in enumerate(self.axs): 123 | if ylims is not None: 124 | ax.set_ylim(ylims[::-1][i // ncols]) 125 | ax.sharey(self.first_col[i // ncols]) 126 | if xlims is not None: 127 | ax.set_xlim(xlims[i % ncols]) 128 | ax.sharex(self.last_row[i % ncols]) 129 | self.standardize_ticks() 130 | if d: 131 | self.draw_diags() 132 | self.set_spines() 133 | 134 | @staticmethod 135 | def _calculate_ratios(lims, scale): 136 | """ 137 | Calculate width or height ratios based on axis limits and scale. 138 | 139 | Parameters 140 | ---------- 141 | lims : tuple of tuples 142 | Axis limits for each subplot. 143 | scale : str 144 | Scaling for the axis ('linear' or 'log'). 145 | """ 146 | if lims is None: 147 | return [1] 148 | 149 | if scale == "log": 150 | ratios = [np.log(i[1]) - np.log(i[0]) for i in lims] 151 | else: 152 | ratios = [i[1] - i[0] for i in lims] 153 | 154 | # handle datetime xlims 155 | if isinstance(ratios[0], timedelta): 156 | ratios = [tt.total_seconds() for tt in ratios] 157 | 158 | return ratios 159 | 160 | @staticmethod 161 | def draw_diag(ax, xpos, ypos, xlen, ylen, **kwargs): 162 | return ax.plot((xpos - xlen, xpos + xlen), (ypos - ylen, ypos + ylen), **kwargs) 163 | 164 | def draw_diags(self, d=None, tilt=None): 165 | """ 166 | 167 | Parameters 168 | ---------- 169 | d: float 170 | Length of diagonal split mark used to indicate broken axes 171 | tilt: float 172 | Angle of diagonal split mark 173 | """ 174 | if d is not None: 175 | self.d = d 176 | if tilt is not None: 177 | self.tilt = tilt 178 | size = self.fig.get_size_inches() 179 | 180 | d_kwargs = dict( 181 | transform=self.fig.transFigure, 182 | color=self.diag_color, 183 | clip_on=False, 184 | lw=rcParams["axes.linewidth"], 185 | ylen=self.d * np.sin(self.tilt * np.pi / 180) * size[0] / size[1], 186 | xlen=self.d * np.cos(self.tilt * np.pi / 180) 187 | ) 188 | 189 | ds = [] 190 | for ax in self.axs: 191 | bounds = ax.get_position().bounds 192 | 193 | if ax.get_subplotspec().is_last_row(): 194 | ypos = bounds[1] 195 | if not ax.get_subplotspec().is_last_col(): 196 | xpos = bounds[0] + bounds[2] 197 | ds += self.draw_diag(ax, xpos, ypos, **d_kwargs) 198 | if not ax.get_subplotspec().is_first_col(): 199 | xpos = bounds[0] 200 | ds += self.draw_diag(ax, xpos, ypos, **d_kwargs) 201 | 202 | if ax.get_subplotspec().is_first_col(): 203 | xpos = bounds[0] 204 | if not ax.get_subplotspec().is_first_row(): 205 | ypos = bounds[1] + bounds[3] 206 | ds += self.draw_diag(ax, xpos, ypos, **d_kwargs) 207 | if not ax.get_subplotspec().is_last_row(): 208 | ypos = bounds[1] 209 | ds += self.draw_diag(ax, xpos, ypos, **d_kwargs) 210 | 211 | if not self.despine: 212 | if ax.get_subplotspec().is_first_row(): 213 | ypos = bounds[1] + bounds[3] 214 | if not ax.get_subplotspec().is_last_col(): 215 | xpos = bounds[0] + bounds[2] 216 | ds += self.draw_diag(ax, xpos, ypos, **d_kwargs) 217 | if not ax.get_subplotspec().is_first_col(): 218 | xpos = bounds[0] 219 | ds += self.draw_diag(ax, xpos, ypos, **d_kwargs) 220 | 221 | if ax.get_subplotspec().is_last_col(): 222 | xpos = bounds[0] + bounds[2] 223 | if not ax.get_subplotspec().is_first_row(): 224 | ypos = bounds[1] + bounds[3] 225 | ds += self.draw_diag(ax, xpos, ypos, **d_kwargs) 226 | if not ax.get_subplotspec().is_last_row(): 227 | ypos = bounds[1] 228 | ds += self.draw_diag(ax, xpos, ypos, **d_kwargs) 229 | self.diag_handles = ds 230 | 231 | def set_spines(self): 232 | """Removes the spines of internal axes that are not boarder spines.""" 233 | 234 | # Helper function to hide axis elements 235 | def hide_axis_elements(axis): 236 | for element in (axis.get_majorticklines() + axis.get_minorticklines() + 237 | axis.get_majorticklabels() + axis.get_minorticklabels()): 238 | element.set_visible(False) 239 | 240 | for ax in self.axs: 241 | ax.xaxis.tick_bottom() 242 | ax.yaxis.tick_left() 243 | subplotspec = ax.get_subplotspec() 244 | if not subplotspec.is_last_row(): 245 | ax.spines["bottom"].set_visible(False) 246 | hide_axis_elements(ax.xaxis) 247 | if self.despine or not subplotspec.is_first_row(): 248 | ax.spines["top"].set_visible(False) 249 | if not subplotspec.is_first_col(): 250 | ax.spines["left"].set_visible(False) 251 | hide_axis_elements(ax.yaxis) 252 | if self.despine or not subplotspec.is_last_col(): 253 | ax.spines["right"].set_visible(False) 254 | 255 | def standardize_ticks(self, xbase=None, ybase=None): 256 | """Make all the internal axes share tick bases 257 | 258 | Parameters 259 | ---------- 260 | xbase, ybase: (optional) None or float 261 | If `xbase` or `ybase` is a float, manually set all tick locators to 262 | this base. Otherwise, use the largest base across internal subplots 263 | for that axis. 264 | """ 265 | if xbase is None: 266 | if self.axs[0].xaxis.get_scale() == "log": 267 | xbase = max( 268 | ax.xaxis.get_ticklocs()[1] / ax.xaxis.get_ticklocs()[0] 269 | for ax in self.axs 270 | if ax.get_subplotspec().is_last_row() 271 | ) 272 | else: 273 | xbase = max( 274 | ax.xaxis.get_ticklocs()[1] - ax.xaxis.get_ticklocs()[0] 275 | for ax in self.axs 276 | if ax.get_subplotspec().is_last_row() 277 | ) 278 | if ybase is None: 279 | if self.axs[0].yaxis.get_scale() == "log": 280 | ybase = max( 281 | ax.yaxis.get_ticklocs()[1] / ax.yaxis.get_ticklocs()[0] 282 | for ax in self.axs 283 | if ax.get_subplotspec().is_first_col() 284 | ) 285 | else: 286 | ybase = max( 287 | ax.yaxis.get_ticklocs()[1] - ax.yaxis.get_ticklocs()[0] 288 | for ax in self.axs 289 | if ax.get_subplotspec().is_first_col() 290 | ) 291 | 292 | for ax in self.axs: 293 | if ax.get_subplotspec().is_first_col(): 294 | if ax.yaxis.get_scale() == "log": 295 | ax.yaxis.set_major_locator(ticker.LogLocator(ybase)) 296 | else: 297 | ax.yaxis.set_major_locator(ticker.MultipleLocator(ybase)) 298 | if ax.get_subplotspec().is_last_row(): 299 | if ax.xaxis.get_scale() == "log": 300 | ax.xaxis.set_major_locator(ticker.LogLocator(xbase)) 301 | else: 302 | ax.xaxis.set_major_locator(ticker.MultipleLocator(xbase)) 303 | 304 | def fix_exponent(self): 305 | for ax in self.axs: 306 | subplotspec = ax.get_subplotspec() 307 | if not (subplotspec.is_first_col() and subplotspec.is_first_row()): 308 | ax.get_yaxis().get_offset_text().set_visible(False) 309 | if not (subplotspec.is_last_col() and subplotspec.is_last_row()): 310 | ax.get_xaxis().get_offset_text().set_visible(False) 311 | 312 | def __getattr__(self, method): 313 | """Catch all methods that are not defined and pass to internal axes 314 | (e.g. plot, errorbar, etc.). 315 | """ 316 | if method in [ 317 | "get_yaxis", 318 | "get_xaxis", 319 | "get_shared_x_axes", 320 | "get_shared_y_axes", 321 | "get_second_yaxis", 322 | "get_second_xaxis", 323 | "get_legend", 324 | "get_title", 325 | "get_xlabel", 326 | "get_ylabel", 327 | ]: 328 | return getattr(self.big_ax, method) 329 | 330 | return lambda *args, **kwargs: self.subax_call(method, args, kwargs) 331 | 332 | def subax_call(self, method, args, kwargs): 333 | """Apply method call to all internal axes. Called by CallCurator.""" 334 | result = [] 335 | for ax in self.axs: 336 | if ax.xaxis.get_scale() == "log": 337 | ax.xaxis.set_major_locator(ticker.LogLocator()) 338 | else: 339 | ax.xaxis.set_major_locator(ticker.AutoLocator()) 340 | if ax.yaxis.get_scale() == "log": 341 | ax.yaxis.set_major_locator(ticker.LogLocator()) 342 | else: 343 | ax.yaxis.set_major_locator(ticker.AutoLocator()) 344 | result.append(getattr(ax, method)(*args, **kwargs)) 345 | 346 | self.standardize_ticks() 347 | self.set_spines() 348 | self.fix_exponent() 349 | 350 | return result 351 | 352 | def set_xlabel(self, label, labelpad=15, **kwargs): 353 | return self.big_ax.set_xlabel(label, labelpad=labelpad, **kwargs) 354 | 355 | def set_ylabel(self, label, labelpad=30, **kwargs): 356 | return self.big_ax.set_ylabel(label, labelpad=labelpad, **kwargs) 357 | 358 | def set_title(self, *args, **kwargs): 359 | return self.big_ax.set_title(*args, **kwargs) 360 | 361 | def legend(self, handles=None, labels=None, *args, **kwargs): 362 | if handles is None or labels is None: 363 | h, l = self.axs[0].get_legend_handles_labels() 364 | if handles is None: 365 | handles = h 366 | if labels is None: 367 | labels = l 368 | return self.big_ax.legend(handles=handles, labels=labels, *args, **kwargs) 369 | 370 | def secondary_yaxis( 371 | self, location="right", functions=None, label=None, labelpad=30 372 | ): 373 | assert location in ["right", "left"], "location must be 'right' or 'left'" 374 | if location == "right": 375 | [ 376 | ax.secondary_yaxis("right", functions=functions) 377 | for ax in self.axs 378 | if ax.get_subplotspec().is_last_col() 379 | ] 380 | else: 381 | [ 382 | ax.secondary_yaxis("left", functions=functions) 383 | for ax in self.axs 384 | if ax.get_subplotspec().is_first_col() 385 | ] 386 | secax = self.big_ax.secondary_yaxis(location, functions=functions) 387 | 388 | secax.spines[location].set_visible(False) 389 | secax.set_yticks([]) 390 | secax.patch.set_facecolor("none") 391 | 392 | if label is not None: 393 | secax.set_ylabel(label, labelpad=labelpad) 394 | 395 | return secax 396 | 397 | def secondary_xaxis(self, location="top", functions=None, label=None, labelpad=30): 398 | assert location in ["top", "bottom"], "location must be 'top' or 'bottom'" 399 | if location == "top": 400 | [ 401 | ax.secondary_xaxis("top", functions=functions) 402 | for ax in self.axs 403 | if ax.get_subplotspec().is_first_row() 404 | ] 405 | else: 406 | [ 407 | ax.secondary_xaxis("bottom", functions=functions) 408 | for ax in self.axs 409 | if ax.get_subplotspec().is_last_row() 410 | ] 411 | secax = self.big_ax.secondary_xaxis(location, functions=functions) 412 | 413 | secax.spines[location].set_visible(False) 414 | secax.set_xticks([]) 415 | secax.patch.set_facecolor("none") 416 | 417 | if label is not None: 418 | secax.set_xlabel(label, labelpad=labelpad) 419 | 420 | return secax 421 | 422 | def text(self, x, y, s, *args, **kwargs): 423 | # find axes object that should contain text 424 | for ax in self.axs: 425 | xlim = ax.get_xlim() 426 | ylim = ax.get_ylim() 427 | if xlim[0] < x < xlim[1] and ylim[0] < y < ylim[1]: 428 | return ax.text(x, y, s, *args, **kwargs) 429 | 430 | raise ValueError("(x,y) coordinate of text not within any axes") 431 | 432 | @property 433 | def spines(self): 434 | if self._spines is None: 435 | self._spines = dict( 436 | top=[ax.spines["top"] for ax in self.axs if ax.get_subplotspec().is_first_row()], 437 | right=[ax.spines["right"] for ax in self.axs if ax.get_subplotspec().is_last_col()], 438 | bottom=[ax.spines["bottom"] for ax in self.axs if ax.get_subplotspec().is_last_row()], 439 | left=[ax.spines["left"] for ax in self.axs if ax.get_subplotspec().is_first_col()], 440 | ) 441 | return self._spines 442 | 443 | 444 | def brokenaxes(*args, **kwargs): 445 | """Convenience method for `BrokenAxes` class. 446 | 447 | Parameters 448 | ---------- 449 | args, kwargs: passed to `BrokenAxes()` 450 | 451 | Returns 452 | ------- 453 | out: `BrokenAxes` 454 | """ 455 | return BrokenAxes(*args, **kwargs) 456 | -------------------------------------------------------------------------------- /datetime_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bendichter/brokenaxes/042e2facd48fb4966bc4cbe7c95532f4a33a544d/datetime_example.png -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 14 | # the i18n builder cannot share the environment and doctrees with the others 15 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 16 | 17 | .PHONY: help 18 | help: 19 | @echo "Please use \`make ' where is one of" 20 | @echo " html to make standalone HTML files" 21 | @echo " dirhtml to make HTML files named index.html in directories" 22 | @echo " singlehtml to make a single large HTML file" 23 | @echo " pickle to make pickle files" 24 | @echo " json to make JSON files" 25 | @echo " htmlhelp to make HTML files and a HTML help project" 26 | @echo " qthelp to make HTML files and a qthelp project" 27 | @echo " applehelp to make an Apple Help Book" 28 | @echo " devhelp to make HTML files and a Devhelp project" 29 | @echo " epub to make an epub" 30 | @echo " epub3 to make an epub3" 31 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 32 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 33 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 34 | @echo " text to make text files" 35 | @echo " man to make manual pages" 36 | @echo " texinfo to make Texinfo files" 37 | @echo " info to make Texinfo files and run them through makeinfo" 38 | @echo " gettext to make PO message catalogs" 39 | @echo " changes to make an overview of all changed/added/deprecated items" 40 | @echo " xml to make Docutils-native XML files" 41 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 42 | @echo " linkcheck to check all external links for integrity" 43 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 44 | @echo " coverage to run coverage check of the documentation (if enabled)" 45 | @echo " dummy to check syntax errors of document sources" 46 | 47 | .PHONY: clean 48 | clean: 49 | rm -rf $(BUILDDIR)/* 50 | 51 | .PHONY: html 52 | html: 53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 54 | @echo 55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 56 | 57 | .PHONY: dirhtml 58 | dirhtml: 59 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 60 | @echo 61 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 62 | 63 | .PHONY: singlehtml 64 | singlehtml: 65 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 66 | @echo 67 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 68 | 69 | .PHONY: pickle 70 | pickle: 71 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 72 | @echo 73 | @echo "Build finished; now you can process the pickle files." 74 | 75 | .PHONY: json 76 | json: 77 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 78 | @echo 79 | @echo "Build finished; now you can process the JSON files." 80 | 81 | .PHONY: htmlhelp 82 | htmlhelp: 83 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 84 | @echo 85 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 86 | ".hhp project file in $(BUILDDIR)/htmlhelp." 87 | 88 | .PHONY: qthelp 89 | qthelp: 90 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 91 | @echo 92 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 93 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 94 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/brokenaxes.qhcp" 95 | @echo "To view the help file:" 96 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/brokenaxes.qhc" 97 | 98 | .PHONY: applehelp 99 | applehelp: 100 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp 101 | @echo 102 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." 103 | @echo "N.B. You won't be able to view it unless you put it in" \ 104 | "~/Library/Documentation/Help or install it in your application" \ 105 | "bundle." 106 | 107 | .PHONY: devhelp 108 | devhelp: 109 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 110 | @echo 111 | @echo "Build finished." 112 | @echo "To view the help file:" 113 | @echo "# mkdir -p $$HOME/.local/share/devhelp/brokenaxes" 114 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/brokenaxes" 115 | @echo "# devhelp" 116 | 117 | .PHONY: epub 118 | epub: 119 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 120 | @echo 121 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 122 | 123 | .PHONY: epub3 124 | epub3: 125 | $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3 126 | @echo 127 | @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3." 128 | 129 | .PHONY: latex 130 | latex: 131 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 132 | @echo 133 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 134 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 135 | "(use \`make latexpdf' here to do that automatically)." 136 | 137 | .PHONY: latexpdf 138 | latexpdf: 139 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 140 | @echo "Running LaTeX files through pdflatex..." 141 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 142 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 143 | 144 | .PHONY: latexpdfja 145 | latexpdfja: 146 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 147 | @echo "Running LaTeX files through platex and dvipdfmx..." 148 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 149 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 150 | 151 | .PHONY: text 152 | text: 153 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 154 | @echo 155 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 156 | 157 | .PHONY: man 158 | man: 159 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 160 | @echo 161 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 162 | 163 | .PHONY: texinfo 164 | texinfo: 165 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 166 | @echo 167 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 168 | @echo "Run \`make' in that directory to run these through makeinfo" \ 169 | "(use \`make info' here to do that automatically)." 170 | 171 | .PHONY: info 172 | info: 173 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 174 | @echo "Running Texinfo files through makeinfo..." 175 | make -C $(BUILDDIR)/texinfo info 176 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 177 | 178 | .PHONY: gettext 179 | gettext: 180 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 181 | @echo 182 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 183 | 184 | .PHONY: changes 185 | changes: 186 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 187 | @echo 188 | @echo "The overview file is in $(BUILDDIR)/changes." 189 | 190 | .PHONY: linkcheck 191 | linkcheck: 192 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 193 | @echo 194 | @echo "Link check complete; look for any errors in the above output " \ 195 | "or in $(BUILDDIR)/linkcheck/output.txt." 196 | 197 | .PHONY: doctest 198 | doctest: 199 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 200 | @echo "Testing of doctests in the sources finished, look at the " \ 201 | "results in $(BUILDDIR)/doctest/output.txt." 202 | 203 | .PHONY: coverage 204 | coverage: 205 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage 206 | @echo "Testing of coverage in the sources finished, look at the " \ 207 | "results in $(BUILDDIR)/coverage/python.txt." 208 | 209 | .PHONY: xml 210 | xml: 211 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 212 | @echo 213 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 214 | 215 | .PHONY: pseudoxml 216 | pseudoxml: 217 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 218 | @echo 219 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 220 | 221 | .PHONY: dummy 222 | dummy: 223 | $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy 224 | @echo 225 | @echo "Build finished. Dummy builder generates no files." 226 | -------------------------------------------------------------------------------- /docs/requirements-doc.txt: -------------------------------------------------------------------------------- 1 | matplotlib 2 | numpy 3 | sphinx 4 | sphinx-gallery 5 | PyQt5 6 | -------------------------------------------------------------------------------- /docs/source/auto_examples/auto_examples_jupyter.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bendichter/brokenaxes/042e2facd48fb4966bc4cbe7c95532f4a33a544d/docs/source/auto_examples/auto_examples_jupyter.zip -------------------------------------------------------------------------------- /docs/source/auto_examples/auto_examples_python.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bendichter/brokenaxes/042e2facd48fb4966bc4cbe7c95532f4a33a544d/docs/source/auto_examples/auto_examples_python.zip -------------------------------------------------------------------------------- /docs/source/auto_examples/images/sphx_glr_plot_logscales_001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bendichter/brokenaxes/042e2facd48fb4966bc4cbe7c95532f4a33a544d/docs/source/auto_examples/images/sphx_glr_plot_logscales_001.png -------------------------------------------------------------------------------- /docs/source/auto_examples/images/sphx_glr_plot_subplots_001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bendichter/brokenaxes/042e2facd48fb4966bc4cbe7c95532f4a33a544d/docs/source/auto_examples/images/sphx_glr_plot_subplots_001.png -------------------------------------------------------------------------------- /docs/source/auto_examples/images/sphx_glr_plot_usage_001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bendichter/brokenaxes/042e2facd48fb4966bc4cbe7c95532f4a33a544d/docs/source/auto_examples/images/sphx_glr_plot_usage_001.png -------------------------------------------------------------------------------- /docs/source/auto_examples/images/thumb/sphx_glr_plot_logscales_thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bendichter/brokenaxes/042e2facd48fb4966bc4cbe7c95532f4a33a544d/docs/source/auto_examples/images/thumb/sphx_glr_plot_logscales_thumb.png -------------------------------------------------------------------------------- /docs/source/auto_examples/images/thumb/sphx_glr_plot_subplots_thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bendichter/brokenaxes/042e2facd48fb4966bc4cbe7c95532f4a33a544d/docs/source/auto_examples/images/thumb/sphx_glr_plot_subplots_thumb.png -------------------------------------------------------------------------------- /docs/source/auto_examples/images/thumb/sphx_glr_plot_usage_thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bendichter/brokenaxes/042e2facd48fb4966bc4cbe7c95532f4a33a544d/docs/source/auto_examples/images/thumb/sphx_glr_plot_usage_thumb.png -------------------------------------------------------------------------------- /docs/source/auto_examples/index.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | 4 | 5 | .. _sphx_glr_auto_examples: 6 | 7 | Gallery of examples 8 | =================== 9 | 10 | 11 | 12 | .. raw:: html 13 | 14 |
15 | 16 | .. only:: html 17 | 18 | .. figure:: /auto_examples/images/thumb/sphx_glr_plot_usage_thumb.png 19 | 20 | :ref:`sphx_glr_auto_examples_plot_usage.py` 21 | 22 | .. raw:: html 23 | 24 |
25 | 26 | 27 | .. toctree:: 28 | :hidden: 29 | 30 | /auto_examples/plot_usage 31 | 32 | .. raw:: html 33 | 34 |
35 | 36 | .. only:: html 37 | 38 | .. figure:: /auto_examples/images/thumb/sphx_glr_plot_subplots_thumb.png 39 | 40 | :ref:`sphx_glr_auto_examples_plot_subplots.py` 41 | 42 | .. raw:: html 43 | 44 |
45 | 46 | 47 | .. toctree:: 48 | :hidden: 49 | 50 | /auto_examples/plot_subplots 51 | 52 | .. raw:: html 53 | 54 |
55 | 56 | .. only:: html 57 | 58 | .. figure:: /auto_examples/images/thumb/sphx_glr_plot_logscales_thumb.png 59 | 60 | :ref:`sphx_glr_auto_examples_plot_logscales.py` 61 | 62 | .. raw:: html 63 | 64 |
65 | 66 | 67 | .. toctree:: 68 | :hidden: 69 | 70 | /auto_examples/plot_logscales 71 | .. raw:: html 72 | 73 |
74 | 75 | 76 | 77 | .. only :: html 78 | 79 | .. container:: sphx-glr-footer 80 | :class: sphx-glr-footer-gallery 81 | 82 | 83 | .. container:: sphx-glr-download 84 | 85 | :download:`Download all examples in Python source code: auto_examples_python.zip ` 86 | 87 | 88 | 89 | .. container:: sphx-glr-download 90 | 91 | :download:`Download all examples in Jupyter notebooks: auto_examples_jupyter.zip ` 92 | 93 | 94 | .. only:: html 95 | 96 | .. rst-class:: sphx-glr-signature 97 | 98 | `Gallery generated by Sphinx-Gallery `_ 99 | -------------------------------------------------------------------------------- /docs/source/auto_examples/plot_logscales.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "metadata": { 4 | "language_info": { 5 | "mimetype": "text/x-python", 6 | "name": "python", 7 | "nbconvert_exporter": "python", 8 | "version": "3.5.3", 9 | "pygments_lexer": "ipython3", 10 | "file_extension": ".py", 11 | "codemirror_mode": { 12 | "name": "ipython", 13 | "version": 3 14 | } 15 | }, 16 | "kernelspec": { 17 | "language": "python", 18 | "name": "python3", 19 | "display_name": "Python 3" 20 | } 21 | }, 22 | "nbformat_minor": 0, 23 | "cells": [ 24 | { 25 | "cell_type": "code", 26 | "execution_count": null, 27 | "source": [ 28 | "%matplotlib inline" 29 | ], 30 | "outputs": [], 31 | "metadata": { 32 | "collapsed": false 33 | } 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "source": [ 38 | "\nLog scales\n==========\n\nBrokenaxe compute automatically the correct layout for a 1:1 scale. However, for\nlogarithmic scales, the 1:1 scale has to be adapted. This is done via the\n`yscale` or `xscale` arguments.\n\n\n" 39 | ], 40 | "metadata": {} 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": null, 45 | "source": [ 46 | "import matplotlib.pyplot as plt\nfrom brokenaxes import brokenaxes\nimport numpy as np\n\nfig = plt.figure(figsize=(5,5))\nbax = brokenaxes(xlims=((1, 500), (600, 10000)),\n\t ylims=((1, 500), (600, 10000)),\n\t\t hspace=.15, xscale='log', yscale='log')\n\nx = np.logspace(0.0, 4, 100)\nbax.loglog(x, x, label='$y=x=10^{0}$ to $10^{4}$')\n\nbax.legend(loc='best')\nbax.grid(axis='both', which='major', ls='-')\nbax.grid(axis='both', which='minor', ls='--', alpha=0.4)\nbax.set_xlabel('x')\nbax.set_ylabel('y')\nplt.show()" 47 | ], 48 | "outputs": [], 49 | "metadata": { 50 | "collapsed": false 51 | } 52 | } 53 | ] 54 | } -------------------------------------------------------------------------------- /docs/source/auto_examples/plot_logscales.py: -------------------------------------------------------------------------------- 1 | """ 2 | Log scales 3 | ========== 4 | 5 | Brokenaxes computes automatically the correct layout for a 1:1 scale. For 6 | logarithmic scales, the 1:1 scale has to be adapted using the `yscale` or 7 | `xscale` arguments. 8 | 9 | """ 10 | 11 | 12 | import matplotlib.pyplot as plt 13 | from brokenaxes import brokenaxes 14 | import numpy as np 15 | 16 | fig = plt.figure(figsize=(5,5)) 17 | bax = brokenaxes(xlims=((1, 500), (600, 10000)), 18 | ylims=((1, 500), (600, 10000)), 19 | hspace=.15, xscale='log', yscale='log') 20 | 21 | x = np.logspace(0.0, 4, 100) 22 | bax.loglog(x, x, label='$y=x=10^{0}$ to $10^{4}$') 23 | 24 | bax.legend(loc='best') 25 | bax.grid(axis='both', which='major', ls='-') 26 | bax.grid(axis='both', which='minor', ls='--', alpha=0.4) 27 | bax.set_xlabel('x') 28 | bax.set_ylabel('y') 29 | plt.show() 30 | -------------------------------------------------------------------------------- /docs/source/auto_examples/plot_logscales.py.md5: -------------------------------------------------------------------------------- 1 | 7e666074f181cc07e6223d1bbc4ef03f -------------------------------------------------------------------------------- /docs/source/auto_examples/plot_logscales.rst: -------------------------------------------------------------------------------- 1 | .. note:: 2 | :class: sphx-glr-download-link-note 3 | 4 | Click :ref:`here ` to download the full example code 5 | .. rst-class:: sphx-glr-example-title 6 | 7 | .. _sphx_glr_auto_examples_plot_logscales.py: 8 | 9 | 10 | Log scales 11 | ========== 12 | 13 | Brokenaxe compute automatically the correct layout for a 1:1 scale. However, for 14 | logarithmic scales, the 1:1 scale has to be adapted. This is done via the 15 | `yscale` or `xscale` arguments. 16 | 17 | 18 | 19 | 20 | .. image:: /auto_examples/images/sphx_glr_plot_logscales_001.png 21 | :class: sphx-glr-single-img 22 | 23 | 24 | 25 | 26 | 27 | .. code-block:: default 28 | 29 | 30 | 31 | import matplotlib.pyplot as plt 32 | from brokenaxes import brokenaxes 33 | import numpy as np 34 | 35 | fig = plt.figure(figsize=(5,5)) 36 | bax = brokenaxes(xlims=((1, 500), (600, 10000)), 37 | ylims=((1, 500), (600, 10000)), 38 | hspace=.15, xscale='log', yscale='log') 39 | 40 | x = np.logspace(0.0, 4, 100) 41 | bax.loglog(x, x, label='$y=x=10^{0}$ to $10^{4}$') 42 | 43 | bax.legend(loc='best') 44 | bax.grid(axis='both', which='major', ls='-') 45 | bax.grid(axis='both', which='minor', ls='--', alpha=0.4) 46 | bax.set_xlabel('x') 47 | bax.set_ylabel('y') 48 | plt.show() 49 | 50 | 51 | .. rst-class:: sphx-glr-timing 52 | 53 | **Total running time of the script:** ( 1 minutes 8.214 seconds) 54 | 55 | 56 | .. _sphx_glr_download_auto_examples_plot_logscales.py: 57 | 58 | 59 | .. only :: html 60 | 61 | .. container:: sphx-glr-footer 62 | :class: sphx-glr-footer-example 63 | 64 | 65 | 66 | .. container:: sphx-glr-download 67 | 68 | :download:`Download Python source code: plot_logscales.py ` 69 | 70 | 71 | 72 | .. container:: sphx-glr-download 73 | 74 | :download:`Download Jupyter notebook: plot_logscales.ipynb ` 75 | 76 | 77 | .. only:: html 78 | 79 | .. rst-class:: sphx-glr-signature 80 | 81 | `Gallery generated by Sphinx-Gallery `_ 82 | -------------------------------------------------------------------------------- /docs/source/auto_examples/plot_subplots.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "metadata": { 4 | "language_info": { 5 | "mimetype": "text/x-python", 6 | "name": "python", 7 | "nbconvert_exporter": "python", 8 | "version": "3.5.3", 9 | "pygments_lexer": "ipython3", 10 | "file_extension": ".py", 11 | "codemirror_mode": { 12 | "name": "ipython", 13 | "version": 3 14 | } 15 | }, 16 | "kernelspec": { 17 | "language": "python", 18 | "name": "python3", 19 | "display_name": "Python 3" 20 | } 21 | }, 22 | "nbformat_minor": 0, 23 | "cells": [ 24 | { 25 | "cell_type": "code", 26 | "execution_count": null, 27 | "source": [ 28 | "%matplotlib inline" 29 | ], 30 | "outputs": [], 31 | "metadata": { 32 | "collapsed": false 33 | } 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "source": [ 38 | "\nHandle subplots with brokenaxes\n===============================\n\nIf you want to use subplots together with brokenaxes, you have to use GridSpec.\n\n\n" 39 | ], 40 | "metadata": {} 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": null, 45 | "source": [ 46 | "from brokenaxes import brokenaxes\nfrom matplotlib.gridspec import GridSpec\nimport numpy as np\n\nsps1, sps2 = GridSpec(2,1)\n\nbax = brokenaxes(xlims=((.1, .3),(.7, .8)), subplot_spec=sps1)\nx = np.linspace(0, 1, 100)\nbax.plot(x, np.sin(x*30), ls=':', color='m')\n\nx = np.random.poisson(3, 1000)\nbax = brokenaxes(xlims=((0, 2.5), (3, 6)), subplot_spec=sps2)\nbax.hist(x, histtype='bar')" 47 | ], 48 | "outputs": [], 49 | "metadata": { 50 | "collapsed": false 51 | } 52 | } 53 | ] 54 | } -------------------------------------------------------------------------------- /docs/source/auto_examples/plot_subplots.py: -------------------------------------------------------------------------------- 1 | """ 2 | Handle subplots with brokenaxes 3 | =============================== 4 | 5 | If you want to use subplots together with brokenaxes, you have to use GridSpec. 6 | 7 | """ 8 | 9 | 10 | from brokenaxes import brokenaxes 11 | from matplotlib.gridspec import GridSpec 12 | import numpy as np 13 | 14 | sps1, sps2 = GridSpec(2,1) 15 | 16 | bax = brokenaxes(xlims=((.1, .3),(.7, .8)), subplot_spec=sps1) 17 | x = np.linspace(0, 1, 100) 18 | bax.plot(x, np.sin(x*30), ls=':', color='m') 19 | 20 | x = np.random.poisson(3, 1000) 21 | bax = brokenaxes(xlims=((0, 2.5), (3, 6)), subplot_spec=sps2) 22 | bax.hist(x, histtype='bar') 23 | -------------------------------------------------------------------------------- /docs/source/auto_examples/plot_subplots.py.md5: -------------------------------------------------------------------------------- 1 | a4a100cb4835f58f5eb0a0a459450581 -------------------------------------------------------------------------------- /docs/source/auto_examples/plot_subplots.rst: -------------------------------------------------------------------------------- 1 | .. note:: 2 | :class: sphx-glr-download-link-note 3 | 4 | Click :ref:`here ` to download the full example code 5 | .. rst-class:: sphx-glr-example-title 6 | 7 | .. _sphx_glr_auto_examples_plot_subplots.py: 8 | 9 | 10 | Handle subplots with brokenaxes 11 | =============================== 12 | 13 | If you want to use subplots together with brokenaxes, you have to use GridSpec. 14 | 15 | 16 | 17 | 18 | .. image:: /auto_examples/images/sphx_glr_plot_subplots_001.png 19 | :class: sphx-glr-single-img 20 | 21 | 22 | 23 | 24 | 25 | .. code-block:: default 26 | 27 | 28 | 29 | from brokenaxes import brokenaxes 30 | from matplotlib.gridspec import GridSpec 31 | import numpy as np 32 | 33 | sps1, sps2 = GridSpec(2,1) 34 | 35 | bax = brokenaxes(xlims=((.1, .3),(.7, .8)), subplot_spec=sps1) 36 | x = np.linspace(0, 1, 100) 37 | bax.plot(x, np.sin(x*30), ls=':', color='m') 38 | 39 | x = np.random.poisson(3, 1000) 40 | bax = brokenaxes(xlims=((0, 2.5), (3, 6)), subplot_spec=sps2) 41 | bax.hist(x, histtype='bar') 42 | 43 | 44 | .. rst-class:: sphx-glr-timing 45 | 46 | **Total running time of the script:** ( 0 minutes 8.709 seconds) 47 | 48 | 49 | .. _sphx_glr_download_auto_examples_plot_subplots.py: 50 | 51 | 52 | .. only :: html 53 | 54 | .. container:: sphx-glr-footer 55 | :class: sphx-glr-footer-example 56 | 57 | 58 | 59 | .. container:: sphx-glr-download 60 | 61 | :download:`Download Python source code: plot_subplots.py ` 62 | 63 | 64 | 65 | .. container:: sphx-glr-download 66 | 67 | :download:`Download Jupyter notebook: plot_subplots.ipynb ` 68 | 69 | 70 | .. only:: html 71 | 72 | .. rst-class:: sphx-glr-signature 73 | 74 | `Gallery generated by Sphinx-Gallery `_ 75 | -------------------------------------------------------------------------------- /docs/source/auto_examples/plot_usage.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "metadata": { 4 | "language_info": { 5 | "mimetype": "text/x-python", 6 | "name": "python", 7 | "nbconvert_exporter": "python", 8 | "version": "3.5.3", 9 | "pygments_lexer": "ipython3", 10 | "file_extension": ".py", 11 | "codemirror_mode": { 12 | "name": "ipython", 13 | "version": 3 14 | } 15 | }, 16 | "kernelspec": { 17 | "language": "python", 18 | "name": "python3", 19 | "display_name": "Python 3" 20 | } 21 | }, 22 | "nbformat_minor": 0, 23 | "cells": [ 24 | { 25 | "cell_type": "code", 26 | "execution_count": null, 27 | "source": [ 28 | "%matplotlib inline" 29 | ], 30 | "outputs": [], 31 | "metadata": { 32 | "collapsed": false 33 | } 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "source": [ 38 | "\nBasic usage\n===========\n\nThis example presents the basic usage of brokenaxes\n\n\n" 39 | ], 40 | "metadata": {} 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": null, 45 | "source": [ 46 | "import matplotlib.pyplot as plt\nfrom brokenaxes import brokenaxes\nimport numpy as np\n\nfig = plt.figure(figsize=(5,2))\nbax = brokenaxes(xlims=((0, .1), (.4, .7)), ylims=((-1, .7), (.79, 1)), hspace=.05)\nx = np.linspace(0, 1, 100)\nbax.plot(x, np.sin(10 * x), label='sin')\nbax.plot(x, np.cos(10 * x), label='cos')\nbax.legend(loc=3)\nbax.set_xlabel('time')\nbax.set_ylabel('value')" 47 | ], 48 | "outputs": [], 49 | "metadata": { 50 | "collapsed": false 51 | } 52 | } 53 | ] 54 | } -------------------------------------------------------------------------------- /docs/source/auto_examples/plot_usage.py: -------------------------------------------------------------------------------- 1 | """ 2 | Basic usage 3 | =========== 4 | 5 | This example presents the basic usage of brokenaxes 6 | 7 | """ 8 | 9 | 10 | import matplotlib.pyplot as plt 11 | from brokenaxes import brokenaxes 12 | import numpy as np 13 | 14 | fig = plt.figure(figsize=(5,2)) 15 | bax = brokenaxes(xlims=((0, .1), (.4, .7)), ylims=((-1, .7), (.79, 1)), hspace=.05) 16 | x = np.linspace(0, 1, 100) 17 | bax.plot(x, np.sin(10 * x), label='sin') 18 | bax.plot(x, np.cos(10 * x), label='cos') 19 | bax.legend(loc=3) 20 | bax.set_xlabel('time') 21 | bax.set_ylabel('value') 22 | -------------------------------------------------------------------------------- /docs/source/auto_examples/plot_usage.py.md5: -------------------------------------------------------------------------------- 1 | c1f6a87acf3ce400d6edcda69045e374 -------------------------------------------------------------------------------- /docs/source/auto_examples/plot_usage.rst: -------------------------------------------------------------------------------- 1 | .. note:: 2 | :class: sphx-glr-download-link-note 3 | 4 | Click :ref:`here ` to download the full example code 5 | .. rst-class:: sphx-glr-example-title 6 | 7 | .. _sphx_glr_auto_examples_plot_usage.py: 8 | 9 | 10 | Basic usage 11 | =========== 12 | 13 | This example presents the basic usage of brokenaxes 14 | 15 | 16 | 17 | 18 | .. image:: /auto_examples/images/sphx_glr_plot_usage_001.png 19 | :class: sphx-glr-single-img 20 | 21 | 22 | 23 | 24 | 25 | .. code-block:: default 26 | 27 | 28 | 29 | import matplotlib.pyplot as plt 30 | from brokenaxes import brokenaxes 31 | import numpy as np 32 | 33 | fig = plt.figure(figsize=(5,2)) 34 | bax = brokenaxes(xlims=((0, .1), (.4, .7)), ylims=((-1, .7), (.79, 1)), hspace=.05) 35 | x = np.linspace(0, 1, 100) 36 | bax.plot(x, np.sin(10 * x), label='sin') 37 | bax.plot(x, np.cos(10 * x), label='cos') 38 | bax.legend(loc=3) 39 | bax.set_xlabel('time') 40 | bax.set_ylabel('value') 41 | 42 | 43 | .. rst-class:: sphx-glr-timing 44 | 45 | **Total running time of the script:** ( 0 minutes 12.282 seconds) 46 | 47 | 48 | .. _sphx_glr_download_auto_examples_plot_usage.py: 49 | 50 | 51 | .. only :: html 52 | 53 | .. container:: sphx-glr-footer 54 | :class: sphx-glr-footer-example 55 | 56 | 57 | 58 | .. container:: sphx-glr-download 59 | 60 | :download:`Download Python source code: plot_usage.py ` 61 | 62 | 63 | 64 | .. container:: sphx-glr-download 65 | 66 | :download:`Download Jupyter notebook: plot_usage.ipynb ` 67 | 68 | 69 | .. only:: html 70 | 71 | .. rst-class:: sphx-glr-signature 72 | 73 | `Gallery generated by Sphinx-Gallery `_ 74 | -------------------------------------------------------------------------------- /docs/source/auto_examples/sg_execution_times.rst: -------------------------------------------------------------------------------- 1 | 2 | :orphan: 3 | 4 | .. _sphx_glr_auto_examples_sg_execution_times: 5 | 6 | Computation times 7 | ================= 8 | **01:29.205** total execution time for **auto_examples** files: 9 | 10 | - **01:08.214**: :ref:`sphx_glr_auto_examples_plot_logscales.py` (``plot_logscales.py``) 11 | - **00:12.282**: :ref:`sphx_glr_auto_examples_plot_usage.py` (``plot_usage.py``) 12 | - **00:08.709**: :ref:`sphx_glr_auto_examples_plot_subplots.py` (``plot_subplots.py``) 13 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # brokenaxes documentation build configuration file, created by 5 | # sphinx-quickstart on Wed Apr 12 09:54:55 2017. 6 | # 7 | # This file is execfile()d with the current directory set to its 8 | # containing dir. 9 | # 10 | # Note that not all possible configuration values are present in this 11 | # autogenerated file. 12 | # 13 | # All configuration values have a default; values that are commented out 14 | # serve to show the default. 15 | 16 | # If extensions (or modules to document with autodoc) are in another directory, 17 | # add these directories to sys.path here. If the directory is relative to the 18 | # documentation root, use os.path.abspath to make it absolute, like shown here. 19 | # 20 | import os 21 | import sys 22 | sys.path.insert(0, os.path.abspath('../../')) 23 | 24 | # -- General configuration ------------------------------------------------ 25 | 26 | # If your documentation needs a minimal Sphinx version, state it here. 27 | # 28 | # needs_sphinx = '1.0' 29 | 30 | # Add any Sphinx extension module names here, as strings. They can be 31 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 32 | # ones. 33 | extensions = [ 34 | 'sphinx.ext.autodoc', 35 | 'sphinx.ext.doctest', 36 | 'sphinx.ext.viewcode', 37 | 'sphinx.ext.githubpages', 38 | 'sphinx.ext.napoleon', 39 | 'sphinx_gallery.gen_gallery', 40 | ] 41 | 42 | # Add any paths that contain templates here, relative to this directory. 43 | templates_path = ['_templates'] 44 | 45 | # The suffix(es) of source filenames. 46 | # You can specify multiple suffix as a list of string: 47 | # 48 | # source_suffix = ['.rst', '.md'] 49 | source_suffix = '.rst' 50 | 51 | # The encoding of source files. 52 | # 53 | # source_encoding = 'utf-8-sig' 54 | 55 | # The master toctree document. 56 | master_doc = 'index' 57 | 58 | # General information about the project. 59 | project = 'brokenaxes' 60 | copyright = '2017, Ben Dichter' 61 | author = 'Ben Dichter' 62 | 63 | # The version info for the project you're documenting, acts as replacement for 64 | # |version| and |release|, also used in various other places throughout the 65 | # built documents. 66 | # 67 | # The short X.Y version. 68 | version = '0.1' 69 | # The full version, including alpha/beta/rc tags. 70 | release = '0.1' 71 | 72 | # The language for content autogenerated by Sphinx. Refer to documentation 73 | # for a list of supported languages. 74 | # 75 | # This is also used if you do content translation via gettext catalogs. 76 | # Usually you set "language" from the command line for these cases. 77 | language = None 78 | 79 | # There are two options for replacing |today|: either, you set today to some 80 | # non-false value, then it is used: 81 | # 82 | # today = '' 83 | # 84 | # Else, today_fmt is used as the format for a strftime call. 85 | # 86 | # today_fmt = '%B %d, %Y' 87 | 88 | # List of patterns, relative to source directory, that match files and 89 | # directories to ignore when looking for source files. 90 | # This patterns also effect to html_static_path and html_extra_path 91 | exclude_patterns = [] 92 | 93 | # The reST default role (used for this markup: `text`) to use for all 94 | # documents. 95 | # 96 | # default_role = None 97 | 98 | # If true, '()' will be appended to :func: etc. cross-reference text. 99 | # 100 | # add_function_parentheses = True 101 | 102 | # If true, the current module name will be prepended to all description 103 | # unit titles (such as .. function::). 104 | # 105 | # add_module_names = True 106 | 107 | # If true, sectionauthor and moduleauthor directives will be shown in the 108 | # output. They are ignored by default. 109 | # 110 | # show_authors = False 111 | 112 | # The name of the Pygments (syntax highlighting) style to use. 113 | pygments_style = 'sphinx' 114 | 115 | # A list of ignored prefixes for module index sorting. 116 | # modindex_common_prefix = [] 117 | 118 | # If true, keep warnings as "system message" paragraphs in the built documents. 119 | # keep_warnings = False 120 | 121 | # If true, `todo` and `todoList` produce output, else they produce nothing. 122 | todo_include_todos = False 123 | 124 | 125 | # -- Options for HTML output ---------------------------------------------- 126 | 127 | # The theme to use for HTML and HTML Help pages. See the documentation for 128 | # a list of builtin themes. 129 | # 130 | html_theme = 'alabaster' 131 | 132 | # Theme options are theme-specific and customize the look and feel of a theme 133 | # further. For a list of options available for each theme, see the 134 | # documentation. 135 | # 136 | # html_theme_options = {} 137 | 138 | # Add any paths that contain custom themes here, relative to this directory. 139 | # html_theme_path = [] 140 | 141 | # The name for this set of Sphinx documents. 142 | # " v documentation" by default. 143 | # 144 | # html_title = 'brokenaxes v0.1' 145 | 146 | # A shorter title for the navigation bar. Default is the same as html_title. 147 | # 148 | # html_short_title = None 149 | 150 | # The name of an image file (relative to this directory) to place at the top 151 | # of the sidebar. 152 | # 153 | # html_logo = None 154 | 155 | # The name of an image file (relative to this directory) to use as a favicon of 156 | # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 157 | # pixels large. 158 | # 159 | # html_favicon = None 160 | 161 | # Add any paths that contain custom static files (such as style sheets) here, 162 | # relative to this directory. They are copied after the builtin static files, 163 | # so a file named "default.css" will overwrite the builtin "default.css". 164 | html_static_path = ['_static'] 165 | 166 | # Add any extra paths that contain custom files (such as robots.txt or 167 | # .htaccess) here, relative to this directory. These files are copied 168 | # directly to the root of the documentation. 169 | # 170 | # html_extra_path = [] 171 | 172 | # If not None, a 'Last updated on:' timestamp is inserted at every page 173 | # bottom, using the given strftime format. 174 | # The empty string is equivalent to '%b %d, %Y'. 175 | # 176 | # html_last_updated_fmt = None 177 | 178 | # If true, SmartyPants will be used to convert quotes and dashes to 179 | # typographically correct entities. 180 | # 181 | # html_use_smartypants = True 182 | 183 | # Custom sidebar templates, maps document names to template names. 184 | # 185 | html_sidebars = {"sidebar": ['auto_example/index.html']} 186 | 187 | # Additional templates that should be rendered to pages, maps page names to 188 | # template names. 189 | # 190 | # html_additional_pages = {} 191 | 192 | # If false, no module index is generated. 193 | # 194 | # html_domain_indices = True 195 | 196 | # If false, no index is generated. 197 | # 198 | # html_use_index = True 199 | 200 | # If true, the index is split into individual pages for each letter. 201 | # 202 | # html_split_index = False 203 | 204 | # If true, links to the reST sources are added to the pages. 205 | # 206 | # html_show_sourcelink = True 207 | 208 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 209 | # 210 | # html_show_sphinx = True 211 | 212 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 213 | # 214 | # html_show_copyright = True 215 | 216 | # If true, an OpenSearch description file will be output, and all pages will 217 | # contain a tag referring to it. The value of this option must be the 218 | # base URL from which the finished HTML is served. 219 | # 220 | # html_use_opensearch = '' 221 | 222 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 223 | # html_file_suffix = None 224 | 225 | # Language to be used for generating the HTML full-text search index. 226 | # Sphinx supports the following languages: 227 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' 228 | # 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr', 'zh' 229 | # 230 | # html_search_language = 'en' 231 | 232 | # A dictionary with options for the search language support, empty by default. 233 | # 'ja' uses this config value. 234 | # 'zh' user can custom change `jieba` dictionary path. 235 | # 236 | # html_search_options = {'type': 'default'} 237 | 238 | # The name of a javascript file (relative to the configuration directory) that 239 | # implements a search results scorer. If empty, the default will be used. 240 | # 241 | # html_search_scorer = 'scorer.js' 242 | 243 | # Output file base name for HTML help builder. 244 | htmlhelp_basename = 'brokenaxesdoc' 245 | 246 | # -- Options for LaTeX output --------------------------------------------- 247 | 248 | latex_elements = { 249 | # The paper size ('letterpaper' or 'a4paper'). 250 | # 251 | # 'papersize': 'letterpaper', 252 | 253 | # The font size ('10pt', '11pt' or '12pt'). 254 | # 255 | # 'pointsize': '10pt', 256 | 257 | # Additional stuff for the LaTeX preamble. 258 | # 259 | # 'preamble': '', 260 | 261 | # Latex figure (float) alignment 262 | # 263 | # 'figure_align': 'htbp', 264 | } 265 | 266 | # Grouping the document tree into LaTeX files. List of tuples 267 | # (source start file, target name, title, 268 | # author, documentclass [howto, manual, or own class]). 269 | latex_documents = [ 270 | (master_doc, 'brokenaxes.tex', 'brokenaxes Documentation', 271 | 'Ben Dichter', 'manual'), 272 | ] 273 | 274 | # The name of an image file (relative to this directory) to place at the top of 275 | # the title page. 276 | # 277 | # latex_logo = None 278 | 279 | # For "manual" documents, if this is true, then toplevel headings are parts, 280 | # not chapters. 281 | # 282 | # latex_use_parts = False 283 | 284 | # If true, show page references after internal links. 285 | # 286 | # latex_show_pagerefs = False 287 | 288 | # If true, show URL addresses after external links. 289 | # 290 | # latex_show_urls = False 291 | 292 | # Documents to append as an appendix to all manuals. 293 | # 294 | # latex_appendices = [] 295 | 296 | # It false, will not define \strong, \code, itleref, \crossref ... but only 297 | # \sphinxstrong, ..., \sphinxtitleref, ... To help avoid clash with user added 298 | # packages. 299 | # 300 | # latex_keep_old_macro_names = True 301 | 302 | # If false, no module index is generated. 303 | # 304 | # latex_domain_indices = True 305 | 306 | 307 | # -- Options for manual page output --------------------------------------- 308 | 309 | # One entry per manual page. List of tuples 310 | # (source start file, name, description, authors, manual section). 311 | man_pages = [ 312 | (master_doc, 'brokenaxes', 'brokenaxes Documentation', 313 | [author], 1) 314 | ] 315 | 316 | # If true, show URL addresses after external links. 317 | # 318 | # man_show_urls = False 319 | 320 | 321 | # -- Options for Texinfo output ------------------------------------------- 322 | 323 | # Grouping the document tree into Texinfo files. List of tuples 324 | # (source start file, target name, title, author, 325 | # dir menu entry, description, category) 326 | texinfo_documents = [ 327 | (master_doc, 'brokenaxes', 'brokenaxes Documentation', 328 | author, 'brokenaxes', 'One line description of project.', 329 | 'Miscellaneous'), 330 | ] 331 | 332 | # Documents to append as an appendix to all manuals. 333 | # 334 | # texinfo_appendices = [] 335 | 336 | # If false, no module index is generated. 337 | # 338 | # texinfo_domain_indices = True 339 | 340 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 341 | # 342 | # texinfo_show_urls = 'footnote' 343 | 344 | # If true, do not generate a @detailmenu in the "Top" node's menu. 345 | # 346 | # texinfo_no_detailmenu = False 347 | 348 | 349 | sphinx_gallery_conf = { 350 | 'examples_dirs': '../../examples', # path to your example scripts 351 | 'gallery_dirs': 'auto_examples', # path where to save gallery generated examples 352 | } 353 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. brokenaxes documentation master file, created by 2 | sphinx-quickstart on Wed Apr 12 09:54:55 2017. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to brokenaxes's documentation! 7 | ====================================== 8 | 9 | brokenaxes is a library for making broken axes plots in python using matplotlib. See the examples for different use-cases. 10 | 11 | 12 | Contents: 13 | 14 | .. toctree:: 15 | :maxdepth: 2 16 | 17 | Examples 18 | -------------------------------------------------------------------------------- /example1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bendichter/brokenaxes/042e2facd48fb4966bc4cbe7c95532f4a33a544d/example1.png -------------------------------------------------------------------------------- /example2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bendichter/brokenaxes/042e2facd48fb4966bc4cbe7c95532f4a33a544d/example2.png -------------------------------------------------------------------------------- /example3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bendichter/brokenaxes/042e2facd48fb4966bc4cbe7c95532f4a33a544d/example3.png -------------------------------------------------------------------------------- /examples/README.txt: -------------------------------------------------------------------------------- 1 | Gallery of examples 2 | =================== 3 | -------------------------------------------------------------------------------- /examples/plot_different_scales.py: -------------------------------------------------------------------------------- 1 | """ 2 | Different scales with brokenaxes 3 | ================================ 4 | 5 | This example shows how to customize the scales and the ticks of each broken 6 | axes. 7 | """ 8 | 9 | ############################################################################# 10 | # brokenaxes lets you choose the aspect ratio of each sub-axes thanks to the 11 | # `height_ratios` and `width_ratios` to over-pass the default 1:1 scale for all 12 | # axes. However, by default the ticks spacing are still identical for all axes. 13 | # In this example, we present how to customize the ticks of your brokenaxes. 14 | 15 | import numpy as np 16 | import matplotlib.pyplot as plt 17 | from brokenaxes import brokenaxes 18 | import matplotlib.ticker as ticker 19 | 20 | 21 | def make_plot(): 22 | x = np.linspace(0, 5*2*np.pi, 300) 23 | y1 = np.sin(x)*100 24 | y2 = np.sin(x+np.pi)*5 + 90 25 | y3 = 30*np.exp(-x) - 50 26 | 27 | bax = brokenaxes( 28 | ylims=[(-100, 0), (80, 100)], 29 | xlims=[(0, 5), (10, 30)], 30 | height_ratios=[1, 3], 31 | width_ratios=[3, 5] 32 | ) 33 | 34 | bax.plot(x, y1, label="Big sin") 35 | bax.plot(x, y2, label="Small sin") 36 | bax.plot(x, y3, label="Exponential 1") 37 | 38 | bax.legend(loc="lower right") 39 | bax.set_title("Example for different scales for the x and y axis") 40 | 41 | return bax 42 | 43 | 44 | ############################################################################# 45 | # Use the AutoLocator() ticker 46 | # ---------------------------- 47 | 48 | plt.figure() 49 | bax = make_plot() 50 | 51 | # Then, we get the different axes created and set the ticks according to the 52 | # axe x and y limits. 53 | 54 | for i, ax in enumerate(bax.last_row): 55 | ax.xaxis.set_major_locator(ticker.AutoLocator()) 56 | ax.set_xlabel('xscale {i}'.format(i=i)) 57 | for i, ax in enumerate(bax.first_col): 58 | ax.yaxis.set_major_locator(ticker.AutoLocator()) 59 | ax.set_ylabel('yscale {i}'.format(i=i)) 60 | 61 | ############################################################################## 62 | # .. note:: It is not necessary to loop through all the axes since they all 63 | # share the same x and y limits in a given column or row. 64 | 65 | 66 | ############################################################################## 67 | # Manually set the ticks 68 | # ---------------------- 69 | # 70 | # Since brokenaxes return normal matplotlib axes, you could also set them 71 | # manually. 72 | 73 | fig2 = plt.figure() 74 | bax = make_plot() 75 | 76 | bax.first_col[0].set_yticks([80, 85, 90, 95, 100]) 77 | bax.first_col[1].set_yticks([-100, -50, 0]) 78 | 79 | bax.last_row[0].set_xticks([0, 1, 2, 3, 4, 5]) 80 | bax.last_row[1].set_xticks([10, 20, 30]) 81 | -------------------------------------------------------------------------------- /examples/plot_logscales.py: -------------------------------------------------------------------------------- 1 | """ 2 | Log scales 3 | ========== 4 | 5 | Brokenaxe compute automatically the correct layout for a 1:1 scale. However, for 6 | logarithmic scales, the 1:1 scale has to be adapted. This is done via the 7 | `yscale` or `xscale` arguments. 8 | 9 | """ 10 | 11 | 12 | import matplotlib.pyplot as plt 13 | from brokenaxes import brokenaxes 14 | import numpy as np 15 | 16 | fig = plt.figure(figsize=(5, 5)) 17 | bax = brokenaxes(xlims=((1, 500), (600, 10000)), 18 | ylims=((1, 500), (600, 10000)), 19 | hspace=.15, xscale='log', yscale='log') 20 | 21 | x = np.logspace(0.0, 4, 100) 22 | bax.loglog(x, x, label='$y=x=10^{0}$ to $10^{4}$') 23 | 24 | bax.legend(loc='best') 25 | bax.grid(axis='both', which='major', ls='-') 26 | bax.grid(axis='both', which='minor', ls='--', alpha=0.4) 27 | bax.set_xlabel('x') 28 | bax.set_ylabel('y') 29 | plt.show() 30 | -------------------------------------------------------------------------------- /examples/plot_second_y_axis.py: -------------------------------------------------------------------------------- 1 | """ 2 | Second Y Axis 3 | ============= 4 | 5 | You can use the following technique to give your brokenaxes plot a second axis on the right. 6 | Analogous code works for creating a secondary x axis. 7 | 8 | """ 9 | 10 | from brokenaxes import brokenaxes 11 | 12 | functions = (lambda x: x*0.453592, lambda x: x/0.453592) 13 | 14 | 15 | bax = brokenaxes( 16 | xlims=((1, 3), (9, 10)), 17 | ylims=((1, 3), (9, 10)), 18 | despine=False 19 | ) 20 | 21 | bax.plot(range(11)) 22 | bax.set_ylabel('pounds') 23 | secax = bax.secondary_yaxis(functions=functions, label='kg') 24 | 25 | 26 | -------------------------------------------------------------------------------- /examples/plot_subplots.py: -------------------------------------------------------------------------------- 1 | """ 2 | Handle subplots with brokenaxes 3 | =============================== 4 | 5 | If you want to use subplots together with brokenaxes, you have to use GridSpec. 6 | 7 | """ 8 | 9 | 10 | from brokenaxes import brokenaxes 11 | from matplotlib.gridspec import GridSpec 12 | import numpy as np 13 | 14 | sps1, sps2 = GridSpec(2, 1) 15 | 16 | bax = brokenaxes(xlims=((.1, .3), (.7, .8)), subplot_spec=sps1) 17 | x = np.linspace(0, 1, 100) 18 | bax.plot(x, np.sin(x*30), ls=':', color='m') 19 | 20 | x = np.random.poisson(3, 1000) 21 | bax = brokenaxes(xlims=((0, 2.5), (3, 6)), subplot_spec=sps2) 22 | bax.hist(x, histtype='bar') 23 | -------------------------------------------------------------------------------- /examples/plot_usage.py: -------------------------------------------------------------------------------- 1 | """ 2 | Basic usage 3 | =========== 4 | 5 | This example presents the basic usage of brokenaxes 6 | 7 | """ 8 | 9 | 10 | import matplotlib.pyplot as plt 11 | from brokenaxes import brokenaxes 12 | import numpy as np 13 | 14 | fig = plt.figure(figsize=(5, 2)) 15 | bax = brokenaxes(xlims=((0, .1), (.4, .7)), ylims=((-1, .7), (.79, 1)), hspace=.05) 16 | x = np.linspace(0, 1, 100) 17 | bax.plot(x, np.sin(10 * x), label='sin') 18 | bax.plot(x, np.cos(10 * x), label='cos') 19 | bax.legend(loc=3) 20 | bax.set_xlabel('time') 21 | bax.set_ylabel('value') 22 | -------------------------------------------------------------------------------- /requirements-test.txt: -------------------------------------------------------------------------------- 1 | pytest>=6.0 2 | pytest-mpl==0.17.0 3 | pytest-cov -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | matplotlib>3.6 -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | license_files = LICENCE 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | # To use a consistent encoding 3 | from codecs import open 4 | from os import path 5 | 6 | 7 | here = path.abspath(path.dirname(__file__)) 8 | 9 | 10 | with open(path.join(here, 'README.md'), encoding='utf-8') as f: 11 | long_description = f.read() 12 | 13 | setup( 14 | name='brokenaxes', 15 | version='0.6.2', 16 | description='Create broken axes', 17 | long_description=long_description, 18 | long_description_content_type="text/markdown", 19 | author='Ben Dichter', 20 | url='https://github.com/bendichter/brokenaxes', 21 | author_email='ben.dichter@gmail.com', 22 | classifiers=[ 23 | 'Intended Audience :: Science/Research', 24 | 'Topic :: Scientific/Engineering', 25 | 'Framework :: Matplotlib', 26 | 'License :: OSI Approved :: MIT License', 27 | 'Programming Language :: Python :: 3.8', 28 | 'Programming Language :: Python :: 3.9', 29 | 'Programming Language :: Python :: 3.10', 30 | 'Programming Language :: Python :: 3.11', 31 | 'Programming Language :: Python :: 3.12', 32 | ], 33 | 34 | keywords='data visualization', 35 | py_modules=["brokenaxes"], 36 | 37 | # List run-time dependencies here. These will be installed by pip when 38 | # your project is installed. For an analysis of "install_requires" vs pip's 39 | # requirements files see: 40 | # https://packaging.python.org/en/latest/requirements.html 41 | install_requires=['matplotlib>3.6'], 42 | 43 | # List additional groups of dependencies here (e.g. development 44 | # dependencies). You can install these using the following syntax, 45 | # for example: 46 | # $ pip install -e .[test] 47 | extras_require={ 48 | 'test': ['pytest>=6.0', 'pytest-cov', 'pytest-mpl'], 49 | }, 50 | 51 | 52 | # To provide executable scripts, use entry points in preference to the 53 | # "scripts" keyword. Entry points provide cross-platform support and allow 54 | # pip to create the appropriate form of executable for the target platform. 55 | #entry_points={ 56 | # 'console_scripts': [ 57 | # 'sample=sample:main', 58 | # ], 59 | #}, 60 | ) 61 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | import matplotlib.pyplot as plt 4 | import numpy as np 5 | import pytest 6 | from matplotlib.gridspec import GridSpec 7 | import matplotlib as mpl 8 | 9 | from brokenaxes import brokenaxes 10 | 11 | 12 | np.random.seed(42) 13 | 14 | 15 | @pytest.mark.mpl_image_compare 16 | def test_standard(): 17 | fig = plt.figure(figsize=(5, 2)) 18 | bax = brokenaxes( 19 | xlims=((0, 0.1), (0.4, 0.7)), ylims=((-1, 0.7), (0.79, 1)), hspace=0.05 20 | ) 21 | x = np.linspace(0, 1, 100) 22 | bax.plot(x, np.sin(10 * x), label="sin") 23 | bax.plot(x, np.cos(10 * x), label="cos") 24 | bax.legend(loc=3) 25 | bax.set_xlabel("time") 26 | bax.set_ylabel("value") 27 | return fig 28 | 29 | 30 | @pytest.mark.mpl_image_compare 31 | def test_subplots(): 32 | sps1, sps2 = GridSpec(2, 1) 33 | 34 | bax = brokenaxes(xlims=((0.1, 0.3), (0.7, 0.8)), subplot_spec=sps1) 35 | x = np.linspace(0, 1, 100) 36 | bax.plot(x, np.sin(x * 30), ls=":", color="m") 37 | 38 | x = np.random.poisson(3, 1000) 39 | bax = brokenaxes(xlims=((0, 2.5), (3, 6)), subplot_spec=sps2) 40 | bax.hist(x, histtype="bar") 41 | 42 | return bax.fig 43 | 44 | 45 | @pytest.mark.mpl_image_compare 46 | def test_log(): 47 | fig = plt.figure(figsize=(5, 5)) 48 | bax = brokenaxes( 49 | xlims=((1, 500), (600, 10000)), 50 | ylims=((1, 500), (600, 10000)), 51 | hspace=0.15, 52 | xscale="log", 53 | yscale="log", 54 | ) 55 | 56 | x = np.logspace(0.0, 4, 100) 57 | bax.loglog(x, x, label="$y=x=10^{0}$ to $10^{4}$") 58 | 59 | bax.legend(loc="best") 60 | bax.grid(axis="both", which="major", ls="-") 61 | bax.grid(axis="both", which="minor", ls="--", alpha=0.4) 62 | bax.set_xlabel("x") 63 | bax.set_ylabel("y") 64 | 65 | return fig 66 | 67 | 68 | @pytest.mark.mpl_image_compare 69 | def test_datetime(): 70 | fig = plt.figure(figsize=(5, 5)) 71 | xx = [datetime.datetime(2020, 1, x) for x in range(1, 20)] 72 | 73 | yy = np.arange(1, 20) 74 | 75 | bax = brokenaxes( 76 | xlims=( 77 | ( 78 | datetime.datetime(2020, 1, 1), 79 | datetime.datetime(2020, 1, 3), 80 | ), 81 | ( 82 | datetime.datetime(2020, 1, 6), 83 | datetime.datetime(2020, 1, 20), 84 | ), 85 | ), 86 | ) 87 | 88 | bax.plot(xx, yy) 89 | fig.autofmt_xdate() 90 | 91 | [x.remove() for x in bax.diag_handles] 92 | bax.draw_diags() 93 | 94 | return fig 95 | 96 | 97 | @pytest.mark.mpl_image_compare 98 | def test_datetime_y(): 99 | fig = plt.figure(figsize=(5, 5)) 100 | yy = [datetime.datetime(2020, 1, x) for x in range(1, 20)] 101 | 102 | xx = np.arange(1, 20) 103 | 104 | bax = brokenaxes( 105 | ylims=( 106 | ( 107 | datetime.datetime(2020, 1, 1), 108 | datetime.datetime(2020, 1, 3), 109 | ), 110 | ( 111 | datetime.datetime(2020, 1, 6), 112 | datetime.datetime(2020, 1, 20), 113 | ), 114 | ) 115 | ) 116 | 117 | bax.plot(xx, yy) 118 | 119 | bax.draw_diags() 120 | 121 | return fig 122 | 123 | 124 | @pytest.mark.mpl_image_compare 125 | def test_legend(): 126 | fig = plt.figure(figsize=(5, 2)) 127 | bax = brokenaxes( 128 | xlims=((0, 0.1), (0.4, 0.7)), ylims=((-1, 0.7), (0.79, 1)), hspace=0.05 129 | ) 130 | x = np.linspace(0, 1, 100) 131 | h1 = bax.plot(x, np.sin(10 * x), label="sin") 132 | h2 = bax.plot(x, np.cos(10 * x), label="cos") 133 | bax.legend(handles=[h1[0][0], h2[0][0]], labels=["1", "2"]) 134 | 135 | return fig 136 | 137 | 138 | @pytest.mark.mpl_image_compare 139 | def test_text(): 140 | bax = brokenaxes( 141 | xlims=((0, 0.1), (0.4, 0.7)), ylims=((-1, 0.7), (0.79, 1)), hspace=0.05 142 | ) 143 | bax.text(0.5, 0.5, "hello") 144 | 145 | return bax.fig 146 | 147 | 148 | @pytest.mark.mpl_image_compare 149 | def test_lims_arrays(): 150 | lims = np.arange(6).reshape((-1, 2)) 151 | brokenaxes(xlims=lims, ylims=lims) 152 | 153 | return plt.gcf() 154 | 155 | 156 | @pytest.mark.mpl_image_compare 157 | def test_pass_fig(): 158 | fig = plt.figure(figsize=(5, 2)) 159 | bax = brokenaxes( 160 | xlims=((0, 0.1), (0.4, 0.7)), ylims=((-1, 0.7), (0.79, 1)), hspace=0.05, fig=fig 161 | ) 162 | assert bax.fig is fig 163 | 164 | return fig 165 | 166 | 167 | @pytest.mark.mpl_image_compare 168 | def test_despine(): 169 | fig = plt.figure(figsize=(5, 2)) 170 | bax = brokenaxes( 171 | xlims=((0, 0.1), (0.4, 0.7)), ylims=((-1, 0.7), (0.79, 1)), hspace=0.05, despine=False, 172 | ) 173 | assert bax.despine is False 174 | 175 | return fig 176 | 177 | 178 | @pytest.mark.mpl_image_compare 179 | def test_set_title(): 180 | fig = plt.figure(figsize=(5, 2)) 181 | bax = brokenaxes( 182 | xlims=((0, 0.1), (0.4, 0.7)), ylims=((-1, 0.7), (0.79, 1)), hspace=0.05 183 | ) 184 | bax.set_title("title") 185 | 186 | return fig 187 | 188 | 189 | @pytest.mark.mpl_image_compare 190 | def test_secondary_axes(): 191 | 192 | fig = plt.figure(figsize=(5, 2)) 193 | bax = brokenaxes( 194 | xlims=((0, 0.1), (0.4, 0.7)), ylims=((-1, 0.7), (0.79, 1)), hspace=0.05 195 | ) 196 | bax.secondary_xaxis("top", label="top") 197 | print(type(isinstance(bax.secondary_xaxis(), mpl.axis.XAxis))) 198 | bax.secondary_xaxis("bottom") 199 | bax.secondary_yaxis("left", label="left") 200 | bax.secondary_yaxis("right") 201 | 202 | return fig 203 | 204 | 205 | @pytest.mark.mpl_image_compare 206 | def test_text(): 207 | fig = plt.figure(figsize=(5, 2)) 208 | bax = brokenaxes(xlims=((0, 0.1), (0.4, 0.7)), ylims=((-1, 0.7), (0.79, 1))) 209 | bax.text(0.5, 0.5, "hello") 210 | 211 | return fig 212 | 213 | 214 | @pytest.mark.mpl_image_compare 215 | def test_draw_diags(): 216 | fig = plt.figure(figsize=(5, 2)) 217 | bax = brokenaxes( 218 | xlims=((0, 0.1), (0.4, 0.7)), ylims=((-1, 0.7), (0.79, 1)), hspace=0.05 219 | ) 220 | bax.draw_diags(tilt=90, d=.05) 221 | 222 | return fig 223 | 224 | 225 | @pytest.mark.mpl_image_compare 226 | def test_set_spine_prop(): 227 | fig = plt.figure(figsize=(5, 2)) 228 | bax = brokenaxes( 229 | xlims=((0, 0.1), (0.4, 0.7)), ylims=((-1, 0.7), (0.79, 1)), hspace=0.05 230 | ) 231 | 232 | [x.set_linewidth(2) for x in bax.spines["bottom"]] 233 | 234 | return fig 235 | 236 | 237 | @pytest.mark.mpl_image_compare 238 | def test_fig_tight_layout(): 239 | fig = plt.figure() 240 | 241 | subplot_specs = GridSpec(2, 2) 242 | 243 | baxs = [] 244 | 245 | xlims = ((.1, .3),(.7, .8)) 246 | x = np.linspace(0, 1, 100) 247 | 248 | for color, sps in zip(['red', 'green', 'blue', 'magenta'], subplot_specs): 249 | 250 | bax = brokenaxes(xlims=xlims, subplot_spec=sps) 251 | bax.plot(x, np.sin(x*30), color=color) 252 | baxs.append(bax) 253 | 254 | plt.tight_layout() 255 | for bax in baxs: 256 | for handle in bax.diag_handles: 257 | handle.remove() 258 | bax.draw_diags() 259 | 260 | return fig 261 | 262 | 263 | def test_get_axis_special(): 264 | fig = plt.figure(figsize=(5, 2)) 265 | bax = brokenaxes( 266 | xlims=((0, 0.1), (0.4, 0.7)), ylims=((-1, 0.7), (0.79, 1)), hspace=0.05 267 | ) 268 | assert isinstance(bax.get_yaxis(), mpl.axis.YAxis) 269 | assert isinstance(bax.get_shared_x_axes(), mpl.cbook.GrouperView) 270 | assert isinstance(bax.get_xaxis(), mpl.axis.XAxis) 271 | assert isinstance(bax.get_shared_y_axes(), mpl.cbook.GrouperView) 272 | 273 | 274 | def test_text_error(): 275 | bax = brokenaxes( 276 | xlims=((0, 0.1), (0.4, 0.7)), ylims=((-1, 0.7), (0.79, 1)), hspace=0.05 277 | ) 278 | with pytest.raises(ValueError): 279 | bax.text(-11, -11, "hello") -------------------------------------------------------------------------------- /test_baseline/test_datetime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bendichter/brokenaxes/042e2facd48fb4966bc4cbe7c95532f4a33a544d/test_baseline/test_datetime.png -------------------------------------------------------------------------------- /test_baseline/test_datetime_y.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bendichter/brokenaxes/042e2facd48fb4966bc4cbe7c95532f4a33a544d/test_baseline/test_datetime_y.png -------------------------------------------------------------------------------- /test_baseline/test_despine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bendichter/brokenaxes/042e2facd48fb4966bc4cbe7c95532f4a33a544d/test_baseline/test_despine.png -------------------------------------------------------------------------------- /test_baseline/test_draw_diags.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bendichter/brokenaxes/042e2facd48fb4966bc4cbe7c95532f4a33a544d/test_baseline/test_draw_diags.png -------------------------------------------------------------------------------- /test_baseline/test_fig_tight_layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bendichter/brokenaxes/042e2facd48fb4966bc4cbe7c95532f4a33a544d/test_baseline/test_fig_tight_layout.png -------------------------------------------------------------------------------- /test_baseline/test_legend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bendichter/brokenaxes/042e2facd48fb4966bc4cbe7c95532f4a33a544d/test_baseline/test_legend.png -------------------------------------------------------------------------------- /test_baseline/test_lims_arrays.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bendichter/brokenaxes/042e2facd48fb4966bc4cbe7c95532f4a33a544d/test_baseline/test_lims_arrays.png -------------------------------------------------------------------------------- /test_baseline/test_log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bendichter/brokenaxes/042e2facd48fb4966bc4cbe7c95532f4a33a544d/test_baseline/test_log.png -------------------------------------------------------------------------------- /test_baseline/test_pass_fig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bendichter/brokenaxes/042e2facd48fb4966bc4cbe7c95532f4a33a544d/test_baseline/test_pass_fig.png -------------------------------------------------------------------------------- /test_baseline/test_secondary_axes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bendichter/brokenaxes/042e2facd48fb4966bc4cbe7c95532f4a33a544d/test_baseline/test_secondary_axes.png -------------------------------------------------------------------------------- /test_baseline/test_set_spine_prop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bendichter/brokenaxes/042e2facd48fb4966bc4cbe7c95532f4a33a544d/test_baseline/test_set_spine_prop.png -------------------------------------------------------------------------------- /test_baseline/test_set_title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bendichter/brokenaxes/042e2facd48fb4966bc4cbe7c95532f4a33a544d/test_baseline/test_set_title.png -------------------------------------------------------------------------------- /test_baseline/test_standard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bendichter/brokenaxes/042e2facd48fb4966bc4cbe7c95532f4a33a544d/test_baseline/test_standard.png -------------------------------------------------------------------------------- /test_baseline/test_subplots.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bendichter/brokenaxes/042e2facd48fb4966bc4cbe7c95532f4a33a544d/test_baseline/test_subplots.png -------------------------------------------------------------------------------- /test_baseline/test_text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bendichter/brokenaxes/042e2facd48fb4966bc4cbe7c95532f4a33a544d/test_baseline/test_text.png --------------------------------------------------------------------------------