Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User experience questions about grid placement and shapes #189

Closed
clintval opened this issue Nov 15, 2018 · 4 comments
Closed

User experience questions about grid placement and shapes #189

clintval opened this issue Nov 15, 2018 · 4 comments

Comments

@clintval
Copy link

clintval commented Nov 15, 2018

Excellent library! I'm exploring it's use as the plotting engine for a tool I'm building. I have a few questions and thought experiments to present based on a demo plot I made. Could your or someone else provide guidance or best practice (use of your API) on decorating figures like this?

signature-24

You can reproduce the figure with:

❯ pip install nucleic==0.6.3
from nucleic import fetch_cosmic_signatures
from nucleic.figures import plot_stratton_spectrum

signatures = fetch_cosmic_signatures()
canvas, (ax1, ax2) = plot_stratton_spectrum(signatures['Signature 24'])
  1. I'm having a hard time understanding the behavior of the grid= argument to Canvas.cartesian(). To help me I created a GridSpec class (namedtuple) to identify the correct argument order but I see strange behavior when manipulating the numbers! Would you be interested in a PR to add a helper class to make the arguments members of a class or namedtuple and not positional? If I wanted something to take up 1 row at the bottom of a Canvas of 10 rows what would that look like?
from collections import namedtuple

from toyplot import Canvas

GridSpec = namedtuple('GridSpec', ('rows', 'columns', 'i', 'rowspan', 'j', 'columnspan'))

canvas = Canvas()
grid = GridSpec(rows=1.2, columns=1, i=0, rowspan=1, j=0, columnspan=1)
canvas.cartesian(grid=grid)
  1. How can I draw squares? Shapes? Right now I'm making two axes, one really thin below the major axes and just plotting a 1-high bar plot and providing a series of colors.

For reference, I have my plotting function here:

https://github.com/clintval/nucleic/blob/383a2aafc30e471aacd8e8323eafefed57dcdf29/nucleic/figures.py#L19-L73

@tshead
Copy link
Contributor

tshead commented Nov 16, 2018

Clint: Many thanks, that's a good-looking plot!

I'm hoping you've already seen https://toyplot.readthedocs.io/en/stable/canvas-layout.html ... notwithstanding, making the grid spec more readable has been on my mind for awhile now. My only objection to the namedtuple (which I had never run across before, thanks for pointing that out) is that it doesn't support optional arguments. How would you feel about grid accepting a plain dict? That would allow varying forms of spec, e.g.:

canvas.cartesian(grid=dict(rows=3, columns=4, index=2))
canvas.cartesian(grid=dict(rows=3, columns=4, i=0, j=2))
canvas.cartesian(grid=dict(rows=3, columns=4, i=0, j=2, rowspan=1, colspan=2))

... and whatever other variations come-up.

@tshead
Copy link
Contributor

tshead commented Nov 16, 2018

As far as shapes go, I've had a long-term bias against turning Toyplot into a drawing library ... although yours is the second library I'm aware of that's using Toyplot as a back-end, so maybe I should just relax into it :)

Toyplot supports rectangles as a mark type, where you specify all four boundaries for every rect. That's maybe semantically slightly better for your use-case than bars. Probably the best option is something that's been on my TODO list for awhile, which is to add a "range" mark for use with number lines. I could probably crank that out pretty quickly.

In the meantime, I assume that you'll want to disable pointer coordinates and data export for the lower set of axes (LMB click over your example, and RMB click on the colored bars to see what I mean).

Cheers,
Tim

@clintval
Copy link
Author

Thanks Tim!

Specifying Cartesian Plot Location

I am averse to shoving things into dictionaries as validation and extensibility become harder. Using dictionaries to parameterize also closes you off for binding function with the data at a later date (e.g. by implementing a method on a class).

A data/case class like a namedtuple (defaults are available in Python3 only, sadly) or custom class with validation would be my bid, but up to the designer!

Matplotlib provides this interface which I think is a little clearer but forces the user to split the Canvas into rows and columns when the figure is instantiated:

https://matplotlib.org/users/gridspec.html

I'm strictly a Py3.6 user, so I will continue using namedtuple as an API helper with toyplot.

Pointer Coordinates / Data Export

Thanks! That is a great tip.

Shapes

Regarding your bias against drawing polygons or curved shapes... If the API is elegant, and you've proven to me you can design an elegant API, why not?

I have a few visualization toolkits I would love to create in Python but must have SVG as a first-class display format. If you implemented shapes, I would use them!

@tshead2
Copy link
Member

tshead2 commented Dec 13, 2018

@clintval - I've added support for numberline ranges in master, which are the semantically right way to approach the bottom portion of your sample plot. https://toyplot.readthedocs.io/en/latest/numberline-coordinates.html for details.

Cheers,
Tim

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants