├── .gitignore ├── CITATION.md ├── LICENSE ├── MANIFEST.in ├── README.md ├── README.txt ├── citations.md ├── examples ├── Scatter_plot_colorbar.pdf ├── Ternary-Examples.ipynb ├── color_coded_heatmap.py ├── colorbar_kwargs.py ├── custom_axis_scaling.py ├── sample_data │ ├── curve.txt │ ├── sample_heatmap_data.txt │ └── scatter_colorbar.txt ├── scatter_colorbar.py └── ternary_contours.py ├── readme_images ├── 16_80_1.png ├── 16_80_stationary.png ├── 23_80_0.png ├── 24_80_1.png ├── boundary_and_gridlines.png ├── btweinstein_example.png ├── btweinstein_example2.png ├── colored_boundary.png ├── colored_trajectory.png ├── heatmap-dual_vs_triangular.png ├── heatmap-dualtriangular.png ├── heatmap-grids.png ├── heatmap-grids.svg ├── heatmap-triangular.png ├── heatmap_rsp.png ├── heatmap_shannon.png ├── heatmap_styles.py ├── heatmap_styles_cubehelix.pdf ├── heatmap_styles_cubehelix.png ├── heatmap_styles_gray.pdf ├── heatmap_styles_gray.png ├── orientations.png ├── rgba_example.png ├── scatter.png ├── trajectory.png └── various_lines.png ├── setup.cfg ├── setup.py ├── ternary ├── __init__.py ├── colormapping.py ├── heatmapping.py ├── helpers.py ├── lines.py ├── plotting.py └── ternary_axes_subplot.py └── tests ├── test_heatmapping.py └── test_helpers.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /CITATION.md: -------------------------------------------------------------------------------- 1 | # Citation 2 | 3 | [![DOI](https://zenodo.org/badge/19505/marcharper/python-ternary.svg)](https://zenodo.org/badge/latestdoi/19505/marcharper/python-ternary) 4 | 5 | Please cite as follows: 6 | 7 | ``` 8 | Marc Harper et al. (2015). python-ternary: Ternary Plots in Python. Zenodo. 10.5281/zenodo.594435 9 | ``` 10 | 11 | Bibtex: 12 | ``` 13 | @article{pythonternary, 14 | title={python-ternary: Ternary Plots in Python}, 15 | author={Marc Harper et al}, 16 | journal={Zenodo 10.5281/zenodo.594435}, 17 | doi={10.5281/zenodo.594435}, 18 | url={https://github.com/marcharper/python-ternary}, 19 | } 20 | ``` 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2015-2021 Marc Harper 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.md 2 | include *.txt 3 | include LICENSE 4 | 5 | graft examples 6 | graft readme_images 7 | graft tests 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # python-ternary 3 | 4 | 5 | [![DOI](https://zenodo.org/badge/19505/marcharper/python-ternary.svg)](https://zenodo.org/badge/latestdoi/19505/marcharper/python-ternary) 6 | [![Join the chat at https://gitter.im/marcharper/python-ternary](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/marcharper/python-ternary?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 7 | 8 | This is a plotting library for use with [matplotlib](http://matplotlib.org/index.html) to make [ternary plots](http://en.wikipedia.org/wiki/Ternary_plot) 9 | plots in the two dimensional simplex projected onto a two dimensional plane. 10 | 11 | The library provides functions for plotting projected lines, curves (trajectories), scatter plots, and heatmaps. There are [several examples](examples/) and a short tutorial below. 12 | 13 | # Gallery 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 |
28 | 29 | 30 |
31 | Last image from: Genetic Drift and Selection in Many-Allele Range Expansions.
32 |
33 | See the citations below for more example images. 34 |
35 | 36 | # Citations and Recent Usage in Publications 37 | 38 | [![DOI](https://zenodo.org/badge/19505/marcharper/python-ternary.svg)](https://zenodo.org/badge/latestdoi/19505/marcharper/python-ternary) 39 | 40 | Have you used python-ternary in a publication? Open a PR or issue to include 41 | your citations or example plots! 42 | 43 | See the [partial list of citations](citations.md) and 44 | [instructions on how to cite](CITATION.md). 45 | 46 | # Installation 47 | 48 | ### Anaconda 49 | 50 | You can install python-ternary with conda: 51 | 52 | ```bash 53 | conda config --add channels conda-forge 54 | conda install python-ternary 55 | ``` 56 | 57 | See [here](https://github.com/conda-forge/python-ternary-feedstock) for more 58 | information. 59 | 60 | ### Pip 61 | 62 | You can install the current release (1.0.6) with pip: 63 | ```bash 64 | pip install python-ternary 65 | ``` 66 | 67 | ### With setup.py 68 | 69 | Alternatively you can clone the repository and run `setup.py` in the usual 70 | manner: 71 | 72 | ```bash 73 | git clone git@github.com:marcharper/python-ternary.git 74 | cd python-ternary 75 | python setup.py install 76 | ``` 77 | 78 | # Usage, Examples, Plotting Functions 79 | 80 | You can explore some of these examples with 81 | [this Jupyter notebook](examples/Ternary-Examples.ipynb). 82 | 83 | The easiest way to use python-ternary is with the wrapper class 84 | `TernaryAxesSubplot`, which mimics Matplotlib's AxesSubplot. Start with: 85 | 86 | ```python 87 | fig, tax = ternary.figure() 88 | ``` 89 | 90 | With a ternary axes object `tax` you can use many of the usual matplotlib 91 | axes object functions: 92 | 93 | ```python 94 | tax.set_title("Scatter Plot", fontsize=20) 95 | tax.scatter(points, marker='s', color='red', label="Red Squares") 96 | tax.legend() 97 | ``` 98 | 99 | Most drawing functions can take standard matplotlib keyword arguments such as 100 | [linestyle](http://matplotlib.org/api/lines_api.html#matplotlib.lines.Line2D.set_linestyle) 101 | and linewidth. You can use LaTeX in titles and labels. 102 | 103 | If you need to act directly on the underlying matplotlib axes, you can access 104 | them easily: 105 | 106 | ```python 107 | ax = tax.get_axes() 108 | ``` 109 | 110 | You can also wrap an existing Matplotlib AxesSubplot object: 111 | 112 | ``` 113 | figure, ax = pyplot.subplots() 114 | tax = ternary.TernaryAxesSubplot(ax=ax) 115 | ``` 116 | 117 | This is useful if you want to use ternary as a part of another figure, such as 118 | 119 | ```python 120 | from matplotlib import pyplot, gridspec 121 | 122 | pyplot.figure() 123 | gs = gridspec.GridSpec(2, 2) 124 | ax = pyplot.subplot(gs[0, 0]) 125 | figure, tax = ternary.figure(ax=ax) 126 | ... 127 | ``` 128 | 129 | Some ternary functions expect the simplex to be partitioned into some number 130 | of steps, determined by the `scale` parameter. A few functions will do this 131 | partitioning automatically for you, but when working with real data or 132 | simulation output, you may have partitioned already. If you are working with 133 | probability distributions, just use `scale=1` (the default). Otherwise the scale 134 | parameter effectively controls the resolution of many plot types 135 | (e.g. heatmaps). 136 | 137 | `TernaryAxesSubplot` objects keep track of the scale, axes, and other 138 | parameters, supplying them as needed to other functions. 139 | 140 | ## Simplex Boundary and Gridlines 141 | 142 | The following code draws a boundary for the simplex and gridlines. 143 | 144 | ```python 145 | import ternary 146 | 147 | ## Boundary and Gridlines 148 | scale = 40 149 | figure, tax = ternary.figure(scale=scale) 150 | 151 | # Draw Boundary and Gridlines 152 | tax.boundary(linewidth=2.0) 153 | tax.gridlines(color="black", multiple=5) 154 | tax.gridlines(color="blue", multiple=1, linewidth=0.5) 155 | 156 | # Set Axis labels and Title 157 | fontsize = 20 158 | tax.set_title("Simplex Boundary and Gridlines", fontsize=fontsize) 159 | tax.left_axis_label("Left label $\\alpha^2$", fontsize=fontsize) 160 | tax.right_axis_label("Right label $\\beta^2$", fontsize=fontsize) 161 | tax.bottom_axis_label("Bottom label $\\Gamma - \\Omega$", fontsize=fontsize) 162 | 163 | # Set ticks 164 | tax.ticks(axis='lbr', linewidth=1) 165 | 166 | # Remove default Matplotlib Axes 167 | tax.clear_matplotlib_ticks() 168 | 169 | ternary.plt.show() 170 | ``` 171 |

172 | 173 |

174 | 175 | ## Drawing lines 176 | 177 | You can draw individual lines between any two points with `line` and lines 178 | parallel to the axes with `horizonal_line`, `left_parallel_line`, and 179 | `right_parallel_line`: 180 | 181 | ```python 182 | import ternary 183 | 184 | scale = 40 185 | figure, tax = ternary.figure(scale=scale) 186 | 187 | # Draw Boundary and Gridlines 188 | tax.boundary(linewidth=2.0) 189 | tax.gridlines(color="blue", multiple=5) 190 | 191 | # Set Axis labels and Title 192 | fontsize = 12 193 | offset = 0.14 194 | tax.set_title("Various Lines\n", fontsize=fontsize) 195 | tax.right_corner_label("X", fontsize=fontsize) 196 | tax.top_corner_label("Y", fontsize=fontsize) 197 | tax.left_corner_label("Z", fontsize=fontsize) 198 | tax.left_axis_label("Left label $\\alpha^2$", fontsize=fontsize, offset=offset) 199 | tax.right_axis_label("Right label $\\beta^2$", fontsize=fontsize, offset=offset) 200 | tax.bottom_axis_label("Bottom label $\\Gamma - \\Omega$", fontsize=fontsize, offset=offset) 201 | 202 | # Draw lines parallel to the axes 203 | tax.horizontal_line(16) 204 | tax.left_parallel_line(10, linewidth=2., color='red', linestyle="--") 205 | tax.right_parallel_line(20, linewidth=3., color='blue') 206 | 207 | # Draw an arbitrary line, ternary will project the points for you 208 | p1 = (22, 8, 10) 209 | p2 = (2, 22, 16) 210 | tax.line(p1, p2, linewidth=3., marker='s', color='green', linestyle=":") 211 | 212 | tax.ticks(axis='lbr', multiple=5, linewidth=1, offset=0.025) 213 | tax.get_axes().axis('off') 214 | tax.clear_matplotlib_ticks() 215 | tax.show() 216 | ``` 217 | 218 | The line drawing functions accept the matplotlib keyword arguments of 219 | [Line2D](http://matplotlib.org/api/lines_api.html). 220 | 221 |

222 | 223 |

224 | 225 | ## Curves 226 | 227 | Curves can be plotted by specifying the points of the curve, just like 228 | matplotlib's plot. Simply use: 229 | 230 | ``` 231 | ternary.plot(points) 232 | ``` 233 | 234 | Points is a list of tuples or numpy arrays, such as 235 | `[(0.5, 0.25, 0.25), (1./3, 1./3, 1./3)]`, 236 | 237 | ```python 238 | import ternary 239 | 240 | ## Sample trajectory plot 241 | figure, tax = ternary.figure(scale=1.0) 242 | tax.boundary() 243 | tax.gridlines(multiple=0.2, color="black") 244 | tax.set_title("Plotting of sample trajectory data", fontsize=20) 245 | points = [] 246 | # Load some data, tuples (x,y,z) 247 | with open("sample_data/curve.txt") as handle: 248 | for line in handle: 249 | points.append(list(map(float, line.split(' ')))) 250 | # Plot the data 251 | tax.plot(points, linewidth=2.0, label="Curve") 252 | tax.ticks(axis='lbr', multiple=0.2, linewidth=1, tick_formats="%.1f") 253 | tax.legend() 254 | tax.show() 255 | ``` 256 | 257 |

258 | 259 |

260 | 261 | There are many more examples in [this paper](http://arxiv.org/abs/1210.5539). 262 | 263 | ## Scatter Plots 264 | 265 | Similarly, ternary can make scatter plots: 266 | 267 | ```python 268 | import ternary 269 | 270 | ### Scatter Plot 271 | scale = 40 272 | figure, tax = ternary.figure(scale=scale) 273 | tax.set_title("Scatter Plot", fontsize=20) 274 | tax.boundary(linewidth=2.0) 275 | tax.gridlines(multiple=5, color="blue") 276 | # Plot a few different styles with a legend 277 | points = random_points(30, scale=scale) 278 | tax.scatter(points, marker='s', color='red', label="Red Squares") 279 | points = random_points(30, scale=scale) 280 | tax.scatter(points, marker='D', color='green', label="Green Diamonds") 281 | tax.legend() 282 | tax.ticks(axis='lbr', linewidth=1, multiple=5) 283 | 284 | tax.show() 285 | ``` 286 | 287 |

288 | 289 |

290 | 291 | ## Heatmaps 292 | 293 | Ternary can plot heatmaps in two ways and three styles. Given a function, ternary 294 | will evaluate the function at the specified number of steps (determined by the 295 | scale, expected to be an integer in this case). The simplex can be split up into 296 | triangles or hexagons and colored according to one of three styles: 297 | 298 | - Triangular -- `triangular` (default): coloring triangles by summing the values on the 299 | vertices 300 | - Dual-triangular -- `dual-triangular`: mapping (i,j,k) to the upright 301 | triangles △ and blending the neigboring triangles for the downward 302 | triangles ▽ 303 | - Hexagonal -- `hexagonal`: which does not blend values at all, and divides 304 | the simplex up into hexagonal regions 305 | 306 | The two triangular heatmap styles and the hexagonal heatmap style can be visualized 307 | as follows: left is triangular, right is dual triangular. 308 | 309 |

310 |
311 |
312 |

313 | 314 | 315 | Thanks to [chebee7i](https://github.com/chebee7i) for the above images. 316 | 317 | Let's define a function on the simplex for illustration, the [Shannon entropy](http://en.wikipedia.org/wiki/Entropy_%28information_theory%29) of a probability distribution: 318 | 319 | ```python 320 | def shannon_entropy(p): 321 | """Computes the Shannon Entropy at a distribution in the simplex.""" 322 | s = 0. 323 | for i in range(len(p)): 324 | try: 325 | s += p[i] * math.log(p[i]) 326 | except ValueError: 327 | continue 328 | return -1.*s 329 | ``` 330 | 331 | We can get a heatmap of this function as follows: 332 | 333 | ```python 334 | import ternary 335 | scale = 60 336 | 337 | figure, tax = ternary.figure(scale=scale) 338 | tax.heatmapf(shannon_entropy, boundary=True, style="triangular") 339 | tax.boundary(linewidth=2.0) 340 | tax.set_title("Shannon Entropy Heatmap") 341 | 342 | tax.show() 343 | ``` 344 | 345 | In this case the keyword argument *boundary* indicates whether you wish to 346 | evaluate points on the boundary of the partition (which is sometimes 347 | undesirable). Specify `style="hexagonal"` for hexagons. Large scalings can use 348 | a lot of RAM since the number of polygons rendered is O(n^2). 349 | 350 | You may specify a [matplotlib colormap](http://matplotlib.org/examples/color/colormaps_reference.html) 351 | (an instance or the colormap name) in the cmap argument. 352 | 353 |

354 |
355 |

356 | 357 | Ternary can also make heatmaps from data. In this case you need to supply a 358 | dictionary mapping `(i, j)` or `(i, j, k)` for `i + j + k = scale` to a float 359 | as input for a heatmap. It is not necessary to include `k` in the dictionary 360 | keys since it can be determined from `scale`, `i`, and `j`. This reduces the 361 | memory requirements when the partition is very fine (significant when `scale` 362 | is in the hundreds). 363 | 364 | Make the heatmap as follows: 365 | 366 | ```python 367 | ternary.heatmap(data, scale, ax=None, cmap=None) 368 | ``` 369 | 370 | or on a `TernaryAxesSubplot` object: 371 | 372 | ```python 373 | tax.heatmap(data, cmap=None) 374 | ``` 375 | 376 | This can produces images such as: 377 | 378 |

379 |
380 | 381 |

382 | 383 | # Axes Ticks and Orientations 384 | 385 | For a given ternary plot there are two valid ways to label the axes ticks 386 | corresponding to the clockwise and counterclockwise orientations. However note 387 | that the axes labels need to be adjusted accordingly, and `ternary` does not 388 | do so automatically when you pass `clockwise=True` to `tax.ticks()`. 389 | 390 |

391 | 392 |

393 | 394 | There is a [more detailed discussion](https://github.com/marcharper/python-ternary/issues/18) 395 | on issue #18 (closed). 396 | 397 | 398 | # RGBA colors 399 | 400 | You can alternatively specify colors as rgba tuples `(r, g, b, a)` 401 | (all between zero and one). To use this feature, pass `colormap=False` to 402 | `heatmap()` so that the library will not attempt to map the tuple to a value 403 | with a matplotlib colormap. Note that this disables the inclusion of a colorbar. 404 | Here is an example: 405 | 406 | ```python 407 | import math 408 | from matplotlib import pyplot as plt 409 | import ternary 410 | 411 | def color_point(x, y, z, scale): 412 | w = 255 413 | x_color = x * w / float(scale) 414 | y_color = y * w / float(scale) 415 | z_color = z * w / float(scale) 416 | r = math.fabs(w - y_color) / w 417 | g = math.fabs(w - x_color) / w 418 | b = math.fabs(w - z_color) / w 419 | return (r, g, b, 1.) 420 | 421 | 422 | def generate_heatmap_data(scale=5): 423 | from ternary.helpers import simplex_iterator 424 | d = dict() 425 | for (i, j, k) in simplex_iterator(scale): 426 | d[(i, j, k)] = color_point(i, j, k, scale) 427 | return d 428 | 429 | 430 | scale = 80 431 | data = generate_heatmap_data(scale) 432 | figure, tax = ternary.figure(scale=scale) 433 | tax.heatmap(data, style="hexagonal", use_rgba=True) 434 | tax.boundary() 435 | tax.set_title("RGBA Heatmap") 436 | plt.show() 437 | 438 | ``` 439 | 440 | This produces the following image: 441 | 442 |

443 | 444 |

445 | 446 | # Unittests 447 | 448 | You can run the test suite as follows: 449 | 450 | ```python 451 | python -m unittest discover tests 452 | ``` 453 | 454 | # Contributing 455 | 456 | Contributions are welcome! Please share any nice example plots, contribute 457 | features, and add unit tests! Use the pull request and issue systems to 458 | contribute. 459 | 460 | # Selected Contributors 461 | 462 | - Marc Harper [marcharper](https://github.com/marcharper): maintainer 463 | - Bryan Weinstein [btweinstein](https://github.com/btweinstein): Hexagonal heatmaps, colored trajectory plots 464 | - [chebee7i](https://github.com/chebee7i): Docs and figures, triangular heatmapping 465 | - [Cory Simon](https://github.com/CorySimon): Axis Colors, colored heatmap example 466 | 467 | # Known-Issues 468 | 469 | At one point there was an issue on macs that causes the axes 470 | labels not to render. The workaround is to manually call 471 | ``` 472 | tax._redraw_labels() 473 | ``` 474 | before showing or rendering the image. 475 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | python-ternary is a plotting library for use with matplotlib to make ternary 2 | plots, including heatmaps. 3 | -------------------------------------------------------------------------------- /citations.md: -------------------------------------------------------------------------------- 1 | # Citations 2 | 3 | The python-ternary library has been cited in a variety of high-quality journals and in many fields including machine learning, statistics, particle physics, cosmology, geochemistry, biology, epidemiology, and genomics. Entries are roughly in reverse chronological order. This list is likely incomplete. 4 | 5 | 6 | ## How to Cite 7 | 8 | [Citation Instructions](CITATION.md) 9 | 10 | 11 | ## Journal Articles, Conference Proceedings, and Preprints 12 | 1. Jürries, Florian, et al. [Targeted hydrolysis and decrepitation of Mn3AlC precipitates: a route to a novel precursor of rare earth free MnAl–C permanent magnets](https://iopscience.iop.org/article/10.1088/1361-6463/ace5b9/meta). Journal of Physics D: Applied Physics 56.41 (2023): 415004. 13 | 1. Merrifield, Anna Louise, et al. [Climate model Selection by Independence, Performance, and Spread (ClimSIPS) for regional applications.](https://egusphere.copernicus.org/preprints/2023/egusphere-2022-1520/) EGUsphere 2023 (2023): 1-49. 14 | 1. Harris, Elizabeth K., et al. [Influence of an industrial discharge on long-term dynamics of abiotic and biotic resources in Lavaca Bay, Texas, USA](https://link.springer.com/article/10.1007/s10661-022-10665-w). Environmental monitoring and assessment 195.1 (2023): 1-24. 15 | 1. Senanayake, Ravithree D., et al. [Machine learning-assisted carbon dot synthesis: prediction of emission color and wavelength](https://pubs.acs.org/doi/pdf/10.1021/acs.jcim.2c01007). Journal of Chemical Information and Modeling 62.23 (2022): 5918-5928 16 | 1. Ronchetti, Claudio, et al. [Machine learning techniques for data analysis in materials science](https://ieeexplore.ieee.org/abstract/document/9951839). 2022 AEIT International Annual Conference (AEIT). IEEE, 2022. 17 | 1. Huang, Chenliang, David R. Rice, and Jason H. Steffen. [MAGRATHEA: an open-source spherical symmetric planet interior structure code](https://academic.oup.com/mnras/article-abstract/513/4/5256/6574421). Monthly Notices of the Royal Astronomical Society 513.4 (2022): 5256-5269. 18 | 3. Chen, Wei-Chih, Da Yan, and Cheng-Chien Chen. [Machine Learning and First-Principles Discovery of Ternary Superhard Materials](https://pubs.acs.org/doi/abs/10.1021/bk-2022-1416.ch009). Machine Learning in Materials Informatics: Methods and Applications. American Chemical Society, 2022. 211-238. 19 | 4. Chen, Wei-Chih, Yogesh K. Vohra, and Cheng-Chien Chen. [Discovering Superhard B–N–O Compounds by Iterative Machine Learning and Evolutionary Structure Predictions](https://pubs.acs.org/doi/full/10.1021/acsomega.2c01818). ACS omega 7.24 (2022): 21035-21042. 20 | 5. Srikanth, Neha, and Rachel Rudinger. [Partial-input baselines show that NLI models can ignore context, but they don't](https://arxiv.org/abs/2205.12181). arXiv preprint arXiv:2205.12181 (2022). 21 | 6. MacDonald, Mariah G., et al. [Confirming the 3: 2 Resonance Chain of K2-138](https://iopscience.iop.org/article/10.3847/1538-3881/ac524c/meta). The Astronomical Journal 163.4 (2022): 162. 22 | 7. Ravel, Guillaume, et al. [Inferring characteristics of bacterial swimming in biofilm matrix from time-lapse confocal laser scanning microscopy](https://arxiv.org/abs/2201.04371). arXiv preprint arXiv:2201.04371 (2022). 23 | 8. Uhl, Johannes H., and Stefan Leyk. [A framework for scale-sensitive, spatially explicit accuracy assessment of binary built-up surface layers](https://arxiv.org/abs/2203.11253). arXiv preprint arXiv:2203.11253 (2022). 24 | 9. Zion, Matan Yah Ben, et al. [Cooperation in a fluid swarm of fuel-free micro-swimmers](https://www.nature.com/articles/s41467-021-27870-9) Nature communications 13.1 [arXiv preprint arXiv:2012.15087](https://arxiv.org/abs/2012.15087). (2022). 25 | 10. Ravel, Guillaume, et al. "Inferring characteristics of bacterial swimming in biofilm matrix from time-lapse confocal laser scanning microscopy." [arXiv preprint arXiv:2201.04371](https://arxiv.org/abs/2201.04371) (2022). 26 | 11. Haldemann, Jonas, et al. [Exoplanet Characterization using Conditional Invertible Neural Networks](https://arxiv.org/abs/2202.00027). arXiv preprint arXiv:2202.00027 (2022). 27 | 12. Kumar, Darshan, et al. [Constraints on the Transition Redshift using the Cosmic Triangle and Hubble Phase Space Portrait](https://www.worldscientific.com/doi/abs/10.1142/S0218271823500396). International Journal of Modern Physics D 32.06 (2023): 2350039. [arXiv:2205.13247](https://arxiv.org/abs/2205.13247) (2022). 28 | 15. Stemplinger, Simon, et al. [Theory of Ternary Fluids under Centrifugal Fields.](https://pubs.acs.org/doi/abs/10.1021/acs.jpcb.1c05875) The Journal of Physical Chemistry B 125.43 (2021): 12054-12062. 29 | 16. Fatras, Kilian, et al. "Minibatch optimal transport distances; analysis and applications." [arXiv preprint arXiv:2101.01792](https://arxiv.org/abs/2101.01792) (2021). 30 | 17. Kim, Minjae, Jung-Kyoo Choi, and Seung Ki Baek. [Win-Stay-Lose-Shift as a self-confirming equilibrium in the iterated Prisoner’s Dilemma.](https://royalsocietypublishing.org/doi/full/10.1098/rspb.2021.1021) Proceedings of the Royal Society B 288.1953 (2021): 20211021. 31 | 18. Nanba, Yusuke, and Michihisa Koyama. [Thermodynamic Stabilities of PdRuM (M= Cu, Rh, Ir, Au) Alloy Nanoparticles Assessed by Wang–Landau Sampling Combined with DFT Calculations and Multiple Regression Analysis.](https://www.journal.csj.jp/doi/abs/10.1246/bcsj.20210199) Bulletin of the Chemical Society of Japan 94.10 (2021): 2484-2492. 32 | 19. Maturana, Carola J., et al. [Novel tool to quantify with single-cell resolution the number of incoming AAV genomes co-expressed in the mouse nervous system.](https://www.nature.com/articles/s41434-021-00272-8) Gene Therapy (2021): 1-6. 33 | 20. Chen, Wei-Chih, et al. [Machine learning and evolutionary prediction of superhard BCN compounds](https://www.nature.com/articles/s41524-021-00585-7). Nature npj Computational Materials 7.1 (2021): 1-8. (2021) 34 | 21. Nolan, Adelaide M., Eric D. Wachsman, and Yifei Mo. [Computation-guided discovery of coating materials to stabilize the interface between lithium garnet solid electrolyte and high-energy cathodes for all-solid-state lithium batteries](https://www.sciencedirect.com/science/article/abs/pii/S2405829721002932). Energy Storage Materials 41 (2021): 571-580. 35 | 22. Tönsmann, Max, et al. [Surface tension of binary and ternary polymer solutions: Experimental data of poly(vinyl acetate), poly(vinyl alcohol) and polyethylene glycol solutions and mixing rule evaluation over the entire concentration range](https://www.sciencedirect.com/science/article/pii/S2468023021004296) Surfaces and Interfaces 26 (2021). 36 | 23. Maturana, Carola J., et al. [Novel tool to quantify with single-cell resolution the number of incoming AAV genomes co-expressed in the mouse nervous system.](https://www.nature.com/articles/s41434-021-00272-8) Gene Therapy (2021): 1-6. 37 | 24. Bechtold, Andreas, et al. [Lunar meteorite Northwest Africa 11962: A regolith breccia containing records of titanium‐rich lunar volcanism and the high alkali suite](https://onlinelibrary.wiley.com/doi/abs/10.1111/maps.13659). Meteoritics & Planetary Science (2021). [(open pdf)](http://oro.open.ac.uk/75904/1/maps.13659.pdf) 38 | 25. Asai, Kento, Takeo Moroi, and Atsuya Niki. [Leptophilic gauge bosons at ILC beam dump experiment](https://www.sciencedirect.com/science/article/pii/S0370269321003142). Physics Letters B (2021): 136374. [ArXiv](https://arxiv.org/abs/2104.00888) 39 | 26. Chung, Wesley, et al. [Beyond variance reduction: Understanding the true impact of baselines on policy optimization](http://proceedings.mlr.press/v139/chung21a.html). International Conference on Machine Learning. PMLR, 2021. [arXiv:2008.13773](https://arxiv.org/abs/2008.13773) 40 | 27. Hao, Weiduo, et al. [The kaolinite shuttle links the Great Oxidation and Lomagundi events](https://doi.org/10.1038/s41467-021-23304-8). Nature Communications 12 (2021): 2944. 41 | 28. Mikhail, Sami, et al. [A genetic metasomatic link between eclogitic and peridotitic diamond inclusions](https://research-repository.st-andrews.ac.uk/handle/10023/21754). Geochemical Perspectives Letters (2021). 42 | 29. Donchev, Alexander G., et al. [Quantum chemical benchmark databases of gold-standard dimer interaction energies](https://www.nature.com/articles/s41597-021-00833-x). [Nature] Scientific data 8.1 (2021): 1-9. 43 | 30. Wang, Fengfan, et al. [Study on offshore seabed sediment classification based on particle size parameters using XGBoost algorithm](https://www.sciencedirect.com/science/article/pii/S0098300421000273). Computers & Geosciences 149 (2021): 104713. 44 | 31. Felkl, Tobias, Juan Herrero-Garcia, and Michael A. Schmidt. [The Singly-Charged Scalar Singlet as the Origin of Neutrino Masses](https://link.springer.com/article/10.1007/JHEP05(2021)122). Journal of High Energy Physics 2021.5 (2021): 1-39; arXiv preprint [arXiv:2102.09898](https://arxiv.org/abs/2102.09898) (2021). 45 | 32. Busnelli, Marco, et al. [Aortic Gene Expression Profiles Show How ApoA-I Levels Modulate Inflammation, Lysosomal Activity, and Sphingolipid Metabolism in Murine Atherosclerosis](https://www.ahajournals.org/doi/abs/10.1161/ATVBAHA.120.315669). Arteriosclerosis, Thrombosis, and Vascular Biology 41.2 (2021): 651-667. 46 | 33. Fink, Christoph, et al. "Mapping the online songbird trade in Indonesia." Applied Geography 134 (2021): 102505. [SocArxiv](https://osf.io/preprints/socarxiv/mxkgq/). 47 | 34. Molter, Felix, et al. [Gaze-dependent evidence accumulation predicts multi-alternative risky choice behaviour](https://journals.plos.org/ploscompbiol/article?id=10.1371/journal.pcbi.1010283) [psyarxiv](https://psyarxiv.com/x6nbf/download/?format=pdf). PsyArXiv. February 12 (2021). 48 | 35. Karkaria, Behzad D., Alex JH Fedorec, and Chris P. Barnes. [Automated design of synthetic microbial communities](https://www.nature.com/articles/s41467-020-20756-2). Nature communications 12.1 (2021): 1-12. [BioRxiv](https://www.biorxiv.org/content/10.1101/2020.06.30.180281v1.abstract). 49 | 36. Fatras, Kilian, et al. [Minibatch optimal transport distances; analysis and applications](https://arxiv.org/abs/2101.01792). arXiv preprint arXiv:2101.01792 (2021). 50 | 37. Tran, Xuan Quy, et al. [Statistical Evaluation of the Solid-Solution State in Ternary Nanoalloys.](https://pubs.acs.org/doi/abs/10.1021/acs.jpcc.0c06813) The Journal of Physical Chemistry C 124.39 (2020): 21843-21852. 51 | 38. Evans, Matthew L., and Andrew J. Morris. [matador: a Python library for analysing, curating and performing high-throughput density-functional theory calculations](https://joss.theoj.org/papers/10.21105/joss.02563.pdf). Journal of Open Source Software 5.54 (2020): 2563. 52 | 39. Liu, Shiyu, et al. [Quantitative analysis of the physiological contributions of glucose to the TCA cycle](https://www.sciencedirect.com/science/article/abs/pii/S1550413120304836). Cell Metabolism 32.4 (2020): 619-628. 53 | 40. Graziani, Carlo. [A Behavioral Multispread Epidemic Model](https://www.medrxiv.org/content/10.1101/2020.08.24.20181107v1.abstract). medRxiv (2020). 54 | 41. Tran, Xuan Quy, et al. [Statistical evaluation of the solid-solution state in ternary nanoalloys](https://pubs.acs.org/doi/abs/10.1021/acs.jpcc.0c06813). The Journal of Physical Chemistry C (2020). 55 | 42. McNally, Joshua S., et al. [Solute displacement in the aqueous phase of water–NaCl–organic ternary mixtures relevant to solvent-driven water treatment](https://pubs.rsc.org/en/content/articlelanding/2020/RA/D0RA06361D) RSC Advances 10.49 (2020) 56 | 43. Marc Harper and Joshua Safyan. [Momentum Accelerates Evolutionary Dynamics](https://arxiv.org/abs/2007.02449). ArXiv preprint. (2020) 57 | 44. Broquet, A., Mark A. Wieczorek, and Wenzhe Fa. [Flexure of the lithosphere beneath the north polar cap of Mars: Implications for ice composition and heat flow](https://agupubs.onlinelibrary.wiley.com/doi/epdf/10.1029/2019GL086746). Geophysical Research Letters (2020). 58 | 45. Choubisa, Hitarth, et al. [Crystal Site Feature Embedding Enables Exploration of Large Chemical Spaces](https://www.sciencedirect.com/science/article/pii/S2590238520301879). Matter (2020). 59 | 46. Daly, Clyde A., and Rigoberto Hernandez. [Learning from the Machine: Uncovering Sustainable Nanoparticle Design Rules.](https://pubs.acs.org/doi/abs/10.1021/acs.jpcc.0c01195) The Journal of Physical Chemistry C (2020). 60 | 47. Qian, Jimmy J., and Erol Akçay. [The balance of interaction types determines the assembly and stability of ecological communities](https://www.nature.com/articles/s41559-020-1121-x). Nature Ecology & Evolution 4.3 (2020): 356-365. [BioRxiv](https://www.biorxiv.org/content/10.1101/643478v1.full) 61 | 48. Bucalo, Maria Soledad, et al. [A Constellation of Horrors: Analysis and Visualization of the #Cuéntalo Movement.](https://dl.acm.org/doi/abs/10.1145/3308560.3316459) Companion Proceedings of The 2019 World Wide Web Conference. 2019. 62 | 49. Ke, Huibin, and Christopher D. Taylor. [Design of Corrosion Resistant Alloys Using Density Functional Theory: Part I. O and Cl Adsorption Energy](https://papers.ssrn.com/sol3/papers.cfm?abstract_id=3491237). O and Cl Adsorption Energy (November 27, 2019) (2019). 63 | 50. Choi, Kwangbom, Narayanan Raghupathy, and Gary A. Churchill. [scBASE: A Bayesian mixture model for the analysis of allelic expression in single cells.](https://www.nature.com/articles/s41467-019-13099-0) Nature Communications 10.1 (2019): 1-1. [bioRxiv](https://www.biorxiv.org/content/10.1101/383224v3.full) (2019): 383224. 64 | 51. Campbell, Ian Jackson, George Nelson Bennett, and Jonathan J. Silberg. [Evolutionary relationships between low potential ferredoxin and flavodoxin electron carriers.](https://www.frontiersin.org/articles/10.3389/fenrg.2019.00079/full) Frontiers in Energy Research 7 (2019): 79. 65 | 52. Bragman, Felix JS, et al. [Stochastic Filter Groups for Multi-Task CNNs: Learning Specialist and Generalist Convolution Kernels](http://openaccess.thecvf.com/content_ICCV_2019/html/Bragman_Stochastic_Filter_Groups_for_Multi-Task_CNNs_Learning_Specialist_and_Generalist_ICCV_2019_paper.html). Proceedings of the IEEE International Conference on Computer Vision. [arXiv:1908.09597](https://arxiv.org/abs/1908.09597) (2019). 66 | 53. Nelson, James, and Stefano Sanvito. [Predicting the Curie temperature of ferromagnets using machine learning](https://journals.aps.org/prmaterials/abstract/10.1103/PhysRevMaterials.3.104405). Phys. Rev. Materials 3, 104405 (2019) [arXiv:1906.08534](https://arxiv.org/abs/1906.08534) 67 | 54. Kirchner, Philipp, et al. [Proteome-wide analysis of chaperone-mediated autophagy targeting motifs.](https://journals.plos.org/plosbiology/article?id=10.1371/journal.pbio.3000301) PLoS biology 17.5 (2019): e3000301. 68 | 55. Gariazzo, S., P. F. de Salas, and S. Pastor Carpi. [Thermalisation of sterile neutrinos in the early Universe in the 3+ 1 scheme with full mixing matrix](https://iopscience.iop.org/article/10.1088/1475-7516/2019/07/014). Journal of Cosmology and Astroparticle Physics, Volume 2019, July 2019. [arXiv:1905.11290](https://arxiv.org/abs/1905.11290) (2019). 69 | 56. Keniley, Shane, and Davide Curreli. "CRANE: A MOOSE-based Open Source Tool for Plasma Chemistry Applications." [arXiv:1905.10004](https://arxiv.org/abs/1905.10004) (2019). 70 | 57. Abada, Asmaa, et al. [Inclusive displaced vertex searches for heavy neutral leptons at the LHC.](https://link.springer.com/article/10.1007/JHEP01(2019)093) Journal of High Energy Physics 2019.1 (2019): 93. [ArXiv](https://arxiv.org/abs/1807.10024). 71 | 58. Bustamante, Mauricio, and Sanjib Kumar Agarwalla. [Universe’s Worth of Electrons to Probe Long-Range Interactions of High-Energy Astrophysical Neutrinos.](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.122.061103) Physical review letters 122.6 (2019): 061103. [ArXiv](https://arxiv.org/abs/1808.02042). 72 | 59. Hamilton, Nicholas E., and Michael Ferry. [ggtern: Ternary diagrams using ggplot2](https://www.jstatsoft.org/article/view/v087c03). Journal of Statistical Software 87.1 (2018): 1-17. 73 | 60. Makha, Mohammed, et al. [Insights into photovoltaic properties of ternary organic solar cells from phase diagrams](https://www.tandfonline.com/doi/abs/10.1080/14686996.2018.1509275). Science and Technology of Advanced Materials 19.1 (2018): 669-682. 74 | 61. Marc Harper and Dashiell Fryer. [Entropic Equilibria Selection of Stationary Extrema in Finite Populations](https://doi.org/10.3390/e20090631). Entropy 2018, 20(9), 631. 75 | 62. Profe, Jörn, et al. [Paleoenvironmental conditions and sedimentation dynamics in Central Europe inferred from geochemical data of the loess-paleosol sequence at Süttő (Hungary)](https://www.sciencedirect.com/science/article/pii/S027737911730834X). Quaternary Science Reviews 196 (2018): 21-37. 76 | 63. Ou, Longwen, et al. [Understanding the impacts of biomass blending on the uncertainty of hydrolyzed sugar yield from a stochastic perspective.](https://pubs.acs.org/doi/full/10.1021/acssuschemeng.8b02150#showReferences). Ou, Longwen, et al. ACS Sustainable Chemistry & Engineering (2018). 77 | 64. Nyberg, Björn, Casey W. Nixon, and David J. Sanderson. [NetworkGT: A GIS tool for geometric and topological analysis of two-dimensional fracture networks](https://pubs.geoscienceworld.org/gsa/geosphere/article/14/4/1618/531129/networkgt-a-gis-tool-for-geometric-and-topological). Geosphere (2018). 78 | 65. Fang Ren, Logan Ward, Travis Williams, Kevin J. Laws, Christopher Wolverton, Jason Hattrick-Simpers and Apurva Mehta. [Accelerated discovery of metallic glasses through iteration of machine learning and high-throughput experiments](http://advances.sciencemag.org/content/4/4/eaaq1566.full). Science Advances: Vol. 4, no. 4. (2018) DOI: 10.1126/sciadv.aaq1566 79 | 66. Pauly, Tyler, and Robin T. Garrod. [Modeling CO, CO2, and H2O Ice Abundances in the Envelopes of Young Stellar Objects in the Magellanic Clouds](http://iopscience.iop.org/article/10.3847/1538-4357/aaa96a/meta). The Astrophysical Journal, 854.1 (2018): 13. 80 | 67. Gotzias, A., Kainourgiakis, M. & Stubos. [Enhanced CO2 selectivity within the cavity of gmelinite frameworks](https://link.springer.com/article/10.1007%2Fs10450-018-9945-2). A. Adsorption (2018). https://doi.org/10.1007/s10450-018-9945-2 81 | 68. Maxim O. Lavrentovich, Wolfram Möbius, Andrew W. Murray, and David R. Nelson. [Genetic Drift and Selection in Many-Allele Range Expansions](http://journals.plos.org/ploscompbiol/article?id=10.1371/journal.pcbi.1005866). [Bryan T. Weinstein](https://github.com/btweinstein), PLoS computational biology 13.12 (2017): e1005866. (2017) 82 | 69. Xinqiang Ding, Jonah Z. Vilseck, Ryan L. Hayes, and Charles L. Brooks. [Gibbs Sampler-Based λ-Dynamics and Rao–Blackwell Estimator for Alchemical Free Energy Calculation](http://pubs.acs.org/doi/abs/10.1021/acs.jctc.7b00204?src=recsys&journalCode=jctcce). Journal of Chemical Theory and Computation. (2017) 83 | 70. Timothy J Durham, Maxwell W Libbrecht, Jeffry Howbert, Jeffrey Bilmes, William S Noble. [PREDICTD: PaRallel Epigenomics Data Imputation With Cloud-based Tensor Decomposition](http://biorxiv.org/content/early/2017/04/04/123927). Nature Communications, doi: 10.1038/s41467-018-03635-9 (2017) 84 | 71. Kottke, Daniel, et al. [Multi-class probabilistic active learning](http://ebooks.iospress.nl/publication/44803) Proc. of the 22nd Europ. Conf. on Artificial Intelligence (ECAI2016). 2016. 85 | 72. Joshua Meyers, Michael Carter, N Yi Mok, and Nathan Brown. [On the origins of three-dimensionality in drug-like molecules](http://www.future-science.com/doi/full/10.4155/fmc-2016-0095). Future Medicinal Chemistry, Vol. 8, No. 14 (2016) 86 | 73. Marc Harper and Dashiell Fryer. [Stationary Stability for Evolutionary Dynamics in Finite Populations](http://www.mdpi.com/1099-4300/18/9/316/htm). Entropy 18.9 (2016): 316. 87 | 74. Cory Simon. [pyIAST: Ideal adsorbed solution theory (IAST) Python package](http://www.sciencedirect.com/science/article/pii/S0010465515004403). Computer Physics Communications (2016) 88 | 75. Marc Harper and Dashiell Fryer. [Lyapunov Functions for Time-Scale Dynamics on Riemannian Geometries of the Simplex](https://link.springer.com/article/10.1007/s13235-014-0124-0). Dynamic Games and Applications (2015) 89 | 90 | ## Dissertations 91 | 1. Rice, David R. [Inferring the Compositions and Interior Structures of Small Planets](https://www.proquest.com/openview/f7504fbe891d2ec3a7673d4e2a1122c4/1?pq-origsite=gscholar&cbl=18750&diss=y). Diss. University of Nevada, Las Vegas, 2023. 92 | 1. Störiko, Anna. [Integrating Molecular-Biological Data and Process-Based Models of Nitrogen Cycling](https://tobias-lib.ub.uni-tuebingen.de/xmlui/handle/10900/135233). Doctoral Dissertation. (2022). 93 | 1. Levo, Emil. [Radiation Damage in High Entropy Alloys.](https://helda.helsinki.fi/handle/10138/337574). Doctoral Dissertation. (2022). 94 | 1. Nikolaev, Fedor. [Incorporating Word Dependencies Into Structured Document Retrieval Models](https://search.proquest.com/openview/94faf009d9ce22f3634b2bc29ebb02b6/1?pq-origsite=gscholar&cbl=18750&diss=y). Wayne State University, 2021. 95 | 1. Fatras, Kilian. [Deep learning and optimal transport: learning from one another](https://www.theses.fr/2021LORIS604) Diss. Lorient, 2021. 96 | 2. Chen, Wei-Chih. [Predictive Modeling of Superhard and Topological Materials by Density Functional Theory and Machine Learning](https://search.proquest.com/openview/85c7c0efd61123b8843fa3f3a34b0de1/1?pq-origsite=gscholar&cbl=18750&diss=y). Diss. The University of Alabama at Birmingham, 2021. 97 | 3. Lacour-Gogny-Goubert, Antoine. [Développement et étude d’alliages réfractaires complexes, à microstructure cubique centrée et orthorhombique, pour des applications aéronautiques.](https://hal.archives-ouvertes.fr/tel-03552645/) Diss. Université Paris-Est, 2020. 98 | 4. Durham, Timothy. [Toward comprehensive characterization of chromatin state](https://digital.lib.washington.edu/researchworks/handle/1773/44274). 2019. 99 | 5. Cremer, Pascal. [Algorithms for Cell Layout](http://hss.ulb.uni-bonn.de/2019/5428/5428.pdf). PhD Dissertation. Universitäts-und Landesbibliothek Bonn, 2019. 100 | 6. Ding, Xinqiang. [Methodological Advances for Drug Discovery and Protein Engineering](https://deepblue.lib.umich.edu/handle/2027.42/147634). 2018. 101 | 7. Weinstein, Bryan T. [Microbial Evolutionary Dynamics and Transport on Solid and Liquid Substrates](https://dash.harvard.edu/handle/1/40050028). Diss. 2018. 102 | 8. Triller, Thomas. [Diffusive properties of the system water/ethanol/triethylene glycol in microgravity and ground conditions](https://d-nb.info/1168324432/34). (Disseration, 2018) 103 | 9. Rueda Corredor, Henry Steven. [Desarrollo e implementación de un software que permita visualizar y procesar los datos de rayos X de microsonda para análisis petrológicos, mineralógicos y geoquímicos.](https://repositorio.uniandes.edu.co/bitstream/handle/1992/17924/u729475.pdf?sequence=1) (2016). 104 | -------------------------------------------------------------------------------- /examples/Scatter_plot_colorbar.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcharper/python-ternary/9f946b69f266e61983f5be60793930fb1c380a6c/examples/Scatter_plot_colorbar.pdf -------------------------------------------------------------------------------- /examples/color_coded_heatmap.py: -------------------------------------------------------------------------------- 1 | import ternary 2 | import matplotlib.pyplot as plt 3 | 4 | 5 | # Function to visualize for heat map 6 | def f(x): 7 | return 1.0 * x[0] / (1.0 * x[0] + 0.2 * x[1] + 0.05 * x[2]) 8 | 9 | # Dictionary of axes colors for bottom (b), left (l), right (r). 10 | axes_colors = {'b': 'g', 'l': 'r', 'r': 'b'} 11 | 12 | scale = 10 13 | 14 | fig, ax = plt.subplots() 15 | ax.axis("off") 16 | figure, tax = ternary.figure(ax=ax, scale=scale) 17 | 18 | tax.heatmapf(f, boundary=False, 19 | style="hexagonal", cmap=plt.cm.get_cmap('Blues'), 20 | cbarlabel='Component 0 uptake', 21 | vmax=1.0, vmin=0.0) 22 | 23 | tax.boundary(linewidth=2.0, axes_colors=axes_colors) 24 | 25 | tax.left_axis_label("$x_1$", offset=0.16, color=axes_colors['l']) 26 | tax.right_axis_label("$x_0$", offset=0.16, color=axes_colors['r']) 27 | tax.bottom_axis_label("$x_2$", offset=0.06, color=axes_colors['b']) 28 | 29 | tax.gridlines(multiple=1, linewidth=2, 30 | horizontal_kwargs={'color': axes_colors['b']}, 31 | left_kwargs={'color': axes_colors['l']}, 32 | right_kwargs={'color': axes_colors['r']}, 33 | alpha=0.7) 34 | 35 | # Set and format axes ticks. 36 | ticks = [i / float(scale) for i in range(scale+1)] 37 | tax.ticks(ticks=ticks, axis='rlb', linewidth=1, clockwise=True, 38 | axes_colors=axes_colors, offset=0.03, tick_formats="%0.1f") 39 | 40 | tax.clear_matplotlib_ticks() 41 | tax._redraw_labels() 42 | plt.tight_layout() 43 | tax.show() 44 | -------------------------------------------------------------------------------- /examples/colorbar_kwargs.py: -------------------------------------------------------------------------------- 1 | import ternary 2 | 3 | ## Boundary and Gridlines 4 | scale = 9 5 | figure, tax = ternary.figure(scale=scale) 6 | tax.ax.axis("off") 7 | figure.set_facecolor('w') 8 | 9 | # Draw Boundary and Gridlines 10 | tax.boundary(linewidth=1.0) 11 | tax.gridlines(color="black", multiple=1, linewidth=0.5,ls='-') 12 | 13 | # Set Axis labels and Title 14 | fontsize = 15 15 | tax.left_axis_label("Barleygrow", fontsize=fontsize, offset=0.12) 16 | tax.right_axis_label("Beans", fontsize=fontsize, offset=0.12) 17 | tax.bottom_axis_label("Oats", fontsize=fontsize, offset=0.025) 18 | 19 | # Set ticks 20 | tax.ticks(axis='blr', linewidth=1,multiple=1) 21 | 22 | 23 | 24 | # Scatter some points 25 | points = [(2,3,5),(3,6,1),(5,4,1),(3,4,3),(2,2,6)] 26 | c = [90,20,30,10,64] 27 | 28 | cb_kwargs = {"shrink" : 0.6, 29 | "orientation" : "horizontal", 30 | "fraction" : 0.1, 31 | "pad" : 0.05, 32 | "aspect" : 30} 33 | 34 | tax.scatter(points,marker='s',c=c,edgecolor='k',s=40,linewidths=0.5, 35 | vmin=0,vmax=100,colorbar=True,colormap='jet',cbarlabel='Farmers', 36 | cb_kwargs=cb_kwargs,zorder=3) 37 | 38 | 39 | tax._redraw_labels() 40 | 41 | # Color coded heatmap example with colorbar kwargs 42 | # Slight modification so that we don't have to re-import pyplot 43 | # but make use of ternary.plt 44 | 45 | 46 | # Function to visualize for heat map 47 | def f(x): 48 | return 1.0 * x[0] / (1.0 * x[0] + 0.2 * x[1] + 0.05 * x[2]) 49 | 50 | # dictionary of axes colors for bottom (b), left (l), right (r) 51 | axes_colors = {'b': 'g', 'l': 'r', 'r':'b'} 52 | 53 | scale = 10 54 | 55 | figure, tax = ternary.figure(scale=scale) 56 | tax.ax.axis("off") 57 | cb_kwargs = {"shrink" : 0.6, 58 | "pad" : 0.05, 59 | "aspect" : 30, 60 | "orientation" : "horizontal"} 61 | 62 | tax.heatmapf(f, boundary=False, 63 | style="hexagonal", cmap=ternary.plt.cm.get_cmap('Blues'), 64 | cbarlabel='Component 0 uptake', 65 | vmax=1.0, vmin=0.0, cb_kwargs=cb_kwargs) 66 | 67 | tax.boundary(linewidth=2.0, axes_colors=axes_colors) 68 | 69 | tax.left_axis_label("$x_1$", offset=0.16, color=axes_colors['l']) 70 | tax.right_axis_label("$x_0$", offset=0.16, color=axes_colors['r']) 71 | tax.bottom_axis_label("$x_2$", offset=-0.06, color=axes_colors['b']) 72 | 73 | tax.gridlines(multiple=1, linewidth=2, 74 | horizontal_kwargs={'color':axes_colors['b']}, 75 | left_kwargs={'color':axes_colors['l']}, 76 | right_kwargs={'color':axes_colors['r']}, 77 | alpha=0.7) 78 | 79 | ticks = [round(i / float(scale), 1) for i in range(scale+1)] 80 | tax.ticks(ticks=ticks, axis='rlb', linewidth=1, clockwise=True, 81 | axes_colors=axes_colors, offset=0.03) 82 | 83 | tax.clear_matplotlib_ticks() 84 | tax._redraw_labels() 85 | ternary.plt.tight_layout() 86 | ternary.plt.show() 87 | -------------------------------------------------------------------------------- /examples/custom_axis_scaling.py: -------------------------------------------------------------------------------- 1 | import ternary 2 | 3 | # Simple example: 4 | ## Boundary and Gridlines 5 | scale = 9 6 | figure, tax = ternary.figure(scale=scale) 7 | 8 | tax.ax.axis("off") 9 | figure.set_facecolor('w') 10 | 11 | # Draw Boundary and Gridlines 12 | tax.boundary(linewidth=1.0) 13 | tax.gridlines(color="black", multiple=1, linewidth=0.5, ls='-') 14 | 15 | # Set Axis labels and Title 16 | fontsize = 16 17 | tax.left_axis_label("Logs", fontsize=fontsize, offset=0.13) 18 | tax.right_axis_label("Dogs", fontsize=fontsize, offset=0.12) 19 | tax.bottom_axis_label("Hogs", fontsize=fontsize, offset=0.06) 20 | 21 | 22 | # Set custom axis limits by passing a dict into set_limits. 23 | # The keys are b, l and r for the three axes and the vals are a list 24 | # of the min and max in data coords for that axis. max-min for each 25 | # axis must be the same as the scale i.e. 9 in this case. 26 | tax.set_axis_limits({'b': [67, 76], 'l': [24, 33], 'r': [0, 9]}) 27 | # get and set the custom ticks: 28 | tax.get_ticks_from_axis_limits() 29 | tax.set_custom_ticks(fontsize=10, offset=0.02) 30 | 31 | # data can be plotted by entering data coords (rather than simplex coords): 32 | points = [(70, 3, 27), (73, 2, 25), (68, 6, 26)] 33 | points_c = tax.convert_coordinates(points,axisorder='brl') 34 | tax.scatter(points_c, marker='o', s=25, c='r') 35 | 36 | tax.ax.set_aspect('equal', adjustable='box') 37 | tax._redraw_labels() 38 | 39 | 40 | ## Simple example with axis tick formatting: 41 | ## Boundary and Gridlines 42 | scale = 9 43 | figure, tax = ternary.figure(scale=scale) 44 | 45 | tax.ax.axis("off") 46 | figure.set_facecolor('w') 47 | 48 | # Draw Boundary and Gridlines 49 | tax.boundary(linewidth=1.0) 50 | tax.gridlines(color="black", multiple=1, linewidth=0.5, ls='-') 51 | 52 | # Set Axis labels and Title 53 | fontsize = 16 54 | tax.left_axis_label("Logs", fontsize=fontsize, offset=0.13) 55 | tax.right_axis_label("Dogs", fontsize=fontsize, offset=0.12) 56 | tax.bottom_axis_label("Hogs", fontsize=fontsize, offset=0.06) 57 | 58 | 59 | # Set custom axis limits by passing a dict into set_limits. 60 | # The keys are b, l and r for the three axes and the vals are a list 61 | # of the min and max in data coords for that axis. max-min for each 62 | # axis must be the same as the scale i.e. 9 in this case. 63 | tax.set_axis_limits({'b': [67, 76], 'l': [24, 33], 'r': [0, 9]}) 64 | # get and set the custom ticks: 65 | # custom tick formats: 66 | # tick_formats can either be a dict, like below or a single format string 67 | # e.g. "%.3e" (valid for all 3 axes) or None, in which case, ints are 68 | # plotted for all 3 axes. 69 | tick_formats = {'b': "%.2f", 'r': "%d", 'l': "%.1f"} 70 | 71 | tax.get_ticks_from_axis_limits() 72 | tax.set_custom_ticks(fontsize=10, offset=0.02, tick_formats=tick_formats) 73 | 74 | # data can be plotted by entering data coords (rather than simplex coords): 75 | points = [(70, 3, 27), (73, 2, 25), (68, 6, 26)] 76 | points_c = tax.convert_coordinates(points,axisorder='brl') 77 | tax.scatter(points_c, marker='o', s=25, c='r') 78 | 79 | tax.ax.set_aspect('equal', adjustable='box') 80 | tax._redraw_labels() 81 | 82 | ## Zoom example: 83 | ## Draw a plot with the full range on the left and a second plot which 84 | ## shows a zoomed region of the left plot. 85 | fig = ternary.plt.figure(figsize=(11, 6)) 86 | ax1 = fig.add_subplot(2, 1, 1) 87 | ax2 = fig.add_subplot(2, 1, 2) 88 | 89 | tax1 = ternary.TernaryAxesSubplot(ax=ax1, scale=100) 90 | tax1.boundary(linewidth=1.0) 91 | tax1.gridlines(color="black", multiple=10, linewidth=0.5, ls='-') 92 | tax1.ax.axis("equal") 93 | tax1.ax.axis("off") 94 | 95 | tax2 = ternary.TernaryAxesSubplot(ax=ax2,scale=30) 96 | axes_colors = {'b': 'r', 'r': 'r', 'l': 'r'} 97 | tax2.boundary(linewidth=1.0, axes_colors=axes_colors) 98 | tax2.gridlines(color="r", multiple=5, linewidth=0.5, ls='-') 99 | tax2.ax.axis("equal") 100 | tax2.ax.axis("off") 101 | 102 | fontsize = 16 103 | tax1.set_title("Entire range") 104 | tax1.left_axis_label("Logs", fontsize=fontsize, offset=0.12) 105 | tax1.right_axis_label("Dogs", fontsize=fontsize, offset=0.12) 106 | tax1.bottom_axis_label("Hogs", fontsize=fontsize, offset=0.) 107 | tax2.set_title("Zoomed region",color='r') 108 | tax2.left_axis_label("Logs", fontsize=fontsize, offset=0.17, color='r') 109 | tax2.right_axis_label("Dogs", fontsize=fontsize, offset=0.17, color='r') 110 | tax2.bottom_axis_label("Hogs", fontsize=fontsize, offset=0.03, color='r') 111 | 112 | tax1.ticks(multiple=10,offset=0.02) 113 | 114 | tax2.set_axis_limits({'b': [60, 75], 'l': [15, 30], 'r': [10, 25]}) 115 | tax2.get_ticks_from_axis_limits(multiple=5) 116 | tick_formats = "%.1f" 117 | tax2.set_custom_ticks(fontsize=10, offset=0.025, multiple=5, 118 | axes_colors=axes_colors, tick_formats=tick_formats) 119 | 120 | # plot some data 121 | points = [(62, 12, 26), (63.5, 13.5, 23), (65, 14, 21), (61, 15, 24), 122 | (62, 16, 22), (67.5, 14.5, 18), (68.2, 16.5, 15.3), (62, 22.5, 15.5)] 123 | 124 | # data coords == simplex coords: 125 | tax1.scatter(points, marker='^', s=25, c='b') 126 | # data coords != simplex coords: 127 | points_c = tax2.convert_coordinates(points, axisorder='brl') 128 | tax2.scatter(points_c, marker='^', s=25, c='b') 129 | 130 | # draw the zoom region on the first plot 131 | tax1.line((60, 10, 30), (75, 10, 15), color='r', lw=2.0) 132 | tax1.line((60, 10, 30), (60, 25, 15), color='r', lw=2.0) 133 | tax1.line((75, 10, 15), (60, 25, 15), color='r', lw=2.0) 134 | 135 | fig.set_facecolor("w") 136 | 137 | tax1.ax.set_position([0.01, 0.05, 0.46, 0.8]) 138 | tax2.ax.set_position([0.50, 0.05, 0.46, 0.8]) 139 | 140 | tax1.resize_drawing_canvas() 141 | tax2.resize_drawing_canvas() 142 | ternary.plt.show() 143 | -------------------------------------------------------------------------------- /examples/sample_data/sample_heatmap_data.txt: -------------------------------------------------------------------------------- 1 | 24 9 27 0.000576293331224 2 | 15 27 18 0.000597312322493 3 | 1 59 0 0.000518034490428 4 | 21 3 36 0.000538706957448 5 | 26 2 32 0.000588226360032 6 | 28 10 22 0.00057788496369 7 | 5 38 17 0.000498688979294 8 | 20 10 30 0.000567932192157 9 | 11 40 9 0.000465294284403 10 | 7 29 24 0.000563439276973 11 | 15 33 12 0.000546726531298 12 | 29 26 5 0.000561048821157 13 | 10 11 39 0.000477498435273 14 | 2 33 25 0.000583686849508 15 | 2 42 16 0.000495347908903 16 | 31 27 2 0.000591497111978 17 | 30 11 19 0.00057059401282 18 | 9 22 29 0.000569998719715 19 | 5 15 40 0.000475193799381 20 | 24 30 6 0.000558212941977 21 | 8 4 48 0.000379415766803 22 | 14 26 20 0.000599917880717 23 | 36 15 9 0.000513508110547 24 | 20 32 8 0.000550001670772 25 | 27 8 25 0.000571548093182 26 | 3 19 38 0.00051918527119 27 | 7 38 15 0.000492102347195 28 | 4 53 3 0.000339990113993 29 | 8 16 36 0.000513625240855 30 | 4 28 28 0.000564695427811 31 | 22 20 18 0.000618232740386 32 | 4 27 29 0.000564064666008 33 | 4 54 2 0.000345614053259 34 | 19 28 13 0.000587904261256 35 | 23 27 10 0.000581049081881 36 | 2 55 3 0.000343377513917 37 | 2 32 26 0.000588226360032 38 | 13 1 46 0.00048585847447 39 | 21 14 25 0.000603091017132 40 | 41 14 5 0.000462688733129 41 | 10 34 16 0.000535133560842 42 | 28 29 3 0.000573094946374 43 | 11 12 37 0.000501914704779 44 | 24 29 7 0.000563439276973 45 | 43 15 2 0.000481793851003 46 | 10 30 20 0.000567932192157 47 | 15 25 20 0.000606046935916 48 | 18 11 31 0.000563350221647 49 | 29 8 23 0.000566517703157 50 | 17 8 35 0.000524024255459 51 | 6 25 29 0.000561326032631 52 | 9 37 14 0.000502091716183 53 | 37 11 12 0.000501914704779 54 | 2 46 12 0.000439466945027 55 | 57 0 3 0.000472067807554 56 | 8 27 25 0.000571548093182 57 | 29 28 3 0.000573094946374 58 | 17 23 20 0.00061527433194 59 | 1 5 54 0.000376584228843 60 | 16 25 19 0.000608054593057 61 | 31 25 4 0.000559052024715 62 | 11 49 0 0.000577577412196 63 | 11 6 43 0.000432035856162 64 | 5 8 47 0.000385449128184 65 | 25 5 30 0.000558551095357 66 | 10 45 5 0.000410563857799 67 | 28 27 5 0.000562303307602 68 | 0 10 50 0.000558882190036 69 | 6 24 30 0.000558212941977 70 | 17 37 6 0.000505573889982 71 | 43 0 17 0.000690632698311 72 | 50 2 8 0.000384523914961 73 | 4 25 31 0.000559052024715 74 | 6 18 36 0.000515949173091 75 | 23 25 12 0.000594636992935 76 | 48 1 11 0.000455005153111 77 | 4 44 12 0.000431349703568 78 | 12 0 48 0.000596634336102 79 | 39 5 16 0.00048723137665 80 | 3 42 15 0.000471212939884 81 | 25 19 16 0.000608054593057 82 | 6 46 8 0.000394128000967 83 | 34 9 17 0.000534309679581 84 | 33 25 2 0.000583686849508 85 | 9 0 51 0.000540788525529 86 | 4 32 24 0.000554714471559 87 | 26 6 28 0.000563205272437 88 | 17 10 33 0.00054475886607 89 | 28 20 12 0.00058513062186 90 | 0 25 35 0.000799937494985 91 | 57 2 1 0.000373898666741 92 | 12 41 7 0.000455032512107 93 | 7 24 29 0.000563439276973 94 | 39 14 7 0.000480132083066 95 | 29 30 1 0.000644598993328 96 | 15 34 11 0.000535861017312 97 | 10 15 35 0.000524714144684 98 | 22 36 2 0.000562897480566 99 | 12 45 3 0.000430776639063 100 | 13 15 32 0.000557125584217 101 | 8 19 33 0.00054230015282 102 | 5 10 45 0.000410563857799 103 | 53 7 0 0.000507639277123 104 | 34 8 18 0.000533613397403 105 | 14 34 12 0.000536349975984 106 | 21 16 23 0.000613089417709 107 | 1 21 38 0.000596129646217 108 | 19 0 41 0.000724583462662 109 | 16 36 8 0.000513625240855 110 | 6 6 48 0.000372188153326 111 | 23 37 0 0.000780130337846 112 | 20 1 39 0.000584780420163 113 | 21 31 8 0.000556645045176 114 | 13 5 42 0.000449836829755 115 | 2 9 49 0.000397579514036 116 | 37 16 7 0.000503520406582 117 | 30 13 17 0.000574518286348 118 | 30 6 24 0.000558212941977 119 | 10 38 12 0.000489858926854 120 | 22 35 3 0.00054703893668 121 | 13 32 15 0.000557125584217 122 | 34 21 5 0.000536736834183 123 | 43 11 6 0.000432035856162 124 | 9 2 49 0.000397579514036 125 | 32 24 4 0.000554714471559 126 | 9 39 12 0.000477841915704 127 | 20 14 26 0.000599917880717 128 | 4 30 26 0.000562177966124 129 | 20 9 31 0.000558920375004 130 | 35 9 16 0.000524284544741 131 | 29 0 31 0.000820418017588 132 | 17 19 24 0.000612777218961 133 | 0 2 58 0.000481572115707 134 | 51 4 5 0.000348420109833 135 | 30 12 18 0.000572833115344 136 | 0 32 28 0.000817820521541 137 | 20 24 16 0.00061119221221 138 | 11 2 47 0.000425247956462 139 | 8 17 35 0.000524024255459 140 | 5 12 43 0.000436768161023 141 | 34 20 6 0.00053414903673 142 | 6 43 11 0.000432035856162 143 | 5 43 12 0.000436768161023 144 | 42 10 8 0.000440682063633 145 | 25 32 3 0.000565485562125 146 | 36 14 10 0.000513611204695 147 | 38 13 9 0.000490157910944 148 | 4 33 23 0.000549203580748 149 | 4 29 27 0.000564064666008 150 | 36 24 0 0.00079077667923 151 | 22 25 13 0.00059925571499 152 | 20 7 33 0.000541563717313 153 | 1 12 47 0.000470390572843 154 | 22 15 23 0.000609861242306 155 | 37 18 5 0.000509461743506 156 | 0 59 1 0.000518034490428 157 | 41 4 15 0.000470617386893 158 | 31 7 22 0.000554785832172 159 | 17 16 27 0.000598219286947 160 | 25 15 20 0.000606046935916 161 | 24 23 13 0.000600539275794 162 | 6 22 32 0.000548409201255 163 | 15 19 26 0.000602275177782 164 | 19 41 0 0.000724583462662 165 | 33 10 17 0.00054475886607 166 | 38 12 10 0.000489858926854 167 | 28 24 8 0.000569654303715 168 | 49 3 8 0.000377801164992 169 | 16 33 11 0.000545892195496 170 | 7 19 34 0.000533385575385 171 | 10 10 40 0.000465039644211 172 | 7 28 25 0.000565946301449 173 | 7 37 16 0.000503520406582 174 | 8 20 32 0.000550001670772 175 | 29 2 29 0.000594130761983 176 | 13 39 8 0.000478638959343 177 | 10 3 47 0.000403607001432 178 | 2 41 17 0.000508422014232 179 | 48 12 0 0.000596634336102 180 | 31 19 10 0.000561240961747 181 | 30 3 27 0.000571817425947 182 | 38 15 7 0.000492102347195 183 | 35 1 24 0.000623793059998 184 | 14 10 36 0.000513611204695 185 | 5 14 41 0.000462688733129 186 | 1 49 10 0.000439869993691 187 | 5 7 48 0.000373883418934 188 | 24 22 14 0.000605008670315 189 | 10 44 6 0.000419094260354 190 | 21 20 19 0.000620024731277 191 | 44 14 2 0.00046787785258 192 | 26 0 34 0.000807537834977 193 | 7 46 7 0.000392997219042 194 | 20 2 38 0.000543676659051 195 | 29 7 24 0.000563439276973 196 | 27 23 10 0.000581049081881 197 | 5 17 38 0.000498688979294 198 | 20 5 35 0.000528571188991 199 | 1 16 43 0.00053128655342 200 | 1 14 45 0.000501256623742 201 | 2 40 18 0.000520904307398 202 | 13 9 38 0.000490157910944 203 | 37 20 3 0.00052939568825 204 | 2 18 40 0.000520904307398 205 | 41 6 13 0.000457900975492 206 | 10 33 17 0.00054475886607 207 | 53 1 6 0.000386369230522 208 | 13 36 11 0.000513742592469 209 | 24 21 15 0.00060858507757 210 | 46 11 3 0.000417114121438 211 | 15 17 28 0.000591215071466 212 | 19 39 2 0.000532689007384 213 | 17 24 19 0.000612777218961 214 | 25 7 28 0.000565946301449 215 | 0 3 57 0.000472067807554 216 | 5 48 7 0.000373883418934 217 | 16 26 18 0.000603713170637 218 | 14 15 31 0.00056690063487 219 | 35 5 20 0.000528571188991 220 | 19 30 11 0.00057059401282 221 | 25 17 18 0.000609069388139 222 | 29 4 27 0.000564064666008 223 | 2 53 5 0.000352159833508 224 | 16 23 21 0.000613089417709 225 | 31 15 14 0.00056690063487 226 | 48 10 2 0.000411225549409 227 | 31 17 12 0.000565069647464 228 | 0 56 4 0.000473881568417 229 | 10 32 18 0.000553490763168 230 | 33 9 18 0.000543482679964 231 | 11 14 35 0.000525103680786 232 | 31 6 23 0.000553894267806 233 | 42 14 4 0.000457792403926 234 | 21 22 17 0.000616530447443 235 | 32 20 8 0.000550001670772 236 | 3 41 16 0.000484072500847 237 | 41 17 2 0.000508422014232 238 | 8 32 20 0.000550001670772 239 | 18 12 30 0.000572833115344 240 | 36 10 14 0.000513611204695 241 | 6 26 28 0.000563205272437 242 | 1 18 41 0.000559440397503 243 | 2 52 6 0.000361360418101 244 | 16 22 22 0.000613724258099 245 | 48 9 3 0.000390432381229 246 | 37 22 1 0.000606470645494 247 | 1 47 12 0.000470390572843 248 | 39 13 8 0.000478638959343 249 | 23 22 15 0.000609861242306 250 | 13 38 9 0.000490157910944 251 | 6 45 9 0.000406384681257 252 | 4 12 44 0.000431349703568 253 | 31 8 21 0.000556645045176 254 | 2 29 29 0.000594130761983 255 | 33 1 26 0.00063616965736 256 | 4 56 0 0.000473881568417 257 | 17 4 39 0.000494870083604 258 | 12 34 14 0.000536349975984 259 | 11 20 29 0.000576745146519 260 | 38 8 14 0.000490815596489 261 | 0 23 37 0.000780130337846 262 | 0 1 59 0.000518034490428 263 | 5 50 5 0.000354655431723 264 | 0 44 16 0.00067257173668 265 | 35 3 22 0.00054703893668 266 | 9 40 11 0.000465294284403 267 | 16 21 23 0.000613089417709 268 | 0 12 48 0.000596634336102 269 | 54 4 2 0.000345614053259 270 | 26 1 33 0.00063616965736 271 | 28 32 0 0.000817820521541 272 | 34 7 19 0.000533385575385 273 | 3 4 53 0.000339990113993 274 | 16 41 3 0.000484072500847 275 | 14 30 16 0.000575562514854 276 | 5 28 27 0.000562303307602 277 | 3 39 18 0.000508163523847 278 | 20 6 34 0.00053414903673 279 | 25 8 27 0.000571548093182 280 | 0 0 60 0.000672927508895 281 | 2 3 55 0.000343377513917 282 | 40 16 4 0.000483014315787 283 | 27 19 14 0.000595523628638 284 | 16 44 0 0.00067257173668 285 | 1 43 16 0.00053128655342 286 | 7 26 27 0.000567205618363 287 | 39 8 13 0.000478638959343 288 | 1 56 3 0.000367461057285 289 | 10 13 37 0.000501947385234 290 | 2 35 23 0.000570968029697 291 | 41 19 0 0.000724583462662 292 | 13 13 34 0.000536521423079 293 | 15 11 34 0.000535861017312 294 | 30 30 0 0.000821286248119 295 | 7 45 8 0.000404493816949 296 | 53 5 2 0.000352159833508 297 | 13 40 7 0.000467731273606 298 | 38 11 11 0.000489774401922 299 | 33 21 6 0.000541807496111 300 | 14 7 39 0.000480132083066 301 | 17 6 37 0.000505573889982 302 | 19 35 6 0.000525503361058 303 | 29 10 21 0.000573498390044 304 | 0 21 39 0.000754738932243 305 | 16 43 1 0.00053128655342 306 | 5 52 3 0.000346374006092 307 | 6 4 50 0.000357258223823 308 | 18 29 13 0.000581753357221 309 | 19 26 15 0.000602275177782 310 | 9 42 9 0.000440213838911 311 | 14 44 2 0.00046787785258 312 | 0 16 44 0.00067257173668 313 | 13 3 44 0.000444439223787 314 | 18 2 40 0.000520904307398 315 | 30 4 26 0.000562177966124 316 | 28 6 26 0.000563205272437 317 | 20 16 24 0.00061119221221 318 | 11 10 39 0.000477498435273 319 | 38 2 20 0.000543676659051 320 | 24 16 20 0.00061119221221 321 | 42 9 9 0.000440213838911 322 | 4 43 13 0.000444660026359 323 | 18 13 29 0.000581753357221 324 | 32 15 13 0.000557125584217 325 | 25 28 7 0.000565946301449 326 | 3 37 20 0.00052939568825 327 | 40 4 16 0.000483014315787 328 | 25 10 25 0.000583598958715 329 | 10 23 27 0.000581049081881 330 | 8 47 5 0.000385449128184 331 | 16 42 2 0.000495347908903 332 | 33 17 10 0.00054475886607 333 | 36 16 8 0.000513625240855 334 | 5 5 50 0.000354655431723 335 | 1 7 52 0.000398085617614 336 | 51 6 3 0.000355326542624 337 | 6 7 47 0.000382603299343 338 | 12 12 36 0.000513797505382 339 | 13 42 5 0.000449836829755 340 | 6 41 13 0.000457900975492 341 | 52 4 4 0.000342124459796 342 | 31 12 17 0.000565069647464 343 | 46 6 8 0.000394128000967 344 | 42 8 10 0.000440682063633 345 | 4 42 14 0.000457792403926 346 | 13 31 16 0.000566278233658 347 | 14 27 19 0.000595523628638 348 | 40 3 17 0.000496425389375 349 | 17 39 4 0.000494870083604 350 | 8 46 6 0.000394128000967 351 | 36 13 11 0.000513742592469 352 | 5 54 1 0.000376584228843 353 | 48 2 10 0.000411225549409 354 | 3 53 4 0.000339990113993 355 | 22 34 4 0.00054256788204 356 | 5 24 31 0.000554832508077 357 | 1 33 26 0.00063616965736 358 | 54 0 6 0.000493456285536 359 | 30 27 3 0.000571817425947 360 | 11 8 41 0.000453414475786 361 | 49 0 11 0.000577577412196 362 | 6 40 14 0.000470520169659 363 | 24 14 22 0.000605008670315 364 | 3 15 42 0.000471212939884 365 | 4 41 15 0.000470617386893 366 | 32 19 9 0.000551712985417 367 | 32 13 15 0.000557125584217 368 | 25 30 5 0.000558551095357 369 | 27 33 0 0.000813515266876 370 | 40 2 18 0.000520904307398 371 | 8 45 7 0.000404493816949 372 | 27 31 2 0.000591497111978 373 | 2 27 31 0.000591497111978 374 | 39 12 9 0.000477841915704 375 | 1 24 35 0.000623793059998 376 | 23 11 26 0.000588081747586 377 | 22 27 11 0.000585532967493 378 | 21 27 12 0.000589536586579 379 | 30 1 29 0.000644598993328 380 | 3 35 22 0.00054703893668 381 | 10 50 0 0.000558882190036 382 | 17 26 17 0.000604196392016 383 | 32 2 26 0.000588226360032 384 | 24 13 23 0.000600539275794 385 | 55 0 5 0.000481789016038 386 | 32 18 10 0.000553490763168 387 | 6 5 49 0.000363438071327 388 | 15 37 8 0.000502518420415 389 | 18 27 15 0.000597312322493 390 | 7 40 13 0.000467731273606 391 | 10 41 9 0.000452685505882 392 | 40 1 19 0.00057251691181 393 | 7 7 46 0.000392997219042 394 | 0 11 49 0.000577577412196 395 | 20 15 25 0.000606046935916 396 | 29 12 19 0.000579543525933 397 | 1 53 6 0.000386369230522 398 | 27 16 17 0.000598219286947 399 | 38 0 22 0.000768085194696 400 | 30 15 15 0.000575915999366 401 | 21 0 39 0.000754738932243 402 | 30 0 30 0.000821286248119 403 | 16 2 42 0.000495347908903 404 | 11 1 48 0.000455005153111 405 | 24 15 21 0.00060858507757 406 | 49 2 9 0.000397579514036 407 | 10 27 23 0.000581049081881 408 | 3 13 44 0.000444439223787 409 | 4 47 9 0.000391839926085 410 | 23 21 16 0.000613089417709 411 | 10 12 38 0.000489858926854 412 | 43 16 1 0.00053128655342 413 | 27 29 4 0.000564064666008 414 | 13 21 26 0.00059670199104 415 | 11 19 30 0.00057059401282 416 | 6 34 20 0.00053414903673 417 | 1 26 33 0.00063616965736 418 | 48 4 8 0.000379415766803 419 | 1 3 56 0.000367461057285 420 | 48 7 5 0.000373883418934 421 | 0 42 18 0.000708016814567 422 | 51 2 7 0.000372328832234 423 | 30 2 28 0.000593470924499 424 | 14 11 35 0.000525103680786 425 | 39 21 0 0.000754738932243 426 | 23 30 7 0.000559707654648 427 | 13 46 1 0.00048585847447 428 | 25 3 32 0.000565485562125 429 | 4 4 52 0.000342124459796 430 | 31 0 29 0.000820418017588 431 | 4 46 10 0.000404772750137 432 | 9 16 35 0.000524284544741 433 | 4 48 8 0.000379415766803 434 | 19 12 29 0.000579543525933 435 | 11 28 21 0.000581741494327 436 | 17 35 8 0.000524024255459 437 | 24 36 0 0.00079077667923 438 | 28 4 28 0.000564695427811 439 | 0 9 51 0.000540788525529 440 | 2 2 56 0.000349138531125 441 | 29 14 17 0.000583277471058 442 | 16 16 28 0.000591639193906 443 | 48 6 6 0.000372188153326 444 | 31 5 24 0.000554832508077 445 | 0 52 8 0.000523584874616 446 | 21 2 37 0.000553774587906 447 | 0 39 21 0.000754738932243 448 | 0 46 14 0.000635017886853 449 | 31 10 19 0.000561240961747 450 | 3 11 46 0.000417114121438 451 | 44 1 15 0.000516443688638 452 | 4 45 11 0.000418002012485 453 | 14 29 17 0.000583277471058 454 | 14 22 24 0.000605008670315 455 | 11 22 27 0.000585532967493 456 | 43 4 13 0.000444660026359 457 | 16 28 16 0.000591639193906 458 | 27 27 6 0.000563833588954 459 | 23 35 2 0.000570968029697 460 | 2 31 27 0.000591497111978 461 | 17 32 11 0.000555087730158 462 | 10 5 45 0.000410563857799 463 | 48 5 7 0.000373883418934 464 | 1 19 40 0.00057251691181 465 | 0 51 9 0.000540788525529 466 | 5 39 16 0.00048723137665 467 | 30 29 1 0.000644598993328 468 | 0 45 15 0.00065398171375 469 | 19 25 16 0.000608054593057 470 | 34 5 21 0.000536736834183 471 | 55 4 1 0.000369686093265 472 | 3 6 51 0.000355326542624 473 | 9 25 26 0.000577564346527 474 | 9 18 33 0.000543482679964 475 | 14 28 18 0.00058995627968 476 | 18 31 11 0.000563350221647 477 | 7 44 9 0.00041670467739 478 | 35 13 12 0.000525327868788 479 | 45 0 15 0.00065398171375 480 | 8 40 12 0.000466122480142 481 | 39 3 18 0.000508163523847 482 | 26 26 8 0.000572181333105 483 | 44 2 14 0.00046787785258 484 | 39 10 11 0.000477498435273 485 | 35 25 0 0.000799937494985 486 | 5 30 25 0.000558551095357 487 | 22 29 9 0.000569998719715 488 | 22 33 5 0.00054387644544 489 | 0 50 10 0.000558882190036 490 | 21 4 35 0.000534865688674 491 | 30 28 2 0.000593470924499 492 | 53 3 4 0.000339990113993 493 | 38 10 12 0.000489858926854 494 | 34 4 22 0.00054256788204 495 | 10 31 19 0.000561240961747 496 | 31 11 18 0.000563350221647 497 | 12 3 45 0.000430776639063 498 | 19 1 40 0.00057251691181 499 | 17 13 30 0.000574518286348 500 | 25 20 15 0.000606046935916 501 | 15 7 38 0.000492102347195 502 | 17 21 22 0.000616530447443 503 | 8 39 13 0.000478638959343 504 | 13 25 22 0.00059925571499 505 | 23 33 4 0.000549203580748 506 | 17 34 9 0.000534309679581 507 | 22 28 10 0.00057788496369 508 | 12 40 8 0.000466122480142 509 | 18 0 42 0.000708016814567 510 | 12 2 46 0.000439466945027 511 | 13 44 3 0.000444439223787 512 | 15 1 44 0.000516443688638 513 | 19 23 18 0.000616376538476 514 | 2 10 48 0.000411225549409 515 | 0 48 12 0.000596634336102 516 | 24 7 29 0.000563439276973 517 | 31 4 25 0.000559052024715 518 | 12 5 43 0.000436768161023 519 | 14 19 27 0.000595523628638 520 | 43 13 4 0.000444660026359 521 | 25 1 34 0.000630629627707 522 | 16 39 5 0.00048723137665 523 | 7 0 53 0.000507639277123 524 | 45 1 14 0.000501256623742 525 | 7 35 18 0.0005242606635 526 | 0 40 20 0.000740199339251 527 | 7 53 0 0.000507639277123 528 | 36 20 4 0.000526164563267 529 | 0 6 54 0.000493456285536 530 | 51 8 1 0.000411152021077 531 | 16 19 25 0.000608054593057 532 | 11 39 10 0.000477498435273 533 | 1 41 18 0.000559440397503 534 | 21 6 33 0.000541807496111 535 | 30 19 11 0.00057059401282 536 | 49 8 3 0.000377801164992 537 | 24 6 30 0.000558212941977 538 | 20 23 17 0.00061527433194 539 | 32 27 1 0.000640366337092 540 | 14 25 21 0.000603091017132 541 | 20 8 32 0.000550001670772 542 | 25 22 13 0.00059925571499 543 | 38 3 19 0.00051918527119 544 | 11 18 31 0.000563350221647 545 | 16 38 6 0.000494473283901 546 | 27 7 26 0.000567205618363 547 | 21 30 9 0.000565035203114 548 | 37 8 15 0.000502518420415 549 | 20 27 13 0.000592904844502 550 | 30 25 5 0.000558551095357 551 | 17 20 23 0.00061527433194 552 | 10 49 1 0.000439869993691 553 | 4 14 42 0.000457792403926 554 | 9 33 18 0.000543482679964 555 | 24 5 31 0.000554832508077 556 | 15 31 14 0.00056690063487 557 | 3 2 55 0.000343377513917 558 | 3 24 33 0.000560486469349 559 | 14 24 22 0.000605008670315 560 | 5 26 29 0.000561048821157 561 | 16 37 7 0.000503520406582 562 | 7 15 38 0.000492102347195 563 | 15 22 23 0.000609861242306 564 | 26 30 4 0.000562177966124 565 | 7 33 20 0.000541563717313 566 | 44 0 16 0.00067257173668 567 | 35 21 4 0.000534865688674 568 | 19 14 27 0.000595523628638 569 | 2 37 21 0.000553774587906 570 | 48 0 12 0.000596634336102 571 | 11 37 12 0.000501914704779 572 | 8 9 43 0.000428118760096 573 | 6 30 24 0.000558212941977 574 | 30 24 6 0.000558212941977 575 | 14 14 32 0.000557395520244 576 | 10 48 2 0.000411225549409 577 | 49 10 1 0.000439869993691 578 | 33 23 4 0.000549203580748 579 | 55 2 3 0.000343377513917 580 | 10 19 31 0.000561240961747 581 | 10 28 22 0.00057788496369 582 | 31 22 7 0.000554785832172 583 | 32 25 3 0.000565485562125 584 | 18 6 36 0.000515949173091 585 | 7 42 11 0.000442181806539 586 | 12 28 20 0.00058513062186 587 | 3 57 0 0.000472067807554 588 | 10 4 46 0.000404772750137 589 | 15 24 21 0.00060858507757 590 | 18 1 41 0.000559440397503 591 | 13 29 18 0.000581753357221 592 | 45 15 0 0.00065398171375 593 | 1 20 39 0.000584780420163 594 | 6 42 12 0.000445024944313 595 | 1 2 57 0.000373898666741 596 | 2 36 22 0.000562897480566 597 | 28 15 17 0.000591215071466 598 | 9 7 44 0.00041670467739 599 | 20 25 15 0.000606046935916 600 | 18 18 24 0.000613308980689 601 | 17 22 21 0.000616530447443 602 | 19 19 22 0.00061880325113 603 | 23 6 31 0.000553894267806 604 | 26 31 3 0.000569273557353 605 | 42 11 7 0.000442181806539 606 | 31 24 5 0.000554832508077 607 | 14 36 10 0.000513611204695 608 | 12 9 39 0.000477841915704 609 | 26 8 26 0.000572181333105 610 | 37 15 8 0.000502518420415 611 | 0 7 53 0.000507639277123 612 | 7 4 49 0.00036776787047 613 | 13 19 28 0.000587904261256 614 | 45 5 10 0.000410563857799 615 | 0 4 56 0.000473881568417 616 | 35 19 6 0.000525503361058 617 | 11 35 14 0.000525103680786 618 | 0 60 0 0.000672927508895 619 | 26 22 12 0.000592716196383 620 | 28 5 27 0.000562303307602 621 | 23 24 13 0.000600539275794 622 | 43 9 8 0.000428118760096 623 | 31 2 27 0.000591497111978 624 | 18 14 28 0.00058995627968 625 | 3 20 37 0.00052939568825 626 | 14 21 25 0.000603091017132 627 | 6 19 35 0.000525503361058 628 | 3 55 2 0.000343377513917 629 | 11 30 19 0.00057059401282 630 | 43 12 5 0.000436768161023 631 | 4 15 41 0.000470617386893 632 | 27 3 30 0.000571817425947 633 | 16 24 20 0.00061119221221 634 | 5 37 18 0.000509461743506 635 | 24 3 33 0.000560486469349 636 | 1 22 37 0.000606470645494 637 | 6 27 27 0.000563833588954 638 | 37 12 11 0.000501914704779 639 | 20 31 9 0.000558920375004 640 | 30 21 9 0.000565035203114 641 | 15 45 0 0.00065398171375 642 | 33 5 22 0.00054387644544 643 | 2 45 13 0.000453724638536 644 | 12 15 33 0.000546726531298 645 | 14 20 26 0.000599917880717 646 | 43 6 11 0.000432035856162 647 | 22 18 20 0.000618232740386 648 | 5 40 15 0.000475193799381 649 | 1 38 21 0.000596129646217 650 | 0 5 55 0.000481789016038 651 | 33 12 15 0.000546726531298 652 | 26 25 9 0.000577564346527 653 | 17 9 34 0.000534309679581 654 | 8 10 42 0.000440682063633 655 | 5 29 26 0.000561048821157 656 | 6 20 34 0.00053414903673 657 | 9 44 7 0.00041670467739 658 | 19 10 31 0.000561240961747 659 | 24 20 16 0.00061119221221 660 | 22 14 24 0.000605008670315 661 | 14 0 46 0.000635017886853 662 | 0 58 2 0.000481572115707 663 | 30 20 10 0.000567932192157 664 | 52 3 5 0.000346374006092 665 | 33 19 8 0.00054230015282 666 | 24 0 36 0.00079077667923 667 | 3 8 49 0.000377801164992 668 | 3 1 56 0.000367461057285 669 | 41 9 10 0.000452685505882 670 | 2 50 8 0.000384523914961 671 | 25 12 23 0.000594636992935 672 | 47 7 6 0.000382603299343 673 | 40 20 0 0.000740199339251 674 | 16 32 12 0.000556335751302 675 | 15 28 17 0.000591215071466 676 | 26 24 10 0.000582959937935 677 | 41 0 19 0.000724583462662 678 | 2 28 30 0.000593470924499 679 | 24 19 17 0.000612777218961 680 | 37 14 9 0.000502091716183 681 | 1 55 4 0.000369686093265 682 | 18 8 34 0.000533613397403 683 | 12 10 38 0.000489858926854 684 | 5 49 6 0.000363438071327 685 | 55 5 0 0.000481789016038 686 | 52 2 6 0.000361360418101 687 | 10 29 21 0.000573498390044 688 | 42 15 3 0.000471212939884 689 | 31 28 1 0.000643184095371 690 | 12 13 35 0.000525327868788 691 | 18 28 14 0.00058995627968 692 | 26 34 0 0.000807537834977 693 | 5 42 13 0.000449836829755 694 | 0 27 33 0.000813515266876 695 | 33 14 13 0.000547166402343 696 | 23 9 28 0.000573763413236 697 | 45 9 6 0.000406384681257 698 | 4 31 25 0.000559052024715 699 | 9 46 5 0.000397766746838 700 | 24 18 18 0.000613308980689 701 | 12 36 12 0.000513797505382 702 | 51 0 9 0.000540788525529 703 | 11 47 2 0.000425247956462 704 | 19 21 20 0.000620024731277 705 | 38 6 16 0.000494473283901 706 | 23 28 9 0.000573763413236 707 | 1 15 44 0.000516443688638 708 | 52 1 7 0.000398085617614 709 | 23 29 8 0.000566517703157 710 | 5 47 8 0.000385449128184 711 | 32 3 25 0.000565485562125 712 | 14 17 29 0.000583277471058 713 | 20 0 40 0.000740199339251 714 | 25 14 21 0.000603091017132 715 | 3 51 6 0.000355326542624 716 | 11 26 23 0.000588081747586 717 | 40 18 2 0.000520904307398 718 | 13 0 47 0.000615843998012 719 | 7 2 51 0.000372328832234 720 | 27 15 18 0.000597312322493 721 | 41 2 17 0.000508422014232 722 | 10 7 43 0.000429342904055 723 | 3 49 8 0.000377801164992 724 | 4 2 54 0.000345614053259 725 | 1 8 51 0.000411152021077 726 | 0 47 13 0.000615843998012 727 | 48 3 9 0.000390432381229 728 | 22 11 27 0.000585532967493 729 | 21 11 28 0.000581741494327 730 | 0 41 19 0.000724583462662 731 | 30 17 13 0.000574518286348 732 | 10 2 48 0.000411225549409 733 | 11 32 17 0.000555087730158 734 | 14 16 30 0.000575562514854 735 | 14 31 15 0.00056690063487 736 | 6 21 33 0.000541807496111 737 | 43 2 15 0.000481793851003 738 | 40 17 3 0.000496425389375 739 | 33 18 9 0.000543482679964 740 | 9 5 46 0.000397766746838 741 | 31 21 8 0.000556645045176 742 | 6 16 38 0.000494473283901 743 | 22 38 0 0.000768085194696 744 | 25 25 10 0.000583598958715 745 | 10 47 3 0.000403607001432 746 | 1 37 22 0.000606470645494 747 | 11 45 4 0.000418002012485 748 | 30 16 14 0.000575562514854 749 | 14 6 40 0.000470520169659 750 | 49 4 7 0.00036776787047 751 | 11 17 32 0.000555087730158 752 | 5 44 11 0.000423624580479 753 | 32 23 5 0.000549926340121 754 | 1 44 15 0.000516443688638 755 | 32 1 27 0.000640366337092 756 | 23 19 18 0.000616376538476 757 | 13 2 45 0.000453724638536 758 | 15 16 29 0.000584054427249 759 | 3 31 26 0.000569273557353 760 | 15 44 1 0.000516443688638 761 | 27 13 20 0.000592904844502 762 | 17 40 3 0.000496425389375 763 | 15 3 42 0.000471212939884 764 | 58 1 1 0.000400921212213 765 | 6 50 4 0.000357258223823 766 | 1 10 49 0.000439869993691 767 | 1 51 8 0.000411152021077 768 | 12 14 34 0.000536349975984 769 | 35 4 21 0.000534865688674 770 | 14 12 34 0.000536349975984 771 | 2 11 47 0.000425247956462 772 | 1 57 2 0.000373898666741 773 | 15 39 6 0.000482751300565 774 | 23 14 23 0.0006056501852 775 | 52 6 2 0.000361360418101 776 | 32 22 6 0.000548409201255 777 | 31 16 13 0.000566278233658 778 | 15 21 24 0.00060858507757 779 | 17 15 28 0.000591215071466 780 | 10 25 25 0.000583598958715 781 | 29 27 4 0.000564064666008 782 | 46 10 4 0.000404772750137 783 | 5 46 9 0.000397766746838 784 | 37 7 16 0.000503520406582 785 | 15 26 19 0.000602275177782 786 | 13 27 20 0.000592904844502 787 | 12 19 29 0.000579543525933 788 | 58 0 2 0.000481572115707 789 | 27 20 13 0.000592904844502 790 | 8 29 23 0.000566517703157 791 | 15 9 36 0.000513508110547 792 | 7 23 30 0.000559707654648 793 | 0 36 24 0.00079077667923 794 | 20 17 23 0.00061527433194 795 | 49 6 5 0.000363438071327 796 | 2 8 50 0.000384523914961 797 | 52 5 3 0.000346374006092 798 | 1 48 11 0.000455005153111 799 | 18 15 27 0.000597312322493 800 | 31 26 3 0.000569273557353 801 | 32 21 7 0.000548719190895 802 | 1 46 13 0.00048585847447 803 | 18 22 20 0.000618232740386 804 | 23 17 20 0.00061527433194 805 | 10 1 49 0.000439869993691 806 | 4 3 53 0.000339990113993 807 | 13 4 43 0.000444660026359 808 | 45 3 12 0.000430776639063 809 | 11 16 33 0.000545892195496 810 | 19 36 5 0.000519452341439 811 | 6 38 16 0.000494473283901 812 | 9 43 8 0.000428118760096 813 | 23 5 32 0.000549926340121 814 | 4 6 50 0.000357258223823 815 | 2 43 15 0.000481793851003 816 | 24 17 19 0.000612777218961 817 | 0 35 25 0.000799937494985 818 | 39 17 4 0.000494870083604 819 | 23 26 11 0.000588081747586 820 | 20 30 10 0.000567932192157 821 | 10 6 44 0.000419094260354 822 | 3 22 35 0.00054703893668 823 | 2 26 32 0.000588226360032 824 | 6 17 37 0.000505573889982 825 | 24 1 35 0.000623793059998 826 | 3 47 10 0.000403607001432 827 | 43 14 3 0.000457961301012 828 | 22 26 12 0.000592716196383 829 | 4 20 36 0.000526164563267 830 | 26 10 24 0.000582959937935 831 | 16 35 9 0.000524284544741 832 | 1 29 30 0.000644598993328 833 | 9 36 15 0.000513508110547 834 | 50 1 9 0.000425176279456 835 | 22 13 25 0.00059925571499 836 | 0 34 26 0.000807537834977 837 | 52 7 1 0.000398085617614 838 | 37 1 22 0.000606470645494 839 | 1 50 9 0.000425176279456 840 | 3 0 57 0.000472067807554 841 | 2 16 42 0.000495347908903 842 | 6 15 39 0.000482751300565 843 | 21 9 30 0.000565035203114 844 | 17 29 14 0.000583277471058 845 | 25 4 31 0.000559052024715 846 | 4 1 55 0.000369686093265 847 | 13 6 41 0.000457900975492 848 | 9 15 36 0.000513508110547 849 | 36 12 12 0.000513797505382 850 | 15 20 25 0.000606046935916 851 | 3 27 30 0.000571817425947 852 | 37 13 10 0.000501947385234 853 | 14 13 33 0.000547166402343 854 | 17 36 7 0.000514274201223 855 | 19 13 28 0.000587904261256 856 | 22 12 26 0.000592716196383 857 | 54 3 3 0.000337791784896 858 | 0 55 5 0.000481789016038 859 | 0 33 27 0.000813515266876 860 | 14 8 38 0.000490815596489 861 | 2 15 43 0.000481793851003 862 | 3 28 29 0.000573094946374 863 | 9 8 43 0.000428118760096 864 | 10 21 29 0.000573498390044 865 | 9 29 22 0.000569998719715 866 | 31 20 9 0.000558920375004 867 | 27 21 12 0.000589536586579 868 | 32 12 16 0.000556335751302 869 | 20 33 7 0.000541563717313 870 | 19 9 32 0.000551712985417 871 | 32 0 28 0.000817820521541 872 | 33 24 3 0.000560486469349 873 | 18 3 39 0.000508163523847 874 | 7 16 37 0.000503520406582 875 | 12 23 25 0.000594636992935 876 | 3 36 21 0.000538706957448 877 | 7 51 2 0.000372328832234 878 | 6 35 19 0.000525503361058 879 | 9 38 13 0.000490157910944 880 | 5 19 36 0.000519452341439 881 | 21 33 6 0.000541807496111 882 | 13 7 40 0.000467731273606 883 | 0 54 6 0.000493456285536 884 | 38 5 17 0.000498688979294 885 | 23 13 24 0.000600539275794 886 | 14 42 4 0.000457792403926 887 | 21 24 15 0.00060858507757 888 | 10 20 30 0.000567932192157 889 | 18 17 25 0.000609069388139 890 | 18 26 16 0.000603713170637 891 | 47 11 2 0.000425247956462 892 | 16 34 10 0.000535133560842 893 | 25 6 29 0.000561326032631 894 | 26 32 2 0.000588226360032 895 | 4 7 49 0.00036776787047 896 | 13 8 39 0.000478638959343 897 | 12 22 26 0.000592716196383 898 | 17 38 5 0.000498688979294 899 | 18 9 33 0.000543482679964 900 | 0 53 7 0.000507639277123 901 | 28 14 18 0.00058995627968 902 | 20 11 29 0.000576745146519 903 | 2 34 24 0.00057791759599 904 | 28 16 16 0.000591639193906 905 | 9 49 2 0.000397579514036 906 | 9 10 41 0.000452685505882 907 | 42 12 6 0.000445024944313 908 | 32 16 12 0.000556335751302 909 | 18 16 26 0.000603713170637 910 | 3 18 39 0.000508163523847 911 | 8 28 24 0.000569654303715 912 | 8 50 2 0.000384523914961 913 | 10 24 26 0.000582959937935 914 | 43 10 7 0.000429342904055 915 | 33 26 1 0.00063616965736 916 | 12 47 1 0.000470390572843 917 | 12 21 27 0.000589536586579 918 | 7 49 4 0.00036776787047 919 | 14 3 43 0.000457961301012 920 | 21 18 21 0.000618854081602 921 | 32 14 14 0.000557395520244 922 | 57 3 0 0.000472067807554 923 | 8 15 37 0.000502518420415 924 | 2 17 41 0.000508422014232 925 | 18 30 12 0.000572833115344 926 | 29 31 0 0.000820418017588 927 | 10 0 50 0.000558882190036 928 | 37 5 18 0.000509461743506 929 | 21 26 13 0.00059670199104 930 | 33 7 20 0.000541563717313 931 | 21 39 0 0.000754738932243 932 | 32 9 19 0.000551712985417 933 | 47 9 4 0.000391839926085 934 | 4 5 51 0.000348420109833 935 | 13 10 37 0.000501947385234 936 | 9 11 40 0.000465294284403 937 | 26 27 7 0.000567205618363 938 | 14 9 37 0.000502091716183 939 | 12 37 11 0.000501914704779 940 | 1 4 55 0.000369686093265 941 | 11 31 18 0.000563350221647 942 | 5 33 22 0.00054387644544 943 | 22 8 30 0.000562167683825 944 | 15 15 30 0.000575915999366 945 | 9 47 4 0.000391839926085 946 | 29 21 10 0.000573498390044 947 | 14 4 42 0.000457792403926 948 | 14 39 7 0.000480132083066 949 | 8 48 4 0.000379415766803 950 | 39 19 2 0.000532689007384 951 | 8 26 26 0.000572181333105 952 | 16 15 29 0.000584054427249 953 | 19 5 36 0.000519452341439 954 | 24 4 32 0.000554714471559 955 | 7 11 42 0.000442181806539 956 | 15 18 27 0.000597312322493 957 | 18 7 35 0.0005242606635 958 | 7 20 33 0.000541563717313 959 | 35 15 10 0.000524714144684 960 | 12 27 21 0.000589536586579 961 | 50 10 0 0.000558882190036 962 | 27 9 24 0.000576293331224 963 | 21 37 2 0.000553774587906 964 | 27 28 5 0.000562303307602 965 | 12 35 13 0.000525327868788 966 | 8 13 39 0.000478638959343 967 | 7 31 22 0.000554785832172 968 | 35 6 19 0.000525503361058 969 | 6 23 31 0.000553894267806 970 | 38 1 21 0.000596129646217 971 | 6 9 45 0.000406384681257 972 | 23 8 29 0.000566517703157 973 | 21 28 11 0.000581741494327 974 | 31 18 11 0.000563350221647 975 | 29 23 8 0.000566517703157 976 | 8 25 27 0.000571548093182 977 | 2 12 46 0.000439466945027 978 | 16 14 30 0.000575562514854 979 | 13 12 35 0.000525327868788 980 | 42 3 15 0.000471212939884 981 | 45 11 4 0.000418002012485 982 | 12 26 22 0.000592716196383 983 | 6 37 17 0.000505573889982 984 | 1 6 53 0.000386369230522 985 | 10 8 42 0.000440682063633 986 | 27 22 11 0.000585532967493 987 | 30 22 8 0.000562167683825 988 | 15 8 37 0.000502518420415 989 | 39 7 14 0.000480132083066 990 | 0 43 17 0.000690632698311 991 | 15 43 2 0.000481793851003 992 | 3 9 48 0.000390432381229 993 | 8 36 16 0.000513625240855 994 | 50 6 4 0.000357258223823 995 | 53 0 7 0.000507639277123 996 | 16 13 31 0.000566278233658 997 | 11 24 25 0.000589362493229 998 | 19 3 38 0.00051918527119 999 | 24 2 34 0.00057791759599 1000 | 7 9 44 0.00041670467739 1001 | 26 9 25 0.000577564346527 1002 | 19 38 3 0.00051918527119 1003 | 12 25 23 0.000594636992935 1004 | 6 36 18 0.000515949173091 1005 | 3 23 34 0.000554319763761 1006 | 20 36 4 0.000526164563267 1007 | 10 40 10 0.000465039644211 1008 | 18 34 8 0.000533613397403 1009 | 25 16 19 0.000608054593057 1010 | 11 15 34 0.000535861017312 1011 | 31 14 15 0.00056690063487 1012 | 8 35 17 0.000524024255459 1013 | 1 58 1 0.000400921212213 1014 | 33 3 24 0.000560486469349 1015 | 39 0 21 0.000754738932243 1016 | 2 24 34 0.00057791759599 1017 | 14 18 28 0.00058995627968 1018 | 12 7 41 0.000455032512107 1019 | 13 14 33 0.000547166402343 1020 | 25 35 0 0.000799937494985 1021 | 36 4 20 0.000526164563267 1022 | 6 1 53 0.000386369230522 1023 | 5 9 46 0.000397766746838 1024 | 41 16 3 0.000484072500847 1025 | 14 5 41 0.000462688733129 1026 | 13 34 13 0.000536521423079 1027 | 1 39 20 0.000584780420163 1028 | 11 0 49 0.000577577412196 1029 | 29 25 6 0.000561326032631 1030 | 15 41 4 0.000470617386893 1031 | 52 8 0 0.000523584874616 1032 | 27 25 8 0.000571548093182 1033 | 8 34 18 0.000533613397403 1034 | 9 21 30 0.000565035203114 1035 | 53 2 5 0.000352159833508 1036 | 8 49 3 0.000377801164992 1037 | 22 22 16 0.000613724258099 1038 | 33 0 27 0.000813515266876 1039 | 8 14 38 0.000490815596489 1040 | 35 11 14 0.000525103680786 1041 | 12 31 17 0.000565069647464 1042 | 22 2 36 0.000562897480566 1043 | 23 32 5 0.000549926340121 1044 | 41 12 7 0.000455032512107 1045 | 35 2 23 0.000570968029697 1046 | 25 18 17 0.000609069388139 1047 | 2 13 45 0.000453724638536 1048 | 38 22 0 0.000768085194696 1049 | 23 12 25 0.000594636992935 1050 | 3 3 54 0.000337791784896 1051 | 8 33 19 0.00054230015282 1052 | 3 12 45 0.000430776639063 1053 | 42 5 13 0.000449836829755 1054 | 41 13 6 0.000457900975492 1055 | 18 25 17 0.000609069388139 1056 | 47 3 10 0.000403607001432 1057 | 10 22 28 0.00057788496369 1058 | 7 18 35 0.0005242606635 1059 | 3 17 40 0.000496425389375 1060 | 3 38 19 0.00051918527119 1061 | 44 7 9 0.00041670467739 1062 | 41 18 1 0.000559440397503 1063 | 6 33 21 0.000541807496111 1064 | 4 8 48 0.000379415766803 1065 | 11 43 6 0.000432035856162 1066 | 27 18 15 0.000597312322493 1067 | 4 18 38 0.000506078605648 1068 | 18 4 38 0.000506078605648 1069 | 15 12 33 0.000546726531298 1070 | 10 18 32 0.000553490763168 1071 | 31 3 26 0.000569273557353 1072 | 37 23 0 0.000780130337846 1073 | 11 48 1 0.000455005153111 1074 | 18 24 18 0.000613308980689 1075 | 2 6 52 0.000361360418101 1076 | 53 4 3 0.000339990113993 1077 | 33 20 7 0.000541563717313 1078 | 3 45 12 0.000430776639063 1079 | 33 2 25 0.000583686849508 1080 | 22 7 31 0.000554785832172 1081 | 16 1 43 0.00053128655342 1082 | 7 39 14 0.000480132083066 1083 | 19 34 7 0.000533385575385 1084 | 12 29 19 0.000579543525933 1085 | 9 9 42 0.000440213838911 1086 | 8 7 45 0.000404493816949 1087 | 2 25 33 0.000583686849508 1088 | 18 38 4 0.000506078605648 1089 | 17 3 40 0.000496425389375 1090 | 13 11 36 0.000513742592469 1091 | 32 7 21 0.000548719190895 1092 | 39 4 17 0.000494870083604 1093 | 50 0 10 0.000558882190036 1094 | 40 12 8 0.000466122480142 1095 | 19 20 21 0.000620024731277 1096 | 18 10 32 0.000553490763168 1097 | 22 19 19 0.00061880325113 1098 | 35 7 18 0.0005242606635 1099 | 30 5 25 0.000558551095357 1100 | 9 3 48 0.000390432381229 1101 | 26 19 15 0.000602275177782 1102 | 5 13 42 0.000449836829755 1103 | 14 1 45 0.000501256623742 1104 | 20 21 19 0.000620024731277 1105 | 57 1 2 0.000373898666741 1106 | 13 16 31 0.000566278233658 1107 | 16 27 17 0.000598219286947 1108 | 1 35 24 0.000623793059998 1109 | 12 4 44 0.000431349703568 1110 | 7 32 21 0.000548719190895 1111 | 21 25 14 0.000603091017132 1112 | 29 29 2 0.000594130761983 1113 | 35 20 5 0.000528571188991 1114 | 0 19 41 0.000724583462662 1115 | 38 19 3 0.00051918527119 1116 | 37 3 20 0.00052939568825 1117 | 20 29 11 0.000576745146519 1118 | 11 36 13 0.000513742592469 1119 | 0 22 38 0.000768085194696 1120 | 4 36 20 0.000526164563267 1121 | 15 5 40 0.000475193799381 1122 | 40 11 9 0.000465294284403 1123 | 53 6 1 0.000386369230522 1124 | 17 31 12 0.000565069647464 1125 | 1 13 46 0.00048585847447 1126 | 33 22 5 0.00054387644544 1127 | 5 0 55 0.000481789016038 1128 | 21 29 10 0.000573498390044 1129 | 27 4 29 0.000564064666008 1130 | 24 8 28 0.000569654303715 1131 | 8 5 47 0.000385449128184 1132 | 23 36 1 0.000615717503072 1133 | 1 31 28 0.000643184095371 1134 | 17 41 2 0.000508422014232 1135 | 6 31 23 0.000553894267806 1136 | 16 17 27 0.000598219286947 1137 | 8 2 50 0.000384523914961 1138 | 1 32 27 0.000640366337092 1139 | 9 31 20 0.000558920375004 1140 | 32 5 23 0.000549926340121 1141 | 3 43 14 0.000457961301012 1142 | 40 10 10 0.000465039644211 1143 | 1 27 32 0.000640366337092 1144 | 10 17 33 0.00054475886607 1145 | 4 19 37 0.000516540749585 1146 | 18 5 37 0.000509461743506 1147 | 22 24 14 0.000605008670315 1148 | 3 34 23 0.000554319763761 1149 | 44 11 5 0.000423624580479 1150 | 23 3 34 0.000554319763761 1151 | 6 54 0 0.000493456285536 1152 | 3 29 28 0.000573094946374 1153 | 27 30 3 0.000571817425947 1154 | 4 22 34 0.00054256788204 1155 | 13 18 29 0.000581753357221 1156 | 56 0 4 0.000473881568417 1157 | 15 0 45 0.00065398171375 1158 | 21 23 16 0.000613089417709 1159 | 38 7 15 0.000492102347195 1160 | 17 27 16 0.000598219286947 1161 | 23 10 27 0.000581049081881 1162 | 28 28 4 0.000564695427811 1163 | 34 3 23 0.000554319763761 1164 | 18 19 23 0.000616376538476 1165 | 1 17 42 0.000545658867504 1166 | 46 14 0 0.000635017886853 1167 | 31 13 16 0.000566278233658 1168 | 21 17 22 0.000616530447443 1169 | 13 23 24 0.000600539275794 1170 | 8 8 44 0.000415957532029 1171 | 25 27 8 0.000571548093182 1172 | 44 10 6 0.000419094260354 1173 | 5 2 53 0.000352159833508 1174 | 25 34 1 0.000630629627707 1175 | 5 55 0 0.000481789016038 1176 | 15 10 35 0.000524714144684 1177 | 20 28 12 0.00058513062186 1178 | 18 33 9 0.000543482679964 1179 | 42 4 14 0.000457792403926 1180 | 18 42 0 0.000708016814567 1181 | 20 22 18 0.000618232740386 1182 | 1 52 7 0.000398085617614 1183 | 37 17 6 0.000505573889982 1184 | 1 34 25 0.000630629627707 1185 | 3 16 41 0.000484072500847 1186 | 2 0 58 0.000481572115707 1187 | 1 11 48 0.000455005153111 1188 | 13 33 14 0.000547166402343 1189 | 4 10 46 0.000404772750137 1190 | 2 54 4 0.000345614053259 1191 | 43 8 9 0.000428118760096 1192 | 4 17 39 0.000494870083604 1193 | 26 23 11 0.000588081747586 1194 | 19 40 1 0.00057251691181 1195 | 44 9 7 0.00041670467739 1196 | 22 10 28 0.00057788496369 1197 | 28 0 32 0.000817820521541 1198 | 13 20 27 0.000592904844502 1199 | 2 19 39 0.000532689007384 1200 | 7 36 17 0.000514274201223 1201 | 18 32 10 0.000553490763168 1202 | 29 1 30 0.000644598993328 1203 | 28 13 19 0.000587904261256 1204 | 10 42 8 0.000440682063633 1205 | 52 0 8 0.000523584874616 1206 | 32 6 22 0.000548409201255 1207 | 39 2 19 0.000532689007384 1208 | 17 11 32 0.000555087730158 1209 | 22 21 17 0.000616530447443 1210 | 34 19 7 0.000533385575385 1211 | 25 33 2 0.000583686849508 1212 | 33 8 19 0.00054230015282 1213 | 26 29 5 0.000561048821157 1214 | 8 6 46 0.000394128000967 1215 | 12 46 2 0.000439466945027 1216 | 5 4 51 0.000348420109833 1217 | 29 15 16 0.000584054427249 1218 | 6 51 3 0.000355326542624 1219 | 5 35 20 0.000528571188991 1220 | 42 2 16 0.000495347908903 1221 | 32 4 24 0.000554714471559 1222 | 17 5 38 0.000498688979294 1223 | 20 3 37 0.00052939568825 1224 | 11 7 42 0.000442181806539 1225 | 3 32 25 0.000565485562125 1226 | 4 51 5 0.000348420109833 1227 | 1 54 5 0.000376584228843 1228 | 27 17 16 0.000598219286947 1229 | 28 11 21 0.000581741494327 1230 | 21 8 31 0.000556645045176 1231 | 9 27 24 0.000576293331224 1232 | 41 5 14 0.000462688733129 1233 | 18 37 5 0.000509461743506 1234 | 17 17 26 0.000604196392016 1235 | 0 49 11 0.000577577412196 1236 | 12 32 16 0.000556335751302 1237 | 4 23 33 0.000549203580748 1238 | 26 28 6 0.000563205272437 1239 | 6 14 40 0.000470520169659 1240 | 4 13 43 0.000444660026359 1241 | 34 18 8 0.000533613397403 1242 | 4 0 56 0.000473881568417 1243 | 43 17 0 0.000690632698311 1244 | 27 26 7 0.000567205618363 1245 | 13 22 25 0.00059925571499 1246 | 23 34 3 0.000554319763761 1247 | 0 37 23 0.000780130337846 1248 | 15 4 41 0.000470617386893 1249 | 19 33 8 0.00054230015282 1250 | 32 26 2 0.000588226360032 1251 | 38 20 2 0.000543676659051 1252 | 9 12 39 0.000477841915704 1253 | 3 14 43 0.000457961301012 1254 | 9 1 50 0.000425176279456 1255 | 18 23 19 0.000616376538476 1256 | 50 9 1 0.000425176279456 1257 | 13 47 0 0.000615843998012 1258 | 21 21 18 0.000618854081602 1259 | 9 24 27 0.000576293331224 1260 | 24 32 4 0.000554714471559 1261 | 3 40 17 0.000496425389375 1262 | 7 47 6 0.000382603299343 1263 | 6 39 15 0.000482751300565 1264 | 5 6 49 0.000363438071327 1265 | 48 11 1 0.000455005153111 1266 | 34 11 15 0.000535861017312 1267 | 11 25 24 0.000589362493229 1268 | 10 43 7 0.000429342904055 1269 | 11 5 44 0.000423624580479 1270 | 16 4 40 0.000483014315787 1271 | 6 10 44 0.000419094260354 1272 | 14 46 0 0.000635017886853 1273 | 10 16 34 0.000535133560842 1274 | 37 21 2 0.000553774587906 1275 | 21 10 29 0.000573498390044 1276 | 50 8 2 0.000384523914961 1277 | 13 37 10 0.000501947385234 1278 | 6 32 22 0.000548409201255 1279 | 32 28 0 0.000817820521541 1280 | 7 6 47 0.000382603299343 1281 | 0 20 40 0.000740199339251 1282 | 4 21 35 0.000534865688674 1283 | 26 11 23 0.000588081747586 1284 | 17 12 31 0.000565069647464 1285 | 44 13 3 0.000444439223787 1286 | 0 31 29 0.000820418017588 1287 | 39 1 20 0.000584780420163 1288 | 17 18 25 0.000609069388139 1289 | 13 24 23 0.000600539275794 1290 | 21 15 24 0.00060858507757 1291 | 5 25 30 0.000558551095357 1292 | 2 23 35 0.000570968029697 1293 | 59 0 1 0.000518034490428 1294 | 19 31 10 0.000561240961747 1295 | 16 0 44 0.00067257173668 1296 | 29 5 26 0.000561048821157 1297 | 12 17 31 0.000565069647464 1298 | 9 14 37 0.000502091716183 1299 | 36 3 21 0.000538706957448 1300 | 11 44 5 0.000423624580479 1301 | 19 22 19 0.00061880325113 1302 | 22 17 21 0.000616530447443 1303 | 16 31 13 0.000566278233658 1304 | 29 19 12 0.000579543525933 1305 | 9 26 25 0.000577564346527 1306 | 9 19 32 0.000551712985417 1307 | 28 21 11 0.000581741494327 1308 | 5 11 44 0.000423624580479 1309 | 2 14 44 0.00046787785258 1310 | 0 30 30 0.000821286248119 1311 | 42 6 12 0.000445024944313 1312 | 27 12 21 0.000589536586579 1313 | 28 8 24 0.000569654303715 1314 | 7 34 19 0.000533385575385 1315 | 11 3 46 0.000417114121438 1316 | 35 22 3 0.00054703893668 1317 | 28 19 13 0.000587904261256 1318 | 38 17 5 0.000498688979294 1319 | 11 38 11 0.000489774401922 1320 | 36 2 22 0.000562897480566 1321 | 21 12 27 0.000589536586579 1322 | 16 8 36 0.000513625240855 1323 | 9 23 28 0.000573763413236 1324 | 8 31 21 0.000556645045176 1325 | 23 23 14 0.0006056501852 1326 | 22 16 22 0.000613724258099 1327 | 16 30 14 0.000575562514854 1328 | 17 14 29 0.000583277471058 1329 | 34 22 4 0.00054256788204 1330 | 6 53 1 0.000386369230522 1331 | 0 29 31 0.000820418017588 1332 | 34 1 25 0.000630629627707 1333 | 27 6 27 0.000563833588954 1334 | 20 35 5 0.000528571188991 1335 | 13 26 21 0.00059670199104 1336 | 35 0 25 0.000799937494985 1337 | 19 29 12 0.000579543525933 1338 | 33 15 12 0.000546726531298 1339 | 9 41 10 0.000452685505882 1340 | 17 1 42 0.000545658867504 1341 | 14 43 3 0.000457961301012 1342 | 5 3 52 0.000346374006092 1343 | 3 10 47 0.000403607001432 1344 | 36 1 23 0.000615717503072 1345 | 41 15 4 0.000470617386893 1346 | 8 30 22 0.000562167683825 1347 | 1 25 34 0.000630629627707 1348 | 9 32 19 0.000551712985417 1349 | 46 13 1 0.00048585847447 1350 | 16 29 15 0.000584054427249 1351 | 24 11 25 0.000589362493229 1352 | 15 30 15 0.000575915999366 1353 | 8 0 52 0.000523584874616 1354 | 23 16 21 0.000613089417709 1355 | 25 9 26 0.000577564346527 1356 | 47 1 12 0.000470390572843 1357 | 6 52 2 0.000361360418101 1358 | 34 15 11 0.000535861017312 1359 | 23 1 36 0.000615717503072 1360 | 20 34 6 0.00053414903673 1361 | 45 2 13 0.000453724638536 1362 | 7 27 26 0.000567205618363 1363 | 15 2 43 0.000481793851003 1364 | 20 20 20 0.000620624876737 1365 | 18 41 1 0.000559440397503 1366 | 28 17 15 0.000591215071466 1367 | 51 9 0 0.000540788525529 1368 | 8 51 1 0.000411152021077 1369 | 1 42 17 0.000545658867504 1370 | 21 7 32 0.000548719190895 1371 | 50 3 7 0.000365979242514 1372 | 39 16 5 0.00048723137665 1373 | 3 33 24 0.000560486469349 1374 | 14 2 44 0.00046787785258 1375 | 46 12 2 0.000439466945027 1376 | 44 6 10 0.000419094260354 1377 | 20 13 27 0.000592904844502 1378 | 45 7 8 0.000404493816949 1379 | 24 24 12 0.000595279433757 1380 | 26 15 19 0.000602275177782 1381 | 19 32 9 0.000551712985417 1382 | 8 18 34 0.000533613397403 1383 | 27 11 22 0.000585532967493 1384 | 17 42 1 0.000545658867504 1385 | 5 53 2 0.000352159833508 1386 | 13 28 19 0.000587904261256 1387 | 19 27 14 0.000595523628638 1388 | 29 9 22 0.000569998719715 1389 | 8 44 8 0.000415957532029 1390 | 36 7 17 0.000514274201223 1391 | 19 18 23 0.000616376538476 1392 | 9 34 17 0.000534309679581 1393 | 0 24 36 0.00079077667923 1394 | 2 1 57 0.000373898666741 1395 | 12 16 32 0.000556335751302 1396 | 26 21 13 0.00059670199104 1397 | 25 29 6 0.000561326032631 1398 | 27 32 1 0.000640366337092 1399 | 16 3 41 0.000484072500847 1400 | 9 35 16 0.000524284544741 1401 | 42 1 17 0.000545658867504 1402 | 4 35 21 0.000534865688674 1403 | 45 4 11 0.000418002012485 1404 | 7 25 28 0.000565946301449 1405 | 46 2 12 0.000439466945027 1406 | 30 8 22 0.000562167683825 1407 | 35 18 7 0.0005242606635 1408 | 28 23 9 0.000573763413236 1409 | 19 4 37 0.000516540749585 1410 | 46 7 7 0.000392997219042 1411 | 22 3 35 0.00054703893668 1412 | 51 7 2 0.000372328832234 1413 | 11 34 15 0.000535861017312 1414 | 36 6 18 0.000515949173091 1415 | 2 20 38 0.000543676659051 1416 | 6 11 43 0.000432035856162 1417 | 17 25 18 0.000609069388139 1418 | 25 0 35 0.000799937494985 1419 | 49 11 0 0.000577577412196 1420 | 46 3 11 0.000417114121438 1421 | 21 35 4 0.000534865688674 1422 | 30 10 20 0.000567932192157 1423 | 26 20 14 0.000599917880717 1424 | 12 20 28 0.00058513062186 1425 | 34 17 9 0.000534309679581 1426 | 3 54 3 0.000337791784896 1427 | 34 26 0 0.000807537834977 1428 | 6 49 5 0.000363438071327 1429 | 4 24 32 0.000554714471559 1430 | 42 0 18 0.000708016814567 1431 | 27 2 31 0.000591497111978 1432 | 5 21 34 0.000536736834183 1433 | 20 39 1 0.000584780420163 1434 | 13 30 17 0.000574518286348 1435 | 4 52 4 0.000342124459796 1436 | 28 22 10 0.00057788496369 1437 | 2 38 20 0.000543676659051 1438 | 9 4 47 0.000391839926085 1439 | 47 13 0 0.000615843998012 1440 | 36 5 19 0.000519452341439 1441 | 41 11 8 0.000453414475786 1442 | 49 1 10 0.000439869993691 1443 | 46 9 5 0.000397766746838 1444 | 33 4 23 0.000549203580748 1445 | 39 15 6 0.000482751300565 1446 | 6 12 42 0.000445024944313 1447 | 23 20 17 0.00061527433194 1448 | 34 16 10 0.000535133560842 1449 | 24 28 8 0.000569654303715 1450 | 33 11 16 0.000545892195496 1451 | 6 48 6 0.000372188153326 1452 | 22 6 32 0.000548409201255 1453 | 3 7 50 0.000365979242514 1454 | 20 38 2 0.000543676659051 1455 | 45 6 9 0.000406384681257 1456 | 8 37 15 0.000502518420415 1457 | 10 35 15 0.000524714144684 1458 | 11 13 36 0.000513742592469 1459 | 16 12 32 0.000556335751302 1460 | 14 45 1 0.000501256623742 1461 | 14 38 8 0.000490815596489 1462 | 9 51 0 0.000540788525529 1463 | 51 5 4 0.000348420109833 1464 | 18 21 21 0.000618854081602 1465 | 50 7 3 0.000365979242514 1466 | 39 20 1 0.000584780420163 1467 | 13 45 2 0.000453724638536 1468 | 20 26 14 0.000599917880717 1469 | 25 2 33 0.000583686849508 1470 | 46 8 6 0.000394128000967 1471 | 2 47 11 0.000425247956462 1472 | 21 19 20 0.000620024731277 1473 | 7 14 39 0.000480132083066 1474 | 41 8 11 0.000453414475786 1475 | 16 7 37 0.000503520406582 1476 | 26 3 31 0.000569273557353 1477 | 1 9 50 0.000425176279456 1478 | 47 6 7 0.000382603299343 1479 | 24 27 9 0.000576293331224 1480 | 28 12 20 0.00058513062186 1481 | 11 27 22 0.000585532967493 1482 | 20 37 3 0.00052939568825 1483 | 18 35 7 0.0005242606635 1484 | 7 48 5 0.000373883418934 1485 | 38 9 13 0.000490157910944 1486 | 29 13 18 0.000581753357221 1487 | 29 17 14 0.000583277471058 1488 | 9 6 45 0.000406384681257 1489 | 37 19 4 0.000516540749585 1490 | 18 20 22 0.000618232740386 1491 | 8 24 28 0.000569654303715 1492 | 13 35 12 0.000525327868788 1493 | 35 8 17 0.000524024255459 1494 | 13 17 30 0.000574518286348 1495 | 7 1 52 0.000398085617614 1496 | 30 7 23 0.000559707654648 1497 | 33 6 21 0.000541807496111 1498 | 8 52 0 0.000523584874616 1499 | 1 0 59 0.000518034490428 1500 | 20 18 22 0.000618232740386 1501 | 47 8 5 0.000385449128184 1502 | 24 26 10 0.000582959937935 1503 | 5 16 39 0.00048723137665 1504 | 40 9 11 0.000465294284403 1505 | 3 5 52 0.000346374006092 1506 | 4 39 17 0.000494870083604 1507 | 45 8 7 0.000404493816949 1508 | 2 21 37 0.000553774587906 1509 | 11 11 38 0.000489774401922 1510 | 16 10 34 0.000535133560842 1511 | 51 3 6 0.000355326542624 1512 | 11 46 3 0.000417114121438 1513 | 8 23 29 0.000566517703157 1514 | 19 24 17 0.000612777218961 1515 | 23 31 6 0.000553894267806 1516 | 22 23 15 0.000609861242306 1517 | 12 44 4 0.000431349703568 1518 | 41 10 9 0.000452685505882 1519 | 42 7 11 0.000442181806539 1520 | 12 24 24 0.000595279433757 1521 | 3 50 7 0.000365979242514 1522 | 24 25 11 0.000589362493229 1523 | 5 1 54 0.000376584228843 1524 | 15 29 16 0.000584054427249 1525 | 27 14 19 0.000595523628638 1526 | 4 38 18 0.000506078605648 1527 | 17 7 36 0.000514274201223 1528 | 35 24 1 0.000623793059998 1529 | 49 7 4 0.00036776787047 1530 | 28 26 6 0.000563205272437 1531 | 22 5 33 0.00054387644544 1532 | 14 35 11 0.000525103680786 1533 | 7 13 40 0.000467731273606 1534 | 36 11 13 0.000513742592469 1535 | 41 7 12 0.000455032512107 1536 | 40 15 5 0.000475193799381 1537 | 2 49 9 0.000397579514036 1538 | 2 58 0 0.000481572115707 1539 | 46 5 9 0.000397766746838 1540 | 31 29 0 0.000820418017588 1541 | 13 41 6 0.000457900975492 1542 | 6 8 46 0.000394128000967 1543 | 11 23 26 0.000588081747586 1544 | 5 18 37 0.000509461743506 1545 | 27 24 9 0.000576293331224 1546 | 26 33 1 0.00063616965736 1547 | 4 37 19 0.000516540749585 1548 | 45 10 5 0.000410563857799 1549 | 10 39 11 0.000477498435273 1550 | 54 5 1 0.000376584228843 1551 | 17 33 10 0.00054475886607 1552 | 14 41 5 0.000462688733129 1553 | 1 36 23 0.000615717503072 1554 | 29 3 28 0.000573094946374 1555 | 8 21 31 0.000556645045176 1556 | 2 48 10 0.000411225549409 1557 | 46 4 10 0.000404772750137 1558 | 54 2 4 0.000345614053259 1559 | 23 18 19 0.000616376538476 1560 | 26 7 27 0.000567205618363 1561 | 38 16 6 0.000494473283901 1562 | 34 13 13 0.000536521423079 1563 | 12 43 5 0.000436768161023 1564 | 3 30 27 0.000571817425947 1565 | 56 4 0 0.000473881568417 1566 | 35 12 13 0.000525327868788 1567 | 39 11 10 0.000477498435273 1568 | 18 39 3 0.000508163523847 1569 | 7 52 1 0.000398085617614 1570 | 25 11 24 0.000589362493229 1571 | 14 40 6 0.000470520169659 1572 | 26 18 16 0.000603713170637 1573 | 39 18 3 0.000508163523847 1574 | 40 13 7 0.000467731273606 1575 | 35 17 8 0.000524024255459 1576 | 15 38 7 0.000492102347195 1577 | 31 9 20 0.000558920375004 1578 | 11 33 16 0.000545892195496 1579 | 26 13 21 0.00059670199104 1580 | 25 21 14 0.000603091017132 1581 | 47 12 1 0.000470390572843 1582 | 11 21 28 0.000581741494327 1583 | 16 11 33 0.000545892195496 1584 | 5 20 35 0.000528571188991 1585 | 1 40 19 0.00057251691181 1586 | 34 12 14 0.000536349975984 1587 | 5 51 4 0.000348420109833 1588 | 56 3 1 0.000367461057285 1589 | 45 12 3 0.000430776639063 1590 | 9 13 38 0.000490157910944 1591 | 28 31 1 0.000643184095371 1592 | 21 1 38 0.000596129646217 1593 | 11 42 7 0.000442181806539 1594 | 6 2 52 0.000361360418101 1595 | 18 40 2 0.000520904307398 1596 | 30 18 12 0.000572833115344 1597 | 15 40 5 0.000475193799381 1598 | 10 9 41 0.000452685505882 1599 | 4 11 45 0.000418002012485 1600 | 12 48 0 0.000596634336102 1601 | 19 37 4 0.000516540749585 1602 | 28 30 2 0.000593470924499 1603 | 26 12 22 0.000592716196383 1604 | 3 52 5 0.000346374006092 1605 | 34 25 1 0.000630629627707 1606 | 34 2 24 0.00057791759599 1607 | 4 16 40 0.000483014315787 1608 | 27 10 23 0.000581049081881 1609 | 56 2 2 0.000349138531125 1610 | 39 9 12 0.000477841915704 1611 | 19 17 24 0.000612777218961 1612 | 19 6 35 0.000525503361058 1613 | 10 14 36 0.000513611204695 1614 | 10 26 24 0.000582959937935 1615 | 9 17 34 0.000534309679581 1616 | 7 5 48 0.000373883418934 1617 | 41 3 16 0.000484072500847 1618 | 20 4 36 0.000526164563267 1619 | 6 13 41 0.000457900975492 1620 | 49 9 2 0.000397579514036 1621 | 46 1 13 0.00048585847447 1622 | 0 14 46 0.000635017886853 1623 | 7 22 31 0.000554785832172 1624 | 8 3 49 0.000377801164992 1625 | 2 39 19 0.000532689007384 1626 | 44 4 12 0.000431349703568 1627 | 3 56 1 0.000367461057285 1628 | 34 24 2 0.00057791759599 1629 | 16 9 35 0.000524284544741 1630 | 5 22 33 0.00054387644544 1631 | 36 9 15 0.000513508110547 1632 | 22 1 37 0.000606470645494 1633 | 0 57 3 0.000472067807554 1634 | 8 38 14 0.000490815596489 1635 | 56 1 3 0.000367461057285 1636 | 45 14 1 0.000501256623742 1637 | 1 28 31 0.000643184095371 1638 | 47 5 8 0.000385449128184 1639 | 45 13 2 0.000453724638536 1640 | 37 9 14 0.000502091716183 1641 | 14 37 9 0.000502091716183 1642 | 4 26 30 0.000562177966124 1643 | 42 13 5 0.000449836829755 1644 | 4 9 47 0.000391839926085 1645 | 37 0 23 0.000780130337846 1646 | 46 0 14 0.000635017886853 1647 | 59 1 0 0.000518034490428 1648 | 20 19 21 0.000620024731277 1649 | 17 28 15 0.000591215071466 1650 | 32 8 20 0.000550001670772 1651 | 35 23 2 0.000570968029697 1652 | 22 4 34 0.00054256788204 1653 | 28 18 14 0.00058995627968 1654 | 15 23 22 0.000609861242306 1655 | 3 26 31 0.000569273557353 1656 | 29 6 25 0.000561326032631 1657 | 2 7 51 0.000372328832234 1658 | 19 15 26 0.000602275177782 1659 | 32 11 17 0.000555087730158 1660 | 11 9 40 0.000465294284403 1661 | 36 19 5 0.000519452341439 1662 | 29 16 15 0.000584054427249 1663 | 50 5 5 0.000354655431723 1664 | 23 0 37 0.000780130337846 1665 | 13 43 4 0.000444660026359 1666 | 44 15 1 0.000516443688638 1667 | 0 18 42 0.000708016814567 1668 | 55 3 2 0.000343377513917 1669 | 48 8 4 0.000379415766803 1670 | 9 28 23 0.000573763413236 1671 | 31 23 6 0.000553894267806 1672 | 8 1 51 0.000411152021077 1673 | 3 44 13 0.000444439223787 1674 | 7 43 10 0.000429342904055 1675 | 47 0 13 0.000615843998012 1676 | 21 13 26 0.00059670199104 1677 | 12 11 37 0.000501914704779 1678 | 21 5 34 0.000536736834183 1679 | 7 50 3 0.000365979242514 1680 | 1 30 29 0.000644598993328 1681 | 29 11 20 0.000576745146519 1682 | 28 3 29 0.000573094946374 1683 | 21 32 7 0.000548719190895 1684 | 36 8 16 0.000513625240855 1685 | 51 1 8 0.000411152021077 1686 | 36 18 6 0.000515949173091 1687 | 50 4 6 0.000357258223823 1688 | 40 8 12 0.000466122480142 1689 | 19 16 25 0.000608054593057 1690 | 23 7 30 0.000559707654648 1691 | 22 31 7 0.000554785832172 1692 | 7 8 45 0.000404493816949 1693 | 37 2 21 0.000553774587906 1694 | 2 44 14 0.00046787785258 1695 | 44 5 11 0.000423624580479 1696 | 24 35 1 0.000623793059998 1697 | 25 31 4 0.000559052024715 1698 | 47 10 3 0.000403607001432 1699 | 17 30 13 0.000574518286348 1700 | 17 43 0 0.000690632698311 1701 | 34 6 20 0.00053414903673 1702 | 12 1 47 0.000470390572843 1703 | 2 30 28 0.000593470924499 1704 | 26 5 29 0.000561048821157 1705 | 35 16 9 0.000524284544741 1706 | 6 44 10 0.000419094260354 1707 | 19 2 39 0.000532689007384 1708 | 28 2 30 0.000593470924499 1709 | 5 31 24 0.000554832508077 1710 | 7 3 50 0.000365979242514 1711 | 0 15 45 0.00065398171375 1712 | 7 21 32 0.000548719190895 1713 | 36 17 7 0.000514274201223 1714 | 29 18 13 0.000581753357221 1715 | 12 8 40 0.000466122480142 1716 | 40 7 13 0.000467731273606 1717 | 2 57 1 0.000373898666741 1718 | 12 33 15 0.000546726531298 1719 | 9 48 3 0.000390432381229 1720 | 55 1 4 0.000369686093265 1721 | 9 30 21 0.000565035203114 1722 | 15 14 31 0.00056690063487 1723 | 24 34 2 0.00057791759599 1724 | 7 41 12 0.000455032512107 1725 | 44 8 8 0.000415957532029 1726 | 44 3 13 0.000444439223787 1727 | 5 23 32 0.000549926340121 1728 | 20 40 0 0.000740199339251 1729 | 43 1 16 0.00053128655342 1730 | 8 22 30 0.000562167683825 1731 | 4 34 22 0.00054256788204 1732 | 16 6 38 0.000494473283901 1733 | 5 27 28 0.000562303307602 1734 | 28 1 31 0.000643184095371 1735 | 21 34 5 0.000536736834183 1736 | 14 33 13 0.000547166402343 1737 | 27 5 28 0.000562303307602 1738 | 6 3 51 0.000355326542624 1739 | 40 6 14 0.000470520169659 1740 | 15 6 39 0.000482751300565 1741 | 2 56 2 0.000349138531125 1742 | 16 18 26 0.000603713170637 1743 | 37 4 19 0.000516540749585 1744 | 1 1 58 0.000400921212213 1745 | 24 33 3 0.000560486469349 1746 | 11 4 45 0.000418002012485 1747 | 43 7 10 0.000429342904055 1748 | 5 41 14 0.000462688733129 1749 | 22 0 38 0.000768085194696 1750 | 10 46 4 0.000404772750137 1751 | 33 13 14 0.000547166402343 1752 | 27 0 33 0.000813515266876 1753 | 17 0 43 0.000690632698311 1754 | 10 37 13 0.000501947385234 1755 | 9 45 6 0.000406384681257 1756 | 19 11 30 0.00057059401282 1757 | 32 17 11 0.000555087730158 1758 | 5 32 23 0.000549926340121 1759 | 14 32 14 0.000557395520244 1760 | 0 13 47 0.000615843998012 1761 | 26 17 17 0.000604196392016 1762 | 36 23 1 0.000615717503072 1763 | 29 20 11 0.000576745146519 1764 | 40 5 15 0.000475193799381 1765 | 23 4 33 0.000549203580748 1766 | 38 4 18 0.000506078605648 1767 | 12 39 9 0.000477841915704 1768 | 9 50 1 0.000425176279456 1769 | 6 47 7 0.000382603299343 1770 | 31 1 28 0.000643184095371 1771 | 7 12 41 0.000455032512107 1772 | 11 41 8 0.000453414475786 1773 | 38 18 4 0.000506078605648 1774 | 37 10 13 0.000501947385234 1775 | 40 19 1 0.00057251691181 1776 | 44 12 4 0.000431349703568 1777 | 25 13 22 0.00059925571499 1778 | 47 4 9 0.000391839926085 1779 | 11 29 20 0.000576745146519 1780 | 33 27 0 0.000813515266876 1781 | 30 9 21 0.000565035203114 1782 | 3 25 32 0.000565485562125 1783 | 35 14 11 0.000525103680786 1784 | 41 1 18 0.000559440397503 1785 | 10 36 14 0.000513611204695 1786 | 28 7 25 0.000565946301449 1787 | 21 36 3 0.000538706957448 1788 | 16 40 4 0.000483014315787 1789 | 26 16 18 0.000603713170637 1790 | 7 30 23 0.000559707654648 1791 | 36 22 2 0.000562897480566 1792 | 2 4 54 0.000345614053259 1793 | 15 32 13 0.000557125584217 1794 | 6 28 26 0.000563205272437 1795 | 12 38 10 0.000489858926854 1796 | 37 6 17 0.000505573889982 1797 | 12 6 42 0.000445024944313 1798 | 12 30 18 0.000572833115344 1799 | 30 26 4 0.000562177966124 1800 | 26 4 30 0.000562177966124 1801 | 40 14 6 0.000470520169659 1802 | 43 5 12 0.000436768161023 1803 | 34 10 16 0.000535133560842 1804 | 58 2 0 0.000481572115707 1805 | 4 40 16 0.000483014315787 1806 | 42 16 2 0.000495347908903 1807 | 15 35 10 0.000524714144684 1808 | 4 50 6 0.000357258223823 1809 | 17 2 41 0.000508422014232 1810 | 18 36 6 0.000515949173091 1811 | 8 11 41 0.000453414475786 1812 | 0 17 43 0.000690632698311 1813 | 5 34 21 0.000536736834183 1814 | 32 10 18 0.000553490763168 1815 | 27 1 32 0.000640366337092 1816 | 39 6 15 0.000482751300565 1817 | 36 21 3 0.000538706957448 1818 | 29 22 9 0.000569998719715 1819 | 15 42 3 0.000471212939884 1820 | 0 28 32 0.000817820521541 1821 | 34 23 3 0.000554319763761 1822 | 3 48 9 0.000390432381229 1823 | 34 0 26 0.000807537834977 1824 | 24 12 24 0.000595279433757 1825 | 0 8 52 0.000523584874616 1826 | 19 7 34 0.000533385575385 1827 | 4 49 7 0.00036776787047 1828 | 25 24 11 0.000589362493229 1829 | 20 12 28 0.00058513062186 1830 | 19 8 33 0.00054230015282 1831 | 16 20 24 0.00061119221221 1832 | 8 43 9 0.000428118760096 1833 | 21 38 1 0.000596129646217 1834 | 60 0 0 0.000672927508895 1835 | 54 6 0 0.000493456285536 1836 | 2 51 7 0.000372328832234 1837 | 34 14 12 0.000536349975984 1838 | 28 25 7 0.000565946301449 1839 | 30 14 16 0.000575562514854 1840 | 15 13 32 0.000557125584217 1841 | 43 3 14 0.000457961301012 1842 | 5 45 10 0.000410563857799 1843 | 12 18 30 0.000572833115344 1844 | 23 15 22 0.000609861242306 1845 | 33 16 11 0.000545892195496 1846 | 14 23 23 0.0006056501852 1847 | 6 29 25 0.000561326032631 1848 | 22 32 6 0.000548409201255 1849 | 54 1 5 0.000376584228843 1850 | 8 42 10 0.000440682063633 1851 | 5 36 19 0.000519452341439 1852 | 26 14 20 0.000599917880717 1853 | 22 30 8 0.000562167683825 1854 | 29 24 7 0.000563439276973 1855 | 0 38 22 0.000768085194696 1856 | 42 18 0 0.000708016814567 1857 | 49 5 6 0.000363438071327 1858 | 22 37 1 0.000606470645494 1859 | 0 26 34 0.000807537834977 1860 | 1 45 14 0.000501256623742 1861 | 9 20 31 0.000558920375004 1862 | 6 0 54 0.000493456285536 1863 | 30 23 7 0.000559707654648 1864 | 44 16 0 0.00067257173668 1865 | 16 5 39 0.00048723137665 1866 | 24 10 26 0.000582959937935 1867 | 7 17 36 0.000514274201223 1868 | 3 21 36 0.000538706957448 1869 | 35 10 15 0.000524714144684 1870 | 4 55 1 0.000369686093265 1871 | 25 26 9 0.000577564346527 1872 | 2 5 53 0.000352159833508 1873 | 2 22 36 0.000562897480566 1874 | 38 14 8 0.000490815596489 1875 | 38 21 1 0.000596129646217 1876 | 8 41 11 0.000453414475786 1877 | 36 0 24 0.00079077667923 1878 | 1 23 36 0.000615717503072 1879 | 28 9 23 0.000573763413236 1880 | 23 2 35 0.000570968029697 1881 | 40 0 20 0.000740199339251 1882 | 15 36 9 0.000513508110547 1883 | 22 9 29 0.000569998719715 1884 | 12 42 6 0.000445024944313 1885 | 8 12 40 0.000466122480142 1886 | 7 10 43 0.000429342904055 1887 | 42 17 1 0.000545658867504 1888 | 3 46 11 0.000417114121438 1889 | 25 23 12 0.000594636992935 1890 | 47 2 11 0.000425247956462 1891 | 24 31 5 0.000554832508077 1892 | -------------------------------------------------------------------------------- /examples/sample_data/scatter_colorbar.txt: -------------------------------------------------------------------------------- 1 | -3.5516350089 -3.768116 [1, 0, 0] 2 | -3.6107113221 -3.7428188 [0, 1, 0] 3 | -5.5340766951 -5.6648602 [0, 0, 1] 4 | -3.8885230693 -3.8800445 [1, 1, 0] 5 | -5.1831640024 -5.3274035 [1, 0, 1] 6 | -4.5860779101 -4.63911865 [0, 1, 1] 7 | -3.8582884462 -3.80612533333 [2, 1, 0] 8 | -4.8246118011 -4.74736266667 [2, 0, 1] 9 | -3.9374442451 -3.927809 [1, 2, 0] 10 | -4.7107623548 -4.69750666667 [1, 1, 1] 11 | -5.4847222747 -5.41731266667 [1, 0, 2] 12 | -4.299050653 -4.32673233333 [0, 2, 1] 13 | -4.9278056438 -4.91713166667 [0, 1, 2] 14 | -3.7918765121 -3.77761425 [3, 1, 0] 15 | -3.9334829147 -3.87298 [2, 2, 0] 16 | -5.2930576848 -5.2492885 [2, 0, 2] 17 | -4.6020280147 -4.62266175 [0, 2, 2] 18 | -4.1732688356 -4.1810615 [0, 3, 1] 19 | -5.0812068962 -5.07164775 [0, 1, 3] 20 | -4.5908249024 -4.4235635 [3, 0, 1] 21 | -4.6292620683 -4.6366255 [2, 1, 1] 22 | -3.9330784897 -3.90738275 [1, 3, 0] 23 | -4.5075466764 -4.48218575 [1, 2, 1] 24 | -5.0790857704 -5.05795275 [1, 1, 2] 25 | -5.5859055259 -5.55121625 [1, 0, 3] 26 | -3.7297509817 -3.7711188 [4, 1, 0] 27 | -4.3513002862 -4.3223486 [4, 0, 1] 28 | -3.8810894808 -3.8212586 [3, 2, 0] 29 | -4.4325382428 -4.3894644 [3, 1, 1] 30 | -4.9762728808 -4.9188892 [3, 0, 2] 31 | -4.4666296158 -4.4877356 [2, 2, 1] 32 | -4.9523843543 -4.953122 [2, 1, 2] 33 | -5.3610469718 -5.392246 [2, 0, 3] 34 | -4.7204991847 -4.7393156 [1, 2, 2] 35 | -4.0430221394 -4.089954 [0, 4, 1] 36 | -4.4284726916 -4.4518532 [0, 3, 2] 37 | -4.8039206658 -4.8049582 [0, 2, 3] 38 | -5.1665783768 -5.1565688 [0, 1, 4] 39 | -3.9482746053 -3.9075942 [2, 3, 0] 40 | -3.8801324223 -3.8785168 [1, 4, 0] 41 | -4.3048528253 -4.2988998 [1, 3, 1] 42 | -5.1075585164 -5.1256452 [1, 1, 3] 43 | -5.4955264297 -5.5354066 [1, 0, 4] 44 | -3.708404418 -3.770192 [5, 1, 0] 45 | -4.2566357209 -4.19883733333 [5, 0, 1] 46 | -3.8666678803 -3.805213 [4, 2, 0] 47 | -4.7813491745 -4.69178716667 [4, 0, 2] 48 | -4.8324965613 -4.798458 [3, 1, 2] 49 | -5.2610223833 -5.266337 [3, 0, 3] 50 | -4.3571877153 -4.259283 [4, 1, 1] 51 | -3.946121769 -3.85589233333 [3, 3, 0] 52 | -4.3979912932 -4.39659016667 [3, 2, 1] 53 | -3.9578580089 -3.921597 [2, 4, 0] 54 | -4.3509212964 -4.33789166667 [2, 3, 1] 55 | -3.8646628238 -3.85115833333 [1, 5, 0] 56 | -4.210446155 -4.20694483333 [1, 4, 1] 57 | -4.7460561247 -4.73313233333 [2, 2, 2] 58 | -5.138545099 -5.133973 [2, 1, 3] 59 | -5.4497436201 -5.47727483333 [2, 0, 4] 60 | -4.5920429908 -4.55728516667 [1, 3, 2] 61 | -4.9261615845 -4.90742216667 [1, 2, 3] 62 | -5.2381707853 -5.20405283333 [1, 1, 4] 63 | -5.5474766625 -5.5434185 [1, 0, 5] 64 | -3.9931738718 -4.03026933333 [0, 5, 1] 65 | -4.3195665973 -4.33611783333 [0, 4, 2] 66 | -4.6222877542 -4.62166833333 [0, 3, 3] 67 | -4.9277442522 -4.91617566667 [0, 2, 4] 68 | -5.243250382 -5.2117395 [0, 1, 5] 69 | -3.6824641556 -3.767123 [6, 1, 0] 70 | -3.821406806 -3.79040471429 [5, 2, 0] 71 | -4.2361902594 -4.16207571429 [5, 1, 1] 72 | -4.6430350904 -4.54129057143 [5, 0, 2] 73 | -3.9048545745 -3.83878885714 [4, 3, 0] 74 | -4.3214478923 -4.225693 [4, 2, 1] 75 | -4.7235312799 -4.621964 [4, 1, 2] 76 | -5.0935793702 -5.031116 [4, 0, 3] 77 | -4.3060264808 -4.32989314286 [3, 3, 1] 78 | -3.9231867574 -3.91952857143 [2, 5, 0] 79 | -4.6798717775 -4.72529871429 [3, 2, 2] 80 | -4.2483479041 -4.25536642857 [2, 4, 1] 81 | -4.5785846185 -4.58113271429 [2, 3, 2] 82 | -5.0294657739 -4.98922357143 [3, 1, 3] 83 | -4.4343518764 -4.41991885714 [1, 4, 2] 84 | -5.1925500978 -5.17112471429 [2, 1, 4] 85 | -5.3711010531 -5.300876 [3, 0, 4] 86 | -4.9012181941 -4.90228271429 [2, 2, 3] 87 | -4.7115376705 -4.71086828571 [1, 3, 3] 88 | -5.2705792795 -5.27069971429 [1, 1, 5] 89 | -5.4850641336 -5.47430714286 [2, 0, 5] 90 | -4.9931658559 -4.99310914286 [1, 2, 4] 91 | -5.542442371 -5.54071371429 [1, 0, 6] 92 | -4.2213694583 -4.243943 [0, 5, 2] 93 | -4.4836834514 -4.49826414286 [0, 4, 3] 94 | -4.7457815334 -4.74761871429 [0, 3, 4] 95 | -5.0105503028 -5.00489371429 [0, 2, 5] 96 | -5.2735987484 -5.25585914286 [0, 1, 6] 97 | -4.1618604201 -4.136912 [6, 0, 1] 98 | -3.9430561929 -3.890784 [3, 4, 0] 99 | -3.8291959782 -3.833868 [1, 6, 0] 100 | -4.1304575436 -4.12222142857 [1, 5, 1] 101 | -3.9333869526 -3.98696685714 [0, 6, 1] 102 | -4.2487402217 -4.153603875 [5, 2, 1] 103 | -4.952668382 -4.9254345 [4, 1, 3] 104 | -4.9215743661 -4.9020525 [5, 0, 3] 105 | -5.2346015584 -5.281576 [4, 0, 4] 106 | -4.1730112808 -4.180098125 [0, 6, 2] 107 | -3.6651610987 -3.763555375 [7, 1, 0] 108 | -4.0897470008 -4.079031125 [7, 0, 1] 109 | -3.7928842444 -3.78196675 [6, 2, 0] 110 | -4.5431993652 -4.427433125 [6, 0, 2] 111 | -5.4194545677 -5.4114045 [3, 0, 5] 112 | -3.9172533636 -3.912145125 [2, 6, 0] 113 | -4.8032931608 -4.76755925 [2, 3, 3] 114 | -4.8423787783 -4.847835375 [1, 3, 4] 115 | -5.0674290296 -5.045691875 [2, 2, 4] 116 | -5.5703108204 -5.5460685 [2, 0, 6] 117 | -5.0958702405 -5.069048625 [1, 2, 5] 118 | -5.3320782195 -5.30919925 [1, 1, 6] 119 | -5.5572163162 -5.543709875 [1, 0, 7] 120 | -4.2880057945 -4.2057985 [4, 3, 1] 121 | -4.2796218857 -4.26705175 [3, 4, 1] 122 | -4.6489074692 -4.557628625 [4, 2, 2] 123 | -3.9419127464 -3.873263375 [4, 4, 0] 124 | -4.5934617794 -4.56407125 [3, 3, 2] 125 | -3.8055462989 -3.8189175 [1, 7, 0] 126 | -4.360901065 -4.334686125 [1, 5, 2] 127 | -4.6172633318 -4.58989425 [1, 4, 3] 128 | -5.3349605533 -5.301304125 [2, 1, 5] 129 | -4.4067338525 -4.392397 [0, 5, 3] 130 | -5.0951323456 -5.064469625 [0, 2, 6] 131 | -5.3205431851 -5.28996125 [0, 1, 7] 132 | -3.8766945586 -3.81710175 [5, 3, 0] 133 | -4.6177314437 -4.49004075 [5, 1, 2] 134 | -3.9612502519 -3.93056025 [3, 5, 0] 135 | -4.877751374 -4.87929125 [3, 2, 3] 136 | -5.1647158882 -5.185500375 [3, 1, 4] 137 | -4.2108244985 -4.093884625 [6, 1, 1] 138 | -4.2451328699 -4.192140375 [2, 5, 1] 139 | -4.0840207737 -4.0766765 [1, 6, 1] 140 | -4.5391462861 -4.473015625 [2, 4, 2] 141 | -3.9040675 -3.957148625 [0, 7, 1] 142 | -4.6302034172 -4.62012425 [0, 4, 4] 143 | -4.8565778696 -4.83882625 [0, 3, 5] 144 | -4.105224808 -4.07548155556 [7, 1, 1] 145 | -4.545368994 -4.45389666667 [5, 2, 2] 146 | -4.192665009 -4.10639533333 [6, 2, 1] 147 | -4.2313633469 -4.152341 [5, 3, 1] 148 | -4.5120032813 -4.42059611111 [6, 1, 2] 149 | -4.8213015828 -4.79074833333 [5, 1, 3] 150 | -5.117983255 -5.06511455556 [5, 0, 4] 151 | -5.0865049862 -5.03806033333 [4, 1, 4] 152 | -5.341372146 -5.27398855556 [4, 0, 5] 153 | -4.23136069 -4.24106344444 [4, 4, 1] 154 | -4.2102926569 -4.20981666667 [3, 5, 1] 155 | -4.5411577195 -4.50239366667 [4, 3, 2] 156 | -4.8150452306 -4.78662922222 [4, 2, 3] 157 | -4.140938015 -4.143179 [2, 6, 1] 158 | -4.4818943853 -4.48634766667 [3, 4, 2] 159 | -4.7327143393 -4.72855277778 [3, 3, 3] 160 | -4.6301308171 -4.60341377778 [2, 4, 3] 161 | -4.8570307462 -4.83371088889 [2, 3, 4] 162 | -5.4489284495 -5.433402 [3, 0, 6] 163 | -5.0831662949 -5.06704666667 [2, 2, 5] 164 | -4.2600740853 -4.26405844444 [1, 6, 2] 165 | -4.4911390658 -4.48037022222 [1, 5, 3] 166 | -4.9232335991 -4.90755622222 [1, 3, 5] 167 | -5.3234106221 -5.27473244444 [2, 1, 6] 168 | -5.1422572155 -5.11832088889 [1, 2, 6] 169 | -5.5170549971 -5.535185 [2, 0, 7] 170 | -4.1010181405 -4.13031788889 [0, 7, 2] 171 | -4.3116469457 -4.33175455556 [0, 6, 3] 172 | -4.7778220934 -4.72123922222 [6, 0, 3] 173 | -3.7636979584 -3.78582055556 [7, 2, 0] 174 | -3.8636435771 -3.92726666667 [0, 8, 1] 175 | -3.6588423313 -3.75984977778 [8, 1, 0] 176 | -4.9967811284 -4.94089511111 [3, 2, 4] 177 | -5.2326038894 -5.18279355556 [3, 1, 5] 178 | -4.929002684 -4.92638455556 [0, 3, 6] 179 | -5.1395555635 -5.11683211111 [0, 2, 7] 180 | -3.8685485938 -3.807925 [6, 3, 0] 181 | -3.9242897758 -3.83940233333 [5, 4, 0] 182 | -3.9544947102 -3.87116588889 [4, 5, 0] 183 | -3.9464346833 -3.91724733333 [3, 6, 0] 184 | -3.8946310949 -3.87697388889 [2, 7, 0] 185 | -3.7861327529 -3.809299 [1, 8, 0] 186 | -4.0230444758 -4.04966944444 [1, 7, 1] 187 | -5.3444573191 -5.32486422222 [1, 1, 7] 188 | -5.560463628 -5.53839922222 [1, 0, 8] 189 | -5.3453205825 -5.312919 [0, 1, 8] 190 | -4.0424099597 -4.04840922222 [8, 0, 1] 191 | -4.4286128245 -4.37175888889 [7, 0, 2] 192 | -4.3961630597 -4.32915844444 [2, 5, 2] 193 | -4.7259107426 -4.68479777778 [1, 4, 4] 194 | -4.534285475 -4.52679811111 [0, 5, 4] 195 | -4.7271667005 -4.72140277778 [0, 4, 5] 196 | -3.6435909094 -3.760073 [9, 1, 0] 197 | -3.9996042003 -4.015916 [9, 0, 1] 198 | -4.3885904124 -4.2951711 [8, 0, 2] 199 | -4.0640994132 -4.0328226 [8, 1, 1] 200 | -4.44451877 -4.3090425 [7, 1, 2] 201 | -4.70467933 -4.5776975 [7, 0, 3] 202 | -3.8979199858 -3.8279354 [6, 4, 0] 203 | -3.8369257454 -3.7923549 [7, 3, 0] 204 | -4.1534395836 -4.0514061 [7, 2, 1] 205 | -4.4735433051 -4.3638444 [6, 2, 2] 206 | -4.1864569052 -4.1016462 [6, 3, 1] 207 | -4.7438370198 -4.6562458 [6, 1, 3] 208 | -5.0261440811 -4.9780596 [5, 1, 4] 209 | -4.2133454559 -4.1478528 [5, 4, 1] 210 | -3.9459501045 -3.857769 [5, 5, 0] 211 | -4.7504373666 -4.732628 [5, 2, 3] 212 | -4.7200564402 -4.6931144 [4, 3, 3] 213 | -5.0117869191 -4.9476955 [6, 0, 4] 214 | -5.2739664681 -5.2601815 [5, 0, 5] 215 | -4.964407793 -4.9201717 [4, 2, 4] 216 | -5.185598455 -5.1383469 [4, 1, 5] 217 | -5.4030275493 -5.3484978 [4, 0, 6] 218 | -4.4895889532 -4.4942395 [5, 3, 2] 219 | -3.9540494155 -3.9002677 [4, 6, 0] 220 | -4.2161238613 -4.2059649 [4, 5, 1] 221 | -3.9414873653 -3.9179358 [3, 7, 0] 222 | -4.1884765655 -4.159668 [3, 6, 1] 223 | -4.426767754 -4.3906296 [3, 5, 2] 224 | -4.6643195966 -4.6339868 [3, 4, 3] 225 | -4.4795315652 -4.4284201 [4, 4, 2] 226 | -4.1081284559 -4.1001906 [2, 7, 1] 227 | -4.3442679341 -4.3278547 [2, 6, 2] 228 | -3.8834702602 -3.8625704 [2, 8, 0] 229 | -3.7679284153 -3.8004972 [1, 9, 0] 230 | -3.987720645 -4.0032055 [1, 8, 1] 231 | -4.2116871002 -4.2074086 [1, 7, 2] 232 | -4.5538262199 -4.5415542 [2, 5, 3] 233 | -4.421069784 -4.4124556 [1, 6, 3] 234 | -4.7766512192 -4.7638867 [2, 4, 4] 235 | -4.8071062715 -4.7907832 [1, 4, 5] 236 | -4.9059481624 -4.8670905 [3, 3, 4] 237 | -5.1230877936 -5.1050966 [3, 2, 5] 238 | -5.3151503049 -5.301167 [3, 1, 6] 239 | -5.5152406939 -5.5064975 [3, 0, 7] 240 | -4.9886727321 -4.9664374 [2, 3, 5] 241 | -5.1855161713 -5.1655773 [2, 2, 6] 242 | -5.3847725964 -5.3546898 [2, 1, 7] 243 | -5.5678785704 -5.553211 [2, 0, 8] 244 | -4.6236277205 -4.6126109 [1, 5, 4] 245 | -5.000524251 -4.9895405 [1, 3, 6] 246 | -5.1859859009 -5.1652321 [1, 2, 7] 247 | -5.3699546348 -5.3515233 [1, 1, 8] 248 | -5.5588069415 -5.537609 [1, 0, 9] 249 | -3.8430157026 -3.9082544 [0, 9, 1] 250 | -4.0712627937 -4.0913811 [0, 8, 2] 251 | -4.2612089647 -4.2733476 [0, 7, 3] 252 | -4.63025218 -4.6195317 [0, 5, 5] 253 | -4.4431625742 -4.4452638 [0, 6, 4] 254 | -4.8072700784 -4.7991914 [0, 4, 6] 255 | -4.9901734373 -4.9791327 [0, 3, 7] 256 | -5.1760767041 -5.1549013 [0, 2, 8] 257 | -5.3553312283 -5.3348554 [0, 1, 9] 258 | -3.7397625478 -3.7747771 [8, 2, 0] 259 | -------------------------------------------------------------------------------- /examples/scatter_colorbar.py: -------------------------------------------------------------------------------- 1 | """An example of the colorbar display on the scatter plot.""" 2 | import ternary 3 | import matplotlib.pyplot as plt 4 | 5 | 6 | def _en_to_enth(energy, concs, A, B, C): 7 | """Converts an energy to an enthalpy. 8 | 9 | Converts energy to enthalpy using the following formula: 10 | Enthalpy = energy - (energy contribution from A) - (energy contribution from B) - 11 | (energy contribution from C) 12 | An absolute value is taken afterward for convenience. 13 | 14 | Parameters 15 | ---------- 16 | energy : float 17 | The energy of the structure 18 | concs : list of floats 19 | The concentrations of each element 20 | A : float 21 | The energy of pure A 22 | B : float 23 | The energy of pure B 24 | C : float 25 | The energy of pure C 26 | 27 | Returns 28 | ------- 29 | enth : float 30 | The enthalpy of formation. 31 | """ 32 | 33 | enth = abs(energy - concs[0]*A - concs[1] * B - concs[2] * C) 34 | return enth 35 | 36 | 37 | def _energy_to_enthalpy(energy): 38 | """Converts energy to enthalpy. 39 | 40 | This function take the energies stored in the energy array and 41 | converts them to formation enthalpy. 42 | 43 | Parameters 44 | --------- 45 | energy : list of lists of floats 46 | 47 | Returns 48 | ------- 49 | enthalpy : list of lists containing the enthalpies. 50 | """ 51 | 52 | pureA = [energy[0][0], energy[0][1]] 53 | pureB = [energy[1][0], energy[1][1]] 54 | pureC = [energy[2][0], energy[2][1]] 55 | 56 | enthalpy = [] 57 | for en in energy: 58 | c = en[2] 59 | conc = [float(i) / sum(c) for i in c] 60 | 61 | CE = _en_to_enth(en[0], conc, pureA[0], pureB[0], pureC[0]) 62 | VASP = _en_to_enth(en[1], conc, pureA[1], pureB[1], pureC[1]) 63 | 64 | enthalpy.append([CE, VASP, c]) 65 | 66 | return enthalpy 67 | 68 | 69 | def _find_error(vals): 70 | """Find the errors in the energy values. 71 | 72 | This function finds the errors in the enthalpys. 73 | 74 | Parameters 75 | ---------- 76 | vals : list of lists of floats 77 | 78 | Returns 79 | ------- 80 | err_vals : list of lists containing the errors. 81 | """ 82 | 83 | err_vals = [] 84 | for en in vals: 85 | c = en[2] 86 | conc = [float(i) / sum(c) for i in c] 87 | 88 | err = abs(en[0] - en[1]) 89 | 90 | err_vals.append([conc, err]) 91 | 92 | return err_vals 93 | 94 | 95 | def _read_data(fname): 96 | """Reads data from file. 97 | 98 | Reads the data in 'fname' into a list where each list entry contains 99 | [energy predicted, energy calculated, list of concentrations]. 100 | 101 | Parameters 102 | ---------- 103 | fname : str 104 | The name and path to the data file. 105 | 106 | Returns 107 | ------- 108 | energy : list of lists of floats 109 | A list of the energies and the concentrations. 110 | """ 111 | 112 | energy = [] 113 | with open(fname,'r') as f: 114 | for line in f: 115 | CE = abs(float(line.strip().split()[0])) 116 | VASP = abs(float(line.strip().split()[1])) 117 | conc = [i for i in line.strip().split()[2:]] 118 | 119 | conc_f = [] 120 | for c in conc: 121 | if '[' in c and ']' in c: 122 | conc_f.append(int(c[1:-1])) 123 | elif '[' in c: 124 | conc_f.append(int(c[1:-1])) 125 | elif ']' in c or ',' in c: 126 | conc_f.append(int(c[:-1])) 127 | else: 128 | conc_f.append(int(c)) 129 | energy.append([CE, VASP, conc_f]) 130 | return energy 131 | 132 | 133 | def conc_err_plot(fname): 134 | """Plots the error in the CE data. 135 | 136 | This plots the error in the CE predictions within a ternary concentration diagram. 137 | 138 | Parameters 139 | ---------- 140 | fname : string containing the input file name. 141 | """ 142 | 143 | energies = _read_data(fname) 144 | enthalpy = _energy_to_enthalpy(energies) 145 | this_errors = _find_error(enthalpy) 146 | 147 | points = [] 148 | colors = [] 149 | for er in this_errors: 150 | concs = er[0] 151 | points.append((concs[0] * 100, concs[1] * 100, concs[2] * 100)) 152 | colors.append(er[1]) 153 | 154 | scale = 100 155 | figure, tax = ternary.figure(scale=scale) 156 | tax.boundary(linewidth=1.0) 157 | tax.set_title("Errors in Convex Hull Predictions.", fontsize=20) 158 | tax.gridlines(multiple=10, color="blue") 159 | tax.scatter(points, vmax=max(colors), colormap=plt.cm.viridis, colorbar=True, c=colors, cmap=plt.cm.viridis) 160 | 161 | tax.show() 162 | 163 | 164 | if __name__ == "__main__": 165 | conc_err_plot('sample_data/scatter_colorbar.txt') 166 | -------------------------------------------------------------------------------- /examples/ternary_contours.py: -------------------------------------------------------------------------------- 1 | """ Compute ternary contours using matplotlib.pyplot.contour function """ 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | import ternary 5 | import math 6 | import itertools 7 | 8 | 9 | def shannon_entropy(p): 10 | """Computes the Shannon Entropy at a distribution in the simplex.""" 11 | s = 0. 12 | for i in range(len(p)): 13 | try: 14 | s += p[i] * math.log(p[i]) 15 | except ValueError: 16 | continue 17 | return -1. * s 18 | 19 | scale = 20 20 | level = [0.25, 0.5, 0.8, 0.9] # values for contours 21 | 22 | # === prepare coordinate list for contours 23 | x_range = np.arange(0, 1.01, 0.01) # ensure that grid spacing is small enough to get smooth contours 24 | coordinate_list = np.asarray(list(itertools.product(x_range, repeat=2))) 25 | coordinate_list = np.append(coordinate_list, (1 - coordinate_list[:, 0] - coordinate_list[:, 1]).reshape(-1, 1), axis=1) 26 | 27 | # === calculate data with coordinate list 28 | data_list = [] 29 | for point in coordinate_list: 30 | data_list.append(shannon_entropy(point)) 31 | data_list = np.asarray(data_list) 32 | data_list[np.sum(coordinate_list[:, 0:2], axis=1) > 1] = np.nan # remove data outside triangle 33 | 34 | # === reshape coordinates and data for use with pyplot contour function 35 | x = coordinate_list[:, 0].reshape(x_range.shape[0], -1) 36 | y = coordinate_list[:, 1].reshape(x_range.shape[0], -1) 37 | 38 | h = data_list.reshape(x_range.shape[0], -1) 39 | 40 | # === use pyplot to calculate contours 41 | contours = plt.contour(x, y, h, level) # this needs to be BEFORE figure definition 42 | plt.clf() # makes sure that contours are not plotted in carthesian plot 43 | 44 | fig, tax = ternary.figure(scale=scale) 45 | 46 | # === plot contour lines 47 | for ii, contour in enumerate(contours.allsegs): 48 | for jj, seg in enumerate(contour): 49 | tax.plot(seg[:, 0:2] * scale, color='r') 50 | 51 | # === plot regular data 52 | tax.heatmapf(shannon_entropy, boundary=True, style='hexagonal', colorbar=True) 53 | 54 | plt.show() 55 | -------------------------------------------------------------------------------- /readme_images/16_80_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcharper/python-ternary/9f946b69f266e61983f5be60793930fb1c380a6c/readme_images/16_80_1.png -------------------------------------------------------------------------------- /readme_images/16_80_stationary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcharper/python-ternary/9f946b69f266e61983f5be60793930fb1c380a6c/readme_images/16_80_stationary.png -------------------------------------------------------------------------------- /readme_images/23_80_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcharper/python-ternary/9f946b69f266e61983f5be60793930fb1c380a6c/readme_images/23_80_0.png -------------------------------------------------------------------------------- /readme_images/24_80_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcharper/python-ternary/9f946b69f266e61983f5be60793930fb1c380a6c/readme_images/24_80_1.png -------------------------------------------------------------------------------- /readme_images/boundary_and_gridlines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcharper/python-ternary/9f946b69f266e61983f5be60793930fb1c380a6c/readme_images/boundary_and_gridlines.png -------------------------------------------------------------------------------- /readme_images/btweinstein_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcharper/python-ternary/9f946b69f266e61983f5be60793930fb1c380a6c/readme_images/btweinstein_example.png -------------------------------------------------------------------------------- /readme_images/btweinstein_example2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcharper/python-ternary/9f946b69f266e61983f5be60793930fb1c380a6c/readme_images/btweinstein_example2.png -------------------------------------------------------------------------------- /readme_images/colored_boundary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcharper/python-ternary/9f946b69f266e61983f5be60793930fb1c380a6c/readme_images/colored_boundary.png -------------------------------------------------------------------------------- /readme_images/colored_trajectory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcharper/python-ternary/9f946b69f266e61983f5be60793930fb1c380a6c/readme_images/colored_trajectory.png -------------------------------------------------------------------------------- /readme_images/heatmap-dual_vs_triangular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcharper/python-ternary/9f946b69f266e61983f5be60793930fb1c380a6c/readme_images/heatmap-dual_vs_triangular.png -------------------------------------------------------------------------------- /readme_images/heatmap-dualtriangular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcharper/python-ternary/9f946b69f266e61983f5be60793930fb1c380a6c/readme_images/heatmap-dualtriangular.png -------------------------------------------------------------------------------- /readme_images/heatmap-grids.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcharper/python-ternary/9f946b69f266e61983f5be60793930fb1c380a6c/readme_images/heatmap-grids.png -------------------------------------------------------------------------------- /readme_images/heatmap-triangular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcharper/python-ternary/9f946b69f266e61983f5be60793930fb1c380a6c/readme_images/heatmap-triangular.png -------------------------------------------------------------------------------- /readme_images/heatmap_rsp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcharper/python-ternary/9f946b69f266e61983f5be60793930fb1c380a6c/readme_images/heatmap_rsp.png -------------------------------------------------------------------------------- /readme_images/heatmap_shannon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcharper/python-ternary/9f946b69f266e61983f5be60793930fb1c380a6c/readme_images/heatmap_shannon.png -------------------------------------------------------------------------------- /readme_images/heatmap_styles.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | 3 | import matplotlib as mpl 4 | import matplotlib.pyplot as plt 5 | import numpy as np 6 | 7 | import ternary 8 | 9 | SQRT3OVER2 = np.sqrt(3) / 2 10 | 11 | def project(p): 12 | # project using the same transformation that was used for the triangles 13 | a, b, c = p 14 | x = a/2 + b 15 | y = SQRT3OVER2 * a 16 | return (x, y) 17 | 18 | def matplotlib_plot(scale, cmap, filename=None): 19 | 20 | points = list(ternary.helpers.simplex_iterator(scale)) 21 | xs, ys = zip(*map(project, points)) 22 | values = range(len(points)) 23 | 24 | f, axes = plt.subplots(1,3, figsize=(8.5, 4.5)) 25 | 26 | styles = ['triangular', 'dual-triangular', 'hexagonal'] 27 | ticks_list = [range(scale + 1), range(scale + 2), range(scale + 1)] 28 | shift = True 29 | for ax, style, ticks in zip(axes, styles, ticks_list): 30 | ax.set_aspect('equal') 31 | ax.set_title(style) 32 | ternary.heatmap(dict(zip(points, values)), 33 | scale=scale, ax=ax, 34 | cmap=cmap, vmax=len(points) + 1, 35 | style=style, colorbar=False) 36 | if style == 'dual-triangular' and shift: 37 | xvals = np.array(xs) + .5 38 | yvals = np.array(ys) + 1/3 39 | else: 40 | xvals = xs 41 | yvals = ys 42 | 43 | ax.scatter(xvals, yvals, s=150, c='c', zorder=3) 44 | ax.set_xticks(ticks) 45 | ax.set_yticks(ticks) 46 | for x, y, value in zip(xvals, yvals, values): 47 | ax.text(x, y, str(value), 48 | fontsize=8, 49 | horizontalalignment='center', 50 | verticalalignment='center') 51 | 52 | # Colorbar 53 | f.tight_layout() 54 | cbax = f.add_axes([0.025, 0.1, 0.95, 0.10]) 55 | norm = mpl.colors.Normalize(vmin=0, vmax=len(points)) 56 | ticks = np.linspace(0, len(points), num=len(points) + 1) 57 | cb1 = mpl.colorbar.ColorbarBase(cbax, cmap=cmap, 58 | norm=norm, 59 | orientation='horizontal') 60 | cb1.set_ticks(ticks) 61 | 62 | if filename is not None: 63 | plt.savefig(filename) 64 | 65 | return ax 66 | 67 | if __name__ == '__main__': 68 | import subprocess 69 | 70 | scale = 3 71 | cmaps = [plt.cm.gray, plt.cm.cubehelix] 72 | 73 | basename = 'heatmap_styles_{}.pdf' 74 | filenames = [basename.format(cmap.name) for cmap in cmaps] 75 | 76 | cmd = 'convert -density 300 -trim {} -quality 100 {}' 77 | for cmap, pdf in zip(cmaps, filenames): 78 | png = pdf[:-3] + 'png' 79 | matplotlib_plot(scale, cmap, filename=pdf) 80 | subprocess.call(cmd.format(pdf, png), shell=True) 81 | 82 | -------------------------------------------------------------------------------- /readme_images/heatmap_styles_cubehelix.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcharper/python-ternary/9f946b69f266e61983f5be60793930fb1c380a6c/readme_images/heatmap_styles_cubehelix.pdf -------------------------------------------------------------------------------- /readme_images/heatmap_styles_cubehelix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcharper/python-ternary/9f946b69f266e61983f5be60793930fb1c380a6c/readme_images/heatmap_styles_cubehelix.png -------------------------------------------------------------------------------- /readme_images/heatmap_styles_gray.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcharper/python-ternary/9f946b69f266e61983f5be60793930fb1c380a6c/readme_images/heatmap_styles_gray.pdf -------------------------------------------------------------------------------- /readme_images/heatmap_styles_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcharper/python-ternary/9f946b69f266e61983f5be60793930fb1c380a6c/readme_images/heatmap_styles_gray.png -------------------------------------------------------------------------------- /readme_images/orientations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcharper/python-ternary/9f946b69f266e61983f5be60793930fb1c380a6c/readme_images/orientations.png -------------------------------------------------------------------------------- /readme_images/rgba_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcharper/python-ternary/9f946b69f266e61983f5be60793930fb1c380a6c/readme_images/rgba_example.png -------------------------------------------------------------------------------- /readme_images/scatter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcharper/python-ternary/9f946b69f266e61983f5be60793930fb1c380a6c/readme_images/scatter.png -------------------------------------------------------------------------------- /readme_images/trajectory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcharper/python-ternary/9f946b69f266e61983f5be60793930fb1c380a6c/readme_images/trajectory.png -------------------------------------------------------------------------------- /readme_images/various_lines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcharper/python-ternary/9f946b69f266e61983f5be60793930fb1c380a6c/readme_images/various_lines.png -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description_file = README.md 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | from distutils.core import setup 3 | 4 | version = "1.0.8" 5 | 6 | with open('README.txt') as file: 7 | long_description = file.read() 8 | 9 | classifiers = [ 10 | "Development Status :: 5 - Production/Stable", 11 | "Intended Audience :: Science/Research", 12 | "License :: OSI Approved :: MIT License", 13 | "Natural Language :: English", 14 | "Programming Language :: Python", 15 | "Programming Language :: Python :: 3.9", 16 | "Programming Language :: Python :: 3.10", 17 | "Programming Language :: Python :: 3.11", 18 | "Programming Language :: Python :: 3.12", 19 | "Topic :: Scientific/Engineering :: Visualization" 20 | ] 21 | 22 | setup( 23 | name="python-ternary", 24 | version=version, 25 | packages=['ternary'], 26 | install_requires=["matplotlib>=2"], 27 | author="Marc Harper and contributors", 28 | author_email="marc.harper@gmail.com", 29 | classifiers=classifiers, 30 | description="Make ternary plots in python with matplotlib", 31 | long_description=long_description, 32 | keywords="matplotlib ternary plotting", 33 | license="MIT", 34 | url="https://github.com/marcharper/python-ternary", 35 | download_url="https://github.com/marcharper/python-ternary/tarball/{}".format(version), 36 | ) 37 | -------------------------------------------------------------------------------- /ternary/__init__.py: -------------------------------------------------------------------------------- 1 | from matplotlib import pyplot as plt 2 | 3 | from .plotting import ( 4 | clear_matplotlib_ticks, 5 | plot, 6 | resize_drawing_canvas, 7 | scatter, 8 | ) 9 | 10 | from .lines import ( 11 | boundary, 12 | gridlines, 13 | line, 14 | horizontal_line, 15 | left_parallel_line, 16 | right_parallel_line, 17 | ) 18 | 19 | from .helpers import project_point 20 | from .colormapping import get_cmap 21 | from .heatmapping import heatmap, heatmapf, svg_heatmap 22 | from .ternary_axes_subplot import figure, TernaryAxesSubplot 23 | 24 | __version__ = "1.0.8" 25 | -------------------------------------------------------------------------------- /ternary/colormapping.py: -------------------------------------------------------------------------------- 1 | import matplotlib 2 | from matplotlib import pyplot as plt 3 | from matplotlib.colors import rgb2hex 4 | 5 | ## Default colormap, other options here: http://www.scipy.org/Cookbook/Matplotlib/Show_colormaps 6 | s = matplotlib.__version__.split('.') 7 | if int(s[0]) >= 2 or (int(s[0]) >= 1 and int(s[1]) >= 5): 8 | DEFAULT_COLOR_MAP_NAME = "viridis" 9 | else: 10 | DEFAULT_COLOR_MAP_NAME = "jet" 11 | 12 | 13 | ## Matplotlib Colormapping ## 14 | 15 | def get_cmap(cmap=None): 16 | """ 17 | Loads a matplotlib colormap if specified or supplies the default. 18 | 19 | Parameters 20 | ---------- 21 | cmap: string or matplotlib.colors.Colormap instance 22 | The name of the Matplotlib colormap to look up. 23 | 24 | Returns 25 | ------- 26 | The desired Matplotlib colormap 27 | 28 | Raises 29 | ------ 30 | ValueError if colormap name is not recognized by Matplotlib 31 | """ 32 | 33 | if isinstance(cmap, matplotlib.colors.Colormap): 34 | return cmap 35 | if isinstance(cmap, str): 36 | cmap_name = cmap 37 | else: 38 | cmap_name = DEFAULT_COLOR_MAP_NAME 39 | return plt.get_cmap(cmap_name) 40 | 41 | 42 | def colormapper(value, lower=0, upper=1, cmap=None): 43 | """ 44 | Maps values to colors by normalizing within [a,b], obtaining rgba from the 45 | given matplotlib color map for heatmap polygon coloring. 46 | 47 | Parameters 48 | ---------- 49 | value: float 50 | The value to be colormapped 51 | lower: float 52 | Lower bound of colors 53 | upper: float 54 | Upper bound of colors 55 | cmap: String or matplotlib.colors.Colormap (optional) 56 | Colormap object to prevent repeated lookup 57 | 58 | Returns 59 | ------- 60 | hex_, float 61 | The value mapped to an appropriate RGBA color value 62 | """ 63 | 64 | cmap = get_cmap(cmap) 65 | if upper - lower == 0: 66 | rgba = cmap(0) 67 | else: 68 | rgba = cmap((value - lower) / float(upper - lower)) 69 | hex_ = rgb2hex(rgba) 70 | return hex_ 71 | 72 | 73 | def colorbar_hack(ax, vmin, vmax, cmap, scientific=False, cbarlabel=None, norm=None, 74 | **kwargs): 75 | """ 76 | Colorbar hack to insert colorbar on ternary plot. 77 | 78 | Called by heatmap, not intended for direct usage. 79 | 80 | Parameters 81 | ---------- 82 | vmin: float 83 | Minimum value to portray in colorbar 84 | vmax: float 85 | Maximum value to portray in colorbar 86 | cmap: Matplotlib colormap 87 | Matplotlib colormap to use 88 | 89 | """ 90 | # http://stackoverflow.com/questions/8342549/matplotlib-add-colorbar-to-a-sequence-of-line-plots 91 | if norm is None: 92 | norm = plt.Normalize(vmin=vmin, vmax=vmax) 93 | sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm) 94 | sm._A = [] 95 | cb = plt.colorbar(sm, ax=ax, **kwargs) 96 | if cbarlabel is not None: 97 | cb.set_label(cbarlabel) 98 | if scientific: 99 | cb.locator = matplotlib.ticker.LinearLocator(numticks=7) 100 | cb.formatter = matplotlib.ticker.ScalarFormatter() 101 | cb.formatter.set_powerlimits((0, 0)) 102 | cb.update_ticks() 103 | -------------------------------------------------------------------------------- /ternary/heatmapping.py: -------------------------------------------------------------------------------- 1 | """ 2 | Various Heatmaps. 3 | """ 4 | 5 | import functools 6 | import numpy as np 7 | from matplotlib import pyplot as plt 8 | 9 | from .helpers import unzip, normalize, simplex_iterator, permute_point, project_point 10 | from .colormapping import get_cmap, colormapper, colorbar_hack 11 | 12 | ### Heatmap Triangulation Coordinates 13 | 14 | ## Triangular Heatmaps 15 | 16 | 17 | def blend_value(data, i, j, k, keys=None): 18 | """Computes the average value of the three vertices of a triangle in the 19 | simplex triangulation, where two of the vertices are on the lower 20 | horizontal.""" 21 | 22 | key_size = len(list(data.keys())[0]) 23 | if not keys: 24 | keys = triangle_coordinates(i, j, k) 25 | # Reduce key from (i, j, k) to (i, j) if necessary 26 | keys = [tuple(key[:key_size]) for key in keys] 27 | 28 | # Sum over the values of the points to blend 29 | try: 30 | s = sum(data[key] for key in keys) 31 | value = s / 3. 32 | except KeyError: 33 | value = None 34 | return value 35 | 36 | 37 | def alt_blend_value(data, i, j, k): 38 | """Computes the average value of the three vertices of a triangle in the 39 | simplex triangulation, where two of the vertices are on the upper 40 | horizontal.""" 41 | 42 | keys = alt_triangle_coordinates(i, j, k) 43 | return blend_value(data, i, j, k, keys=keys) 44 | 45 | 46 | def triangle_coordinates(i, j, k): 47 | """ 48 | Computes coordinates of the constituent triangles of a triangulation for the 49 | simplex. These triangles are parallel to the lower axis on the lower side. 50 | 51 | Parameters 52 | ---------- 53 | i,j,k: enumeration of the desired triangle 54 | 55 | Returns 56 | ------- 57 | A numpy array of coordinates of the hexagon (unprojected) 58 | """ 59 | 60 | return [(i, j, k), (i + 1, j, k - 1), (i, j + 1, k - 1)] 61 | 62 | 63 | def alt_triangle_coordinates(i, j, k): 64 | """ 65 | Computes coordinates of the constituent triangles of a triangulation for the 66 | simplex. These triangles are parallel to the lower axis on the upper side. 67 | 68 | Parameters 69 | ---------- 70 | i,j,k: enumeration of the desired triangle 71 | 72 | Returns 73 | ------- 74 | A numpy array of coordinates of the hexagon (unprojected) 75 | """ 76 | 77 | return [(i, j + 1, k - 1), (i + 1, j, k - 1), (i + 1, j + 1, k - 2)] 78 | 79 | 80 | ## Hexagonal Heatmaps ## 81 | 82 | def generate_hexagon_deltas(): 83 | """ 84 | Generates a dictionary of the necessary additive vectors to generate the 85 | hexagon points for the hexagonal heatmap. 86 | """ 87 | 88 | zero = np.array([0, 0, 0]) 89 | alpha = np.array([-1./3, 2./3, 0]) 90 | deltaup = np.array([1./3, 1./3, 0]) 91 | deltadown = np.array([2./3, -1./3, 0]) 92 | i_vec = np.array([0, 1./2, -1./2]) 93 | i_vec_down = np.array([1./2, -1./2, 0]) 94 | deltaX_vec = np.array([1./2, 0, -1./2]) 95 | 96 | d = dict() 97 | # Corner Points 98 | d["100"] = [zero, -deltaX_vec, -deltadown, -i_vec_down] 99 | d["010"] = [zero, i_vec_down, -alpha, -i_vec] 100 | d["001"] = [zero, i_vec, deltaup, deltaX_vec] 101 | # On the Edges 102 | d["011"] = [i_vec, deltaup, deltadown, -alpha, -i_vec] 103 | d["101"] = [-deltaX_vec, -deltadown, alpha, deltaup, deltaX_vec] 104 | d["110"] = [i_vec_down, -alpha, -deltaup, -deltadown, -i_vec_down] 105 | # Interior point 106 | d["111"] = [alpha, deltaup, deltadown, -alpha, -deltaup, -deltadown] 107 | 108 | return d 109 | 110 | 111 | hexagon_deltas = generate_hexagon_deltas() 112 | 113 | 114 | def hexagon_coordinates(i, j, k): 115 | """ 116 | Computes coordinates of the constituent hexagons of a hexagonal heatmap. 117 | 118 | Parameters 119 | ---------- 120 | i, j, k: enumeration of the desired hexagon 121 | 122 | Returns 123 | ------- 124 | A numpy array of coordinates of the hexagon (unprojected) 125 | """ 126 | 127 | signature = "" 128 | for x in [i, j, k]: 129 | if x == 0: 130 | signature += "0" 131 | else: 132 | signature += "1" 133 | deltas = hexagon_deltas[signature] 134 | center = np.array([i, j, k]) 135 | return np.array([center + x for x in deltas]) 136 | 137 | 138 | ## Heatmaps ## 139 | 140 | def polygon_generator(data, scale, style, permutation=None): 141 | """Generator for the vertices of the polygon to be colored and its color, 142 | depending on style. Called by heatmap.""" 143 | 144 | # We'll project the coordinates inside this function to prevent 145 | # passing around permutation more than necessary 146 | project = functools.partial(project_point, permutation=permutation) 147 | 148 | if isinstance(data, dict): 149 | data_gen = data.items() 150 | else: 151 | # Only works with style == 'h' 152 | data_gen = data 153 | 154 | for key, value in data_gen: 155 | if value is None: 156 | continue 157 | i = key[0] 158 | j = key[1] 159 | k = scale - i - j 160 | if style == 'h': 161 | # Note here we permute first and then project normally, in 162 | # contrast to the cases below. 163 | i, j, k = list(permute_point([i, j, k], permutation=permutation)) 164 | vertices = hexagon_coordinates(i, j, k) 165 | yield map(project_point, vertices), value 166 | elif style == 'd': 167 | # Upright triangles 168 | if (i <= scale) and (j <= scale) and (k >= 0): 169 | vertices = triangle_coordinates(i, j, k) 170 | yield map(project, vertices), value 171 | # Upside-down triangles 172 | if (i < scale) and (j < scale) and (k >= 1): 173 | vertices = alt_triangle_coordinates(i, j, k) 174 | value = blend_value(data, i, j, k) 175 | yield map(project, vertices), value 176 | elif style == 't': 177 | # Upright triangles 178 | if (i < scale) and (j < scale) and (k > 0): 179 | vertices = triangle_coordinates(i, j, k) 180 | value = blend_value(data, i, j, k) 181 | yield map(project, vertices), value 182 | # If not on the boundary add the upside-down triangle 183 | if (i < scale) and (j < scale) and (k > 1): 184 | vertices = alt_triangle_coordinates(i, j, k) 185 | value = alt_blend_value(data, i, j, k) 186 | yield map(project, vertices), value 187 | 188 | 189 | def heatmap(data, scale, vmin=None, vmax=None, cmap=None, ax=None, 190 | scientific=False, style='triangular', colorbar=True, 191 | permutation=None, use_rgba=False, cbarlabel=None, cb_kwargs=None): 192 | """ 193 | Plots heatmap of given color values. 194 | 195 | Parameters 196 | ---------- 197 | data: dictionary 198 | A dictionary mapping the i, j polygon to the heatmap color, where 199 | i + j + k = scale. 200 | scale: Integer 201 | The scale used to partition the simplex. 202 | vmin: float, None 203 | The minimum color value, used to normalize colors. Computed if absent. 204 | vmax: float, None 205 | The maximum color value, used to normalize colors. Computed if absent. 206 | cmap: String or matplotlib.colors.Colormap, None 207 | The name of the Matplotlib colormap to use. 208 | ax: Matplotlib AxesSubplot, None 209 | The subplot to draw on. 210 | scientific: Bool, False 211 | Whether to use scientific notation for colorbar numbers. 212 | style: String, "triangular" 213 | The style of the heatmap, "triangular", "dual-triangular" or "hexagonal" 214 | colorbar: bool, True 215 | Show colorbar. 216 | permutation: string, None 217 | A permutation of the coordinates 218 | use_rgba: bool, False 219 | Use rgba color values 220 | cbarlabel: string, None 221 | Text label for the colorbar 222 | cb_kwargs: dict 223 | dict of kwargs to pass to colorbar 224 | 225 | Returns 226 | ------- 227 | ax: The matplotlib axis 228 | """ 229 | 230 | if not ax: 231 | fig, ax = plt.subplots() 232 | # If use_rgba, make the RGBA values numpy arrays so that they can 233 | # be averaged. 234 | if use_rgba: 235 | for k, v in data.items(): 236 | data[k] = np.array(v) 237 | else: 238 | cmap = get_cmap(cmap) 239 | if vmin is None: 240 | vmin = min(data.values()) 241 | if vmax is None: 242 | vmax = max(data.values()) 243 | style = style.lower()[0] 244 | if style not in ["t", "h", 'd']: 245 | raise ValueError("Heatmap style must be 'triangular', 'dual-triangular', or 'hexagonal'") 246 | 247 | vertices_values = polygon_generator(data, scale, style, 248 | permutation=permutation) 249 | 250 | # Draw the polygons and color them 251 | for vertices, value in vertices_values: 252 | if value is None: 253 | continue 254 | if not use_rgba: 255 | color = colormapper(value, vmin, vmax, cmap=cmap) 256 | else: 257 | color = value # rgba tuple (r,g,b,a) all in [0,1] 258 | # Matplotlib wants a list of xs and a list of ys 259 | xs, ys = unzip(vertices) 260 | ax.fill(xs, ys, facecolor=color, edgecolor=color) 261 | 262 | if not cb_kwargs: 263 | cb_kwargs = dict() 264 | if colorbar: 265 | colorbar_hack(ax, vmin, vmax, cmap, scientific=scientific, 266 | cbarlabel=cbarlabel, **cb_kwargs) 267 | return ax 268 | 269 | 270 | ## User Convenience Functions ## 271 | 272 | 273 | def heatmapf(func, scale=10, boundary=True, cmap=None, ax=None, 274 | scientific=False, style='triangular', colorbar=True, 275 | permutation=None, vmin=None, vmax=None, cbarlabel=None, 276 | cb_kwargs=None): 277 | """ 278 | Computes func on heatmap partition coordinates and plots heatmap. In other 279 | words, computes the function on lattice points of the simplex (normalized 280 | points) and creates a heatmap from the values. 281 | 282 | Parameters 283 | ---------- 284 | func: Function 285 | A function of 3-tuples to be heatmapped 286 | scale: Integer 287 | The scale used to partition the simplex 288 | boundary: Bool, True 289 | Include the boundary points or not 290 | cmap: String, None 291 | The name of the Matplotlib colormap to use 292 | ax: Matplotlib axis object, None 293 | The axis to draw the colormap on 294 | style: String, "triangular" 295 | The style of the heatmap, "triangular", "dual-triangular" or "hexagonal" 296 | scientific: Bool, False 297 | Whether to use scientific notation for colorbar numbers. 298 | colorbar: bool, True 299 | Show colorbar. 300 | permutation: string, None 301 | A permutation of the coordinates 302 | vmin: float 303 | The minimum color value, used to normalize colors. 304 | vmax: float 305 | The maximum color value, used to normalize colors. 306 | cb_kwargs: dict 307 | dict of kwargs to pass to colorbar 308 | 309 | Returns 310 | ------- 311 | ax, The matplotlib axis 312 | """ 313 | 314 | # Apply the function to a simplex partition 315 | data = dict() 316 | for i, j, k in simplex_iterator(scale=scale, boundary=boundary): 317 | data[(i, j)] = func(normalize([i, j, k])) 318 | # Pass everything to the heatmapper 319 | ax = heatmap(data, scale, cmap=cmap, ax=ax, style=style, 320 | scientific=scientific, colorbar=colorbar, 321 | permutation=permutation, vmin=vmin, vmax=vmax, 322 | cbarlabel=cbarlabel, cb_kwargs=cb_kwargs) 323 | return ax 324 | 325 | 326 | def svg_polygon(coordinates, color): 327 | """ 328 | Create an svg triangle for the stationary heatmap. 329 | 330 | Parameters 331 | ---------- 332 | coordinates: list 333 | The coordinates defining the polygon 334 | color: string 335 | RGB color value e.g. #26ffd1 336 | 337 | Returns 338 | ------- 339 | string, the svg string for the polygon 340 | """ 341 | 342 | coord_str = [] 343 | for c in coordinates: 344 | coord_str.append(",".join(map(str, c))) 345 | coord_str = " ".join(coord_str) 346 | polygon = '\n' % (coord_str, color, color) 347 | return polygon 348 | 349 | 350 | def svg_heatmap(data, scale, filename, vmax=None, vmin=None, style='h', 351 | permutation=None, cmap=None): 352 | """ 353 | Create a heatmap in SVG format. Intended for use with very large datasets, 354 | which would require large amounts of RAM using matplotlib. You can convert 355 | the image to another format with e.g. ImageMagick: 356 | 357 | convert -density 1200 -resize -rotate 180 1000x1000 your.svg your.png 358 | 359 | Parameters 360 | ---------- 361 | 362 | data: dictionary or k, v generator 363 | A dictionary mapping the i, j polygon to the heatmap color, where 364 | i + j + k = scale. If using a generator, style must be 'h'. 365 | scale: Integer 366 | The scale used to partition the simplex. 367 | filename: string 368 | The filename to write the SVG data to. 369 | vmin: float 370 | The minimum color value, used to normalize colors. 371 | vmax: float 372 | The maximum color value, used to normalize colors. 373 | cmap: String or matplotlib.colors.Colormap, None 374 | The name of the Matplotlib colormap to use. 375 | style: String, "h" 376 | The style of the heatmap, "triangular", "dual-triangular" or "hexagonal" 377 | permutation: string, None 378 | A permutation of the coordinates 379 | """ 380 | 381 | style = style.lower()[0] 382 | if style not in ["t", "h", 'd']: 383 | raise ValueError("Heatmap style must be 'triangular', 'dual-triangular', or 'hexagonal'") 384 | 385 | if not isinstance(data, dict): 386 | if not style == 'h': 387 | raise ValueError("Data can only be given as a generator for hexagonal style heatmaps because of blending for adjacent polygons.") 388 | elif vmax is None or vmin is None: 389 | raise ValueError("vmax and vmin must be supplied for data given as a generator.") 390 | 391 | cmap = get_cmap(cmap) 392 | 393 | if vmin is None: 394 | vmin = min(data.values()) 395 | if vmax is None: 396 | vmax = max(data.values()) 397 | 398 | height = scale * np.sqrt(3) / 2 + 2 399 | 400 | output_file = open(filename, 'w') 401 | output_file.write('\n' % (height, scale)) 402 | 403 | vertices_values = polygon_generator(data, scale, style, 404 | permutation=permutation) 405 | 406 | # Draw the polygons and color them 407 | for vertices, value in vertices_values: 408 | color = colormapper(value, vmin, vmax, cmap=cmap) 409 | output_file.write(svg_polygon(vertices, color)) 410 | 411 | output_file.write('\n') 412 | 413 | 414 | def background_color(ax, color, scale, zorder=-1000, alpha=None): 415 | """Draws a triangle behind the plot to serve as the background color.""" 416 | vertices = [(scale, 0, 0), (0, scale, 0), (0, 0, scale)] 417 | vertices = map(project_point, vertices) 418 | xs, ys = unzip(vertices) 419 | poly = ax.fill(xs, ys, facecolor=color, edgecolor=color, zorder=zorder, alpha=alpha) 420 | return poly 421 | -------------------------------------------------------------------------------- /ternary/helpers.py: -------------------------------------------------------------------------------- 1 | """ 2 | Helper functions and utilities for projecting to the simplex and various tasks. 3 | """ 4 | 5 | import numpy as np 6 | 7 | 8 | ### Constants ### 9 | 10 | SQRT3 = np.sqrt(3) 11 | SQRT3OVER2 = SQRT3 / 2. 12 | 13 | ### Auxilliary Functions ### 14 | 15 | 16 | def unzip(l): 17 | """[(a1, b1), ..., (an, bn)] ----> ([a1, ..., an], [b1, ..., bn])""" 18 | return list(zip(*l)) 19 | 20 | 21 | def normalize(l): 22 | """ 23 | Normalizes input list. 24 | 25 | Parameters 26 | ---------- 27 | l: list 28 | The list to be normalized 29 | 30 | Returns 31 | ------- 32 | The normalized list or numpy array 33 | 34 | Raises 35 | ------ 36 | ValueError, if the list sums to zero 37 | """ 38 | 39 | s = float(sum(l)) 40 | if s == 0: 41 | raise ValueError("Cannot normalize list with sum 0") 42 | return [x / s for x in l] 43 | 44 | 45 | def simplex_iterator(scale, boundary=True): 46 | """ 47 | Systematically iterates through a lattice of points on the 2-simplex. 48 | 49 | Parameters 50 | ---------- 51 | scale: Int 52 | The normalized scale of the simplex, i.e. N such that points (x,y,z) 53 | satisify x + y + z == N 54 | 55 | boundary: bool, True 56 | Include the boundary points (tuples where at least one 57 | coordinate is zero) 58 | 59 | Yields 60 | ------ 61 | 3-tuples, There are binom(n+2, 2) points (the triangular 62 | number for scale + 1, less 3*(scale+1) if boundary=False 63 | """ 64 | 65 | start = 0 66 | if not boundary: 67 | start = 1 68 | for i in range(start, scale + (1 - start)): 69 | for j in range(start, scale + (1 - start) - i): 70 | k = scale - i - j 71 | yield (i, j, k) 72 | 73 | 74 | ## Ternary Projections ## 75 | 76 | def permute_point(p, permutation=None): 77 | """ 78 | Permutes the point according to the permutation keyword argument. The 79 | default permutation is "012" which does not change the order of the 80 | coordinate. To rotate counterclockwise, use "120" and to rotate clockwise 81 | use "201".""" 82 | if not permutation: 83 | return p 84 | return [p[int(permutation[i])] for i in range(len(p))] 85 | 86 | 87 | def project_point(p, permutation=None): 88 | """ 89 | Maps (x,y,z) coordinates to planar simplex. 90 | 91 | Parameters 92 | ---------- 93 | p: 3-tuple 94 | The point to be projected p = (x, y, z) 95 | permutation: string, None, equivalent to "012" 96 | The order of the coordinates, counterclockwise from the origin 97 | """ 98 | permuted = permute_point(p, permutation=permutation) 99 | a = permuted[0] 100 | b = permuted[1] 101 | x = a + b/2. 102 | y = SQRT3OVER2 * b 103 | return np.array([x, y]) 104 | 105 | 106 | def planar_to_coordinates(p, scale): 107 | """ 108 | Planar simplex (regular x,y) to maps (x,y,z) ternary coordinates. The order of the coordinates is counterclockwise 109 | from the origin. 110 | 111 | Parameters 112 | ---------- 113 | p: 2-tuple 114 | The planar simplex (x, y) point to be transformed to maps (x,y,z) coordinates 115 | 116 | scale: Int 117 | The normalized scale of the simplex, i.e. N such that points (x,y,z) 118 | satisfy x + y + z == N 119 | 120 | """ 121 | y = p[1] / SQRT3OVER2 122 | x = p[0] - y / 2. 123 | z = scale - x - y 124 | return np.array([x, y, z]) 125 | 126 | 127 | def project_sequence(s, permutation=None): 128 | """ 129 | Projects a point or sequence of points using `project_point` to lists xs, ys 130 | for plotting with Matplotlib. 131 | 132 | Parameters 133 | ---------- 134 | s, Sequence-like 135 | The sequence of points (3-tuples) to be projected. 136 | 137 | Returns 138 | ------- 139 | xs, ys: The sequence of projected points in coordinates as two lists 140 | """ 141 | 142 | xs, ys = unzip([project_point(p, permutation=permutation) for p in s]) 143 | return xs, ys 144 | 145 | 146 | # Convert coordinates for custom plots with limits 147 | 148 | def convert_coordinates(q, conversion, axisorder): 149 | """ 150 | Convert a 3-tuple in data coordinates into to simplex data 151 | coordinates for plotting. 152 | 153 | Parameters 154 | ---------- 155 | q: 3-tuple 156 | the point to be plotted in data coordinates 157 | 158 | conversion: dict 159 | keys = ['b','l','r'] 160 | values = lambda function giving the conversion 161 | axisorder: String giving the order of the axes for the coordinate tuple 162 | e.g. 'blr' for bottom, left, right coordinates. 163 | 164 | Returns 165 | ------- 166 | p: 3-tuple 167 | The point converted to simplex coordinates. 168 | """ 169 | p = [] 170 | for k in range(3): 171 | p.append(conversion[axisorder[k]](q[k])) 172 | 173 | return tuple(p) 174 | 175 | 176 | def get_conversion(scale, limits): 177 | """ 178 | Get the conversion equations for each axis. 179 | 180 | limits: dict of min and max values for the axes in the order blr. 181 | """ 182 | fb = float(scale) / float(limits['b'][1] - limits['b'][0]) 183 | fl = float(scale) / float(limits['l'][1] - limits['l'][0]) 184 | fr = float(scale) / float(limits['r'][1] - limits['r'][0]) 185 | 186 | conversion = {"b": lambda x: (x - limits['b'][0]) * fb, 187 | "l": lambda x: (x - limits['l'][0]) * fl, 188 | "r": lambda x: (x - limits['r'][0]) * fr} 189 | 190 | return conversion 191 | 192 | 193 | def convert_coordinates_sequence(qs, scale, limits, axisorder): 194 | """ 195 | Take a sequence of 3-tuples in data coordinates and convert them 196 | to simplex coordinates for plotting. This is needed for custom 197 | plots where the scale of the simplex axes is set within limits rather 198 | than being defined by the scale parameter. 199 | 200 | Parameters 201 | ---------- 202 | qs, sequence of 3-tuples 203 | The points to be plotted in data coordinates. 204 | 205 | scale: int 206 | The scale parameter for the plot. 207 | limits: dict 208 | keys = ['b','l','r'] 209 | values = min,max data values for this axis. 210 | axisorder: String giving the order of the axes for the coordinate tuple 211 | e.g. 'blr' for bottom, left, right coordinates. 212 | 213 | Returns 214 | ------- 215 | s, list of 3-tuples 216 | the points converted to simplex coordinates 217 | """ 218 | conversion = get_conversion(scale, limits) 219 | 220 | return [convert_coordinates(q, conversion, axisorder) for q in qs] 221 | -------------------------------------------------------------------------------- /ternary/lines.py: -------------------------------------------------------------------------------- 1 | """ 2 | Line plotting functions, draw boundary and gridlines. 3 | """ 4 | 5 | from numpy import arange 6 | from matplotlib.lines import Line2D 7 | 8 | from .helpers import project_point 9 | 10 | 11 | ## Lines ## 12 | 13 | def line(ax, p1, p2, permutation=None, **kwargs): 14 | """ 15 | Draws a line on `ax` from p1 to p2. 16 | 17 | Parameters 18 | ---------- 19 | ax: Matplotlib AxesSubplot, None 20 | The subplot to draw on. 21 | p1: 2-tuple 22 | The (x,y) starting coordinates 23 | p2: 2-tuple 24 | The (x,y) ending coordinates 25 | kwargs: 26 | Any kwargs to pass through to Matplotlib. 27 | """ 28 | 29 | pp1 = project_point(p1, permutation=permutation) 30 | pp2 = project_point(p2, permutation=permutation) 31 | ax.add_line(Line2D((pp1[0], pp2[0]), (pp1[1], pp2[1]), **kwargs)) 32 | 33 | 34 | def horizontal_line(ax, scale, i, **kwargs): 35 | """ 36 | Draws the i-th horizontal line parallel to the lower axis. 37 | 38 | Parameters 39 | ---------- 40 | ax: Matplotlib AxesSubplot 41 | The subplot to draw on. 42 | scale: float, 1.0 43 | Simplex scale size. 44 | i: float 45 | The index of the line to draw 46 | kwargs: Dictionary 47 | Any kwargs to pass through to Matplotlib. 48 | """ 49 | 50 | p1 = (0, i, scale - i) 51 | p2 = (scale - i, i, 0) 52 | line(ax, p1, p2, **kwargs) 53 | 54 | 55 | def left_parallel_line(ax, scale, i, **kwargs): 56 | """ 57 | Draws the i-th line parallel to the left axis. 58 | 59 | Parameters 60 | ---------- 61 | ax: Matplotlib AxesSubplot 62 | The subplot to draw on. 63 | scale: float 64 | Simplex scale size. 65 | i: float 66 | The index of the line to draw 67 | kwargs: Dictionary 68 | Any kwargs to pass through to Matplotlib. 69 | """ 70 | 71 | p1 = (i, scale - i, 0) 72 | p2 = (i, 0, scale - i) 73 | line(ax, p1, p2, **kwargs) 74 | 75 | 76 | def right_parallel_line(ax, scale, i, **kwargs): 77 | """ 78 | Draws the i-th line parallel to the right axis. 79 | 80 | Parameters 81 | ---------- 82 | ax: Matplotlib AxesSubplot 83 | The subplot to draw on. 84 | scale: float 85 | Simplex scale size. 86 | i: float 87 | The index of the line to draw 88 | kwargs: Dictionary 89 | Any kwargs to pass through to Matplotlib. 90 | """ 91 | 92 | p1 = (0, scale - i, i) 93 | p2 = (scale - i, 0, i) 94 | line(ax, p1, p2, **kwargs) 95 | 96 | 97 | ## Boundary, Gridlines ## 98 | 99 | def boundary(ax, scale, axes_colors=None, **kwargs): 100 | """ 101 | Plots the boundary of the simplex. Creates and returns matplotlib axis if 102 | none given. 103 | 104 | Parameters 105 | ---------- 106 | ax: Matplotlib AxesSubplot, None 107 | The subplot to draw on. 108 | scale: float 109 | Simplex scale size. 110 | kwargs: 111 | Any kwargs to pass through to matplotlib. 112 | axes_colors: dict 113 | Option for coloring boundaries different colors. 114 | e.g. {'l': 'g'} for coloring the left axis boundary green 115 | """ 116 | 117 | # Set default color as black. 118 | if axes_colors is None: 119 | axes_colors = dict() 120 | for _axis in ['l', 'r', 'b']: 121 | if _axis not in axes_colors.keys(): 122 | axes_colors[_axis] = 'black' 123 | 124 | horizontal_line(ax, scale, 0, color=axes_colors['b'], **kwargs) 125 | left_parallel_line(ax, scale, 0, color=axes_colors['l'], **kwargs) 126 | right_parallel_line(ax, scale, 0, color=axes_colors['r'], **kwargs) 127 | return ax 128 | 129 | 130 | def merge_dicts(base, updates): 131 | """ 132 | Given two dicts, merge them into a new dict as a shallow copy. 133 | 134 | Parameters 135 | ---------- 136 | base: dict 137 | The base dictionary. 138 | updates: dict 139 | Secondary dictionary whose values override the base. 140 | """ 141 | if not base: 142 | base = dict() 143 | if not updates: 144 | updates = dict() 145 | z = base.copy() 146 | z.update(updates) 147 | return z 148 | 149 | 150 | def gridlines(ax, scale, multiple=None, horizontal_kwargs=None, 151 | left_kwargs=None, right_kwargs=None, **kwargs): 152 | """ 153 | Plots grid lines excluding boundary. 154 | 155 | Parameters 156 | ---------- 157 | ax: Matplotlib AxesSubplot, None 158 | The subplot to draw on. 159 | scale: float 160 | Simplex scale size. 161 | multiple: float, None 162 | Specifies which inner gridelines to draw. For example, if scale=30 and 163 | multiple=6, only 5 inner gridlines will be drawn. 164 | horizontal_kwargs: dict, None 165 | Any kwargs to pass through to matplotlib for horizontal gridlines 166 | left_kwargs: dict, None 167 | Any kwargs to pass through to matplotlib for left parallel gridlines 168 | right_kwargs: dict, None 169 | Any kwargs to pass through to matplotlib for right parallel gridlines 170 | kwargs: 171 | Any kwargs to pass through to matplotlib, if not using 172 | horizontal_kwargs, left_kwargs, or right_kwargs 173 | """ 174 | 175 | if 'linewidth' not in kwargs: 176 | kwargs["linewidth"] = 0.5 177 | if 'linestyle' not in kwargs: 178 | kwargs["linestyle"] = ':' 179 | horizontal_kwargs = merge_dicts(kwargs, horizontal_kwargs) 180 | left_kwargs = merge_dicts(kwargs, left_kwargs) 181 | right_kwargs = merge_dicts(kwargs, right_kwargs) 182 | if not multiple: 183 | multiple = 1. 184 | ## Draw grid-lines 185 | # Parallel to horizontal axis 186 | for i in arange(0, scale, multiple): 187 | horizontal_line(ax, scale, i, **horizontal_kwargs) 188 | # Parallel to left and right axes 189 | for i in arange(0, scale + multiple, multiple): 190 | left_parallel_line(ax, scale, i, **left_kwargs) 191 | right_parallel_line(ax, scale, i, **right_kwargs) 192 | return ax 193 | 194 | 195 | def normalize_tick_formats(tick_formats): 196 | if type(tick_formats) == dict: 197 | return tick_formats 198 | if tick_formats is None: 199 | s = '%d' 200 | elif type(tick_formats) == str: 201 | s = tick_formats 202 | else: 203 | raise TypeError("tick_formats must be a dictionary of strings" 204 | " a string, or None.") 205 | return {'b': s, 'l': s, 'r': s} 206 | 207 | 208 | def ticks(ax, scale, ticks=None, locations=None, multiple=1, axis='b', 209 | offset=0.01, clockwise=False, axes_colors=None, fontsize=10, 210 | tick_formats=None, **kwargs): 211 | """ 212 | Sets tick marks and labels. 213 | 214 | Parameters 215 | ---------- 216 | ax: Matplotlib AxesSubplot, None 217 | The subplot to draw on. 218 | scale: float, 1.0 219 | Simplex scale size. 220 | ticks: list of strings, None 221 | The tick labels 222 | locations: list of points, None 223 | The locations of the ticks 224 | multiple: float, None 225 | Specifies which ticks gridelines to draw. For example, if scale=30 and 226 | multiple=6, only 5 ticks will be drawn. 227 | axis: str, 'b' 228 | The axis or axes to draw the ticks for. `axis` must be a substring of 229 | 'lrb' (as sets) 230 | offset: float, 0.01 231 | controls the length of the ticks 232 | clockwise: bool, False 233 | Draw ticks marks clockwise or counterclockwise 234 | axes_colors: Dict, None 235 | Option to color ticks differently for each axis, 'l', 'r', 'b' 236 | e.g. {'l': 'g', 'r':'b', 'b': 'y'} 237 | tick_formats: None, Dict, Str 238 | If None, all axes will be labelled with ints. If Dict, the keys are 239 | 'b', 'l' and 'r' and the values are format strings e.g. "%.3f" for 240 | a float with 3 decimal places or "%.3e" for scientific format with 241 | 3 decimal places or "%d" for ints. If tick_formats is a string, it 242 | is assumed that this is a format string to be applied to all axes. 243 | kwargs: 244 | Any kwargs to pass through to matplotlib. 245 | 246 | """ 247 | 248 | axis = axis.lower() 249 | valid_axis_chars = set(['l', 'r', 'b']) 250 | axis_chars = set(axis) 251 | if not axis_chars.issubset(valid_axis_chars): 252 | raise ValueError("axis must be some combination of 'l', 'r', and 'b'") 253 | 254 | if ticks and not locations: 255 | num_ticks = len(ticks) 256 | if num_ticks != 0: 257 | multiple = scale / (num_ticks - 1) 258 | locations = arange(0, scale + multiple, multiple) 259 | 260 | if not ticks: 261 | locations = arange(0, scale + multiple, multiple) 262 | ticks = locations 263 | 264 | tick_formats = normalize_tick_formats(tick_formats) 265 | 266 | # Default color: black 267 | if axes_colors is None: 268 | axes_colors = dict() 269 | for _axis in valid_axis_chars: 270 | if _axis not in axes_colors: 271 | axes_colors[_axis] = 'black' 272 | 273 | offset *= scale 274 | 275 | if 'r' in axis: 276 | for index, i in enumerate(locations): 277 | loc1 = (scale - i, i, 0) 278 | if clockwise: 279 | # Right parallel 280 | loc2 = (scale - i, i + offset, 0) 281 | text_location = (scale - i, i + 2 * offset, 0) 282 | tick = ticks[-(index+1)] 283 | else: 284 | # Horizontal 285 | loc2 = (scale - i + offset, i, 0) 286 | text_location = (scale - i + 3.1 * offset, i - 0.5 * offset, 0) 287 | tick = ticks[index] 288 | line(ax, loc1, loc2, color=axes_colors['r'], **kwargs) 289 | x, y = project_point(text_location) 290 | if isinstance(tick, str): 291 | s = tick 292 | else: 293 | s = tick_formats['r'] % tick 294 | ax.text(x, y, s, horizontalalignment="center", 295 | color=axes_colors['r'], fontsize=fontsize) 296 | 297 | if 'l' in axis: 298 | for index, i in enumerate(locations): 299 | loc1 = (0, i, 0) 300 | if clockwise: 301 | # Horizontal 302 | loc2 = (-offset, i, 0) 303 | text_location = (-2 * offset, i - 0.5 * offset, 0) 304 | tick = ticks[index] 305 | else: 306 | # Right parallel 307 | loc2 = (-offset, i + offset, 0) 308 | text_location = (-2 * offset, i + 1.5 * offset, 0) 309 | tick = ticks[-(index+1)] 310 | line(ax, loc1, loc2, color=axes_colors['l'], **kwargs) 311 | x, y = project_point(text_location) 312 | if isinstance(tick, str): 313 | s = tick 314 | else: 315 | s = tick_formats['l'] % tick 316 | ax.text(x, y, s, horizontalalignment="center", 317 | color=axes_colors['l'], fontsize=fontsize) 318 | 319 | if 'b' in axis: 320 | for index, i in enumerate(locations): 321 | loc1 = (i, 0, 0) 322 | if clockwise: 323 | # Right parallel 324 | loc2 = (i + offset, -offset, 0) 325 | text_location = (i + 3 * offset, -3.5 * offset, 0) 326 | tick = ticks[-(index+1)] 327 | else: 328 | # Left parallel 329 | loc2 = (i, -offset, 0) 330 | text_location = (i + 0.5 * offset, -3.5 * offset, 0) 331 | tick = ticks[index] 332 | line(ax, loc1, loc2, color=axes_colors['b'], **kwargs) 333 | x, y = project_point(text_location) 334 | if isinstance(tick, str): 335 | s = tick 336 | else: 337 | s = tick_formats['b'] % tick 338 | ax.text(x, y, s, horizontalalignment="center", 339 | color=axes_colors['b'], fontsize=fontsize) 340 | -------------------------------------------------------------------------------- /ternary/plotting.py: -------------------------------------------------------------------------------- 1 | """ 2 | Plotting functions: scatter, plot (curves), axis labelling. 3 | """ 4 | 5 | import matplotlib 6 | from matplotlib import pyplot as plt 7 | import numpy as np 8 | 9 | from .helpers import project_sequence 10 | from .colormapping import get_cmap, colorbar_hack 11 | 12 | 13 | ### Drawing Helpers ### 14 | 15 | def resize_drawing_canvas(ax, scale=1.): 16 | """ 17 | Makes sure the drawing surface is large enough to display projected 18 | content. 19 | 20 | Parameters 21 | ---------- 22 | ax: Matplotlib AxesSubplot, None 23 | The subplot to draw on. 24 | scale: float, 1.0 25 | Simplex scale size. 26 | """ 27 | ax.set_ylim((-0.10 * scale, .90 * scale)) 28 | ax.set_xlim((-0.05 * scale, 1.05 * scale)) 29 | 30 | 31 | def clear_matplotlib_ticks(ax=None, axis="both"): 32 | """ 33 | Clears the default matplotlib axes, or the one specified by the axis 34 | argument. 35 | 36 | Parameters 37 | ---------- 38 | ax: Matplotlib AxesSubplot, None 39 | The subplot to draw on. 40 | axis: string, "both" 41 | The axis to clear: "x" or "horizontal", "y" or "vertical", or "both" 42 | """ 43 | if not ax: 44 | return 45 | if axis.lower() in ["both", "x", "horizontal"]: 46 | ax.set_xticks([], minor=False) 47 | if axis.lower() in ["both", "y", "vertical"]: 48 | ax.set_yticks([], minor=False) 49 | 50 | 51 | ## Curve Plotting ## 52 | 53 | def plot(points, ax=None, permutation=None, **kwargs): 54 | """ 55 | Analogous to maplotlib.plot. Plots trajectory points where each point is a 56 | tuple (x,y,z) satisfying x + y + z = scale (not checked). The tuples are 57 | projected and plotted as a curve. 58 | 59 | Parameters 60 | ---------- 61 | points: List of 3-tuples 62 | The list of tuples to be plotted as a connected curve. 63 | ax: Matplotlib AxesSubplot, None 64 | The subplot to draw on. 65 | kwargs: 66 | Any kwargs to pass through to matplotlib. 67 | """ 68 | if not ax: 69 | fig, ax = plt.subplots() 70 | xs, ys = project_sequence(points, permutation=permutation) 71 | ax.plot(xs, ys, **kwargs) 72 | return ax 73 | 74 | 75 | def plot_colored_trajectory(points, cmap=None, ax=None, permutation=None, 76 | **kwargs): 77 | """ 78 | Plots trajectories with changing color, simlar to `plot`. Trajectory points 79 | are tuples (x,y,z) satisfying x + y + z = scale (not checked). The tuples are 80 | projected and plotted as a curve. 81 | 82 | Parameters 83 | ---------- 84 | points: List of 3-tuples 85 | The list of tuples to be plotted as a connected curve. 86 | ax: Matplotlib AxesSubplot, None 87 | The subplot to draw on. 88 | cmap: String or matplotlib.colors.Colormap, None 89 | The name of the Matplotlib colormap to use. 90 | kwargs: 91 | Any kwargs to pass through to matplotlib. 92 | """ 93 | if not ax: 94 | fig, ax = plt.subplots() 95 | cmap = get_cmap(cmap) 96 | xs, ys = project_sequence(points, permutation=permutation) 97 | 98 | # We want to color each segment independently...which is annoying. 99 | segments = [] 100 | for i in range(len(xs) - 1): 101 | cur_line = [] 102 | x_before = xs[i] 103 | y_before = ys[i] 104 | x_after = xs[i+1] 105 | y_after = ys[i+1] 106 | 107 | cur_line.append([x_before, y_before]) 108 | cur_line.append([x_after, y_after]) 109 | segments.append(cur_line) 110 | segments = np.array(segments) 111 | 112 | line_segments = matplotlib.collections.LineCollection(segments, cmap=cmap, **kwargs) 113 | line_segments.set_array(np.arange(len(segments))) 114 | ax.add_collection(line_segments) 115 | 116 | return ax 117 | 118 | 119 | def scatter(points, ax=None, permutation=None, colorbar=False, colormap=None, 120 | vmin=0, vmax=1, scientific=False, cbarlabel=None, cb_kwargs=None, 121 | **kwargs): 122 | """ 123 | Plots trajectory points where each point satisfies x + y + z = scale. 124 | First argument is a list or numpy array of tuples of length 3. 125 | 126 | Parameters 127 | ---------- 128 | points: List of 3-tuples 129 | The list of tuples to be scatter-plotted. 130 | ax: Matplotlib AxesSubplot, None 131 | The subplot to draw on. 132 | colorbar: bool, False 133 | Show colorbar. 134 | colormap: String or matplotlib.colors.Colormap, None 135 | The name of the Matplotlib colormap to use. 136 | vmin: int, 0 137 | Minimum value for colorbar. 138 | vmax: int, 1 139 | Maximum value for colorbar. 140 | cb_kwargs: dict 141 | Any additional kwargs to pass to colorbar 142 | kwargs: 143 | Any kwargs to pass through to matplotlib. 144 | """ 145 | if not ax: 146 | fig, ax = plt.subplots() 147 | xs, ys = project_sequence(points, permutation=permutation) 148 | ax.scatter(xs, ys, vmin=vmin, vmax=vmax, cmap=colormap, **kwargs) 149 | 150 | if colorbar and (colormap != None): 151 | if cb_kwargs != None: 152 | colorbar_hack(ax, vmin, vmax, colormap, scientific=scientific, 153 | cbarlabel=cbarlabel, **cb_kwargs) 154 | else: 155 | colorbar_hack(ax, vmin, vmax, colormap, scientific=scientific, 156 | cbarlabel=cbarlabel) 157 | 158 | return ax 159 | -------------------------------------------------------------------------------- /ternary/ternary_axes_subplot.py: -------------------------------------------------------------------------------- 1 | """ 2 | Wrapper class for all ternary plotting functions. 3 | """ 4 | 5 | from collections import namedtuple 6 | from functools import partial 7 | 8 | import numpy as np 9 | from matplotlib import pyplot as plt 10 | 11 | from . import heatmapping 12 | from . import lines 13 | from . import plotting 14 | from .helpers import project_point, convert_coordinates_sequence 15 | 16 | 17 | BackgroundParameters = namedtuple('BackgroundParameters', ['color', 'alpha', 'zorder']) 18 | 19 | 20 | def figure(ax=None, scale=None, permutation=None): 21 | """ 22 | Wraps a Matplotlib AxesSubplot or generates a new one. Emulates matplotlib's 23 | > figure, ax = plt.subplots() 24 | 25 | Parameters 26 | ---------- 27 | ax: AxesSubplot, None 28 | The matplotlib AxesSubplot to wrap 29 | scale: float, None 30 | The scale factor of the ternary plot 31 | """ 32 | 33 | ternary_ax = TernaryAxesSubplot(ax=ax, scale=scale, permutation=permutation) 34 | return ternary_ax.get_figure(), ternary_ax 35 | 36 | 37 | def mpl_redraw_callback(event, tax): 38 | """ 39 | Callback to properly rotate and redraw text labels when the plot is drawn 40 | or resized. 41 | 42 | Parameters 43 | ---------- 44 | event: a matplotlib event 45 | either 'resize_event' or 'draw_event' 46 | tax: TernaryAxesSubplot 47 | the TernaryAxesSubplot 48 | """ 49 | tax._redraw_labels() 50 | 51 | 52 | class TernaryAxesSubplot(object): 53 | """ 54 | Wrapper for python-ternary and matplotlib figure. Parameters for member 55 | functions simply pass through to ternary's functions with the same names. 56 | This class manages the matplotlib axes, the scale, and the boundary scale 57 | to ease the use of ternary plotting functions. 58 | """ 59 | 60 | def __init__(self, ax=None, scale=None, permutation=None): 61 | if not scale: 62 | scale = 1.0 63 | if ax: 64 | self.ax = ax 65 | else: 66 | _, self.ax = plt.subplots() 67 | self.set_scale(scale=scale) 68 | self._permutation = permutation 69 | self._boundary_scale = scale 70 | # Container for the axis labels supplied by the user 71 | self._labels = dict() 72 | self._corner_labels = dict() 73 | self._ticks = dict() 74 | # Container for the redrawing of labels 75 | self._to_remove = [] 76 | self._connect_callbacks() 77 | # Background 78 | self._background_parameters = None 79 | # Cache for the background triangle object, so it can be removed and redrawn as needed. 80 | self._background_triangle = None 81 | self.set_background_color(color="whitesmoke", zorder=-1000, alpha=0.75) 82 | 83 | def _connect_callbacks(self): 84 | """Connect resize matplotlib callbacks.""" 85 | figure = self.get_figure() 86 | callback = partial(mpl_redraw_callback, tax=self) 87 | event_names = ('resize_event', 'draw_event') 88 | for event_name in event_names: 89 | figure.canvas.mpl_connect(event_name, callback) 90 | 91 | def __repr__(self): 92 | return "TernaryAxesSubplot: %s" % self.ax.__hash__() 93 | 94 | def get_axes(self): 95 | """Returns the underlying matplotlib AxesSubplot object.""" 96 | return self.ax 97 | 98 | def get_figure(self): 99 | """Return the underlying matplotlib figure object.""" 100 | ax = self.get_axes() 101 | return ax.get_figure() 102 | 103 | def set_scale(self, scale=None): 104 | self._scale = scale 105 | self.resize_drawing_canvas() 106 | 107 | def get_scale(self): 108 | return self._scale 109 | 110 | def set_axis_limits(self, axis_limits=None): 111 | """ 112 | Set min and max data limits for each of the three axes. 113 | 114 | axis_limits = dict 115 | keys are 'b','l' and 'r' for the three axes 116 | vals are lists of the min and max values for the axis in 117 | data units. 118 | """ 119 | self._axis_limits = axis_limits 120 | 121 | def get_axis_limits(self): 122 | return self._axis_limits 123 | 124 | # Title and Axis Labels 125 | 126 | def set_title(self, title, **kwargs): 127 | """Sets the title on the underlying matplotlib AxesSubplot.""" 128 | ax = self.get_axes() 129 | ax.set_title(title, **kwargs) 130 | 131 | def left_axis_label(self, label, position=None, rotation=60, offset=0.08, 132 | **kwargs): 133 | """ 134 | Sets the label on the left axis. 135 | 136 | Parameters 137 | ---------- 138 | label: String 139 | The axis label 140 | position: 3-Tuple of floats, None 141 | The position of the text label 142 | rotation: float, 60 143 | The angle of rotation of the label 144 | offset: float, 145 | Used to compute the distance of the label from the axis 146 | kwargs: 147 | Any kwargs to pass through to matplotlib. 148 | """ 149 | 150 | if not position: 151 | position = (-offset, 3./5, 2./5) 152 | self._labels["left"] = (label, position, rotation, kwargs) 153 | 154 | def right_axis_label(self, label, position=None, rotation=-60, offset=0.08, 155 | **kwargs): 156 | 157 | """ 158 | Sets the label on the right axis. 159 | 160 | Parameters 161 | ---------- 162 | label: String 163 | The axis label 164 | position: 3-Tuple of floats, None 165 | The position of the text label 166 | rotation: float, -60 167 | The angle of rotation of the label 168 | offset: float, 169 | Used to compute the distance of the label from the axis 170 | kwargs: 171 | Any kwargs to pass through to matplotlib. 172 | """ 173 | 174 | if not position: 175 | position = (2. / 5 + offset, 3. / 5, 0) 176 | self._labels["right"] = (label, position, rotation, kwargs) 177 | 178 | def bottom_axis_label(self, label, position=None, rotation=0, offset=0.02, 179 | **kwargs): 180 | """ 181 | Sets the label on the bottom axis. 182 | 183 | Parameters 184 | ---------- 185 | label: String 186 | The axis label 187 | position: 3-Tuple of floats, None 188 | The position of the text label 189 | rotation: float, 0 190 | The angle of rotation of the label 191 | offset: float, 192 | Used to compute the distance of the label from the axis 193 | kwargs: 194 | Any kwargs to pass through to matplotlib. 195 | """ 196 | 197 | if not position: 198 | position = (0.5, -offset / 2., 0.5) 199 | self._labels["bottom"] = (label, position, rotation, kwargs) 200 | 201 | def right_corner_label(self, label, position=None, rotation=0, offset=0.08, 202 | **kwargs): 203 | """ 204 | Sets the label on the right corner (complements left axis). 205 | 206 | Parameters 207 | ---------- 208 | label: String 209 | The axis label 210 | position: 3-Tuple of floats, None 211 | The position of the text label 212 | rotation: float, 0 213 | The angle of rotation of the label 214 | offset: float, 215 | Used to compute the distance of the label from the axis 216 | kwargs: 217 | Any kwargs to pass through to matplotlib. 218 | """ 219 | 220 | if not position: 221 | position = (1, offset / 2, 0) 222 | self._corner_labels["right"] = (label, position, rotation, kwargs) 223 | 224 | def left_corner_label(self, label, position=None, rotation=0, offset=0.08, 225 | **kwargs): 226 | """ 227 | Sets the label on the left corner (complements right axis.) 228 | 229 | Parameters 230 | ---------- 231 | label: string 232 | The axis label 233 | position: 3-Tuple of floats, None 234 | The position of the text label 235 | rotation: float, 0 236 | The angle of rotation of the label 237 | offset: float, 238 | Used to compute the distance of the label from the axis 239 | kwargs: 240 | Any kwargs to pass through to matplotlib. 241 | """ 242 | 243 | if not position: 244 | position = (-offset / 2, offset / 2, 0) 245 | self._corner_labels["left"] = (label, position, rotation, kwargs) 246 | 247 | def top_corner_label(self, label, position=None, rotation=0, offset=0.2, 248 | **kwargs): 249 | """ 250 | Sets the label on the bottom axis. 251 | 252 | Parameters 253 | ---------- 254 | label: String 255 | The axis label 256 | position: 3-Tuple of floats, None 257 | The position of the text label 258 | rotation: float, 0 259 | The angle of rotation of the label 260 | offset: float, 261 | Used to compute the distance of the label from the axis 262 | kwargs: 263 | Any kwargs to pass through to matplotlib. 264 | """ 265 | 266 | if not position: 267 | position = (-offset / 2, 1 + offset, 0) 268 | self._corner_labels["top"] = (label, position, rotation, kwargs) 269 | 270 | def annotate(self, text, position, **kwargs): 271 | ax = self.get_axes() 272 | p = project_point(position) 273 | ax.annotate(text, (p[0], p[1]), **kwargs) 274 | 275 | # Boundary and Gridlines 276 | 277 | def boundary(self, scale=None, axes_colors=None, **kwargs): 278 | # Sometimes you want to draw a bigger boundary 279 | if not scale: 280 | scale = self._boundary_scale # defaults to self._scale 281 | ax = self.get_axes() 282 | self.resize_drawing_canvas(scale) 283 | lines.boundary(scale=scale, ax=ax, axes_colors=axes_colors, **kwargs) 284 | 285 | def gridlines(self, multiple=None, horizontal_kwargs=None, left_kwargs=None, 286 | right_kwargs=None, **kwargs): 287 | ax = self.get_axes() 288 | scale = self.get_scale() 289 | lines.gridlines(scale=scale, multiple=multiple, 290 | ax=ax, horizontal_kwargs=horizontal_kwargs, 291 | left_kwargs=left_kwargs, right_kwargs=right_kwargs, 292 | **kwargs) 293 | 294 | # Various Lines 295 | 296 | def line(self, p1, p2, **kwargs): 297 | ax = self.get_axes() 298 | lines.line(ax, p1, p2, **kwargs) 299 | 300 | def horizontal_line(self, i, **kwargs): 301 | ax = self.get_axes() 302 | scale = self.get_scale() 303 | lines.horizontal_line(ax, scale, i, **kwargs) 304 | 305 | def left_parallel_line(self, i, **kwargs): 306 | ax = self.get_axes() 307 | scale = self.get_scale() 308 | lines.left_parallel_line(ax, scale, i, **kwargs) 309 | 310 | def right_parallel_line(self, i, **kwargs): 311 | ax = self.get_axes() 312 | scale = self.get_scale() 313 | lines.right_parallel_line(ax, scale, i, **kwargs) 314 | 315 | # Matplotlib passthroughs 316 | 317 | def close(self): 318 | fig = self.get_figure() 319 | plt.close(fig) 320 | 321 | def legend(self, *args, **kwargs): 322 | ax = self.get_axes() 323 | ax.legend(*args, **kwargs) 324 | 325 | def savefig(self, filename, **kwargs): 326 | self._redraw_labels() 327 | fig = self.get_figure() 328 | if 'dpi' not in kwargs: 329 | kwargs['dpi'] = 200 330 | fig.savefig(filename, **kwargs) 331 | 332 | def show(self, *args, **kwargs): 333 | self._redraw_labels() 334 | plt.show(*args, **kwargs) 335 | 336 | # Axis ticks 337 | 338 | def clear_matplotlib_ticks(self, axis="both"): 339 | """Clears the default matplotlib ticks.""" 340 | ax = self.get_axes() 341 | plotting.clear_matplotlib_ticks(ax=ax, axis=axis) 342 | 343 | def get_ticks_from_axis_limits(self, multiple=1): 344 | """ 345 | Taking self._axis_limits and self._boundary_scale get the scaled 346 | ticks for all three axes and store them in self._ticks under the 347 | keys 'b' for bottom, 'l' for left and 'r' for right axes. 348 | """ 349 | for k in ['b', 'l', 'r']: 350 | self._ticks[k] = np.linspace( 351 | self._axis_limits[k][0], 352 | self._axis_limits[k][1], 353 | int(self._boundary_scale / float(multiple) + 1) 354 | ).tolist() 355 | 356 | def set_custom_ticks(self, locations=None, clockwise=False, multiple=1, 357 | axes_colors=None, tick_formats=None, **kwargs): 358 | """ 359 | Having called get_ticks_from_axis_limits, set the custom ticks on the 360 | plot. 361 | """ 362 | for k in ['b', 'l', 'r']: 363 | self.ticks(ticks=self._ticks[k], locations=locations, 364 | axis=k, clockwise=clockwise, multiple=multiple, 365 | axes_colors=axes_colors, tick_formats=tick_formats, 366 | **kwargs) 367 | 368 | def ticks(self, ticks=None, locations=None, multiple=1, axis='blr', 369 | clockwise=False, axes_colors=None, tick_formats=None, **kwargs): 370 | ax = self.get_axes() 371 | scale = self.get_scale() 372 | lines.ticks(ax, scale, ticks=ticks, locations=locations, 373 | multiple=multiple, clockwise=clockwise, axis=axis, 374 | axes_colors=axes_colors, tick_formats=tick_formats, 375 | **kwargs) 376 | 377 | # Redrawing and resizing 378 | 379 | def resize_drawing_canvas(self, scale=None): 380 | ax = self.get_axes() 381 | if not scale: 382 | scale = self.get_scale() 383 | plotting.resize_drawing_canvas(ax, scale=scale) 384 | 385 | def _redraw_labels(self): 386 | """Redraw axis labels, typically after draw or resize events.""" 387 | ax = self.get_axes() 388 | # Remove any previous labels 389 | for mpl_object in self._to_remove: 390 | mpl_object.remove() 391 | self._to_remove = [] 392 | # Redraw the labels with the appropriate angles 393 | label_data = list(self._labels.values()) 394 | label_data.extend(self._corner_labels.values()) 395 | for (label, position, rotation, kwargs) in label_data: 396 | transform = ax.transAxes 397 | x, y = project_point(position) 398 | # Calculate the new angle. 399 | position = np.array([x, y]) 400 | new_rotation = ax.transData.transform_angles( 401 | np.array((rotation,)), position.reshape((1, 2)))[0] 402 | text = ax.text(x, y, label, rotation=new_rotation, 403 | transform=transform, horizontalalignment="center", 404 | **kwargs) 405 | text.set_rotation_mode("anchor") 406 | self._to_remove.append(text) 407 | 408 | def convert_coordinates(self, points, axisorder='blr'): 409 | """ 410 | Convert data coordinates to simplex coordinates for plotting 411 | in the case that axis limits have been applied. 412 | """ 413 | return convert_coordinates_sequence(points,self._boundary_scale, 414 | self._axis_limits, axisorder) 415 | 416 | # Various Plots 417 | 418 | def scatter(self, points, **kwargs): 419 | ax = self.get_axes() 420 | permutation = self._permutation 421 | plot_ = plotting.scatter(points, ax=ax, permutation=permutation, 422 | **kwargs) 423 | return plot_ 424 | 425 | def plot(self, points, **kwargs): 426 | ax = self.get_axes() 427 | permutation = self._permutation 428 | plotting.plot(points, ax=ax, permutation=permutation, 429 | **kwargs) 430 | 431 | def plot_colored_trajectory(self, points, cmap=None, **kwargs): 432 | ax = self.get_axes() 433 | permutation = self._permutation 434 | plotting.plot_colored_trajectory(points, cmap=cmap, ax=ax, 435 | permutation=permutation, **kwargs) 436 | 437 | def heatmap(self, data, scale=None, cmap=None, scientific=False, 438 | style='triangular', colorbar=True, use_rgba=False, 439 | vmin=None, vmax=None, cbarlabel=None, cb_kwargs=None): 440 | permutation = self._permutation 441 | if not scale: 442 | scale = self.get_scale() 443 | if style.lower()[0] == 'd': 444 | self._boundary_scale = scale + 1 445 | ax = self.get_axes() 446 | heatmapping.heatmap(data, scale, cmap=cmap, style=style, ax=ax, 447 | scientific=scientific, colorbar=colorbar, 448 | permutation=permutation, use_rgba=use_rgba, 449 | vmin=vmin, vmax=vmax, cbarlabel=cbarlabel, 450 | cb_kwargs=cb_kwargs) 451 | 452 | def heatmapf(self, func, scale=None, cmap=None, boundary=True, 453 | style='triangular', colorbar=True, scientific=False, 454 | vmin=None, vmax=None, cbarlabel=None, cb_kwargs=None): 455 | if not scale: 456 | scale = self.get_scale() 457 | if style.lower()[0] == 'd': 458 | self._boundary_scale = scale + 1 459 | permutation = self._permutation 460 | ax = self.get_axes() 461 | heatmapping.heatmapf(func, scale, cmap=cmap, style=style, 462 | boundary=boundary, ax=ax, scientific=scientific, 463 | colorbar=colorbar, permutation=permutation, 464 | vmin=vmin, vmax=vmax, cbarlabel=cbarlabel, 465 | cb_kwargs=cb_kwargs) 466 | 467 | def set_background_color(self, color="whitesmoke", zorder=-1000, alpha=0.75): 468 | self._background_parameters = BackgroundParameters(color=color, alpha=alpha, zorder=zorder) 469 | self._draw_background() 470 | 471 | def _draw_background(self): 472 | color, alpha, zorder = self._background_parameters 473 | scale = self.get_scale() 474 | ax = self.get_axes() 475 | 476 | # Remove any existing background 477 | if self._background_triangle: 478 | self._background_triangle.remove() 479 | 480 | # Draw the background 481 | self._background_triangle = heatmapping.background_color(ax, color, scale, alpha=alpha, zorder=zorder)[0] 482 | -------------------------------------------------------------------------------- /tests/test_heatmapping.py: -------------------------------------------------------------------------------- 1 | 2 | import unittest 3 | 4 | from numpy.testing import assert_array_almost_equal 5 | 6 | from ternary.heatmapping import triangle_coordinates, alt_triangle_coordinates, hexagon_coordinates 7 | from ternary.helpers import SQRT3OVER2 8 | 9 | class FunctionCases(unittest.TestCase): 10 | 11 | def test_coordinates(self): 12 | # Should be an equalilateral triangle 13 | coords = triangle_coordinates(1, 1, 1) 14 | expected = [(1, 1, 1), (2, 1, 0), (1, 2, 0)] 15 | self.assertEqual(coords, expected) 16 | 17 | coords = alt_triangle_coordinates(2, 2, 2) 18 | expected = [(2, 3, 1), (3, 2, 1), (3, 3, 0)] 19 | self.assertEqual(coords, expected) 20 | 21 | coords = hexagon_coordinates(1, 1, 1) 22 | expected = [(2./3, 5./3, 1.0), (4./3, 4./3, 1.0), (5./3, 2./3, 1.0), 23 | (4./3, 1./3, 1.0), (2./3, 2./3, 1.), (1./3, 4./3, 1.0)] 24 | assert_array_almost_equal(coords, expected) 25 | 26 | 27 | if __name__ == "__main__": 28 | unittest.main() 29 | -------------------------------------------------------------------------------- /tests/test_helpers.py: -------------------------------------------------------------------------------- 1 | import random 2 | import unittest 3 | 4 | from numpy.testing import assert_array_equal, assert_array_almost_equal 5 | 6 | from ternary.helpers import normalize, project_point, planar_to_coordinates, simplex_iterator, SQRT3OVER2 7 | 8 | 9 | class FunctionCases(unittest.TestCase): 10 | 11 | def test_normalize(self): 12 | l = [1, 2, 3] 13 | normalized = normalize(l) 14 | expected = [1./6, 2./6, 3./6] 15 | self.assertEqual(normalized, expected) 16 | # Test Exception 17 | self.assertRaises(ValueError, normalize, [0, 0, 0]) 18 | 19 | def test_simplex_iterator(self): 20 | scale = 0 21 | expected = [(0, 0, 0)] 22 | points = list(simplex_iterator(scale=scale)) 23 | self.assertEqual(points, expected) 24 | 25 | scale = 1 26 | expected = [(0, 0, 1), (0, 1, 0), (1, 0, 0)] 27 | points = list(simplex_iterator(scale=scale)) 28 | self.assertEqual(points, expected) 29 | 30 | scale = 2 31 | expected = [(0, 0, 2), (0, 1, 1), (0, 2, 0), (1, 0, 1), (1, 1, 0), (2, 0, 0)] 32 | points = list(simplex_iterator(scale=scale)) 33 | self.assertEqual(points, expected) 34 | 35 | scale = 3 36 | expected = [(0, 0, 3), (0, 1, 2), (0, 2, 1), (0, 3, 0), (1, 0, 2), (1, 1, 1), (1, 2, 0), (2, 0, 1), (2, 1, 0), 37 | (3, 0, 0)] 38 | points = list(simplex_iterator(scale=scale)) 39 | self.assertEqual(points, expected) 40 | 41 | def test_simplex_iterator_without_boundary(self): 42 | scale = 0 43 | expected = [] 44 | points = list(simplex_iterator(scale=scale, boundary=False)) 45 | self.assertEqual(points, expected) 46 | 47 | scale = 1 48 | expected = [] 49 | points = list(simplex_iterator(scale=scale, boundary=False)) 50 | self.assertEqual(points, expected) 51 | 52 | scale = 2 53 | expected = [] 54 | points = list(simplex_iterator(scale=scale, boundary=False)) 55 | self.assertEqual(points, expected) 56 | 57 | scale = 3 58 | expected = [(1, 1, 1)] 59 | points = list(simplex_iterator(scale=scale, boundary=False)) 60 | self.assertEqual(points, expected) 61 | 62 | @staticmethod 63 | def test_project_point(): 64 | point = (0, 0, 0) 65 | projected = project_point(point) 66 | expected = (0.0, 0.0) 67 | assert_array_equal(projected, expected) 68 | 69 | point = (1, 0, 0) 70 | projected = project_point(point) 71 | expected = (1.0, 0.0) 72 | assert_array_equal(projected, expected) 73 | 74 | point = (0, 1, 0) 75 | projected = project_point(point) 76 | expected = (0.5, SQRT3OVER2) 77 | assert_array_equal(projected, expected) 78 | 79 | point = (0, 0, 1) 80 | projected = project_point(point) 81 | expected = (0, 0) 82 | assert_array_equal(projected, expected) 83 | 84 | point = (1, 1, 1) 85 | projected = project_point(point) 86 | expected = (1.5, SQRT3OVER2) 87 | assert_array_equal(projected, expected) 88 | 89 | @staticmethod 90 | def test_planar_to_coordinates(): 91 | projected = (0.0, 0.0) 92 | point = planar_to_coordinates(projected, scale=100) 93 | expected = (0.0, 0.0, 100.0) 94 | assert_array_equal(point, expected) 95 | 96 | projected = (100.0, 0.0) 97 | point = planar_to_coordinates(projected, scale=100) 98 | expected = (100.0, 0.0, 0.0) 99 | assert_array_equal(point, expected) 100 | 101 | projected = (40.0, 0) 102 | point = planar_to_coordinates(projected, scale=100) 103 | expected = (40.0, 0.0, 60.0) 104 | assert_array_equal(point, expected) 105 | 106 | projected = (10.0, SQRT3OVER2) 107 | point = planar_to_coordinates(projected, scale=100) 108 | expected = (9.5, 1.0, 89.5) 109 | assert_array_equal(point, expected) 110 | 111 | projected = (10.0, SQRT3OVER2) 112 | point = planar_to_coordinates(projected, scale=100) 113 | expected = (9.5, 1.0, 89.5) 114 | assert_array_equal(point, expected) 115 | 116 | @staticmethod 117 | def test_coordinate_maps(): 118 | """Test that the coordinate projection functions are in fact inverses.""" 119 | def random_point(scale=1): 120 | x = random.random() * scale 121 | y = random.random() * (scale - x) 122 | z = scale - x - y 123 | return x, y, z 124 | 125 | for _ in range(20): 126 | scale = random.randint(1, 100) 127 | p = random_point(scale=scale) 128 | projected = project_point(p) 129 | p2 = planar_to_coordinates(projected, scale=scale) 130 | assert_array_almost_equal(p, p2) 131 | 132 | 133 | if __name__ == "__main__": 134 | unittest.main() 135 | --------------------------------------------------------------------------------