├── .github
└── workflows
│ ├── docs.yml
│ └── testing.yml
├── .gitignore
├── LICENSE.txt
├── README.rst
├── doc
├── Makefile
├── _templates
│ └── autosummary
│ │ └── class.rst
├── make.bat
└── source
│ ├── conf.py
│ └── index.rst
├── requirements-dev.txt
├── requirements-doc.txt
├── setup.py
└── trendvis
├── __init__.py
├── gridclass.py
├── gridwrapper.py
├── testing.py
├── tests
├── baseline_images
│ └── test_trendvis
│ │ ├── xgrid_clean.png
│ │ ├── xgrid_cutout.png
│ │ ├── xgrid_frame.png
│ │ ├── xgrid_multitwin.png
│ │ ├── xgrid_twins.png
│ │ ├── xgrid_ylabels.png
│ │ ├── ygrid_clean.png
│ │ ├── ygrid_cutout.png
│ │ ├── ygrid_frame.png
│ │ ├── ygrid_multitwin.png
│ │ ├── ygrid_twins.png
│ │ └── ygrid_xlabels.png
└── test_trendvis.py
├── xgrid_ystack.py
└── ygrid_xstack.py
/.github/workflows/docs.yml:
--------------------------------------------------------------------------------
1 | name: Docs
2 |
3 | on: [push, pull_request]
4 |
5 |
6 | jobs:
7 | build:
8 | runs-on: ubuntu-20.04
9 | steps:
10 | - uses: actions/checkout@v2
11 | - name: Install Python dependencies
12 | run: pip install -r requirements-doc.txt
13 |
14 | - name: Install mpl-gui
15 | run: python -m pip install -v .
16 | - name: Build
17 | run: make -Cdoc html
18 | - name: Publish
19 | if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
20 | uses: peaceiris/actions-gh-pages@v3
21 | with:
22 | github_token: ${{ secrets.GITHUB_TOKEN }}
23 | publish_dir: ./doc/build/html
24 | force_orphan: true
25 |
--------------------------------------------------------------------------------
/.github/workflows/testing.yml:
--------------------------------------------------------------------------------
1 | name: Unit Tests
2 |
3 | on:
4 | push:
5 | pull_request:
6 | schedule:
7 | - cron: '00 4 * * *' # daily at 4AM
8 |
9 | jobs:
10 | build:
11 |
12 | runs-on: ubuntu-latest
13 | strategy:
14 | matrix:
15 | python-version: ['3.8', '3.9', '3.10', '3.11']
16 | fail-fast: false
17 | steps:
18 |
19 | - uses: actions/checkout@v2
20 | with:
21 | fetch-depth: 0
22 |
23 | - name: Set up Python ${{ matrix.python-version }}
24 | uses: actions/setup-python@v2
25 | with:
26 | python-version: ${{ matrix.python-version }}
27 |
28 | - name: Install test requirements
29 | shell: bash -l {0}
30 | run: |
31 | set -vxeuo pipefail
32 | python -m pip install -r requirements-dev.txt
33 | python -m pip install -v .
34 | python -m pip list
35 |
36 | - name: Test with pytest
37 | shell: bash -l {0}
38 | run: |
39 | set -vxeuo pipefail
40 | coverage run -m pytest -v
41 | coverage report
42 |
43 | - name: Upload code coverage
44 | uses: codecov/codecov-action@v1
45 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | *egg-info
3 | trendvis/version.py
4 | result_images
5 | *~
6 | doc/build
7 | doc/source/generated
8 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015, mscross
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 |
7 | * Redistributions of source code must retain the above copyright notice, this
8 | list of conditions and the following disclaimer.
9 |
10 | * Redistributions in binary form must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided with the distribution.
13 |
14 | * Neither the name of trendvis nor the names of its
15 | contributors may be used to endorse or promote products derived from
16 | this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
29 |
30 |
31 | ##############################################################################
32 | setup.py is derived from scikit-image. This is also covered by the 3-clause
33 | BSD license. The original code for setup.py:
34 |
35 | Copyright (C) 2011, the scikit-image team
36 | All rights reserved.
37 |
38 | Redistribution and use in source and binary forms, with or without
39 | modification, are permitted provided that the following conditions are
40 | met:
41 |
42 | 1. Redistributions of source code must retain the above copyright
43 | notice, this list of conditions and the following disclaimer.
44 | 2. Redistributions in binary form must reproduce the above copyright
45 | notice, this list of conditions and the following disclaimer in
46 | the documentation and/or other materials provided with the
47 | distribution.
48 | 3. Neither the name of skimage nor the names of its contributors may be
49 | used to endorse or promote products derived from this software without
50 | specific prior written permission.
51 |
52 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
53 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
54 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
55 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
56 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
57 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
58 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
60 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
61 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
62 | POSSIBILITY OF SUCH DAMAGE.
63 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | ========
2 | TrendVis
3 | ========
4 |
5 | TrendVis is a plotting package that uses
6 | `matplotlib `_ to create information-dense,
7 | sparkline-like, quantitative visualizations of multiple disparate data sets in
8 | a common plot area against a common variable. This plot type is particularly
9 | well-suited for time-series data. The results speak for themselves:
10 |
11 | .. image:: https://raw.githubusercontent.com/mscross/scipy_proceedings/trendvis/papers/mellissa_cross_t/barredplot.png
12 | :target: https://raw.githubusercontent.com/mscross/scipy_proceedings/trendvis/papers/mellissa_cross_t/barredplot.png
13 | :alt: TrendVis example
14 |
15 | For further reading on TrendVis, see the `SciPy 2015 Proceedings `_.
16 |
17 | ============
18 | Installation
19 | ============
20 |
21 | TrendVis is pure Python, natively supports Python 2 and 3, and depends only on
22 | `matplotlib `_ version 1.2 or greater.
23 |
24 | Setup and installation is simple::
25 |
26 | pip install -U trendvis
27 |
28 | or, if you would like to develop the package, fork and clone the repo then run::
29 |
30 | python setup.py develop
31 |
32 | at package root.
33 |
34 | ==============
35 | Quick Examples
36 | ==============
37 |
38 | Below are several examples showing various features in TrendVis and a typical
39 | workflow. Version >= 0.2.1 is required.
40 |
41 | Single column ``XGrid``
42 | -----------------------
43 |
44 | .. code-block:: python
45 |
46 | import numpy as np
47 | import matplotlib.pyplot as plt
48 | import trendvis
49 |
50 | # Pseudorandom data and plot attributes
51 | random_generator = np.random.RandomState(seed=123)
52 | yvals = random_generator.rand(10)
53 |
54 | # Plot attributes
55 | nums = 10
56 | lw = 1.5
57 |
58 | # convenience function trendvis.gridwrapper() is available
59 | # to initialize XGrid and do most of the formatting shown here
60 | ex0 = trendvis.XGrid([1,2,1], figsize=(5,5))
61 |
62 | # Convenience function for plotting line data
63 | # Automatically colors y axis spines to
64 | # match line colors (auto_spinecolor=True)
65 | trendvis.plot_data(ex0,
66 | [[(np.linspace(0, 9.5, num=nums), yvals, 'blue')],
67 | [(np.linspace(1, 9, num=nums), yvals*5, 'red')],
68 | [(np.linspace(0.5, 10, num=nums), yvals*10, 'green')]],
69 | lw=lw, markeredgecolor='none', marker='s')
70 |
71 | # Get rid of extra spines
72 | ex0.cleanup_grid()
73 | ex0.set_spinewidth(lw)
74 |
75 | ex0.set_all_ticknums([(2, 1)], [(0.2, 0.1), (1, 0.5), (2, 1)])
76 | ex0.set_ticks(major_dim=(7, 3), minor_dim=(4, 2))
77 |
78 | ex0.set_ylabels(['stack axis 0', 'stack axis 1', 'stack axis 2'])
79 |
80 | # In XGrid.fig.axes, axes live in a 1 level list
81 | # In XGrid.axes, axes live in a nested list of [row][column]
82 | ex0.axes[2][0].set_xlabel('Main Axis', fontsize=14)
83 |
84 | # Compact the plot
85 | ex0.fig.subplots_adjust(hspace=-0.3)
86 |
87 | .. image:: https://cloud.githubusercontent.com/assets/2184487/8859118/f4706b72-3140-11e5-9351-5182977a991c.png
88 | :target: https://cloud.githubusercontent.com/assets/2184487/8859118/f4706b72-3140-11e5-9351-5182977a991c.png
89 | :alt: Single column XGrid
90 |
91 | Two-row ``YGrid`` with frame
92 | ----------------------------
93 |
94 | .. code-block:: python
95 |
96 | import numpy as np
97 | import matplotlib.pyplot as plt
98 | import trendvis
99 |
100 | # Pseudorandom data
101 | random_generator = np.random.RandomState(seed=1234)
102 | xvals = random_generator.rand(20)
103 |
104 | # Plot attributes
105 | numpts = 20
106 | lw = 1.5
107 |
108 | # Initialize a YGrid
109 | ex1 = trendvis.YGrid([1, 2, 1], yratios=[1, 2], figsize=(5,5))
110 |
111 | # Convenience function
112 | trendvis.plot_data(ex1,
113 | [[(xvals, np.linspace(2, 18.5, num=numpts), 'blue')],
114 | [(xvals*5, np.linspace(1, 17, num=numpts), 'red')],
115 | [(xvals*10, np.linspace(0.5, 20, num=numpts), 'green')]],
116 | lw=lw, auto_spinecolor=True, markeredgecolor='none', marker='s')
117 |
118 | # Remove extra spines, color stack (y) ticks
119 | ex1.cleanup_grid()
120 | ex1.set_spinewidth(lw)
121 |
122 | # Tick, tick label formatting
123 | ex1.set_all_ticknums([(0.2, 0.1), (1, 0.5), (2, 1)], [(2, 1), (2, 1)])
124 | ex1.set_ticks(major_dim=(7, 3), minor_dim=(4, 2))
125 | ex1.set_ylim([(0, 15, 20), (1, 0, 11)])
126 |
127 | # Axes labels
128 | ex1.set_xlabels(['stack axis 0', 'stack axis 1', 'stack axis 2'])
129 | ex1.axes[0][0].set_ylabel('Main Axis 0', fontsize=14)
130 | ex1.axes[2][1].set_ylabel('Main Axis 1', fontsize=14,
131 | rotation=270, labelpad=14)
132 |
133 | # Draw boxes around each row
134 | ex1.draw_frame()
135 |
136 | # Broken axis cutout marks also available, try this instead of the frame:
137 | # ex0.draw_cutout(di=0.05)
138 |
139 | # Compact the plot
140 | ex1.fig.subplots_adjust(wspace=-0.3)
141 |
142 | .. image:: https://cloud.githubusercontent.com/assets/2184487/8859244/b07c0f9c-3141-11e5-8c1c-7d20f77ce7ee.png
143 | :target: https://cloud.githubusercontent.com/assets/2184487/8859244/b07c0f9c-3141-11e5-8c1c-7d20f77ce7ee.png
144 | :alt: Two-column YGrid
145 |
146 | Three-column ``XGrid`` with advanced features
147 | ---------------------------------------------
148 |
149 | .. code-block:: python
150 |
151 | import numpy as np
152 | import matplotlib.pyplot as plt
153 | import trendvis
154 |
155 | # Make some pseudorandom data
156 | random_generator = np.random.RandomState(seed=123)
157 | yvals = random_generator.rand(40)
158 | yvals1 = np.copy(yvals)
159 | yvals1[20:] = np.array([0.2, 0.3, 0.2, 0.5, 0.34, 0.24,
160 | 0.15, 0.23, 0.26, 0.21] * 2)
161 | numpts = 40
162 | lw = 1.5
163 | x0 = np.linspace(2, 49.5, num=numpts)
164 | x1 = np.linspace(1, 49, num=numpts)
165 | x11 = np.linspace(1.5, 47.5, num=numpts)
166 | twin0 = np.linspace(2, 50, num=numpts)
167 | twin1 = np.linspace(0.5, 48, num=numpts)
168 |
169 | # Initialize XGrid and twin axes
170 | ex2 = trendvis.XGrid([3, 4], xratios=[1, 3, 2], figsize=(5, 5),
171 | startside='right')
172 | ex2.make_twins([0, 1])
173 |
174 | # Convenience function
175 | trendvis.plot_data(ex2,
176 | [[(x0, yvals, 'blue')],
177 | [(x1, yvals1*5, 'red'), (x11, yvals1*5.2, 'orchid')],
178 | [],
179 | [(twin1, yvals*2, '0.5')]],
180 | lw=lw, marker=None)
181 |
182 | # Adjust twinned y-axis positions for readability
183 | ex2.move_spines(twin_shift=0.6)
184 |
185 | # For any other kind of plot (fill_between, scatter, errorbar, etc),
186 | # get axis and plot directly
187 | # Note: ex2.axes[2][2] == ex2.get_axis(0, xpos=2, is_twin=True)
188 | for ax in ex2.axes[2]:
189 | ax.fill_between(twin0, yvals+0.075, yvals-0.1,
190 | edgecolor='none', color='darkorange')
191 |
192 | # Handle axis ticks
193 | ex2.cleanup_grid()
194 | ex2.set_spinewidth(lw)
195 | ex2.autocolor_spines()
196 | ex2.set_all_ticknums([(2, 1), (2, 1), (2, 1)],
197 | [(0.2, 0.1), (1, 0.5), (1, 0.25), (0.5, 0.25)])
198 | ex2.set_ticks(major_dim=(6, 1.5), minor_dim=(3, 1))
199 |
200 | ex2.set_ylabels(['row 0', 'row 1', 'twin row 0', 'twin row 1'])
201 |
202 | # Rotate x-axis tick labels
203 | for ax in ex2.fig.axes:
204 | plt.setp(ax.xaxis.get_majorticklabels(), rotation=45)
205 |
206 | # Draw a vertical bar behind the data - horizontal bars available too
207 | ex2.draw_bar(ex2.axes[1][2], ex2.axes[0][2], (45, 47), color='lightblue')
208 |
209 | # Ok to set axis limits after drawing on figure using TrendVis methods,
210 | # TrendVis will reset the bar to the right place!
211 | ex2.set_xlim([(0, 0, 3), (1, 13, 24), (2, 43, 50)])
212 | ex2.set_ylim([(2, 0, 2)])
213 |
214 | # matplotlib annotations supported
215 | ex2.get_axis(0).text(0, 0.75, 'Text')
216 |
217 | # Cutouts instead of frames
218 | ex2.draw_cutout(lw=lw)
219 |
220 | # Set the suptitle and compact the plot
221 | ex2.fig.suptitle('Title', fontsize=16, y=1.05);
222 | ex2.fig.subplots_adjust(hspace=-0.1)
223 |
224 | .. image:: https://cloud.githubusercontent.com/assets/2184487/8860699/097e51fa-314b-11e5-93e5-eb158aa5b801.png
225 | :target: https://cloud.githubusercontent.com/assets/2184487/8860699/097e51fa-314b-11e5-93e5-eb158aa5b801.png
226 | :alt: Three-column XGrid with advanced features
227 |
228 | ===========================
229 | Examples in Published Works
230 | ===========================
231 | *Great Basin hydrology, paleoclimate, and connections with the North Atlantic: A speleothem stable isotope and trace element record from Lehman Caves, NV* by Mellissa Cross, David McGee, Wallace S. Broecker, Jay Quade, Jeremy D. Shakun, Hai Cheng, Yanbin Lu, and R. Lawrence Edwards. doi:`10.1016/j.quascirev.2015.06.016 `_
232 | Figures 2, 3, 4, 5, and panels 1 and 2 in figure 6 made with TrendVis.
233 |
234 | Additional references to works containing TrendVis figures are welcome!
235 |
236 |
--------------------------------------------------------------------------------
/doc/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 | # User-friendly check for sphinx-build
11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
13 | endif
14 |
15 | # Internal variables.
16 | PAPEROPT_a4 = -D latex_paper_size=a4
17 | PAPEROPT_letter = -D latex_paper_size=letter
18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
19 | # the i18n builder cannot share the environment and doctrees with the others
20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
21 |
22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
23 |
24 | help:
25 | @echo "Please use \`make ' where is one of"
26 | @echo " html to make standalone HTML files"
27 | @echo " dirhtml to make HTML files named index.html in directories"
28 | @echo " singlehtml to make a single large HTML file"
29 | @echo " pickle to make pickle files"
30 | @echo " json to make JSON files"
31 | @echo " htmlhelp to make HTML files and a HTML help project"
32 | @echo " qthelp to make HTML files and a qthelp project"
33 | @echo " devhelp to make HTML files and a Devhelp project"
34 | @echo " epub to make an epub"
35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
36 | @echo " latexpdf to make LaTeX files and run them through pdflatex"
37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
38 | @echo " text to make text files"
39 | @echo " man to make manual pages"
40 | @echo " texinfo to make Texinfo files"
41 | @echo " info to make Texinfo files and run them through makeinfo"
42 | @echo " gettext to make PO message catalogs"
43 | @echo " changes to make an overview of all changed/added/deprecated items"
44 | @echo " xml to make Docutils-native XML files"
45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes"
46 | @echo " linkcheck to check all external links for integrity"
47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)"
48 |
49 | clean:
50 | rm -rf $(BUILDDIR)/*
51 |
52 | html:
53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
54 | @echo
55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
56 |
57 | dirhtml:
58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
59 | @echo
60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
61 |
62 | singlehtml:
63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
64 | @echo
65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
66 |
67 | pickle:
68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
69 | @echo
70 | @echo "Build finished; now you can process the pickle files."
71 |
72 | json:
73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
74 | @echo
75 | @echo "Build finished; now you can process the JSON files."
76 |
77 | htmlhelp:
78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
79 | @echo
80 | @echo "Build finished; now you can run HTML Help Workshop with the" \
81 | ".hhp project file in $(BUILDDIR)/htmlhelp."
82 |
83 | qthelp:
84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
85 | @echo
86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \
87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/trendvis.qhcp"
89 | @echo "To view the help file:"
90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/trendvis.qhc"
91 |
92 | devhelp:
93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
94 | @echo
95 | @echo "Build finished."
96 | @echo "To view the help file:"
97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/trendvis"
98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/trendvis"
99 | @echo "# devhelp"
100 |
101 | epub:
102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
103 | @echo
104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
105 |
106 | latex:
107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
108 | @echo
109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \
111 | "(use \`make latexpdf' here to do that automatically)."
112 |
113 | latexpdf:
114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
115 | @echo "Running LaTeX files through pdflatex..."
116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf
117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
118 |
119 | latexpdfja:
120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
121 | @echo "Running LaTeX files through platex and dvipdfmx..."
122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
124 |
125 | text:
126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
127 | @echo
128 | @echo "Build finished. The text files are in $(BUILDDIR)/text."
129 |
130 | man:
131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
132 | @echo
133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
134 |
135 | texinfo:
136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
137 | @echo
138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
139 | @echo "Run \`make' in that directory to run these through makeinfo" \
140 | "(use \`make info' here to do that automatically)."
141 |
142 | info:
143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
144 | @echo "Running Texinfo files through makeinfo..."
145 | make -C $(BUILDDIR)/texinfo info
146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
147 |
148 | gettext:
149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
150 | @echo
151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
152 |
153 | changes:
154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
155 | @echo
156 | @echo "The overview file is in $(BUILDDIR)/changes."
157 |
158 | linkcheck:
159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
160 | @echo
161 | @echo "Link check complete; look for any errors in the above output " \
162 | "or in $(BUILDDIR)/linkcheck/output.txt."
163 |
164 | doctest:
165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
166 | @echo "Testing of doctests in the sources finished, look at the " \
167 | "results in $(BUILDDIR)/doctest/output.txt."
168 |
169 | xml:
170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
171 | @echo
172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
173 |
174 | pseudoxml:
175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
176 | @echo
177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
178 |
--------------------------------------------------------------------------------
/doc/_templates/autosummary/class.rst:
--------------------------------------------------------------------------------
1 | {% extends "!autosummary/class.rst" %}
2 |
3 | {% block methods %}
4 | {% if methods %}
5 |
6 | .. HACK -- the point here is that we don't want this to appear in the output, but the autosummary should still generate the pages.
7 | .. autosummary::
8 | :toctree:
9 | {% for item in all_methods %}
10 | {%- if not item.startswith('_') or item in ['__call__'] %}
11 | {{ name }}.{{ item }}
12 | {%- endif -%}
13 | {%- endfor %}
14 |
15 | {% endif %}
16 | {% endblock %}
17 |
18 | {% block attributes %}
19 | {% if attributes %}
20 |
21 | .. HACK -- the point here is that we don't want this to appear in the output, but the autosummary should still generate the pages.
22 | .. autosummary::
23 | :toctree:
24 | {% for item in all_attributes %}
25 | {%- if not item.startswith('_') %}
26 | {{ name }}.{{ item }}
27 | {%- endif -%}
28 | {%- endfor %}
29 |
30 | {% endif %}
31 | {% endblock %}
32 |
--------------------------------------------------------------------------------
/doc/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | REM Command file for Sphinx documentation
4 |
5 | if "%SPHINXBUILD%" == "" (
6 | set SPHINXBUILD=sphinx-build
7 | )
8 | set BUILDDIR=build
9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source
10 | set I18NSPHINXOPTS=%SPHINXOPTS% source
11 | if NOT "%PAPER%" == "" (
12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
14 | )
15 |
16 | if "%1" == "" goto help
17 |
18 | if "%1" == "help" (
19 | :help
20 | echo.Please use `make ^` where ^ is one of
21 | echo. html to make standalone HTML files
22 | echo. dirhtml to make HTML files named index.html in directories
23 | echo. singlehtml to make a single large HTML file
24 | echo. pickle to make pickle files
25 | echo. json to make JSON files
26 | echo. htmlhelp to make HTML files and a HTML help project
27 | echo. qthelp to make HTML files and a qthelp project
28 | echo. devhelp to make HTML files and a Devhelp project
29 | echo. epub to make an epub
30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
31 | echo. text to make text files
32 | echo. man to make manual pages
33 | echo. texinfo to make Texinfo files
34 | echo. gettext to make PO message catalogs
35 | echo. changes to make an overview over all changed/added/deprecated items
36 | echo. xml to make Docutils-native XML files
37 | echo. pseudoxml to make pseudoxml-XML files for display purposes
38 | echo. linkcheck to check all external links for integrity
39 | echo. doctest to run all doctests embedded in the documentation if enabled
40 | goto end
41 | )
42 |
43 | if "%1" == "clean" (
44 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
45 | del /q /s %BUILDDIR%\*
46 | goto end
47 | )
48 |
49 |
50 | %SPHINXBUILD% 2> nul
51 | if errorlevel 9009 (
52 | echo.
53 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
54 | echo.installed, then set the SPHINXBUILD environment variable to point
55 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
56 | echo.may add the Sphinx directory to PATH.
57 | echo.
58 | echo.If you don't have Sphinx installed, grab it from
59 | echo.http://sphinx-doc.org/
60 | exit /b 1
61 | )
62 |
63 | if "%1" == "html" (
64 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
65 | if errorlevel 1 exit /b 1
66 | echo.
67 | echo.Build finished. The HTML pages are in %BUILDDIR%/html.
68 | goto end
69 | )
70 |
71 | if "%1" == "dirhtml" (
72 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
73 | if errorlevel 1 exit /b 1
74 | echo.
75 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
76 | goto end
77 | )
78 |
79 | if "%1" == "singlehtml" (
80 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
81 | if errorlevel 1 exit /b 1
82 | echo.
83 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
84 | goto end
85 | )
86 |
87 | if "%1" == "pickle" (
88 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
89 | if errorlevel 1 exit /b 1
90 | echo.
91 | echo.Build finished; now you can process the pickle files.
92 | goto end
93 | )
94 |
95 | if "%1" == "json" (
96 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
97 | if errorlevel 1 exit /b 1
98 | echo.
99 | echo.Build finished; now you can process the JSON files.
100 | goto end
101 | )
102 |
103 | if "%1" == "htmlhelp" (
104 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
105 | if errorlevel 1 exit /b 1
106 | echo.
107 | echo.Build finished; now you can run HTML Help Workshop with the ^
108 | .hhp project file in %BUILDDIR%/htmlhelp.
109 | goto end
110 | )
111 |
112 | if "%1" == "qthelp" (
113 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
114 | if errorlevel 1 exit /b 1
115 | echo.
116 | echo.Build finished; now you can run "qcollectiongenerator" with the ^
117 | .qhcp project file in %BUILDDIR%/qthelp, like this:
118 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\trendvis.qhcp
119 | echo.To view the help file:
120 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\trendvis.ghc
121 | goto end
122 | )
123 |
124 | if "%1" == "devhelp" (
125 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
126 | if errorlevel 1 exit /b 1
127 | echo.
128 | echo.Build finished.
129 | goto end
130 | )
131 |
132 | if "%1" == "epub" (
133 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
134 | if errorlevel 1 exit /b 1
135 | echo.
136 | echo.Build finished. The epub file is in %BUILDDIR%/epub.
137 | goto end
138 | )
139 |
140 | if "%1" == "latex" (
141 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
142 | if errorlevel 1 exit /b 1
143 | echo.
144 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
145 | goto end
146 | )
147 |
148 | if "%1" == "latexpdf" (
149 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
150 | cd %BUILDDIR%/latex
151 | make all-pdf
152 | cd %BUILDDIR%/..
153 | echo.
154 | echo.Build finished; the PDF files are in %BUILDDIR%/latex.
155 | goto end
156 | )
157 |
158 | if "%1" == "latexpdfja" (
159 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
160 | cd %BUILDDIR%/latex
161 | make all-pdf-ja
162 | cd %BUILDDIR%/..
163 | echo.
164 | echo.Build finished; the PDF files are in %BUILDDIR%/latex.
165 | goto end
166 | )
167 |
168 | if "%1" == "text" (
169 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
170 | if errorlevel 1 exit /b 1
171 | echo.
172 | echo.Build finished. The text files are in %BUILDDIR%/text.
173 | goto end
174 | )
175 |
176 | if "%1" == "man" (
177 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
178 | if errorlevel 1 exit /b 1
179 | echo.
180 | echo.Build finished. The manual pages are in %BUILDDIR%/man.
181 | goto end
182 | )
183 |
184 | if "%1" == "texinfo" (
185 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
186 | if errorlevel 1 exit /b 1
187 | echo.
188 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
189 | goto end
190 | )
191 |
192 | if "%1" == "gettext" (
193 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
194 | if errorlevel 1 exit /b 1
195 | echo.
196 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
197 | goto end
198 | )
199 |
200 | if "%1" == "changes" (
201 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
202 | if errorlevel 1 exit /b 1
203 | echo.
204 | echo.The overview file is in %BUILDDIR%/changes.
205 | goto end
206 | )
207 |
208 | if "%1" == "linkcheck" (
209 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
210 | if errorlevel 1 exit /b 1
211 | echo.
212 | echo.Link check complete; look for any errors in the above output ^
213 | or in %BUILDDIR%/linkcheck/output.txt.
214 | goto end
215 | )
216 |
217 | if "%1" == "doctest" (
218 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
219 | if errorlevel 1 exit /b 1
220 | echo.
221 | echo.Testing of doctests in the sources finished, look at the ^
222 | results in %BUILDDIR%/doctest/output.txt.
223 | goto end
224 | )
225 |
226 | if "%1" == "xml" (
227 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
228 | if errorlevel 1 exit /b 1
229 | echo.
230 | echo.Build finished. The XML files are in %BUILDDIR%/xml.
231 | goto end
232 | )
233 |
234 | if "%1" == "pseudoxml" (
235 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
236 | if errorlevel 1 exit /b 1
237 | echo.
238 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
239 | goto end
240 | )
241 |
242 | :end
243 |
--------------------------------------------------------------------------------
/doc/source/conf.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # trendvis documentation build configuration file, created by
5 | # sphinx-quickstart on Wed Jul 1 13:32:53 2015.
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 | import sys
17 | import os
18 |
19 | # If extensions (or modules to document with autodoc) are in another directory,
20 | # add these directories to sys.path here. If the directory is relative to the
21 | # documentation root, use os.path.abspath to make it absolute, like shown here.
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 | needs_sphinx = '1.3'
28 |
29 | # Add any Sphinx extension module names here, as strings. They can be
30 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
31 | # ones.
32 | extensions = [
33 | 'sphinx.ext.autodoc',
34 | 'sphinx.ext.intersphinx',
35 | 'sphinx.ext.coverage',
36 | 'sphinx.ext.mathjax',
37 | 'sphinx.ext.viewcode',
38 | 'sphinx.ext.autosummary',
39 | 'matplotlib.sphinxext.plot_directive',
40 | 'IPython.sphinxext.ipython_directive',
41 | 'IPython.sphinxext.ipython_console_highlighting',
42 | 'numpydoc',
43 | ]
44 |
45 |
46 | autosummary_generate = True
47 |
48 | numpydoc_show_class_members = False
49 | autodoc_default_flags = ['members']
50 |
51 |
52 | # Add any paths that contain templates here, relative to this directory.
53 | templates_path = ['_templates']
54 |
55 | # The suffix of source filenames.
56 | source_suffix = '.rst'
57 |
58 | # The encoding of source files.
59 | #source_encoding = 'utf-8-sig'
60 |
61 | # The master toctree document.
62 | master_doc = 'index'
63 |
64 | # General information about the project.
65 | project = 'trendvis'
66 | copyright = '2015, Mellissa Cross'
67 |
68 | # The version info for the project you're documenting, acts as replacement for
69 | # |version| and |release|, also used in various other places throughout the
70 | # built documents.
71 | #
72 | # The short X.Y version.
73 | version = '0.9.0'
74 | # The full version, including alpha/beta/rc tags.
75 | release = '0.9.0'
76 |
77 | # The language for content autogenerated by Sphinx. Refer to documentation
78 | # for a list of supported languages.
79 | #language = None
80 |
81 | # There are two options for replacing |today|: either, you set today to some
82 | # non-false value, then it is used:
83 | #today = ''
84 | # Else, today_fmt is used as the format for a strftime call.
85 | #today_fmt = '%B %d, %Y'
86 |
87 | # List of patterns, relative to source directory, that match files and
88 | # directories to ignore when looking for source files.
89 | exclude_patterns = []
90 |
91 | # The reST default role (used for this markup: `text`) to use for all
92 | # documents.
93 | default_role = 'obj'
94 |
95 | # If true, '()' will be appended to :func: etc. cross-reference text.
96 | #add_function_parentheses = True
97 |
98 | # If true, the current module name will be prepended to all description
99 | # unit titles (such as .. function::).
100 | #add_module_names = True
101 |
102 | # If true, sectionauthor and moduleauthor directives will be shown in the
103 | # output. They are ignored by default.
104 | #show_authors = False
105 |
106 | # The name of the Pygments (syntax highlighting) style to use.
107 | pygments_style = 'sphinx'
108 |
109 | # A list of ignored prefixes for module index sorting.
110 | #modindex_common_prefix = []
111 |
112 | # If true, keep warnings as "system message" paragraphs in the built documents.
113 | #keep_warnings = False
114 |
115 |
116 | # -- Options for HTML output ----------------------------------------------
117 |
118 | # The theme to use for HTML and HTML Help pages. See the documentation for
119 | # a list of builtin themes.
120 | # html_theme = 'basic'
121 |
122 | # Theme options are theme-specific and customize the look and feel of a theme
123 | # further. For a list of options available for each theme, see the
124 | # documentation.
125 | #html_theme_options = {}
126 |
127 | # Add any paths that contain custom themes here, relative to this directory.
128 | #html_theme_path = []
129 |
130 | # The name for this set of Sphinx documents. If None, it defaults to
131 | # " v documentation".
132 | #html_title = None
133 |
134 | # A shorter title for the navigation bar. Default is the same as html_title.
135 | #html_short_title = None
136 |
137 | # The name of an image file (relative to this directory) to place at the top
138 | # of the sidebar.
139 | #html_logo = None
140 |
141 | # The name of an image file (within the static path) to use as favicon of the
142 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
143 | # pixels large.
144 | #html_favicon = None
145 |
146 | # Add any paths that contain custom static files (such as style sheets) here,
147 | # relative to this directory. They are copied after the builtin static files,
148 | # so a file named "default.css" will overwrite the builtin "default.css".
149 | html_static_path = ['_static']
150 |
151 | # Add any extra paths that contain custom files (such as robots.txt or
152 | # .htaccess) here, relative to this directory. These files are copied
153 | # directly to the root of the documentation.
154 | #html_extra_path = []
155 |
156 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
157 | # using the given strftime format.
158 | #html_last_updated_fmt = '%b %d, %Y'
159 |
160 | # If true, SmartyPants will be used to convert quotes and dashes to
161 | # typographically correct entities.
162 | #html_use_smartypants = True
163 |
164 | # Custom sidebar templates, maps document names to template names.
165 | #html_sidebars = {}
166 |
167 | # Additional templates that should be rendered to pages, maps page names to
168 | # template names.
169 | #html_additional_pages = {}
170 |
171 | # If false, no module index is generated.
172 | #html_domain_indices = True
173 |
174 | # If false, no index is generated.
175 | #html_use_index = True
176 |
177 | # If true, the index is split into individual pages for each letter.
178 | #html_split_index = False
179 |
180 | # If true, links to the reST sources are added to the pages.
181 | #html_show_sourcelink = True
182 |
183 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
184 | #html_show_sphinx = True
185 |
186 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
187 | #html_show_copyright = True
188 |
189 | # If true, an OpenSearch description file will be output, and all pages will
190 | # contain a tag referring to it. The value of this option must be the
191 | # base URL from which the finished HTML is served.
192 | #html_use_opensearch = ''
193 |
194 | # This is the file name suffix for HTML files (e.g. ".xhtml").
195 | #html_file_suffix = None
196 |
197 | # Output file base name for HTML help builder.
198 | htmlhelp_basename = 'trendvisdoc'
199 |
200 |
201 | # -- Options for LaTeX output ---------------------------------------------
202 |
203 | latex_elements = {
204 | # The paper size ('letterpaper' or 'a4paper').
205 | #'papersize': 'letterpaper',
206 |
207 | # The font size ('10pt', '11pt' or '12pt').
208 | #'pointsize': '10pt',
209 |
210 | # Additional stuff for the LaTeX preamble.
211 | #'preamble': '',
212 | }
213 |
214 | # Grouping the document tree into LaTeX files. List of tuples
215 | # (source start file, target name, title,
216 | # author, documentclass [howto, manual, or own class]).
217 | latex_documents = [
218 | ('index', 'trendvis.tex', 'trendvis Documentation',
219 | 'Mellissa Cross', 'manual'),
220 | ]
221 |
222 | # The name of an image file (relative to this directory) to place at the top of
223 | # the title page.
224 | #latex_logo = None
225 |
226 | # For "manual" documents, if this is true, then toplevel headings are parts,
227 | # not chapters.
228 | #latex_use_parts = False
229 |
230 | # If true, show page references after internal links.
231 | #latex_show_pagerefs = False
232 |
233 | # If true, show URL addresses after external links.
234 | #latex_show_urls = False
235 |
236 | # Documents to append as an appendix to all manuals.
237 | #latex_appendices = []
238 |
239 | # If false, no module index is generated.
240 | #latex_domain_indices = True
241 |
242 |
243 | # -- Options for manual page output ---------------------------------------
244 |
245 | # One entry per manual page. List of tuples
246 | # (source start file, name, description, authors, manual section).
247 | man_pages = [
248 | ('index', 'trendvis', 'trendvis Documentation',
249 | ['Mellissa Cross'], 1)
250 | ]
251 |
252 | # If true, show URL addresses after external links.
253 | #man_show_urls = False
254 |
255 |
256 | # -- Options for Texinfo output -------------------------------------------
257 |
258 | # Grouping the document tree into Texinfo files. List of tuples
259 | # (source start file, target name, title, author,
260 | # dir menu entry, description, category)
261 | texinfo_documents = [
262 | ('index', 'trendvis', 'trendvis Documentation',
263 | 'Mellissa Cross', 'trendvis', 'One line description of project.',
264 | 'Miscellaneous'),
265 | ]
266 |
267 | # Documents to append as an appendix to all manuals.
268 | #texinfo_appendices = []
269 |
270 | # If false, no module index is generated.
271 | #texinfo_domain_indices = True
272 |
273 | # How to display URL addresses: 'footnote', 'no', or 'inline'.
274 | #texinfo_show_urls = 'footnote'
275 |
276 | # If true, do not generate a @detailmenu in the "Top" node's menu.
277 | #texinfo_no_detailmenu = False
278 |
279 | intersphinx_mapping = {'python': ('https://docs.python.org/3.4', None),
280 | 'matplotlb': ('http://matplotlib.org', None)}
281 |
282 | ################# numpydoc config ####################
283 | numpydoc_show_class_members = False
284 |
--------------------------------------------------------------------------------
/doc/source/index.rst:
--------------------------------------------------------------------------------
1 | .. currentmodule:: trendvis
2 |
3 | ==========
4 | TrendVis
5 | ==========
6 |
7 | :py:mod:`trendvis` API
8 | ======================
9 |
10 | .. autosummary::
11 | :toctree: generated/
12 |
13 | XGrid
14 | YGrid
15 | make_grid
16 | plot_data
17 |
18 |
19 | The public API of :py:mod:`trendvis` consists of two classes, `XGrid` and `YGrid`, and two convenience functions :func:`make_grid` and :func:`plot_data`. The preferred interface is through `XGrid` and `YGrid`, but the convenience functions are provided to quickly create and format an `XGrid` or `YGrid` and draw line plots.
20 |
21 |
22 | Motivation
23 | ==========
24 |
25 |
26 | When plotting multiple datasets against a common x or y axis, a figure can quickly become cluttered with overlain curves and/or unnecessary axis spines. `TrendVis` enables construction of complex figures with data plotted in a common plot area against different y or x ('stack') axes and a common x or y (main) axis respectively.
27 |
28 |
29 | Examples
30 | ========
31 |
32 | Simple XGrid
33 | ------------
34 |
35 | .. plot::
36 | :include-source:
37 |
38 | import numpy as np
39 | import matplotlib.pyplot as plt
40 | import trendvis
41 |
42 | # Simple XGrid with frame
43 | yvals = np.random.rand(10)
44 | nums = 10
45 | lw = 1.5
46 |
47 | ex0 = trendvis.XGrid([1,2,1], figsize=(5,5))
48 |
49 | trendvis.plot_data(ex0, [[(np.linspace(0, 9.5, num=nums), yvals, 'blue')],
50 | [(np.linspace(1, 9, num=nums), yvals*5, 'red')],
51 | [(np.linspace(0.5, 10, num=nums), yvals*10, 'green')]],
52 | lw=1.5, auto_spinecolor=False, markeredgecolor='none', marker='s')
53 |
54 | ex0.cleanup_grid()
55 | ex0.autocolor_spines(ticks_only=True)
56 |
57 | ex0.set_spinewidth(lw)
58 |
59 | ex0.set_all_ticknums([(2, 1)], [(0.2, 0.1), (1, 0.5), (2, 1)])
60 | ex0.set_ticks(major_dim=(7, 3), minor_dim=(4, 2))
61 |
62 | ex0.set_ylabels(['axis 0', 'axis 1', 'axis 2'])
63 | ex0.axes[2][0].set_xlabel('Main Axis', fontsize=14)
64 |
65 | ex0.draw_frame()
66 | ex0.fig.subplots_adjust(hspace=-0.3)
67 |
68 |
69 | Simple YGrid
70 | ------------
71 | Creating a `YGrid` is essentially the same as an `XGrid`.
72 |
73 | .. plot::
74 | :include-source:
75 |
76 | import numpy as np
77 | import matplotlib.pyplot as plt
78 | import trendvis
79 |
80 | # Simple YGrid without frame
81 | xvals = np.random.rand(10)
82 | nums = 10
83 | lw = 1.5
84 |
85 | ex0 = trendvis.YGrid([1,2,1], figsize=(5,5))
86 |
87 | trendvis.plot_data(ex0, [[(xvals, np.linspace(0, 9.5, num=nums), 'blue')],
88 | [(xvals*5, np.linspace(1, 9, num=nums), 'red')],
89 | [(xvals*10, np.linspace(0.5, 10, num=nums),'green')]],
90 | lw=1.5, auto_spinecolor=True, markeredgecolor='none', marker='s')
91 |
92 | ex0.cleanup_grid()
93 | ex0.set_spinewidth(lw)
94 |
95 | ex0.set_all_ticknums([(0.2, 0.1), (1, 0.5), (2, 1)], [(2, 1)])
96 | ex0.set_ticks(major_dim=(7, 3), minor_dim=(4, 2))
97 |
98 | ex0.set_xlabels(['axis 0', 'axis 1', 'axis 2'])
99 | ex0.axes[0][0].set_ylabel('Main Axis', fontsize=14)
100 |
101 | ex0.fig.subplots_adjust(wspace=-0.3)
102 |
--------------------------------------------------------------------------------
/requirements-dev.txt:
--------------------------------------------------------------------------------
1 | # These are required for developing the package (running the tests, building
2 | # the documentation) but not necessarily required for _using_ it.
3 | codecov
4 | coverage
5 | flake8
6 | pytest
7 | sphinx
8 | twine
9 | black
10 | # These are dependencies of various sphinx extensions for documentation.
11 | -r requirements-doc.txt
12 |
--------------------------------------------------------------------------------
/requirements-doc.txt:
--------------------------------------------------------------------------------
1 | # List required packages in this file, one per line.
2 | ipython
3 | jinja2>3
4 | matplotlib
5 | mpl-sphinx-theme
6 | numpydoc
7 | sphinx
8 | sphinx-copybutton
9 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env python
2 |
3 | import os
4 | import sys
5 | import re
6 | import setuptools
7 | from setuptools import setup
8 |
9 |
10 | descr = """TrendVis (a.k.a. `trendvis`): Complex plotting in matplotlib.
11 |
12 | This package is designed to programmatically create complex, publication-
13 | quality figures using matplotlib. TrendVis' speciality is creating
14 | multiple vertically or horizontally offset plots with a
15 | common x or y axis, respectively, in what is visually one compact
16 | plotting area, facilitating comparisons among various datasets.
17 | """
18 |
19 |
20 | DISTNAME = 'TrendVis'
21 | DESCRIPTION = 'Publication-quality data trend visualization'
22 | LONG_DESCRIPTION = descr
23 | MAINTAINER = 'Mellissa Cross'
24 | MAINTAINER_EMAIL = 'mellissa.cross@gmail.com'
25 | LICENSE = 'Modified BSD'
26 | DOWNLOAD_URL = ''
27 | VERSION = '0.2.2'
28 | PYTHON_VERSION = (2, 6)
29 | DEPENDENCIES = {'matplotlib': (1, 2)}
30 |
31 |
32 | def check_requirements():
33 | if sys.version_info < PYTHON_VERSION:
34 | raise SystemExit('Python version %d.%d required; found %d.%d.'
35 | % (PYTHON_VERSION[0], PYTHON_VERSION[1],
36 | sys.version_info[0], sys.version_info[1]))
37 |
38 | for package_name, min_version in DEPENDENCIES.items():
39 | dep_err = False
40 | try:
41 | package = __import__(package_name)
42 | except ImportError:
43 | dep_err = True
44 | else:
45 | package_version = get_package_version(package)
46 | if min_version > package_version:
47 | dep_err = True
48 |
49 | if dep_err:
50 | raise ImportError('`%s` version %d.%d or later required.'
51 | % ((package_name, ) + min_version))
52 |
53 |
54 | def get_package_version(package):
55 | version = []
56 | for version_attr in ('version', 'VERSION', '__version__'):
57 | if hasattr(package, version_attr) \
58 | and isinstance(getattr(package, version_attr), str):
59 | version_info = getattr(package, version_attr, '')
60 | for part in re.split('\D+', version_info):
61 | try:
62 | version.append(int(part))
63 | except ValueError:
64 | pass
65 |
66 | return tuple(version)
67 |
68 |
69 | def write_version_py(filename='trendvis/version.py'):
70 | template = """# THIS FILE IS GENERATED FROM THE TRENDVIS SETUP.PY
71 | version='%s'
72 | """
73 |
74 | vfile = open(os.path.join(os.path.dirname(__file__),
75 | filename), 'w')
76 |
77 | try:
78 | vfile.write(template % VERSION)
79 | finally:
80 | vfile.close()
81 |
82 |
83 | if __name__ == '__main__':
84 | check_requirements()
85 | write_version_py()
86 |
87 | setup(
88 | name=DISTNAME,
89 | description=DESCRIPTION,
90 | long_description=LONG_DESCRIPTION,
91 | maintainer=MAINTAINER,
92 | maintainer_email=MAINTAINER_EMAIL,
93 | license=LICENSE,
94 | download_url=DOWNLOAD_URL,
95 | version=VERSION,
96 |
97 | classifiers=[
98 | 'Development Status :: 4 - Beta',
99 | 'Environment :: Console',
100 | 'Intended Audience :: Developers',
101 | 'Intended Audience :: Science/Research',
102 | 'License :: OSI Approved :: BSD License',
103 | 'Programming Language :: Python',
104 | 'Programming Language :: Python :: 3',
105 | 'Topic :: Scientific/Engineering',
106 | 'Operating System :: Microsoft :: Windows',
107 | 'Operating System :: POSIX',
108 | 'Operating System :: Unix',
109 | 'Operating System :: MacOS'],
110 |
111 |
112 | packages=setuptools.find_packages(),
113 | include_package_data=True,
114 | zip_safe=False
115 | )
116 |
--------------------------------------------------------------------------------
/trendvis/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | TrendVis package containing tools for creating complex,
3 | publication quality figures in a few styles:
4 |
5 | * y-axes stacked against a common x-axis or a common broken x-axis
6 | (which may be demarcated as breaks or columns)
7 | * x-axes stack3d against a common y-axis or a common broken y-axis
8 | (which may be demarcated as breaks or rows)
9 |
10 | Additionally, TrendVis contains drawing tools to frame subplot area(s)
11 | or highlight various columns, bars, or rectangles of interest.
12 |
13 | """
14 |
15 | __all__ = ['XGrid',
16 | 'YGrid',
17 | 'make_grid',
18 | 'plot_data']
19 |
20 | from .xgrid_ystack import XGrid
21 |
22 | from .ygrid_xstack import YGrid
23 |
24 | from .gridwrapper import make_grid, plot_data
25 |
--------------------------------------------------------------------------------
/trendvis/gridclass.py:
--------------------------------------------------------------------------------
1 | from __future__ import division, print_function, absolute_import
2 | from matplotlib.ticker import MultipleLocator
3 | import matplotlib.pyplot as plt
4 | import matplotlib.lines as lines
5 |
6 |
7 | class Grid(object):
8 | """
9 | Superclass for ``YGrid`` and ``XGrid``.
10 |
11 | """
12 |
13 | def __init__(self, xratios, yratios, mainax_x, figsize, **kwargs):
14 | """
15 | Initialize grid attributes. Should only be called through
16 | ``XGrid`` or ``YGrid`` subclasses.
17 |
18 | Parameters
19 | ----------
20 | xratios : int or list of ints
21 | The relative sizes of the columns. Not directly comparable
22 | to ``yratios``
23 | yratios : int or list of ints
24 | The relative sizes of the rows. Not directly comparable to
25 | ``xratios``
26 | mainax_x : Boolean
27 | [True|False]. Indicates if x is the main axis. Determines
28 | some attributes
29 | figsize : tuple of ints or floats
30 | The figure dimensions in inches. If None, defaults to matplotlib
31 | rc figure.figsize.
32 | **kwargs
33 | Any plt.figure arguments
34 |
35 | """
36 | figsize = figsize or plt.rcParams['figure.figsize']
37 | self.fig = plt.figure(figsize=figsize)
38 |
39 | self.gridrows, self.yratios = self._ratios_arelists(yratios)
40 | self.gridcols, self.xratios = self._ratios_arelists(xratios)
41 |
42 | self.numrows = len(self.yratios)
43 | self.numcols = len(self.xratios)
44 |
45 | self.axes = []
46 |
47 | self.bf_urcorners = []
48 | self.bf_llcorners = []
49 | self.bf_patchinds = []
50 | self.bf_uraxis = []
51 | self.bf_llaxis = []
52 |
53 | self.relative_shifts = None
54 | self.stack_shifts = None
55 |
56 | self.twinds = None
57 | self.twin_dim = 0
58 | self.reltwin_shifts = None
59 | self.twin_shifts = None
60 |
61 | self.grid_isclean = False
62 |
63 | self.spinelist = ['top', 'bottom', 'left', 'right']
64 | self.spinewidth = 1
65 |
66 | if mainax_x:
67 | self.mainax_id = 'x'
68 | self.stackax_id = 'y'
69 | self.stackdim = self.numrows
70 | self.mainax_dim = self.numcols
71 | self.sp1 = 'right'
72 | self.sp2 = 'left'
73 |
74 | self.startpos = 'top'
75 |
76 | self.mainax_ticks = {'top' : ('on', 'off'),
77 | 'both' : ('on', 'on'),
78 | 'bottom': ('off', 'on'),
79 | 'none' : ('off', 'off')}
80 |
81 | self.alt_sides = {'left' : 'right',
82 | 'right': 'left'}
83 |
84 | self.side_inds = {'left' : 0,
85 | 'right': -1}
86 |
87 | self.spine_begone = {'top' : {'left' : ['bottom', 'right'],
88 | 'right': ['bottom', 'left'],
89 | 'none' : ['bottom', 'left',
90 | 'right']},
91 | 'none' : {'left' : ['top', 'bottom', 'right'],
92 | 'right': ['top', 'bottom', 'left'],
93 | 'none' : ['top', 'bottom', 'left',
94 | 'right']},
95 | 'bottom': {'left' : ['top', 'right'],
96 | 'right': ['top', 'left'],
97 | 'none' : ['top', 'left',
98 | 'right']},
99 | 'both' : {'left' : ['right'],
100 | 'right': ['left'],
101 | 'none' : ['right', 'left']}}
102 | else:
103 | self.mainax_id = 'y'
104 | self.stackax_id = 'x'
105 | self.stackdim = self.numcols
106 | self.mainax_dim = self.numrows
107 | self.sp1 = 'top'
108 | self.sp2 = 'bottom'
109 |
110 | self.startpos = 'left'
111 |
112 | self.mainax_ticks = {'left' : ('on', 'off'),
113 | 'both' : ('on', 'on'),
114 | 'right': ('off', 'on'),
115 | 'none' : ('off', 'off')}
116 |
117 | self.alt_sides = {'top' : 'bottom',
118 | 'bottom': 'top'}
119 |
120 | self.side_inds = {'top' : 0,
121 | 'bottom': -1}
122 |
123 | self.spine_begone = {'left' : {'top' : ['bottom', 'right'],
124 | 'bottom': ['top', 'right'],
125 | 'none' : ['bottom', 'top',
126 | 'right']},
127 | 'none' : {'top' : ['bottom', 'left',
128 | 'right'],
129 | 'bottom': ['top', 'left', 'right'],
130 | 'none' : ['top', 'bottom', 'left',
131 | 'right']},
132 | 'right': {'top' : ['bottom', 'left'],
133 | 'bottom': ['top', 'left'],
134 | 'none' : ['top', 'bottom',
135 | 'left']},
136 | 'both' : {'top' : ['bottom'],
137 | 'bottom': ['top'],
138 | 'none' : ['top', 'bottom']}}
139 |
140 | self._update_total_stackdim()
141 |
142 | def set_dataside(self, startside, alternate_sides):
143 | """
144 | Set the ``dataside_list`` that indicates which stacked ax spine will be
145 | the visible data spine.
146 |
147 | Parameters
148 | ----------
149 | startside : string
150 | ['left'|'right'] or ['top'|'bottom']. The side the first row
151 | (column) in ``self.axes`` will have its y (x) axis spine on.
152 | alternate_sides : Boolean
153 | [True|False]. Stacked axis spines alternate sides or are all on
154 | ``startside``.
155 |
156 | """
157 |
158 | self.dataside_list = [startside]
159 |
160 | if alternate_sides:
161 | for i in range(1, self.stackdim):
162 | newside = self.alt_sides[self.dataside_list[i - 1]]
163 | self.dataside_list.append(newside)
164 |
165 | else:
166 | self.dataside_list = self.dataside_list * self.stackdim
167 |
168 | self._update_twinsides()
169 |
170 | def set_stackposition(self, onespine_forboth):
171 | """
172 | Set ``self.stackpos_list`` indicating which non-stacked axes are shown.
173 |
174 | Parameters
175 | ----------
176 | onespine_forboth : Boolean
177 | [True|False]. If the plot stack is only 1 row (column), then
178 | both main axis spines may be visible (False), or only the bottom
179 | (left) spine.
180 |
181 | """
182 |
183 | alt_pos = {'left': 'right',
184 | 'top' : 'bottom'}
185 |
186 | # Position list in the case of a stack of 1
187 | if self.stackdim == 1:
188 | if onespine_forboth:
189 | if self.mainax_id == 'x':
190 | self.startpos = 'bottom'
191 | self.stackpos_list = [self.startpos]
192 | else:
193 | self.stackpos_list = ['both']
194 |
195 | else:
196 | num_nones = self.stackdim - 2
197 | self.stackpos_list = ([self.startpos] + ['none'] * num_nones +
198 | [alt_pos[self.startpos]])
199 |
200 | def move_spines(self, axis_shift=None, twin_shift=None):
201 | """
202 | Wrapper around self.set_relative_axshift(),
203 | self.set_absolute_axshift(), self.excecute_spineshift()
204 |
205 | Parameters
206 | -----------------
207 | axis_shift : float or list of floats
208 | Default None. Set universal (float) or individual (list of
209 | floats where len(``axis_shift``) = ``self.stackdim``)
210 | axis spine shift
211 | twin_shift : float or list of floats
212 | Default None. Set universal (float) or individual (list of
213 | floats where len(``twin_shift``) = ``self.twin_dim``)
214 | twinned axis spine shift.
215 | """
216 |
217 | self.set_relative_axshift(axis_shift=axis_shift, twin_shift=twin_shift)
218 | self.set_absolute_axshift()
219 | self.excecute_spineshift()
220 |
221 | def set_relative_axshift(self, axis_shift=None, twin_shift=None):
222 | """
223 | Set relative shift of stacked axis spines.
224 |
225 | Parameters
226 | -----------------
227 | axis_shift : float or list of floats
228 | Default None. Set universal (float) or individual (list of
229 | floats where len(``axis_shift``) = ``self.stackdim``)
230 | axis spine shift
231 | twin_shift : float or list of floats
232 | Default None. Set universal (float) or individual (list of
233 | floats where len(``twin_shift``) = ``self.twin_dim``)
234 | twinned axis spine shift.
235 |
236 | """
237 |
238 | if axis_shift is not None:
239 | try:
240 | # Check if iterable
241 | axis_shift[0]
242 | except:
243 | # if not a list, apply same shift to both
244 | self.relative_shifts = [axis_shift] * self.stackdim
245 | else:
246 | if len(axis_shift) != self.stackdim:
247 | print('Warning: len(axis_shift) != ' + str(self.stackdim))
248 | self.relative_shifts = [axis_shift[0]] * self.stackdim
249 | else:
250 | self.relative_shifts = axis_shift
251 |
252 | if twin_shift is not None and self.twinds is not None:
253 | try:
254 | twin_shift[0]
255 | except:
256 | self.reltwin_shifts = [twin_shift] * self.twin_dim
257 | else:
258 | if len(twin_shift) != self.twin_dim:
259 | print('Warning: len(twin_shift) != number of twinned ax!')
260 | self.reltwin_shifts = [twin_shift[0]] * self.twin_dim
261 | else:
262 | self.reltwin_shifts = twin_shift
263 |
264 | def set_absolute_axshift(self):
265 | """
266 | Translate ``self.relative_shifts`` to absolute values. Absolute values
267 | are based on which side the axis to be moved appears.
268 |
269 | """
270 |
271 | if self.relative_shifts is not None:
272 |
273 | # Reset aboslute shifts
274 | self.stack_shifts = []
275 |
276 | for shift, dataside in zip(self.relative_shifts,
277 | self.dataside_list):
278 |
279 | if dataside == self.sp2:
280 | self.stack_shifts.append(0 - shift)
281 | else:
282 | self.stack_shifts.append(1 + shift)
283 |
284 | if self.reltwin_shifts is not None:
285 | self.twin_shifts = []
286 |
287 | for shift, dataside in zip(self.reltwin_shifts,
288 | self.dataside_list[self.stackdim:]):
289 | if dataside == self.sp2:
290 | self.twin_shifts.append(0 - shift)
291 | else:
292 | self.twin_shifts.append(1 + shift)
293 |
294 | def excecute_spineshift(self):
295 | """
296 | Move the stacked spines around.
297 |
298 | """
299 |
300 | if self.stack_shifts is not None:
301 |
302 | for subgrid, ds, sh in zip(self.axes, self.dataside_list,
303 | self.stack_shifts):
304 |
305 | for ax in subgrid:
306 | ax.spines[ds].set_position(('axes', sh))
307 |
308 | if self.twin_shifts is not None:
309 |
310 | for subgrid, ds, sh in zip(self.axes[self.stackdim:],
311 | self.dataside_list[self.stackdim:],
312 | self.twin_shifts):
313 | for ax in subgrid:
314 | ax.spines[ds].set_position(('axes', sh))
315 |
316 | def move_one_spine(self, ax, which, shift):
317 | """
318 | Move ``which`` stacked ``ax`` spine by relative ``shift``.
319 |
320 | Parameters
321 | ----------
322 | ax : axes instance
323 | The axes instance which needs a spine moved.
324 | Can be obtained via ``self.get_axis()``
325 | which : string
326 | If ``XGrid``, ['left'|'right'], ``YGrid`` ['top'|'bottom'].
327 | Used to indentify spine and to calculate outward shift
328 | from ``shift``.
329 | shift : float
330 | Change in position relative to figure size.
331 |
332 | """
333 |
334 | if which is self.sp1:
335 | shift = 1 + shift
336 | elif which is self.sp2:
337 | shift = 0 - shift
338 | else:
339 | raise ValueError('Not a stacked ax spine')
340 |
341 | ax.spines[which].set_position(('axes', shift))
342 |
343 | def reset_spineshift(self):
344 | """
345 | Reset all spines to normal position.
346 |
347 | """
348 |
349 | shifts = {'x': {'left' : 0.0,
350 | 'right' : 1.0},
351 | 'y': {'bottom' : 0.0,
352 | 'top' : 1.0}}
353 |
354 | sd = shifts[self.mainax_id]
355 |
356 | for subgrid in self.axes:
357 | for ax in subgrid:
358 | for key in sd:
359 | ax.spines[key].set_position(('axes', sd[key]))
360 |
361 | self.relative_shifts = None
362 | self.stack_shifts = None
363 | self.reltwin_shifts = None
364 | self.twin_shifts = None
365 |
366 | def reveal_spines(self):
367 | """
368 | Undo the spine-hiding actions of ``self.cleanup_grid()``.
369 |
370 | """
371 |
372 | for subgrid in self.axes:
373 | for ax in subgrid:
374 | for sp in self.spinelist:
375 | ax.spines[sp].set_visible(True)
376 |
377 | self.grid_isclean = False
378 |
379 | def set_ax_visibility(self, ax, which, visible):
380 | """
381 | Hide (``visible``=``False``) or show (``visible``=``True``) an axis
382 | side (``which``). Will hide/show spine, ticks, and ticklabels.
383 |
384 | Parameters
385 | ----------
386 | ax : axes instance
387 | The axes instance to set spine, tick visibility for.
388 | Can be obtained via ``self.get_axis()``
389 | which : string
390 | The axis spine, ticks, ticklabels to hide/show.
391 | ['left'|'right'|'top'|'bottom']
392 | visible : Boolean
393 | Set visible or invisible
394 |
395 | """
396 |
397 | ax.spines[which].set_visible(visible)
398 |
399 | if which == 'left' or which == 'right':
400 | if visible:
401 | ytick_dict = {'both' : {'left' : 'both',
402 | 'right': 'both'},
403 | 'left' : {'left' : 'left',
404 | 'right': 'both'},
405 | 'right' : {'left' : 'both',
406 | 'right': 'right'},
407 | 'unknown': {'left' : 'left',
408 | 'right': 'right'},
409 | 'default': {'left' : 'both',
410 | 'right': 'both'}}
411 | else:
412 | ytick_dict = {'both' : {'left' : 'right',
413 | 'right': 'left'},
414 | 'left' : {'left' : 'none',
415 | 'right': 'left'},
416 | 'right' : {'left' : 'right',
417 | 'right': 'none'},
418 | 'unknown': {'left' : 'none',
419 | 'right': 'none'},
420 | 'default': {'left' : 'right',
421 | 'right': 'left'}}
422 | # Key is new tick pos, value is labelright, labelleft
423 | ylabeldict = {'left' : ['off', 'on'],
424 | 'right': ['on', 'off'],
425 | 'both' : ['on', 'on'],
426 | 'none' : ['off', 'off']}
427 |
428 | old_tickpos = ax.yaxis.get_ticks_position()
429 | new_tickpos = ytick_dict[old_tickpos][which]
430 | ax.yaxis.set_ticks_position(new_tickpos)
431 | r, f = ylabeldict[new_tickpos]
432 | ax.yaxis.set_tick_params(labelright=r, labelleft=f)
433 | self.grid_isclean = False
434 |
435 | if which == 'top' or which == 'bottom':
436 | if visible:
437 | xtick_dict = {'both' : {'top' : 'both',
438 | 'bottom': 'both'},
439 | 'top' : {'top' : 'top',
440 | 'bottom': 'both'},
441 | 'bottom' : {'top' : 'both',
442 | 'bottom': 'bottom'},
443 | 'unknown': {'top' : 'top',
444 | 'bottom': 'bottom'},
445 | 'default': {'top' : 'both',
446 | 'bottom': 'both'}}
447 | else:
448 | xtick_dict = {'both' : {'top' : 'bottom',
449 | 'bottom': 'top'},
450 | 'top' : {'top' : 'none',
451 | 'bottom': 'top'},
452 | 'bottom' : {'top' : 'bottom',
453 | 'bottom': 'none'},
454 | 'unknown': {'top' : 'none',
455 | 'bottom': 'none'},
456 | 'default': {'top' : 'bottom',
457 | 'bottom': 'top'}}
458 | # Key is new tick pos, value is labeltop, labelbottom
459 | xlabeldict = {'bottom': ['off', 'on'],
460 | 'top' : ['on', 'off'],
461 | 'none' : ['off', 'off']}
462 |
463 | old_tickpos = ax.xaxis.get_ticks_position()
464 | new_tickpos = xtick_dict[old_tickpos][which]
465 | ax.xaxis.set_ticks_position(new_tickpos)
466 | t, b = xlabeldict[new_tickpos]
467 | ax.xaxis.set_tick_params(labeltop=t, labelbottom=b)
468 | self.grid_isclean = False
469 |
470 | def set_spinewidth(self, spinewidth):
471 | """
472 | Edit the linewidth of the axis spines. ``self.spinewidth`` used as
473 | default linewidths in drawing frames and cutouts.
474 |
475 | Parameters
476 | ----------
477 | spinewidth : int
478 | Linewidth in points.
479 |
480 | """
481 |
482 | for subgrid in self.axes:
483 | for ax in subgrid:
484 | for sp in self.spinelist:
485 | ax.spines[sp].set_linewidth(spinewidth)
486 |
487 | self.spinewidth = spinewidth
488 |
489 | def remove_twins(self):
490 | """
491 | Get rid of all twinned axes.
492 |
493 | """
494 |
495 | for subgrid in self.axes[self.stackdim:]:
496 | for ax in subgrid:
497 | self.fig.delaxes(ax)
498 |
499 | self.twinds = None
500 | self.twin_dim = 0
501 | self.reltwin_shifts = None
502 | self.twin_shifts = None
503 | self.dataside_list = self.dataside_list[:self.stackdim]
504 | self.stackpos_list = self.stackpos_list[:self.stackdim]
505 | self.axes = self.axes[:self.stackdim]
506 |
507 | self._update_total_stackdim()
508 |
509 | def set_xaxis_ticknum(self, axis, xticks, scale='linear'):
510 | """
511 | Set x tick scale and, if linear, major and minor tick locators.
512 |
513 | Parameters
514 | ----------
515 | axis : ``matplotlib Axes`` instance
516 | ``Axes`` instance to set x-axis scale and potentially
517 | major and minor tick locators. Can get with ``self.get_axis()``
518 | xticks : tuple
519 | Tuple of (major, minor) x axis tick multiples.
520 | scale : string
521 | Default 'linear'. ['log'|'linear']. X axis scale.
522 |
523 | """
524 |
525 | if scale == 'linear':
526 |
527 | xmajor_loc = MultipleLocator(xticks[0])
528 | xminor_loc = MultipleLocator(xticks[1])
529 |
530 | axis.xaxis.set_major_locator(xmajor_loc)
531 | axis.xaxis.set_minor_locator(xminor_loc)
532 |
533 | else:
534 | axis.set_xscale(scale)
535 |
536 | def set_yaxis_ticknum(self, axis, yticks, scale='linear'):
537 | """
538 | Set y tick scale and, if linear, major and minor tick locators.
539 |
540 | Parameters
541 | ----------
542 | axis : ``matplotlib Axes`` instance
543 | ``Axes`` instance to set y-axis scale and potentially
544 | major and minor tick locators. Can get with ``self.get_axis()``
545 | xticks : tuple
546 | Tuple of (major, minor) y axis tick multiples.
547 | scale : string
548 | Default 'linear'. ['log'|'linear']. Y axis scale.
549 |
550 | """
551 |
552 | if scale == 'linear':
553 | ymajor_loc = MultipleLocator(yticks[0])
554 | yminor_loc = MultipleLocator(yticks[1])
555 |
556 | axis.yaxis.set_major_locator(ymajor_loc)
557 | axis.yaxis.set_minor_locator(yminor_loc)
558 |
559 | else:
560 | axis.set_yscale(scale)
561 |
562 | def autocolor_spines(self, ticks_only=False):
563 | """
564 | Set the axis stacked ax spine and/or tick color based on the color of
565 | the first line on the axis.
566 |
567 | If no line is found on an axis, then ``autocolor_spines()`` will
568 | default to the color of the first item in the list of axis children.
569 |
570 | Parameters
571 | ----------
572 | ticks_only : Boolean
573 | Default ``False``. If ``True``, then the tick color will change
574 | and the axis color will not. If ``False``, both will change.
575 |
576 | """
577 |
578 | for subgrid in self.axes:
579 | for ax in subgrid:
580 | # Default is first child, unless the first line is found
581 | # later among the children. Should account for difference
582 | # among recent versions of matplotlib
583 | child = ax.get_children()[0]
584 | for kid in ax.get_children():
585 | if isinstance(kid, lines.Line2D):
586 | child = kid
587 | break
588 | try:
589 | color = child.get_color()
590 | except AttributeError:
591 | color = child.get_facecolor()
592 | if len(color) < 3:
593 | color = color[0]
594 |
595 | try:
596 | self.set_axcolor(ax, color, ticks_only=ticks_only)
597 | except:
598 | pass
599 |
600 | def set_axcolor(self, ax, color, ticks_only=False, spines_only=False):
601 | """
602 | Set the stacked ax spine and tick color of the given Axes.
603 |
604 | Parameters
605 | ----------
606 | ax : ``matplotlib Axes`` instance
607 | Can get with ``self.get_axis()``
608 | color : string, tuple of floats
609 | Any color accepted by ``matplotlib``.
610 | ticks_only : Boolean
611 | Default ``False``. If ``True``, then the tick color will change
612 | and the axis color will not. If ``False``, both will change.
613 |
614 | """
615 |
616 | if not spines_only:
617 | ax.tick_params(axis=self.stackax_id, color=color, which='both')
618 |
619 | if not ticks_only:
620 | ax.spines[self.sp1].set_color(color)
621 | ax.spines[self.sp2].set_color(color)
622 |
623 | def reset_spinecolor(self, spines_only=False):
624 | """
625 | Reset all spine colors to black.
626 |
627 | """
628 |
629 | for subgrid in self.axes:
630 | for ax in subgrid:
631 | self.set_axcolor(ax, 'black', spines_only=spines_only)
632 |
633 | def draw_frame(self, lw='default', zorder=-1, edgecolor='black',
634 | facecolor='none', **kwargs):
635 | """
636 | Draw frame around each column (``XGrid``) or row (``YGrid`) of plot.
637 |
638 | E.g., if ``self.mainax_dim`` == 1, then a frame will be drawn around
639 | the whole figure, visually anchoring axes; if ``self.mainax_dim`` > 1,
640 | then each mainax section will have a frame drawn around it.
641 |
642 | Parameters
643 | ----------
644 | lw : int
645 | Default 'default'. If default, then ``lw`` will be set to
646 | ``self.spinewidth``.
647 | zorder : int
648 | Default -1. The zorder of the frame.
649 | edgecolor : string or tuple of floats
650 | Default 'black'. Any ``matplotlib``-accepted color.
651 | facecolor : string or tuple of floats
652 | Default 'none'. The background color. Any ``matplotlib``-accepted
653 | color.
654 | **kwargs
655 | Passed to ``plt.Rectangle``; any valid
656 | ``matplotlib.patches.Patch`` kwargs
657 |
658 | """
659 |
660 | last_instack = self.stackdim - 1
661 | patchlist = self.fig.patches
662 |
663 | if lw == 'default':
664 | lw = self.spinewidth
665 |
666 | for main in range(0, self.mainax_dim):
667 | if self.mainax_id == 'x':
668 | ll_axis = self.axes[last_instack][main]
669 | ur_axis = self.axes[0][main]
670 | else:
671 | ll_axis = self.axes[0][main]
672 | ur_axis = self.axes[last_instack][main]
673 |
674 | lldx = ll_axis.get_xlim()[0]
675 | lldy = ll_axis.get_ylim()[0]
676 |
677 | urdx = ur_axis.get_xlim()[1]
678 | urdy = ur_axis.get_ylim()[1]
679 |
680 | self.bf_llcorners.append((lldx, lldy))
681 | self.bf_urcorners.append((urdx, urdy))
682 | self.bf_llaxis.append(ll_axis)
683 | self.bf_uraxis.append(ur_axis)
684 |
685 | ll_corner = self._convert_coords(ll_axis, (lldx, lldy))
686 | ur_corner = self._convert_coords(ur_axis, (urdx, urdy))
687 |
688 | width, height = self._rect_dim(ur_corner, ll_corner)
689 |
690 | patchlist.append(plt.Rectangle(ll_corner, width, height,
691 | zorder=zorder, facecolor=facecolor,
692 | edgecolor=edgecolor, lw=lw,
693 | transform=self.fig.transFigure,
694 | **kwargs))
695 |
696 | self.bf_patchinds.append(len(patchlist) - 1)
697 |
698 | def draw_bar(self, ll_axis, ur_axis, bar_limits, orientation='vertical',
699 | zorder=-1, **kwargs):
700 | """
701 | Draws vertical or horizontal bars across the ENTIRE plot space,
702 | anchoring them on opposite axes.
703 |
704 | If horizontal (vertical) bars are drawn on ``XGrid`` (``YGrid``), then
705 | adjusting plot spacing afterwards will appear to displace bar, because
706 | they are drawn on the figure and not the axes.
707 |
708 | Parameters
709 | ----------
710 | ll_axis : ``matplotlib Axes`` instance
711 | The axis that will contain the lower left corner of the bar
712 | ur_axis : ``matplotlib Axes`` instance
713 | The axis that will contain the upper right corner of the bar
714 | bar_limits : length 2 tuple of ints or floats,
715 | The lower, upper data limits of the bar
716 | orientation : string
717 | Default 'vertical'. Indicates the orientation of the long
718 | axis of the bar
719 | zorder : int
720 | Default -1. Zorder of the bar.
721 | **kwargs
722 | Passed to ``plt.Rectangle``; any valid
723 | ``matplotlib.patches.Patch`` kwargs
724 |
725 | """
726 |
727 | if orientation == 'vertical':
728 | lldx, urdx = bar_limits
729 | lldy = ll_axis.get_ylim()[0]
730 | urdy = ur_axis.get_ylim()[1]
731 | else:
732 | lldy, urdy = bar_limits
733 | lldx = ll_axis.get_xlim()[0]
734 | urdx = ur_axis.get_xlim()[1]
735 |
736 | self.bf_llcorners.append((lldx, lldy))
737 | self.bf_urcorners.append((urdx, urdy))
738 | self.bf_llaxis.append(ll_axis)
739 | self.bf_uraxis.append(ur_axis)
740 |
741 | ll_corner = self._convert_coords(ll_axis, (lldx, lldy))
742 | ur_corner = self._convert_coords(ur_axis, (urdx, urdy))
743 |
744 | width, height = self._rect_dim(ur_corner, ll_corner)
745 |
746 | self.fig.patches.append(plt.Rectangle(ll_corner, width, height,
747 | zorder=zorder,
748 | transform=self.fig.transFigure,
749 | **kwargs))
750 |
751 | self.bf_patchinds.append(len(self.fig.patches) - 1)
752 |
753 | def adjust_bar_frame(self):
754 | """
755 | Bars and frames made via ``self.draw_frame()`` and ``self.draw_bar()``
756 | are drawn on figure coordinates, so these patches and the axes
757 | move relative to each other when axis limits or subplot spacings are
758 | changed.
759 |
760 | This function realigns existing bars and frames with the original data
761 | coordinates.
762 |
763 | """
764 | # Check that there are any items to adjust
765 | if len(self.bf_llcorners) > 0:
766 | for ll, ur, llax, urax, ind in zip(self.bf_llcorners,
767 | self.bf_urcorners,
768 | self.bf_llaxis,
769 | self.bf_uraxis,
770 | self.bf_patchinds):
771 | # Grab rectangle
772 | rect = self.fig.patches[ind]
773 |
774 | # Get new figure coordinates
775 | new_x, new_y = self._convert_coords(llax, ll)
776 | ur_corner = self._convert_coords(urax, ur)
777 |
778 | # Get new figure dimensions
779 | width, height = self._rect_dim(ur_corner, (new_x, new_y))
780 |
781 | rect.set_bounds(new_x, new_y, width, height)
782 |
783 | def _update_twinsides(self):
784 | """
785 | Update the sides that twinned axes appear on in the event of a
786 | change to ``self.dataside_list``.
787 |
788 | """
789 |
790 | if self.twinds is not None:
791 | self.dataside_list = self.dataside_list[:self.stackdim]
792 | for ind in self.twinds:
793 | twinside = self.alt_sides[self.dataside_list[ind]]
794 | self.dataside_list.append(twinside)
795 |
796 | def _update_total_stackdim(self):
797 | """
798 | Update the number of combined original and twinned stacked axes.
799 |
800 | """
801 |
802 | self.total_stackdim = self.stackdim + self.twin_dim
803 |
804 | def _pop_data_ax(self, subgrid, side):
805 | """
806 | Pop out data axis from row or column.
807 |
808 | Parameters
809 | ----------
810 | subgrid : list of ``Axes`` instances
811 | Row or column of ``Axes``
812 | side : string
813 | The side that the visible stacked spine is on.
814 |
815 | """
816 |
817 | data_ind = self.side_inds[side]
818 | data_ax = subgrid.pop(data_ind)
819 |
820 | return data_ind, data_ax
821 |
822 | def _replace_data_ax(self, subgrid, data_ind, data_ax):
823 | """
824 | Put data axis back in its original place in row or column.
825 |
826 | Parameters
827 | ----------
828 | subgrid : list of axes
829 | Row or column of axes
830 | data_ind : int
831 | [0|-1]. The side that the visible stacked spine is on.
832 | data_ax : ``matplotlib Axes`` instance
833 | The ``Axes`` instance from ``subgrid`` that has the
834 | visible stacked spine.
835 |
836 | """
837 |
838 | if data_ind == 0:
839 | subgrid.insert(data_ind, data_ax)
840 | else:
841 | subgrid.append(data_ax)
842 |
843 | def _make_lists(self, dim, item, choice1, choice2):
844 | """
845 | Make a list of ``choice1`` and/or ``choice2`` of length dim.
846 |
847 | Parameters
848 | ----------
849 | dim : int
850 | The length of the list, usually ``self.mainax_dim`` or
851 | ``self.total_stackdim``
852 | item : string or list of ints
853 | ['all'|'none'|list of indices].
854 | Determines whether returned ``itemlist`` is all ``choice1``
855 | ('none'), all ``choice2`` ('all'), or is
856 | ``choice1`` with ``choice2`` at given indices.
857 | choice1 : any object instance
858 | One of the items to potentially make a list of
859 | choice2 : any object instance
860 | The other item to potentially create a list of
861 |
862 | Returns
863 | -------
864 | itemlist : list
865 | List of some combination of ``choice1`` and ``choice2``.
866 |
867 | """
868 |
869 | if item == 'none':
870 | itemlist = [choice1] * dim
871 | elif item == 'all':
872 | itemlist = [choice2] * dim
873 | else:
874 | itemlist = []
875 | for i in range(0, dim):
876 | if i in item:
877 | itemlist.append(choice2)
878 | else:
879 | itemlist.append(choice1)
880 |
881 | return itemlist
882 |
883 | def _set_ticks(self, subgrid_inds, ax_inds, xy_axis, which,
884 | tick_dim, labelsize, pad, direction):
885 | """
886 | Set the x and/or y axis major and/or minor ticks at the given
887 | ``subgrid_inds``, ``ax_inds`` locations.
888 |
889 | Parameters
890 | ----------
891 | subgrid_inds : list of ints
892 | The indices of the rows (``XGrid``) or columns (``YGrid``)
893 | containing the axes that need tick parameters adjusted
894 | ax_inds : list of ints
895 | The indices of the axes within the indicated subgrids that need
896 | tick parameters adjusted
897 | xy_axis : string
898 | ['x'|'y'|'both']
899 | which : string
900 | The set of ticks to adjust. ['major'|'minor']
901 | tick_dim : tuple of ints or floats
902 | The (length, width) of ``which`` ticks.
903 | labelsize : int
904 | Tick label fontsize in points.
905 | pad : int
906 | Spacing between the tick and tick label in points.
907 | direction : string
908 | Tick direction. ['in'|'out'|'inout']
909 |
910 | """
911 |
912 | for s in subgrid_inds:
913 | for ax in ax_inds:
914 | self.axes[s][ax].tick_params(axis=xy_axis, which=which,
915 | length=tick_dim[0],
916 | width=tick_dim[1],
917 | labelsize=labelsize, pad=pad,
918 | direction=direction)
919 |
920 | def _convert_coords(self, axis, coordinates):
921 | """
922 | Convert data ``coordinates`` to axis coordinates.
923 |
924 | Parameters
925 | ----------
926 | axis : ``matplotlib Axes`` instance
927 | The axes instance used to convert data ``coordinates`` into axes
928 | and figure coordinates
929 | coordinates : tuple of floats
930 | Coordinates in the data coordinate system
931 |
932 | Returns
933 | -------
934 | fig_coords : tuple of floats
935 | ``coordinates`` translated to the figure coordinate system
936 |
937 | """
938 |
939 | # Convert to axes coordinates
940 | ax_coords = axis.transData.transform(coordinates)
941 |
942 | # Convert to figure coordinates
943 | fig_coords = self.fig.transFigure.inverted().transform(ax_coords)
944 |
945 | return fig_coords
946 |
947 | def _rect_dim(self, ur_corner, ll_corner):
948 | """
949 | Find w, h dimensions of rectange drawn in figure coordinates.
950 |
951 | """
952 |
953 | width, height = (ur - ll for ur, ll in zip(ur_corner, ll_corner))
954 |
955 | return width, height
956 |
957 | def _ratios_arelists(self, ratios):
958 | """
959 | Check if ``ratios`` are lists; rectify if not.
960 |
961 | """
962 |
963 | try:
964 | rsum = sum(ratios)
965 | except TypeError:
966 | rsum = ratios
967 | rlist = [ratios]
968 | else:
969 | rlist = ratios
970 |
971 | return rsum, rlist
972 |
--------------------------------------------------------------------------------
/trendvis/gridwrapper.py:
--------------------------------------------------------------------------------
1 | from __future__ import division, print_function, absolute_import
2 | from .xgrid_ystack import XGrid
3 | from .ygrid_xstack import YGrid
4 |
5 |
6 | def make_grid(xratios, yratios, figsize, xticks, yticks, main_axis,
7 | plotdata=None, xlim=None, ylim=None, to_twin=None,
8 | axis_shift=None, twinax_shift=None, tick_fontsize=10, **kwargs):
9 |
10 | """
11 | Build a plot with a stack of multiple y (x) axes against a main x (y) axis
12 |
13 | This is an easy grid and grid formatting set-up, but not all options are
14 | accessible via this interface.
15 |
16 | Parameters
17 | ----------
18 | xratios : int or list of ints
19 | The relative sizes of the columns. Not directly comparable
20 | to ``yratios``
21 | yratios : int or list of ints
22 | The relative sizes of the rows. Not directly comparable to ``xratios``
23 | figsize : tuple of ints or floats
24 | The figure dimensions in inches
25 | xticks : list of tuples
26 | List of (major, minor) tick mark multiples. Used to set major and
27 | minor locators. One tuple per x axis
28 | yticks : list of tuples
29 | List of (major, minor) tick mark multiples. Used to set major and
30 | minor locators. One tuple per y axis
31 | main_axis : string
32 | ['x'|'y']. The common axis that all plots will share.
33 |
34 | Keyword Arguments
35 | -----------------
36 | plotdata : list of lists of tuples
37 | Default ``None``.
38 | Tuple format: (x, y, color, {}, [ax inds within row/col])
39 | One sublist per row or column (including twins). To skip plotting on a
40 | row or column, insert empty sublist at position corresponding to
41 | the index of the row or column.
42 | xlim : list of tuples of ints and/or floats
43 | Default ``None``. List of (column, min, max).
44 | If xdim is 1, then column is ignored.
45 | Also, if only one x axis needs ``xlim``, can just pass the tuple
46 | ylim : List of tuples of ints and/or flaots
47 | Default ``None``. List of (row, min, max).
48 | If ydim is 1, then row is ignored.
49 | Also, if only one y axis needs ``ylim``, can just pass a tuple.
50 | to_twin : list of ints
51 | Default ``None``. The indices of the rows (columns) to twin
52 | axis_shift : float or list of floats
53 | Default ``None``. Universal (float) or individual (list of floats)
54 | original axis spine relative shift. Units are fraction of figure
55 | twinax_shift : float or list of floats
56 | Default ``None``. Universal (float) or individual (list of floats)
57 | twinned axis spine relative shift. Units are fraction of figure
58 | tick_fontsize : int
59 | Default 10. The fontsize of tick labels.
60 |
61 | Other Parameters
62 | ----------------
63 | kwargs : passed to ``plot_data()``, then to ``axes.plot()``
64 |
65 | Returns
66 | -------
67 | grid : ``YGrid`` or ``XGrid`` instance
68 |
69 | """
70 |
71 | mainax = main_axis.lower()[0]
72 |
73 | startside_dict = {'x': 'left',
74 | 'y': 'top'}
75 |
76 | startside = startside_dict[mainax]
77 |
78 | if mainax == 'y':
79 | # Set up grid, grid formatting
80 | grid = XGrid(xratios, yratios, figsize, startside=startside,
81 | alternate_sides=True, onespine_forboth=False)
82 |
83 | elif mainax == 'x':
84 | grid = YGrid(xratios, yratios, figsize, startside=startside,
85 | alternate_sides=True, onespine_forboth=False)
86 |
87 | else:
88 | raise ValueError('main_axis arg, ' + main_axis + ' is not recognized')
89 |
90 | if to_twin is not None:
91 | grid.make_twins(to_twin)
92 |
93 | grid.set_ticknums(xticks, yticks)
94 |
95 | grid.set_ticks(labelsize=tick_fontsize)
96 |
97 | if xlim is not None:
98 | grid.set_xlim(xlim)
99 |
100 | if ylim is not None:
101 | grid.set_ylim(ylim)
102 |
103 | if axis_shift is not None or twinax_shift is not None:
104 | grid.set_relative_axshift(axis_shift=axis_shift,
105 | twin_shift=twinax_shift)
106 | grid.set_absolute_axshift()
107 | grid.move_spines()
108 |
109 | grid.set_spinewidth(2)
110 |
111 | grid.cleanup_grid()
112 |
113 | if plotdata is not None:
114 | plot_data(grid, plotdata, **kwargs)
115 |
116 | return grid
117 |
118 |
119 | def plot_data(grid, plotdata, auto_spinecolor=True, marker='o', ls='-',
120 | zorder=10, lw=1, **kwargs):
121 | """
122 | Easy way to plot a lot of line data at once. Other plotting calls
123 | can be made by accessing individual axes in ``grid.axes``.
124 |
125 | Parameters
126 | ----------
127 | grid : ``XGrid`` or ``YGrid`` instance
128 | The Grid of axes on which to plot.
129 | plotdata : list of lists of tuples
130 | Default ``None``.
131 | Tuple format: (x, y, color, [ax inds within row/col])
132 | One sublist per row or column (including twins). To skip plotting on a
133 | row or column, insert empty sublist at position corresponding to
134 | the index of the row or column.
135 |
136 | Keyword Arguments
137 | -----------------
138 | auto_spinecolor : Boolean
139 | If ``True``, will color each stacked axis spines and ticks with
140 | the color of the first plot on the axis.
141 | marker : string
142 | Default 'o'. Any ``matplotlib`` marker.
143 | ls : string
144 | Default '-'. Any ``matplotlib`` linestyle.
145 | zorder : int
146 | Default 10. The zorder of the plot.
147 | lw : string
148 | Default 1. Linewidth in points.
149 |
150 | Other Parameters
151 | ----------------
152 | kwargs : passed to ``axes.plot()``
153 |
154 | """
155 |
156 | for subgrid, subgrid_data in zip(grid.axes, plotdata):
157 | for ax_ind in range(0, grid.mainax_dim):
158 | for dataset in subgrid_data:
159 | try:
160 | if ax_ind in dataset[3]:
161 | subgrid[ax_ind].plot(dataset[0], dataset[1], lw=lw,
162 | color=dataset[2], marker=marker,
163 | ls=ls, zorder=zorder, **kwargs)
164 | except:
165 | subgrid[ax_ind].plot(dataset[0], dataset[1],
166 | color=dataset[2], marker=marker,
167 | zorder=zorder, lw=lw, ls=ls, **kwargs)
168 | if auto_spinecolor:
169 | grid.autocolor_spines(0)
170 |
--------------------------------------------------------------------------------
/trendvis/testing.py:
--------------------------------------------------------------------------------
1 | from matplotlib.testing.decorators import image_comparison as _ic
2 | from functools import partial
3 |
4 | image_comparison = partial(
5 | _ic,
6 | extensions=["png"],
7 | tol=22,
8 | style=["classic", "_classic_test_patch", {"figure.figsize": (10, 10)}],
9 | )
10 |
--------------------------------------------------------------------------------
/trendvis/tests/baseline_images/test_trendvis/xgrid_clean.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matplotlib/trendvis/de646e14b143122df79395af9eb7382c526a9910/trendvis/tests/baseline_images/test_trendvis/xgrid_clean.png
--------------------------------------------------------------------------------
/trendvis/tests/baseline_images/test_trendvis/xgrid_cutout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matplotlib/trendvis/de646e14b143122df79395af9eb7382c526a9910/trendvis/tests/baseline_images/test_trendvis/xgrid_cutout.png
--------------------------------------------------------------------------------
/trendvis/tests/baseline_images/test_trendvis/xgrid_frame.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matplotlib/trendvis/de646e14b143122df79395af9eb7382c526a9910/trendvis/tests/baseline_images/test_trendvis/xgrid_frame.png
--------------------------------------------------------------------------------
/trendvis/tests/baseline_images/test_trendvis/xgrid_multitwin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matplotlib/trendvis/de646e14b143122df79395af9eb7382c526a9910/trendvis/tests/baseline_images/test_trendvis/xgrid_multitwin.png
--------------------------------------------------------------------------------
/trendvis/tests/baseline_images/test_trendvis/xgrid_twins.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matplotlib/trendvis/de646e14b143122df79395af9eb7382c526a9910/trendvis/tests/baseline_images/test_trendvis/xgrid_twins.png
--------------------------------------------------------------------------------
/trendvis/tests/baseline_images/test_trendvis/xgrid_ylabels.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matplotlib/trendvis/de646e14b143122df79395af9eb7382c526a9910/trendvis/tests/baseline_images/test_trendvis/xgrid_ylabels.png
--------------------------------------------------------------------------------
/trendvis/tests/baseline_images/test_trendvis/ygrid_clean.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matplotlib/trendvis/de646e14b143122df79395af9eb7382c526a9910/trendvis/tests/baseline_images/test_trendvis/ygrid_clean.png
--------------------------------------------------------------------------------
/trendvis/tests/baseline_images/test_trendvis/ygrid_cutout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matplotlib/trendvis/de646e14b143122df79395af9eb7382c526a9910/trendvis/tests/baseline_images/test_trendvis/ygrid_cutout.png
--------------------------------------------------------------------------------
/trendvis/tests/baseline_images/test_trendvis/ygrid_frame.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matplotlib/trendvis/de646e14b143122df79395af9eb7382c526a9910/trendvis/tests/baseline_images/test_trendvis/ygrid_frame.png
--------------------------------------------------------------------------------
/trendvis/tests/baseline_images/test_trendvis/ygrid_multitwin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matplotlib/trendvis/de646e14b143122df79395af9eb7382c526a9910/trendvis/tests/baseline_images/test_trendvis/ygrid_multitwin.png
--------------------------------------------------------------------------------
/trendvis/tests/baseline_images/test_trendvis/ygrid_twins.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matplotlib/trendvis/de646e14b143122df79395af9eb7382c526a9910/trendvis/tests/baseline_images/test_trendvis/ygrid_twins.png
--------------------------------------------------------------------------------
/trendvis/tests/baseline_images/test_trendvis/ygrid_xlabels.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matplotlib/trendvis/de646e14b143122df79395af9eb7382c526a9910/trendvis/tests/baseline_images/test_trendvis/ygrid_xlabels.png
--------------------------------------------------------------------------------
/trendvis/tests/test_trendvis.py:
--------------------------------------------------------------------------------
1 | from __future__ import division, absolute_import, print_function
2 |
3 | """
4 | TrendVis testing
5 |
6 | ???
7 |
8 | """
9 |
10 | import trendvis
11 |
12 | from trendvis.testing import image_comparison
13 |
14 | import matplotlib.pyplot as plt
15 |
16 | # baseline images use the old default figsize of 10x10 inches.
17 | plt.rcParams["figure.figsize"] = "10, 10"
18 |
19 |
20 | def test_xgrid_init():
21 | trendvis.XGrid([2, 3, 1, 2])
22 |
23 |
24 | def test_ygrid_init():
25 | trendvis.YGrid([2, 3, 1, 2])
26 |
27 |
28 | @image_comparison(baseline_images=["xgrid_clean"])
29 | def test_xgrid_clean():
30 | testgrid = trendvis.XGrid([2, 3, 1, 2])
31 | testgrid.cleanup_grid()
32 |
33 |
34 | @image_comparison(baseline_images=["ygrid_clean"])
35 | def test_ygrid_clean():
36 | testgrid = trendvis.YGrid([4, 6])
37 | testgrid.cleanup_grid()
38 |
39 |
40 | @image_comparison(baseline_images=["xgrid_twins"])
41 | def test_xgrid_twins():
42 | testgrid = trendvis.XGrid([2, 5, 1], alternate_sides=False, xratios=[2, 1])
43 | testgrid.make_twins([0, 2])
44 | testgrid.cleanup_grid()
45 |
46 |
47 | @image_comparison(baseline_images=["ygrid_twins"])
48 | def test_ygrid_twins():
49 | testgrid = trendvis.YGrid([2, 2, 1], alternate_sides=False, yratios=[2, 1])
50 | testgrid.make_twins([0, 2])
51 | testgrid.cleanup_grid()
52 |
53 |
54 | @image_comparison(baseline_images=["xgrid_multitwin"])
55 | def test_xgrid_multitwin():
56 | testgrid = trendvis.XGrid([2, 1])
57 | testgrid.make_twins([0, 1])
58 | testgrid.make_twins(3)
59 |
60 |
61 | @image_comparison(baseline_images=["ygrid_multitwin"])
62 | def test_ygrid_multitwin():
63 | testgrid = trendvis.YGrid([2, 1])
64 | testgrid.make_twins([0, 1])
65 | testgrid.make_twins(3)
66 |
67 |
68 | def test_get_axes_xgrid():
69 | testgrid = trendvis.XGrid([2, 1])
70 | testgrid.make_twins([0])
71 |
72 | assert testgrid.get_axis(0, is_twin=True) == testgrid.axes[2][0]
73 |
74 |
75 | def test_get_axes_ygrid():
76 | testgrid = trendvis.YGrid([2, 1])
77 | testgrid.make_twins([0])
78 |
79 | assert testgrid.get_axis(0, is_twin=True) == testgrid.axes[2][0]
80 |
81 |
82 | #
83 | # def test_get_twin_rownum():
84 |
85 |
86 | #
87 | # def test_get_twin_colnum():
88 |
89 |
90 | def test_reverse_axes_xgrid():
91 | testgrid = trendvis.XGrid([2, 1])
92 | lim0 = testgrid.fig.axes[0].get_ylim()
93 | lim1 = testgrid.fig.axes[1].get_ylim()
94 | mainlim = testgrid.fig.axes[0].get_xlim()
95 |
96 | testgrid.reverse_yaxis()
97 | testgrid.reverse_xaxis()
98 |
99 | assert lim0 == testgrid.fig.axes[0].get_ylim()[::-1]
100 | assert lim1 == testgrid.fig.axes[1].get_ylim()[::-1]
101 | assert mainlim == testgrid.fig.axes[1].get_xlim()[::-1]
102 |
103 |
104 | def test_reverse_axes_ygrid():
105 | testgrid = trendvis.YGrid([2, 1])
106 | lim0 = testgrid.fig.axes[0].get_xlim()
107 | lim1 = testgrid.fig.axes[1].get_xlim()
108 | mainlim = testgrid.fig.axes[0].get_ylim()
109 |
110 | testgrid.reverse_xaxis()
111 | testgrid.reverse_yaxis()
112 |
113 | assert lim0 == testgrid.fig.axes[0].get_xlim()[::-1]
114 | assert lim1 == testgrid.fig.axes[1].get_xlim()[::-1]
115 | assert mainlim == testgrid.fig.axes[1].get_ylim()[::-1]
116 |
117 |
118 | @image_comparison(baseline_images=["xgrid_cutout"])
119 | def test_xgrid_cutout():
120 | testgrid = trendvis.XGrid([1], xratios=[1, 2])
121 | testgrid.cleanup_grid()
122 | testgrid.draw_cutout()
123 |
124 |
125 | @image_comparison(baseline_images=["ygrid_cutout"])
126 | def test_ygrid_cutout():
127 | testgrid = trendvis.YGrid([1], yratios=[1, 2])
128 | testgrid.cleanup_grid()
129 | testgrid.draw_cutout()
130 |
131 |
132 | @image_comparison(baseline_images=["xgrid_ylabels"])
133 | def test_xgrid_ylabels():
134 | testgrid = trendvis.XGrid([1, 2])
135 | testgrid.set_ylabels(["0", "1"])
136 |
137 |
138 | @image_comparison(baseline_images=["ygrid_xlabels"])
139 | def test_ygrid_xlabels():
140 | testgrid = trendvis.YGrid([1, 2, 1])
141 | testgrid.set_xlabels(["0", "1", None])
142 |
143 |
144 | @image_comparison(baseline_images=["xgrid_frame"])
145 | def test_xgrid_frame():
146 | testgrid = trendvis.XGrid([1, 2, 1], xratios=[1, 1])
147 | testgrid.cleanup_grid()
148 | testgrid.draw_frame()
149 |
150 |
151 | @image_comparison(baseline_images=["ygrid_frame"])
152 | def test_ygrid_frame():
153 | testgrid = trendvis.YGrid([1, 2, 1], yratios=[1, 1])
154 | testgrid.cleanup_grid()
155 | testgrid.draw_frame()
156 |
157 |
158 | if __name__ == "__main__":
159 | import nose
160 | import sys
161 |
162 | nose.main(addplugins=[KnownFailure()])
163 |
164 | args = ["-s", "--with-doctest"]
165 | argv = sys.argv
166 | argv = argv[:1] + args + argv[1:]
167 | nose.runmodule(argv=argv, exit=False)
168 |
--------------------------------------------------------------------------------
/trendvis/xgrid_ystack.py:
--------------------------------------------------------------------------------
1 | from __future__ import division, print_function, absolute_import
2 | import matplotlib.pyplot as plt
3 | from matplotlib.ticker import FormatStrFormatter
4 | from .gridclass import Grid
5 |
6 |
7 | class XGrid(Grid):
8 | """
9 | Construct a plot with the x axis as the main axis and a stack of y axes.
10 |
11 | """
12 |
13 | def __init__(self, ystack_ratios, xratios=1, figsize=None,
14 | startside='left', alternate_sides=True,
15 | onespine_forboth=False, **kwargs):
16 | """
17 | Initialize X_Grid
18 |
19 | Parameters
20 | ----------
21 | ystack_ratiosratios : int or list of ints
22 | The relative sizes of the rows. Not directly comparable
23 | to ``xratios``
24 | xratios : int or list of ints
25 | Default 1. The relative sizes of the main axis column(s).
26 | Not directly comparable to ``ystack_ratios``
27 | figsize : tuple of ints or floats
28 | Default None. The figure dimensions in inches.
29 | If not provided, defaults to matplotlib rc figure.figsize.
30 | startside : string
31 | Default 'left'. ['left'|'right']. The side the topmost y axis
32 | will be on.
33 | alternate_sides : Boolean
34 | Default ``True``. [True|False].
35 | Stacked axis spines alternate sides or are all on ``startside``.
36 | onespine_forboth : Boolean
37 | Default ``False``. [True|False]. If the plot stack is only 1 row,
38 | then both main axis spines can be visible (``False``),
39 | or only the bottom spine (``True``).
40 | **kwargs
41 | Any plt.figure arguments. Passed to Grid.__init__(),
42 | plt.figure()
43 |
44 | """
45 |
46 | # Initialize parent class
47 | # Last arg is True because mainax_x
48 | Grid.__init__(self, xratios, ystack_ratios, True, figsize, **kwargs)
49 |
50 | # Set initial x and y grid positions (top left)
51 | xpos = 0
52 | ypos = 0
53 |
54 | # Create axes row by row
55 | for rowspan in self.yratios:
56 | row = []
57 |
58 | for c, colspan in enumerate(self.xratios):
59 | sharex = None
60 | sharey = None
61 |
62 | # All axes in a row share y axis with first axis in row
63 | if xpos > 0:
64 | sharey = row[0]
65 |
66 | # All axes in a column share x axis with first axis in column
67 | if ypos > 0:
68 | sharex = self.axes[0][c]
69 |
70 | ax = plt.subplot2grid((self.gridrows, self.gridcols),
71 | (ypos, xpos), rowspan=rowspan,
72 | colspan=colspan, sharey=sharey,
73 | sharex=sharex)
74 |
75 | ax.patch.set_visible(False)
76 |
77 | row.append(ax)
78 | xpos += colspan
79 |
80 | self.axes.append(row)
81 |
82 | # Reset x position to left side, move to next y position
83 | xpos = 0
84 | ypos += rowspan
85 |
86 | for ax in self.axes[0]:
87 | ax.xaxis.set_label_position('top')
88 |
89 | self.set_dataside(startside, alternate_sides)
90 | self.set_stackposition(onespine_forboth)
91 |
92 | def make_twins(self, rows_to_twin):
93 | """
94 | Twin rows
95 |
96 | Parameters
97 | ----------
98 | rows_to_twin : int or list of ints
99 | Indices of the row or rows to twin
100 |
101 | """
102 |
103 | try:
104 | new_twin_dim = len(rows_to_twin)
105 | except TypeError:
106 | new_twin_dim = 1
107 | rows_to_twin = [rows_to_twin]
108 |
109 | if self.twinds is None:
110 | self.twinds = rows_to_twin
111 | self.twin_dim = new_twin_dim
112 | else:
113 | self.twinds.extend(rows_to_twin)
114 | self.twin_dim += new_twin_dim
115 |
116 | self._update_total_stackdim()
117 |
118 | for ind in rows_to_twin:
119 |
120 | twin_row = []
121 |
122 | for ax in self.axes[ind]:
123 | twin = ax.twinx()
124 | ax.set_zorder(twin.get_zorder() + 1)
125 | twin_row.append(twin)
126 |
127 | twinside = self.alt_sides[self.dataside_list[ind]]
128 | self.dataside_list.append(twinside)
129 | self.stackpos_list.append('none')
130 |
131 | # Make the y-axes shared
132 | if len(twin_row) > 1:
133 | twin_row[0].get_shared_y_axes().join(*twin_row)
134 |
135 | self.axes.append(twin_row)
136 |
137 | self.grid_isclean = False
138 |
139 | def adjust_spacing(self, hspace, adjust_bar_frame=True):
140 | """
141 | Adjust the vertical spacing between rows.
142 |
143 | Parameters
144 | ----------
145 | hspace : float
146 | Spacing between rows
147 | adjust_bar_frame : Boolean
148 | Default True. Realign ``matplotlib Rectangle patches``
149 | made via ``self.draw_bar`` and ``self.draw_frame``.
150 |
151 | """
152 |
153 | self.fig.subplots_adjust(hspace=hspace)
154 |
155 | if adjust_bar_frame:
156 | self.adjust_bar_frame()
157 |
158 | def cleanup_grid(self):
159 | """
160 | Remove unnecessary spines from grid
161 |
162 | """
163 |
164 | if not self.grid_isclean:
165 |
166 | for row, dataside, stackpos in zip(self.axes, self.dataside_list,
167 | self.stackpos_list):
168 |
169 | # Get mainax tick labelling settings
170 | ltop, lbottom = self.mainax_ticks[stackpos]
171 |
172 | # Set mainax tick parameters and positions
173 | for ax in row:
174 | ax.xaxis.set_tick_params(labeltop=ltop,
175 | labelbottom=lbottom)
176 | ax.xaxis.set_ticks_position(stackpos)
177 |
178 | data_ind, data_ax = self._pop_data_ax(row, dataside)
179 |
180 | # Set tick marks and label position, spines
181 | data_ax.yaxis.set_ticks_position(dataside)
182 |
183 | for sp in self.spine_begone[stackpos][dataside]:
184 | data_ax.spines[sp].set_visible(False)
185 |
186 | for ax in row:
187 | # Remove tick marks, tick labels
188 | ax.yaxis.set_ticks_position('none')
189 | ax.yaxis.set_tick_params(labelright='off', labelleft='off')
190 |
191 | # Remove spines
192 | for sp in self.spine_begone[stackpos]['none']:
193 | ax.spines[sp].set_visible(False)
194 |
195 | self._replace_data_ax(row, data_ind, data_ax)
196 |
197 | self.grid_isclean = True
198 |
199 | def get_axis(self, ypos, xpos=0, is_twin=False, twinstance=0):
200 | """
201 | Get axis at a particular x, y location.
202 |
203 | If a twin is desired, then there are two options:
204 | 1. Set ``ypos`` to actual storage position in ``self.axes``
205 | Can find twin ``ypos`` via ``self.get_twin_rownum()``
206 | 2. Set ``ypos`` to the physical position in ``XGrid``, set
207 | ``is_twin``=``True``, and if there is more than one twin at that
208 | location, set ``twinstance`` to indicate desired twin (e.g.
209 | 0 indicates the first twin to be created in that row position).
210 |
211 | For original axes, storage position and physical position are the same,
212 | except if twins exist and negative ``ypos`` indices are used.
213 |
214 | Parameters
215 | ----------
216 | ypos : int
217 | The row that the axis is located in
218 | xpos : int
219 | Default 0. The column the axis is in
220 | is_twin : Boolean
221 | Default ``False``. If ``is_twin``, ``self.get_axis()`` will grab
222 | the twin at the given physical ``xpos``, ``ypos`` rather than the
223 | original axis.
224 | twinstance : int
225 | Default 0. If there is more than one twin at ``xpos``, ``ypos``,
226 | then this will indicate which twin to grab
227 |
228 | Returns
229 | -------
230 | ax : ``Axes`` instance
231 | ``matplotlib Axes`` instance at the given ``xpos``, ``ypos``,
232 | (``twinstance``)
233 |
234 | """
235 |
236 | if is_twin:
237 | # ypos corresponds to twind(s), which are in a particular order
238 | # Get indices of where ypos appears in the list of twins
239 | twindices = [i for i, tw in enumerate(self.twinds) if tw == ypos]
240 |
241 | # Get position of desired instance of ypos
242 | which_twin = twindices[twinstance]
243 |
244 | # New ypos is the original axis count, plus the location of twin
245 | ypos = self.stackdim + which_twin
246 |
247 | # Subgrid (row, y), ax (col, x)
248 | ax = self.axes[ypos][xpos]
249 |
250 | return ax
251 |
252 | def get_twin_rownum(self, ypos, twinstance=None):
253 | """
254 | Original axes are easily located by row number in ``self.axes``.
255 | If there are multiple twins, finding those in ``self.axes`` may be
256 | difficult, esp. if twins were created haphazardly.
257 |
258 | This prints the index required by ``self.axes`` to fetch the
259 | twin row.
260 |
261 | Parameters
262 | ----------
263 | ypos : int
264 | The row that was twinned
265 | twinstance : int
266 | Default ``None``, print all twin row indices at ``ypos``.
267 | Indicates which twin row index to print
268 |
269 | """
270 |
271 | twindices = [i for i, tw in enumerate(self.twinds) if tw == ypos]
272 |
273 | if twinstance is None and len(twindices) > 1:
274 | newypos = [i + self.stackdim for i in twindices]
275 | elif twinstance is None:
276 | newypos = [self.stackdim + twindices[0]]
277 | else:
278 | newypos = [self.stackdim + twindices[twinstance]]
279 |
280 | print('The twin(s) of row ' + str(ypos) + ' are stored in ' +
281 | '`self.axes` as row(s):')
282 |
283 | for ny in newypos:
284 | print(ny)
285 |
286 | def set_all_ticknums(self, xticks, yticks, logxscale='none',
287 | logyscale='none'):
288 | """
289 | Set the y and x axis scales, the y and x axis ticks (if linear), and
290 | the tick number format. Wrapper around ``Grid.set_yaxis_ticknum()``,
291 | ``Grid.set_xaxis_ticknum()``.
292 |
293 | Parameters
294 | ----------
295 | xticks : list of tuples
296 | List of (major, minor) tick mark multiples. Used to set major and
297 | minor locators. One tuple per main axis.
298 | Use ``None`` to skip setting a major, minor ``MultipleLocator``
299 | for an axis
300 | yticks : list of tuples
301 | List of (major, minor) tick mark multiples. Used to set major and
302 | minor locators. One tuple per y axis (original stack + twins).
303 | Use ``None`` to skip setting a major, minor ``MultipleLocator``
304 | for an axis
305 | logxscale : string or list of ints
306 | Default 'none'. ['none'|'all'|list of x-axis indices].
307 | Indicate which x axes should be log scaled instead of linear.
308 | logyscale : string or list of ints
309 | Default 'none'. ['none'|'all'|list of y-axis indices].
310 | Indicate which y axes should be log scaled instead of linear.
311 |
312 | """
313 |
314 | if len(xticks) != self.mainax_dim:
315 | raise ValueError('xticks provided for ' + str(len(xticks)) + '/' +
316 | str(self.mainax_dim) + ' x-axes')
317 | if len(yticks) != self.total_stackdim:
318 | raise ValueError('yticks provided for ' + str(len(yticks)) + '/' +
319 | str(self.total_stackdim) + ' y-axes')
320 |
321 | xscale = self._make_lists(self.mainax_dim, logxscale, 'linear', 'log')
322 | yscale = self._make_lists(self.total_stackdim, logyscale,
323 | 'linear', 'log')
324 |
325 | for row, yt, ysc in zip(self.axes, yticks, yscale):
326 | for ax, xt, xsc in zip(row, xticks, xscale):
327 |
328 | if yt is not None or ysc == 'log':
329 | self.set_yaxis_ticknum(ax, yt, scale=ysc)
330 | if xt is not None or xsc == 'log':
331 | self.set_xaxis_ticknum(ax, xt, scale=xsc)
332 |
333 | def ticknum_format(self, ax='all', xformatter='%d', yformatter='%d'):
334 | """
335 | Set tick number formatters for x and/or y axes.
336 |
337 | Parameters
338 | ----------
339 | ax : string or axes instance
340 | Default 'all', cycle through axes and set formatters.
341 | If axes instance, will only set x and/or y formatter of that axes
342 | instance. Can acquire axis using ``self.get_axis()``
343 | xformatter : string or list of strings
344 | Default '%d'. String formatting magic to apply to all x axes
345 | Can use ``None`` to skip setting ``xformatter``
346 | yformatter : string or list of strings
347 | Default '%d'. String formatting magic to apply to all y axes
348 | (string) or individual y axes (list of strings,
349 | length = ``self.total_stackdim``). Can use ``None`` to
350 | skip setting ``yformatter``, or insert ``None`` into list to
351 | skip setting formatter for a particular axis row
352 |
353 | """
354 |
355 | if ax != 'all':
356 | if xformatter is not None:
357 | xfrmttr = FormatStrFormatter(xformatter)
358 | ax.xaxis.set_major_formatter(xfrmttr)
359 | if yformatter is not None:
360 | yfrmttr = FormatStrFormatter(yformatter)
361 | ax.yaxis.set_major_formatter(xfrmttr)
362 |
363 | else:
364 | if yformatter is not None:
365 | if type(yformatter) is str:
366 | yfrmttr = FormatStrFormatter(yformatter)
367 | for row in self.axes:
368 | for ax in row:
369 | ax.yaxis.set_major_formatter(yfrmttr)
370 | else:
371 | for yf, row in zip(yformatter, self.axes):
372 | if yf is not None:
373 | yfrmttr = FormatStrFormatter(yf)
374 | for ax in row:
375 | ax.yaxis.set_major_formatter(yfrmttr)
376 |
377 | if xformatter is not None:
378 | xfrmttr = FormatStrFormatter(xformatter)
379 | for row in self.axes:
380 | for ax in row:
381 | ax.xaxis.set_major_formatter(xfrmttr)
382 |
383 | def reverse_yaxis(self, reverse_y='all', adjust_bar_frame=True):
384 | """
385 | Reverse all or any y axis.
386 |
387 | Parameters
388 | ----------
389 | reverse_y : string or list of ints
390 | Default 'all'. 'all' or list of indices of the y axes to be
391 | reversed accepted. If unsure of index for a twin y axis in
392 | ``self.axes``, find using ``self.get_twin_rownum()``
393 | adjust_bar_frame : Boolean
394 | Default True. Realign ``matplotlib Rectangle patches``
395 | made via ``self.draw_bar`` and ``self.draw_frame``.
396 |
397 | """
398 |
399 | if reverse_y == 'all':
400 | reverse_y = range(0, self.total_stackdim)
401 |
402 | # Invert yaxis of first axis in each row
403 | for r in reverse_y:
404 | self.axes[r][0].invert_yaxis()
405 |
406 | if adjust_bar_frame:
407 | self.adjust_bar_frame()
408 |
409 | def reverse_xaxis(self, reverse_x='all', adjust_bar_frame=True):
410 | """
411 | Reverse all or any x axis.
412 |
413 | Parameters
414 | ----------
415 | reverse_x : string or list of ints
416 | Default 'all'. 'all' or list of indices of the x axes to be
417 | reversed accepted. If unsure of index for a twin y axis in
418 | ``self.axes``, find using ``self.get_twin_rownum()``
419 | adjust_bar_frame : Boolean
420 | Default True. Realign ``matplotlib Rectangle patches``
421 | made via ``self.draw_bar`` and ``self.draw_frame``.
422 |
423 | """
424 |
425 | if reverse_x == 'all':
426 | reverse_x = range(0, self.mainax_dim)
427 |
428 | # Invert x axis of each axis in first row
429 | for r in reverse_x:
430 | self.axes[0][r].invert_xaxis()
431 |
432 | if adjust_bar_frame:
433 | self.adjust_bar_frame()
434 |
435 | def set_ylim(self, ylim, adjust_bar_frame=True):
436 | """
437 | Set y limits.
438 |
439 | Parameters
440 | ----------
441 | ylim : list of tuples of ints and/or floats
442 | List of (row, min, max). If ydim is 1, then row is ignored.
443 | Also, if only one y axis needs ``ylim``, can just pass the tuple.
444 | If unsure of row index for a twin y axis in ``self.axes``,
445 | find using ``self.get_twin_rownum()``
446 | adjust_bar_frame : Boolean
447 | Default True. Realign ``matplotlib Rectangle patches``
448 | made via ``self.draw_bar`` and ``self.draw_frame``.
449 |
450 | """
451 |
452 | if self.total_stackdim == 1:
453 | try:
454 | ylim.extend([])
455 | except AttributeError:
456 | pass
457 | else:
458 | ylim = ylim[0]
459 |
460 | for row in self.axes:
461 | for ax in row:
462 | ax.set_ylim(ylim[-2], ylim[-1])
463 |
464 | else:
465 | try:
466 | ylim.extend([])
467 | except AttributeError:
468 | ylim = [ylim]
469 |
470 | for yl in ylim:
471 | for ax in self.axes[yl[0]]:
472 | ax.set_ylim(yl[1], yl[2])
473 |
474 | if adjust_bar_frame:
475 | self.adjust_bar_frame()
476 |
477 | def set_xlim(self, xlim, adjust_bar_frame=True):
478 | """
479 | Set x limits.
480 |
481 | Parameters
482 | ----------
483 | xlim : List of tuples of ints and/or flaots
484 | List of (column, min, max). If xdim is 1, then column is ignored.
485 | Also, if only one x axis needs ``xlim``, can just pass a tuple.
486 | adjust_bar_frame : Boolean
487 | Default True. Realign ``matplotlib Rectangle patches``
488 | made via ``self.draw_bar`` and ``self.draw_frame``.
489 |
490 | """
491 |
492 | if self.mainax_dim == 1:
493 | try:
494 | xlim.extend([])
495 | except AttributeError:
496 | pass
497 | else:
498 | xlim = xlim[0]
499 |
500 | for row in self.axes:
501 | for ax in row:
502 | ax.set_xlim(xlim[-2], xlim[-1])
503 |
504 | else:
505 | try:
506 | xlim.extend([])
507 | except AttributeError:
508 | xlim = [xlim]
509 |
510 | for xl in xlim:
511 | for row in self.axes:
512 | row[xl[0]].set_xlim(xl[1], xl[2])
513 |
514 | if adjust_bar_frame:
515 | self.adjust_bar_frame()
516 |
517 | def set_ticks(self, row='all', column='all', xy_axis='both', which='both',
518 | major_dim=(6, 2), minor_dim=(4, 1), labelsize=10, pad=10,
519 | major_dir='out', minor_dir='out'):
520 | """
521 | Set x and/or y axis ticks for all or specified axes.
522 |
523 | Does not set axis color.
524 |
525 | Parameters
526 | ----------
527 | row : string or list of ints
528 | Default 'all'. The rows containing the axes that need tick
529 | parameters adjusted, 'all' or list of indices. If unsure of row
530 | index for a twin y axis in ``self.axes``, find using
531 | ``self.get_twin_rownum()``
532 | column: string or list of ints
533 | Default 'all'. The columns containing the axes that need tick
534 | parameters adjusted, 'all' or list of indices
535 | xy_axis : string
536 | Default 'both'. ['x'|'y'|'both']
537 | which : string
538 | Default 'both'. ['major'|'minor'|'both'], the set of ticks
539 | to adjust.
540 | major_dim : tuple of ints or floats
541 | Default (6, 2). The (length, width) of the major ticks.
542 | minor_dim : tuple of ints or floats
543 | Default (4, 1). The (length, width) of the minor ticks.
544 | labelsize : int
545 | Default 10. Tick label fontsize in points.
546 | pad : int
547 | Default 10. Spacing between the tick and tick label in points.
548 | major_tickdir : string
549 | Default 'out'. ['out'|'in'|'inout']. The major tick direction.
550 | minor_tickdir : string
551 | Default 'out'. ['out'|'in'|'inout']. The minor tick direction.
552 |
553 | """
554 |
555 | if row == 'all':
556 | row = range(0, self.total_stackdim)
557 | if column == 'all':
558 | column = range(0, self.mainax_dim)
559 |
560 | if which != 'major':
561 | Grid._set_ticks(self, row, column, xy_axis, 'minor', minor_dim,
562 | labelsize, pad, minor_dir)
563 |
564 | if which != 'minor':
565 | Grid._set_ticks(self, row, column, xy_axis, 'major', major_dim,
566 | labelsize, pad, major_dir)
567 |
568 | def draw_cutout(self, di=0.025, lw='default', **kwargs):
569 | """
570 | Draw cut marks to signify broken x axes.
571 |
572 | Only drawn when ``self.mainax_dim`` > 1.
573 |
574 | Parameters
575 | ----------
576 | di : float
577 | Default 0.025. The dimensions of the cutout mark as a
578 | fraction of the smallest axis length.
579 | lw : int
580 | Default 'default'. If 'default', ``lw = self.spinewidth``.
581 | **kwargs
582 | Passed to ``axes.plot()``. Any valid ``kwargs``.
583 |
584 | """
585 |
586 | if self.mainax_dim > 1:
587 |
588 | # Adjust di so that cutouts will look exactly the same
589 | # on every axis, no matter their relative sizes
590 | minx = min(self.xratios)
591 | x = [di * (minx / x) for x in self.xratios]
592 |
593 | miny = min(self.yratios)
594 | y0 = di * (miny / self.yratios[0])
595 | y1 = di * (miny / self.yratios[-1])
596 |
597 | # Upper and lower y position
598 | upper_y = (1 - (2 * y0), 1 + (2 * y0))
599 | lower_y = (-(2 * y1), (2 * y1))
600 |
601 | low_ind = self.stackdim - 1
602 |
603 | if lw == 'default':
604 | lw = self.spinewidth
605 |
606 | top_ax = self.axes[0][0]
607 | low_ax = self.axes[low_ind][0]
608 | right = (1 - x[0], 1 + x[0])
609 |
610 | # first axes in rows, right only
611 | kwargs = dict(transform=top_ax.transAxes, clip_on=False,
612 | color='black', lw=lw, **kwargs)
613 | top_ax.plot(right, upper_y, **kwargs)
614 |
615 | kwargs.update(transform=low_ax.transAxes)
616 | low_ax.plot(right, lower_y, **kwargs)
617 |
618 | # Middle axes
619 | for i in range(1, self.mainax_dim - 1):
620 | top_ax = self.axes[0][i]
621 | low_ax = self.axes[low_ind][i]
622 | left = (-x[i], x[i])
623 | right = (1 - x[i], 1 + x[i])
624 |
625 | kwargs.update(transform=top_ax.transAxes)
626 | top_ax.plot(left, upper_y, **kwargs)
627 | top_ax.plot(right, upper_y, **kwargs)
628 |
629 | kwargs.update(transform=low_ax.transAxes)
630 | low_ax.plot(left, lower_y, **kwargs)
631 | low_ax.plot(right, lower_y, **kwargs)
632 |
633 | # Last axes in rows, left only
634 | top_ax = self.axes[0][-1]
635 | low_ax = self.axes[low_ind][-1]
636 | left = (-x[-1], x[-1])
637 |
638 | kwargs.update(transform=top_ax.transAxes)
639 | top_ax.plot(left, upper_y, **kwargs)
640 |
641 | kwargs.update(transform=low_ax.transAxes)
642 | low_ax.plot(left, lower_y, **kwargs)
643 |
644 | def set_ylabels(self, ylabels, fontsize=None, labelpad=12, **kwargs):
645 | """
646 | Tool for setting all y axis labels at once. Can skip labelling an axis
647 | by providing a ``None`` in corresponding position in list.
648 |
649 | Parameters
650 | ----------
651 | ylabels : list of strings
652 | The list of labels, one per y-axis. Insert ``None`` in list to
653 | skip an axis.
654 | fontsize : int
655 | Default None. The font size of ``ylabels``
656 | labelpad : int
657 | Default 12. The spacing between the tick labels and the axis
658 | labels.
659 | **kwargs
660 | Passed to ``axes.set_ylabel()``.
661 | Any ``matplotlib Text`` properties
662 |
663 | Notes
664 | -----
665 | Caution- this will set twin axis labels in the order that twins were
666 | created, which may not correspond to physical position in grid.
667 |
668 | """
669 |
670 | for row, side, yl in zip(self.axes, self.dataside_list, ylabels):
671 | if yl is not None:
672 | if side == 'right':
673 | row[-1].yaxis.set_label_position('right')
674 | row[-1].set_ylabel(yl, fontsize=fontsize,
675 | labelpad=labelpad, rotation=270,
676 | verticalalignment='bottom', **kwargs)
677 | else:
678 | row[0].yaxis.set_label_position('left')
679 | row[0].set_ylabel(yl, fontsize=fontsize, labelpad=labelpad,
680 | **kwargs)
681 |
--------------------------------------------------------------------------------
/trendvis/ygrid_xstack.py:
--------------------------------------------------------------------------------
1 | from __future__ import division, print_function, absolute_import
2 | import matplotlib.pyplot as plt
3 | from matplotlib.ticker import FormatStrFormatter
4 | from .gridclass import Grid
5 |
6 |
7 | class YGrid(Grid):
8 | """
9 | Construct a plot with the y axis as the main axis and a stack of x axes.
10 |
11 | """
12 |
13 | def __init__(self, xstack_ratios, yratios=1, figsize=None,
14 | startside='top', alternate_sides=True,
15 | onespine_forboth=False, **kwargs):
16 | """
17 | Initialize Y_Grid
18 |
19 | Parameters
20 | ----------
21 | xstack_ratios : int or list of ints
22 | The relative sizes of the columns. Not directly comparable
23 | to ``yratios``
24 | yratios : int or list of ints
25 | Default 1. The relative sizes of the main axis row(s).
26 | Not directly comparable to ``xstack_ratios``
27 | figsize : tuple of ints or floats
28 | Default None. The figure dimensions in inches.
29 | If not provided, defaults to matplotlib rc figure.figsize.
30 | startside : string
31 | Default 'top'. ['top'|'bottom']. The side the leftmost x axis
32 | will be on.
33 | alternate_sides : Boolean
34 | Default ``True``. [True|False].
35 | Stacked axis spines alternate sides or are all on ``startside``.
36 | onespine_forboth : Boolean
37 | Default ``False``. [True|False]. If the plot stack is only 1
38 | column, then both main axis spines can be visible (``False``),
39 | or only the left spine is visible (``True``).
40 | **kwargs
41 | Any plt.figure arguments. Passed to Grid.__init__(),
42 | plt.figure().
43 |
44 | """
45 |
46 | # Initialize parent class
47 | # Last arg is False because mainax_x
48 | Grid.__init__(self, xstack_ratios, yratios, False, figsize, **kwargs)
49 |
50 | # Set initial x and y grid positions (top left)
51 | xpos = 0
52 | ypos = 0
53 |
54 | # Create axes column by column
55 | for colspan in self.xratios:
56 | col = []
57 |
58 | for r, rowspan in enumerate(self.yratios):
59 | sharex = None
60 | sharey = None
61 |
62 | # All axes in column share x axis with first in column
63 | if ypos > 0:
64 | sharex = col[0]
65 |
66 | # All axes in row share y axis with first in row
67 | if xpos > 0:
68 | sharey = self.axes[0][r]
69 |
70 | ax = plt.subplot2grid((self.gridrows, self.gridcols),
71 | (ypos, xpos), rowspan=rowspan,
72 | colspan=colspan, sharex=sharex,
73 | sharey=sharey)
74 |
75 | ax.patch.set_visible(False)
76 |
77 | col.append(ax)
78 | ypos += rowspan
79 |
80 | self.axes.append(col)
81 |
82 | # Reset y position to top, move to next x position
83 | ypos = 0
84 | xpos += colspan
85 |
86 | for ax in self.axes[-1]:
87 | ax.yaxis.set_label_position('right')
88 |
89 | self.set_dataside(startside, alternate_sides)
90 | self.set_stackposition(onespine_forboth)
91 |
92 | def make_twins(self, cols_to_twin):
93 | """
94 | Twin columns
95 |
96 | Parameters
97 | ----------
98 | cols_to_twin : int or list of ints
99 | Indices of the column or columns to twin
100 |
101 | """
102 |
103 | try:
104 | new_twin_dim = len(cols_to_twin)
105 | except TypeError:
106 | new_twin_dim = 1
107 | cols_to_twin = [cols_to_twin]
108 |
109 | if self.twinds is None:
110 | self.twinds = cols_to_twin
111 | self.twin_dim = new_twin_dim
112 | else:
113 | self.twinds.extend(cols_to_twin)
114 | self.twin_dim += new_twin_dim
115 |
116 | self._update_total_stackdim()
117 |
118 | for ind in cols_to_twin:
119 |
120 | twin_col = []
121 |
122 | for ax in self.axes[ind]:
123 | twin = ax.twiny()
124 | ax.set_zorder(twin.get_zorder() + 1)
125 | twin_col.append(twin)
126 |
127 | twinside = self.alt_sides[self.dataside_list[ind]]
128 | self.dataside_list.append(twinside)
129 | self.stackpos_list.append('none')
130 |
131 | # Make the x axes shared
132 | if len(twin_col) > 1:
133 | twin_col[0].get_shared_x_axes().join(*twin_col)
134 |
135 | self.axes.append(twin_col)
136 |
137 | self.grid_isclean = False
138 |
139 | def adjust_spacing(self, wspace, adjust_bar_frame=True):
140 | """
141 | Adjust the horizontal spacing between columns.
142 |
143 | Parameters
144 | ----------
145 | wspace : float
146 | Spacing between columns
147 | adjust_bar_frame : Boolean
148 | Default True. Realign ``matplotlib Rectangle patches``
149 | made via ``self.draw_bar`` and ``self.draw_frame``.
150 |
151 | """
152 |
153 | self.fig.subplots_adjust(wspace=wspace)
154 |
155 | if adjust_bar_frame:
156 | self.adjust_bar_frame()
157 |
158 | def cleanup_grid(self):
159 | """
160 | Remove unnecessary spines from grid
161 |
162 | """
163 |
164 | if not self.grid_isclean:
165 |
166 | for col, dataside, stackpos in zip(self.axes, self.dataside_list,
167 | self.stackpos_list):
168 |
169 | # Get mainax tick labelling settings
170 | lleft, lright = self.mainax_ticks[stackpos]
171 |
172 | # Set mainax tick parameters and positions
173 | for ax in col:
174 | ax.yaxis.set_tick_params(labelleft=lleft,
175 | labelright=lright)
176 | ax.yaxis.set_ticks_position(stackpos)
177 |
178 | data_ind, data_ax = self._pop_data_ax(col, dataside)
179 |
180 | # Set tick marks and label position, spines
181 | data_ax.xaxis.set_ticks_position(dataside)
182 |
183 | for sp in self.spine_begone[stackpos][dataside]:
184 | data_ax.spines[sp].set_visible(False)
185 |
186 | for ax in col:
187 | # Remove tick marks, tick labels
188 | ax.xaxis.set_ticks_position('none')
189 | ax.xaxis.set_tick_params(labeltop='off', labelbottom='off')
190 |
191 | # Remove spines
192 | for sp in self.spine_begone[stackpos]['none']:
193 | ax.spines[sp].set_visible(False)
194 |
195 | self._replace_data_ax(col, data_ind, data_ax)
196 |
197 | self.grid_isclean = True
198 |
199 | def get_axis(self, xpos, ypos=0, is_twin=False, twinstance=0):
200 | """
201 | Get axis at a particular x, y position.
202 |
203 | If a twin is desired, then there are two options:
204 | 1. Set ``xpos`` to actual storage position in ``self.axes``
205 | Can find twin ``xpos`` via ``self.get_twin_colnum()``
206 | 2. Set ``xpos`` to the physical position in ``YGrid``, set
207 | ``is_twin``=``True``, and if there is more than one twin at that
208 | location, set ``twinstance`` to indicate desired twin (e.g.
209 | 0 indicates the first twin to be created in that col position).
210 |
211 | For original axes, storage position and physical position are the same,
212 | except if twins exist and negative ``xpos`` indices are used.
213 |
214 | Parameters
215 | ----------
216 | xpos : int
217 | The column that the axis is located in.
218 | ypos : int
219 | Default 0. The row the axis is in.
220 | is_twin : Boolean
221 | Default ``False``. If ``is_twin``, ``self.get_axis()`` will grab
222 | the twin at the given ``xpos``, ``ypos`` rather than the
223 | original axis.
224 | twinstance : int
225 | Default 0. If there is more than one twin at ``xpos``, ``ypos``,
226 | then this will indicate which twin to grab.
227 |
228 | Returns
229 | -------
230 | ax : ``Axes`` instance
231 | ``matplotlib Axes`` instance at the given ``xpos``, ``ypos``,
232 | (``twinstance``)
233 |
234 | """
235 |
236 | if is_twin:
237 | # xpos corresponds to twind(s), which are in a particular order
238 | # Get indices of where xpos appears in the list of twins
239 | twindices = [i for i, tw in enumerate(self.twinds) if tw == xpos]
240 |
241 | # Get position of desired instance of xpos
242 | which_twin = twindices[twinstance]
243 |
244 | # New xpos is the original axis count, plus the location of twin
245 | xpos = self.stackdim + which_twin
246 |
247 | # Subgrid (col, x), ax (row, y)
248 | ax = self.axes[xpos][ypos]
249 |
250 | return ax
251 |
252 | def get_twin_colnum(self, xpos, twinstance=None):
253 | """
254 | Original axes are easily located by column number in ``self.axes``.
255 | If there are are multiple twins, finding those in ``self.axes``
256 | may be difficult, esp. if twins were created haphazardly
257 |
258 | This prints the index required by ``self.axes`` to fetch the
259 | twin column.
260 |
261 | Parameters
262 | ----------
263 | xpos : int
264 | The column that was twinned
265 | twinstance : int
266 | Default ``None``, print all twin column indices at ``xpos``.
267 | Indicates which twin column index to print
268 |
269 | """
270 |
271 | twindices = [i for i, tw in enumerate(self.twinds) if tw == xpos]
272 |
273 | if twinstance is None and len(twindices) > 1:
274 | newxpos = [i + self.stackdim for i in twindices]
275 | elif twinstance is None:
276 | newxpos = [self.stackdim + twindices[0]]
277 | else:
278 | newxpos = [self.stackdim + twindices[twinstance]]
279 |
280 | print('The twin(s) of column ' + str(xpos) + ' are stored in ' +
281 | '`self.axes` as column(s):')
282 |
283 | for nx in newxpos:
284 | print(nx)
285 |
286 | def set_all_ticknums(self, xticks, yticks, logxscale='none',
287 | logyscale='none'):
288 | """
289 | Set the y and x axis scales, the y and x axis ticks (if linear), and
290 | the tick number format. Wrapper around ``Grid.set_yaxis_ticknum()``,
291 | ``Grid.set_xaxis_ticknum()``.
292 |
293 | Parameters
294 | ----------
295 | xticks : list of tuples
296 | List of (major, minor) tick mark multiples. Used to set major and
297 | minor locators. One tuple per x axis (original stack + twins).
298 | Use ``None`` to skip setting a major, minor ``MultipleLocator``
299 | for an axis
300 | yticks : list of tuples
301 | List of (major, minor) tick mark multiples. Used to set major and
302 | minor locators. One tuple per main axis.
303 | Use ``None`` to skip setting a major, minor ``MultipleLocator``
304 | for an axis
305 | logxscale : string or list of ints
306 | Default 'none'. ['none'|'all'|list of x-axis indices].
307 | Indicate which x axes should be log scaled instead of linear.
308 | logyscale : string or list of ints
309 | Default 'none'. ['none'|'all'|list of y-axis indices].
310 | Indicate which y axes should be log scaled instead of linear.
311 |
312 | """
313 |
314 | if len(xticks) != self.total_stackdim:
315 | raise ValueError('xticks provided for ' + str(len(xticks)) + '/' +
316 | str(self.total_stackdim) + ' x-axes')
317 | if len(yticks) != self.mainax_dim:
318 | raise ValueError('yticks provided for ' + str(len(yticks)) + '/' +
319 | str(self.mainax_dim) + ' y-axes')
320 |
321 | xscale = self._make_lists(self.total_stackdim, logxscale,
322 | 'linear', 'log')
323 | yscale = self._make_lists(self.mainax_dim, logyscale, 'linear', 'log')
324 |
325 | for col, xt, xsc in zip(self.axes, xticks, xscale):
326 | for ax, yt, ysc in zip(col, yticks, yscale):
327 |
328 | if yt is not None or ysc == 'log':
329 | self.set_yaxis_ticknum(ax, yt, scale=ysc)
330 | if xt is not None or xsc == 'log':
331 | self.set_xaxis_ticknum(ax, xt, scale=xsc)
332 |
333 | def ticknum_format(self, ax='all', xformatter='%d', yformatter='%d'):
334 | """
335 | Set tick number formatters for x and/or y axes.
336 |
337 | Parameters
338 | ----------
339 | ax : string or axes instance
340 | Default 'all', cycle through axes and set formatters.
341 | If axes instance, will only set x and/or y formatter of that axes
342 | instance. Can acquire axis using ``self.get_axis()``
343 | xformatter : string or list of strings
344 | Default '%d'. String formatting magic to apply to all x axes
345 | (string) or individual x axes (list of strings,
346 | length = ``self.total_stackdim``). Can use ``None`` to skip
347 | setting ``xformatter``, or insert ``None`` into list to skip
348 | setting ``xformatter`` for a particular axis column
349 | yformatter : string
350 | Default '%d'. String formatting magic to apply to all y axes.
351 | Can use ``None`` to skip setting ``yformatter``
352 |
353 | """
354 |
355 | if ax != 'all':
356 | if xformatter is not None:
357 | xfrmttr = FormatStrFormatter(xformatter)
358 | ax.xaxis.set_major_formatter(xfrmttr)
359 | if yformatter is not None:
360 | yfrmttr = FormatStrFormatter(yformatter)
361 | ax.yaxis.set_major_formatter(xfrmttr)
362 |
363 | else:
364 | if xformatter is not None:
365 | if type(xformatter) is str:
366 | xfrmttr = FormatStrFormatter(xformatter)
367 | for col in self.axes:
368 | for ax in col:
369 | ax.xaxis.set_major_formatter(xfrmttr)
370 | else:
371 | for xf, col in zip(xformatter, self.axes):
372 | if xf is not None:
373 | xfrmttr = FormatStrFormatter(xf)
374 | for ax in col:
375 | ax.xaxis.set_major_formatter(xfrmttr)
376 |
377 | if yformatter is not None:
378 | yfrmttr = FormatStrFormatter(yformatter)
379 | for col in self.axes:
380 | for ax in col:
381 | ax.yaxis.set_major_formatter(yfrmttr)
382 |
383 | def reverse_yaxis(self, reverse_y='all', adjust_bar_frame=True):
384 | """
385 | Reverse any or all y axes.
386 |
387 | Parameters
388 | ----------
389 | reverse_y : string or list of ints
390 | Default 'all'. 'all' or list of indices of the y axes to be
391 | reversed accepted. If unsure of index for a twin x axis in
392 | ``self.axes``, find using ``self.get_twin_colnum()``.
393 | adjust_bar_frame : Boolean
394 | Default True. Realign ``matplotlib Rectangle patches``
395 | made via ``self.draw_bar`` and ``self.draw_frame``.
396 |
397 | """
398 |
399 | if reverse_y == 'all':
400 | reverse_y = range(0, self.mainax_dim)
401 |
402 | # Invert y axis of each axis in the first column
403 | for r in reverse_y:
404 | self.axes[0][r].invert_yaxis()
405 |
406 | if adjust_bar_frame:
407 | self.adjust_bar_frame()
408 |
409 | def reverse_xaxis(self, reverse_x='all', adjust_bar_frame=True):
410 | """
411 | Reverse any or all x axes.
412 |
413 | Parameters
414 | ----------
415 | reverse_x : string or list of ints
416 | Default 'all'. 'all' or list of indices of the x axes to be
417 | reversed accepted. If unsure of index for a twin x axis in
418 | ``self.axes``, find using ``self.get_twin_colnum()``.
419 | adjust_bar_frame : Boolean
420 | Default True. Realign ``matplotlib Rectangle patches``
421 | made via ``self.draw_bar`` and ``self.draw_frame``.
422 |
423 | """
424 |
425 | if reverse_x == 'all':
426 | reverse_x = range(0, self.total_stackdim)
427 |
428 | # Invert x axis of first axis in each column
429 | for r in reverse_x:
430 | self.axes[r][0].invert_xaxis()
431 |
432 | if adjust_bar_frame:
433 | self.adjust_bar_frame()
434 |
435 | def set_xlim(self, xlim, adjust_bar_frame=True):
436 | """
437 | Set x limits.
438 |
439 | Parameters
440 | ----------
441 | xlim : list of tuples of ints and/or floats
442 | List of (column, min, max). If xdim is 1, then column is ignored.
443 | Also, if only one x axis needs ``xlim``, can just pass the tuple.
444 | If unsure of column index for a twin x axis in ``self.axes``,
445 | find using ``self.get_twin_colnum()``
446 | adjust_bar_frame : Boolean
447 | Default True. Realign ``matplotlib Rectangle patches``
448 | made via ``self.draw_bar`` and ``self.draw_frame``.
449 |
450 | """
451 |
452 | if self.total_stackdim == 1:
453 | try:
454 | xlim.extend([])
455 | except AttributeError:
456 | pass
457 | else:
458 | xlim = xlim[0]
459 |
460 | for col in self.axes:
461 | for ax in col:
462 | ax.set_xlim(xlim[-2], xlim[-1])
463 |
464 | else:
465 | try:
466 | xlim.extend([])
467 | except AttributeError:
468 | xlim = [xlim]
469 |
470 | for xl in xlim:
471 | for ax in self.axes[xl[0]]:
472 | ax.set_xlim(xl[1], xl[2])
473 |
474 | if adjust_bar_frame:
475 | self.adjust_bar_frame()
476 |
477 | def set_ylim(self, ylim, adjust_bar_frame=True):
478 | """
479 | Set y limits.
480 |
481 | Parameters
482 | ----------
483 | ylim : List of tuples of ints and/or flaots
484 | List of (row, min, max). If ydim is 1, then row is ignored.
485 | Also, if only one y axis need ``ylim``, can just pass a tuple.
486 | adjust_bar_frame : Boolean
487 | Default True. Realign ``matplotlib Rectangle patches``
488 | made via ``self.draw_bar`` and ``self.draw_frame``.
489 |
490 | """
491 |
492 | if self.mainax_dim == 1:
493 | try:
494 | ylim.extend([])
495 | except AttributeError:
496 | pass
497 | else:
498 | ylim = ylim[0]
499 |
500 | for col in self.axes:
501 | for ax in col:
502 | ax.set_ylim(ylim[-2], ylim[-1])
503 |
504 | else:
505 | try:
506 | ylim.extend([])
507 | except AttributeError:
508 | ylim = [ylim]
509 |
510 | for yl in ylim:
511 | for col in self.axes:
512 | col[yl[0]].set_ylim(yl[1], yl[2])
513 |
514 | if adjust_bar_frame:
515 | self.adjust_bar_frame()
516 |
517 | def set_ticks(self, row='all', column='all', xy_axis='both', which='both',
518 | major_dim=(6, 2), minor_dim=(4, 1), labelsize=10, pad=10,
519 | major_tickdir='out', minor_tickdir='out'):
520 | """
521 | Set x and/or y axis ticks for all or specified axes.
522 |
523 | Does not set axis color
524 |
525 | Parameters
526 | ----------
527 | row : string or list of ints
528 | Default 'all'. The rows containing the axes that need tick
529 | parameters adjusted, 'all' or list of indices
530 | column: string or list of ints
531 | Default 'all'. The columns containing the axes that need tick
532 | parameters adjusted, 'all' or list of indices. If unsure of column
533 | index for a twin x axis in ``self.axes``, find using
534 | ``self.get_twin_colnum()``
535 | xy_axis : string
536 | Default 'both'. ['x'|'y'|'both']
537 | which : string
538 | Default 'both'. ['major'|'minor'|'both'], the set of ticks
539 | to adjust.
540 | major_dim : tuple of ints or floats
541 | Default (6, 2). The (length, width) of the major ticks.
542 | minor_dim : tuple of ints or floats
543 | Default (4, 1). The (length, width) of the minor ticks.
544 | labelsize : int
545 | Default 10. Tick label fontsize.
546 | pad : int
547 | Default 10. Spacing between the tick and tick label.
548 | major_tickdir : string
549 | Default 'out'. ['out'|'in'|'inout']. The major tick direction.
550 | minor_tickdir : string
551 | Default 'out'. ['out'|'in'|'inout']. The minor tick direction.
552 |
553 | """
554 |
555 | if row == 'all':
556 | row = range(0, self.mainax_dim)
557 | if column == 'all':
558 | column = range(0, self.total_stackdim)
559 |
560 | if which != 'major':
561 | Grid._set_ticks(self, column, row, xy_axis, 'minor', minor_dim,
562 | labelsize, pad, minor_tickdir)
563 |
564 | if which != 'minor':
565 | Grid._set_ticks(self, column, row, xy_axis, 'major', major_dim,
566 | labelsize, pad, major_tickdir)
567 |
568 | def draw_cutout(self, di=0.025, lw='default', **kwargs):
569 | """
570 | Draw cut marks to signifiy broken y axes.
571 |
572 | Only drawn when ``self.mainax_dim`` > 1.
573 |
574 | Parameters
575 | ----------
576 | di : float
577 | Default 0.025. The dimensions of the cutout mark as a
578 | fraction of the smallest axis length.
579 | lw : int
580 | Default 'default'. If 'default', ``lw = self.spinewidth``.
581 | **kwargs
582 | Passed to ``axes.plot()``. Any valid ``kwargs``.
583 |
584 | """
585 |
586 | if self.mainax_dim > 1:
587 |
588 | # Adjust di so that cutouts will look exactly the same
589 | # on every axis, no matter their relative sizes
590 | miny = min(self.yratios)
591 | y = [di * (miny / y) for y in self.yratios]
592 |
593 | minx = min(self.xratios)
594 | x0 = di * (minx / self.xratios[0])
595 | x1 = di * (minx / self.xratios[-1])
596 |
597 | # Left and right x position
598 | left_x = (-(2 * x0), (2 * x0))
599 | right_x = (1 - (2 * x1), 1 + (2 * x1))
600 |
601 | right_ind = self.stackdim - 1
602 |
603 | if lw == 'default':
604 | lw = self.spinewidth
605 |
606 | l_ax = self.axes[0][0]
607 | r_ax = self.axes[right_ind][0]
608 | lower = (-y[0], y[0])
609 |
610 | # first axes in columns, lower only
611 | kwargs = dict(transform=l_ax.transAxes, clip_on=False,
612 | color='black', lw=lw, **kwargs)
613 | l_ax.plot(left_x, lower, **kwargs)
614 |
615 | kwargs.update(transform=r_ax.transAxes)
616 | r_ax.plot(right_x, lower, **kwargs)
617 |
618 | # Middle axes
619 | for i in range(1, self.mainax_dim - 1):
620 | l_ax = self.axes[0][i]
621 | r_ax = self.axes[right_ind][i]
622 | upper = (1 - y[i], 1 + y[i])
623 | lower = (-y[i], y[i])
624 |
625 | kwargs.update(transform=l_ax.transAxes)
626 | l_ax.plot(left_x, upper, **kwargs)
627 | l_ax.plot(left_x, lower, **kwargs)
628 |
629 | kwargs.update(transform=r_ax.transAxes)
630 | r_ax.plot(right_x, upper, **kwargs)
631 | r_ax.plot(right_x, lower, **kwargs)
632 |
633 | # Last axes in columns, upper only
634 | l_ax = self.axes[0][-1]
635 | r_ax = self.axes[right_ind][-1]
636 | upper = (1 - y[-1], 1 + y[-1])
637 |
638 | kwargs.update(transform=l_ax.transAxes)
639 | l_ax.plot(left_x, upper, **kwargs)
640 |
641 | kwargs.update(transform=r_ax.transAxes)
642 | r_ax.plot(right_x, upper, **kwargs)
643 |
644 | def set_xlabels(self, xlabels, fontsize=None, labelpad=12, **kwargs):
645 | """
646 | Tool for setting all x axis labels at once. Can skip labelling an axis
647 | by providing a ``None`` in corresponding poiition in list.
648 |
649 | Parameters
650 | ----------
651 | xlabels : list of strings
652 | The list of labels, one per x-axis. Insert ``None`` in list to
653 | skip an axis.
654 | fontsize : int
655 | Default None. The font size of ``xlabels``
656 | labelpad : int
657 | Default 12. The spacing between the tick labels adn the axis
658 | labels.
659 | **kwargs
660 | Passed to ``axes.set_xlabel()``.
661 | Any ``matplotlib Text`` properties
662 |
663 | Notes
664 | -----
665 | Caution- this will set twin axis labels in the order that twins were
666 | created, which may not correspond to physical position in grid.
667 |
668 | """
669 |
670 | for col, side, xl in zip(self.axes, self.dataside_list, xlabels):
671 | if xl is not None:
672 |
673 | if side == 'top':
674 | ind = 0
675 | else:
676 | ind = -1
677 |
678 | col[ind].xaxis.set_label_position(side)
679 | col[ind].set_xlabel(xl, fontsize=fontsize,
680 | labelpad=labelpad, **kwargs)
681 |
--------------------------------------------------------------------------------