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

Counterclockwise coordinates and coordinate API, improve relevant documentation #18

Closed
marcharper opened this issue Jun 24, 2015 · 12 comments

Comments

@marcharper
Copy link
Owner

Re: issue #13 , determine how to allow users to specify the plotting coordinates and direction. Currently we have "012" for counterclockwise plotting of the three coordinates (as described in #13).

@marcharper
Copy link
Owner Author

@chebee7i So it turns out the reversing the orientation from counterclockwise to clockwise is equivalent to applying the permutation "210". I actually did the vector arithmetic to make sure. So I simply updated the TernaryAxesPlot class to take an orientation kwarg with value either '+' or '-', and if it's '-', to compose the permutation with '210' to reverse the orientation.

I went with "brl" as the user-specified permutation format for now; the library will also accept "210" as before.

I pushed the changes to a branch called "orientation". Let me know if you have any comments!

@chebee7i
Copy link
Contributor

My understanding of the CW-CCW choice is that it does not affect how you project and had more to do with how you choose to label the axes. So if you had already plotted data and then changed to a CW orientation, the plotted data should not change locations on the simplex.

In the attached figure, the corners are still associated with the same distributions. And in fact, each coordinate is still associated with the same ticklines (e.g. coordinate 2 has diagonal lines pointing "up and to the right" in both labelings). All that has changed is where the ticks are placed.

coordinate-direction

@marcharper
Copy link
Owner Author

It does make a difference -- there are six ways that one can plot the data corresponding to the symmetry group of the equilateral triangle -- "left-to-right" vs "right-to-left" (reflections about the bisecting axes) and the order of the (outer) axes. That reflecting about the center line on the lower axis (changing the orientation) is equivalent to a particular permutation of the indices is just a reflection of the fact that the dihedral group D_{2*3} is isomorphic to the symmetric group S_3 on three elements.

@chebee7i
Copy link
Contributor

chebee7i commented Jul 1, 2015

My claim is that for each of the six permutations, there are two equivalent ways of labeling the edges of the simplex. Note that in the above image, the bottom axis is slightly different. For the CCW image, the tick marks are forward slashes / corresponding to diagonal lines for the 0th coordinate. For the CW image, the tick marks are back slashes \ corresponding to diagonal lines for the 2nd coordinate. Since the distributions at each of the corners are the same in both pictures, they correspond to the same permutation.

Below is another image that tries to demonstrate their equivalence. In each of the images, the corners represent the same distributions. The z-coordinate (coordinate at index 2) is in the lower left corner and it is marked by the red grid lines (backslashes). If we label the top of those slashes, then we get a CCW image with the left axis labeled "Z". If we label the bottom of those slashes, then we get a CW image with the bottom axis labeled "Z".

coordinate-direction2

TLDR: The permutation fixes the corners, but it does not determine how you label the edges (axes). For the latter, there is a degeneracy caused by each of the isolines touching two edges (e.g. there are two ways to get to each corner).

@marcharper
Copy link
Owner Author

Perhaps we're just looking at the permutations/orientations in different ways. "xyz" to me means plot "x-then-y-then-z" counterclockwise, i.e x on the lower axis (left-to-right staring from the lower-left-corner), y on the right, z on the left. If I want "x-then-y-then-z" clockwise, that's xyz (starting right-to-left at the lower right corner), which amounts to applying 210. By keeping the lower axis as the variable x, I'm changing the lines of constancy from \ to / . That's not the same as your center right-hand-plot (I would have to rotate and reflect to get back to the same initial data).

Looking at your two lower plots, switching between the two tick mark labellings (ignoring the axis labels for the moment) is the same as reflecting about the vertical symmetry axis, just applying "210" as in this example:

This doesn't change which variable is plotted on the lower axis (the first / index 0), just the direction, but it does swap the variables on the left and right boundaries. If the tickmarks were present this reflection would correctly reflect those as well. So they aren't completely different.

A third convention would be to start in the lower left corner always and move around clockwise or counterclockwise (which would be reflecting about the left-to-right bisecting symmetry line; but that's still different than your suggestion, since the two plots would not be the same sans labelling. This would be the same as applying the permutation "021" I think.

In your original comment the point that I wasn't getting (still?) is that if the user specifies orientation='-' that ternary shouldn't swap the left and right axes variables, just the way the graph is labelled, and I understand why that is desirable from an API perspective. But isn't that also ambiguous? If the orientation is clockwise, then the variable plotted along the left axis is the "second one" not the third one. So no matter which convention is chosen someone is going to be surprised by the output.

In other words, if the orientation is a thing per se then it would seem to indicate how the data is being plotted. I can see how for a heatmap or a scatter plot one just wants the points to normalize to the same position and not have the plot content change. But if I'm plotting trajectories that have their own intrinsic orientations, I think I prefer the image above, e.g. keeping it as "paper-bests-rock-bests-scissors" in both orientations (my plot) versus "scissors-loses-to-rock-loses-to-paper" (my interpretation of your images).

That leaves at least four possibilities:

  • Preserve the plot, as you suggest, so that the orientation only affects labelling (currently manual)
  • Preserve the lower axis as the first variable left-to-right or right-to-left (the proposed 210 convention)
  • Preserve the lower left corner as the starting point and plot upward or rightward (021)
  • Support multiple conventions somehow

@chebee7i
Copy link
Contributor

chebee7i commented Jul 1, 2015

This is a good discussion.

Perhaps we're just looking at the permutations/orientations in different ways. "xyz" to me means plot "x-then-y-then-z" counterclockwise, i.e x on the lower axis (left-to-right staring from the lower-left-corner), y on the right, z on the left. If I want "x-then-y-then-z" clockwise, that's xyz (starting right-to-left at the lower right corner), which amounts to applying 210. By keeping the lower axis as the variable x, I'm changing the lines of constancy from \ to / . That's not the same as your center right-hand-plot (I would have to rotate and reflect to get back to the same initial data).

Yes, we are definitely approaching this differently.

So I agree that if you want to present the API as permutation='012', clockwise=True, then your convention makes more sense, and that is because you are assigning indexes to axes: {bottom, left, right}. We'd have to store their choice for clockwise, as it will influence how we (eventually) draw ticks.

The other convention, and the one I was pushing for, is that you assign to corners instead: {Left, Top, Right}. That fixes the grid, and after that, the particular labeling of the edges doesn't matter. From this perspective, the user doesn't really care how the data was plotted, all they care about is how to locate particular distributions on the grid. And doing that requires an arbitrary choice between two edge labelings, clockwise or counterclockwise. So independently, you choose whether you want the labels to proceed clockwise or counterclockwise, and the algorithm properly assigns the edge labels so that they stay consistent with the grid and corners.

I like your convention for edges, but I also like the corner convention, and I think the corner convention might be more commonplace. To provide another data point, note that ggtern follows the convention that I was advocating for:

library("ggtern")
data(Feldspar)
ggtern(data = Feldspar, aes(Ab, An, Or)) +   # specifies Ab to left corner, An to top, Or to right corner
  geom_confidence(color="blue",linetype=1) + 
  geom_point() +
  limit_tern(.85,1,.85) +
  theme_showarrows() +
  theme_clockwise() + 
  #theme_counterclockwise() + 
  labs(title = 'Feldspar - Elkins and Grove 1990 + Confidence Levels')

ggtern-ccw
ggtern-cw

I think we can leave out the third option you suggested, and so maybe we should look into how to support the other two conventions. I don't think it would be too hard...perhaps using different keywords or providing different methods. Perhaps something similar to: #13 (comment)

@marcharper
Copy link
Owner Author

Now that it all makes sense to me, I'm fine with supporting both as long as there is a reasonable API and it doesn't require pushing a ton of kwargs around everywhere. ggtern's default is not my most common use case -- I usually want to know what is happening at population state (i, j, k) by looking at the (i, j) polygon; what the distribution normalizes to is not usually something that matters.

I'm open to suggestions -- perhaps there needs to be an auxiliary class that manages this. "Axes" isn't going to work, it's already used more than once in the library (matplotlib's axes objects for example). Plus (as you say) these choices are going to affect how the axes are labelled, how the tickmarks are put in, etc. Finally, I'd like the ternary functions to work independently as far as possible -- so far I've been able to maintain an API like matplotlib's -- but that may not be possible.

@chebee7i
Copy link
Contributor

chebee7i commented Jul 1, 2015

I'll give it some thought and/or simple prototyping. Before I do that, can we verify that we actually agree on what each of these conventions is doing?

For the permutation='012' syntax, permutation[i] specifies an index in the population vector that is mapped to axes[i] where axes = ['bottom', 'right', 'left'] if counterclockwise and axes=['bottom', 'left', 'right'] if clockwise.

For the permutation='LTR' syntax, permutation[i] specifies a corner on the simplex which is to be associated with a population vector peaked at index i. The orientation is chosen independently.

For the permutation='brl'syntax, permutation[i] specifies an axis which plots the value of the population vector at index i. The orientation is chosen independently.

I think the 4 examples below should clarify the text above an help us see the difference. I'm fairly confident that we agree on what the 012 and LTR syntaxes mean, but was uncertain what you wanted the brl syntax to be. [This doesn't include that other potential convention you mentioned.]

coordinate-direction-conventions

@marcharper
Copy link
Owner Author

So far I've just been mapping brl to 012, and 012 just means that the values of the population vector in the 0 and 1 indices are going into the projection function. This matches the heatmap convention of specifying the i-j-th polygon (moving i steps horizontally left-to-right and j steps up). After projecting it's actually "plotting" along the base axis then along the left-parallel lines; for clockwise it's reverse on the base then upward along the right-parallel lines (the z-constant ones above), so blr is more correct descriptively.

ggplot seems to use LTR as its default, so the tickmarks correspond to lines of constancy of the left axis variable rather than vertical ticks corresponding to the variable on the lower axis. I'm thinking now it's better to just support the same convention that ggplot uses even though that's not how I generally think about it. For me it doesn't really matter -- I'm just looking for behavior of dynamics and I don't care if the simplex is rotated or reflected -- but clearly it's important to other people, and they are more likely to be used to ggplot's convention.

@chebee7i
Copy link
Contributor

chebee7i commented Jul 2, 2015

Ok, I'm moving presently, so I will be a bit delayed, but I plan on writing up something pretty simple and completely separate, basically a query object that describes the simplex configuration. I'll include some documentation (is using Sphinx of interest?), and will be pedantic about it. I figure if we can't make it clear between us, we'll have no hope of making it clear for all the other perspectives out there.

@marcharper
Copy link
Owner Author

Sure sphinx is fine. Thanks for working on this, good luck moving!

@marcharper
Copy link
Owner Author

So this is partially solved now, there are tickmarks that respect a clockwise parameter. Users can specify the plotting order with a permutation.

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

No branches or pull requests

2 participants