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

added fig and sub as parameters for plot creation #500

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

ahoc
Copy link

@ahoc ahoc commented Nov 7, 2018

Use like:
cp = nv.CircosPlot(G, fig=30, sub=(1,2,1))
to avoid automatic creation of new figure.

Closes #499.

Use like:
cp = nv.CircosPlot(G, fig=30, sub=(1,2,1))
to avoid automatic creation of new figure.
Use like:
import nxviz as nv
import palettable.colorbrewer as pc
cp = nv.CircosPlot(G, cmap=pc.sequential.Reds_9)
cp.draw()
Copy link
Owner

@ericmjl ericmjl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ahoc thanks for the PR! Your proposed code introduces some bigger changes than I had originally thought about. Before I'm ready to accept this PR, I'd like to ensure that those changes ensure that the nxviz declarative API is preserved, that conditional logic is minimized, or if necessary, then tested against the appropriate combinations of conditionals.

Could you, then, do the following?

  1. Add some tests into the test suite for catching cmap conditions for node colors?
  2. Provide your thoughts on the three API options I have outlined?
  3. Add yourself to the list of contributors in AUTHORS.rst?

In case you're thinking of implementing more features, I'd ask you to hold off, or at least do it in a separate separate branch (i.e. ahoc:feature-branch) and put in a separate PR. Doing so makes it much easier to review this PR.

@@ -153,7 +153,12 @@ def __init__(
figsize = (6, 6)
if "figsize" in kwargs.keys():
figsize = kwargs["figsize"]
self.figure = plt.figure(figsize=figsize)
if "fig" in kwargs.keys():
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The API you've proposed here uses **kwargs. I'm cool with the idea, but I also want to be thoughtful about the API. nxviz's API is its biggest 'selling point', so I put more oversight on the API than I do on features. Code maintainability is important too, though it only takes a slight backseat to the API.

I was wondering if we could do an API design thought experiment here.

For the use case where we are plotting a CircosPlot to an existing figure, which one looks better to you?

Option 1, which uses idioms from the PyData ecosystem

# Pre-instantiate a figure and axes object.
fig = plt.figure()
ax = fig.add_subplot(111)
c = CircosPlot(G, ..., ax=ax)

Option 2, your current proposed API

c = CircosPlot(G, ..., fig=30, sub=(1,1,1)

Option 3, a "fig_kwargs" API

fig_kwargs = dict(
    figsize=(10, 10),
    subplots=(1,1,1)
)
c = CircosPlot(G, ..., fig_kwargs=fig_kwargs)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of the three, I personally lean most towards option 1, which plays nicely with how people use other Python packages. Of the remaining two, I have no strong opinions. If I were to weight the three options, out of 100 points, I would assign 70-15-15.

What are your thoughts?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, 1 is certainly better than 2 (but requires more changes?). 3 could be an option if one would plan to later implement more options and have them nicely bundled. So: 70-10-20.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ahoc I think option 1 only requires an isolated conditional: if ax is not specified, then make a figure and an axes object. In this case, ax should be None by default.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right

self.figure.colorbar(self.sm, cax=cax)
#self.figure.subplots_adjust(right=0.8)
#cax = self.figure.add_axes([0.85, 0.2, 0.05, 0.6])
#self.figure.colorbar(self.sm, cax=cax)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After uncommenting the above three lines, have you run tests locally on your computer to check that the tests pass?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The three commented lines are the original ones (Sorry, I commented instead of deleting them). If, as I suggest they are replaced by:

Suggested change
#self.figure.colorbar(self.sm, cax=cax)
self.figure.colorbar(self.sm, ax=self.ax)

thus plotting to ax and not to a newly created cax it works also for several subplots in one figure. But the colorbars look a bit narrower. I will add some examples to the test suite.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it! Yes, please add a few tests.

Btw, I would avoid putting actual plotting code in the test suite. I have not yet set up plotting tests properly. You can test for logical properties of the objects instead.

self.ax.relim()
self.ax.autoscale_view()
self.ax.set_aspect("equal")

def compute_node_colors(self):
def compute_node_colors(self, **kwargs):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The **kwargs pattern I have seen thus far leaves options tough to document.

What are your thoughts on the following suggested change?

Suggested change
def compute_node_colors(self, **kwargs):
def compute_node_colors(self, cmap: str=None):
"""
:param cmap: The matplotlib colormap to use.
"""

It would then precipitate the following change in line 259 below.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much better, that was just a hack.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, please accept the changes; you can do so here, or just copy/paste it into your own branch.

@@ -250,6 +256,9 @@ def compute_node_colors(self):
elif dtype == "continuous" and is_data_diverging(data):
cmap = get_cmap(cmaps["diverging"].mpl_colormap)

if "cmap" in kwargs.keys():
cmap = get_cmap(kwargs["cmap"].mpl_colormap)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lines 259 and 260 optionally change cmap if it is set. This may override the above conditions. What are your thoughts here?

Will manually selecting a cmap result in unintended behavior that are not currently caught by the tests?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so, but can do some more tests. It would be better though to put it before the above conditons.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ahoc thanks for getting back.

Yes, please add a few tests for the different options that might show up. Rather than think of the generalized cases, having a few specific cases first is sufficient. (I don't want to over-complicate the testing up-front, we can discover the general cases later.)

This was referenced Nov 7, 2018
@ericmjl
Copy link
Owner

ericmjl commented Dec 5, 2018

@ahoc, please let me know if you're still interested in completing this PR.

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

Successfully merging this pull request may close these issues.

None yet

2 participants