-
-
Notifications
You must be signed in to change notification settings - Fork 96
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
Experimental: new position nudge functions #183
Conversation
A new nudge function that nudges to left or right, and up or down, based of an x and/or y "divide". Is this useful?
Thank you so much! This sounds great, and I will try to have a closer look soon. I like that you are thinking about nudging in a different way than I do. It's interesting that the R CMD check failed on Windows for your pull request. I don't see any error messages that can help to figure out what happened. By contrast, the current ggrepel code does not fail on Windows ... do you have any ideas why this might be happening? I'm puzzled. |
These are really cool functions! Thanks again, I'm impressed. I think this would be a really nice addition. I just want to discuss a bit of design first, thinking out loud about the user experience. I looked over all the examples, and I don't think they're excessive at all. Actually, even more examples might be nice to get new users up to speed. I think the arguments to your functions are intuitive once you see enough examples. Comment 1: Function namesAs users type the names of these functions, would it be more natural to name them like this? position_nudge_repel_r
position_nudge_repel_s
position_nudge_repel_t
position_nudge_repel That way, if we want one of the (r, s, t) variants, it is the same keystrokes as the "boring" nudge (i.e. Comment 2: Merge 4 functions into 1?I'm also wondering what you think about merging all 4 functions into one function. Do you think it might be more or less comfortable for users to have 1 instead of 4 functions? Maybe something like: PositionNudgeRepel <- ggproto("PositionNudgeRepel", Position,
x = 0,
y = 0,
x_center = NA,
y_center = NA,
x_split = NA,
y_split = NA,
spline = NA,
setup_params = ...,
compute_layer = ...
) Then, a user might write this to use two of your specialty nudges (radially away from center, horizontally away from x_split): position_nudge_repel(
x = 1,
y = 1,
x_center = mean,
y_center = mean,
x_split = mean,
y_split = NA
) Some if-statements would check which of the new parameters are not On the other hand, there is something nice about having separate functions. For the radial nudge, a user doesn't necessarily need to remember to pass position_r_nudge_repel(x = 1, y = 1) I think I'm leaning toward the one-function instead of the four-function design. What are your thoughts about this? Do you prefer the separate functions? Thanks so much for contributing! :) |
Thank, you! Somehow, our discussion last week and looking at your nudge function got me thinking, and as I implemented one of the functions I got the idea for the next one... |
I run the tests after installing either of the branches: in both cases they pass the tests. |
Merge position_nudge_repel_r() and position_nudge_repel_s() into position_nudge_repel().
I have now done the merging of the three functions. I think that I managed to get the default behavior set so that users will almost never have to pass an argument to parameter |
Great! I will have a look soon. I'm excited about this pull request — I think users are going to love it. |
Fix defaults and edits examples.
Add method = "linear" and direction = "split"
|
Add formal parameter abline so that arbitrary straight lines can be entered. Add a couple of examples of its use.
Ensures a minimum distance from line/curve to nudged label by overriding "normal" nudging for points very near or on the line or curve. Testing still needed.
One important thing to consider is the names of the new formal parameters. Once they are in use in a released version it will be very difficult to change them, so your feedback on this would be useful. I will now do some testing of these position functions with tests in 'ggspectra' based on real use cases like light spectra from the sun and various lamp types. I think it is better not to merge the two functions. I am afraid a single merged function would become too complex to use and document. I think that now the interfaces a rather intuitive (at least for me) and easy to grasp. |
I do not see commits from you in the last few hours, but now I see "All checks have passed" for my most recent commit! Surprising! |
This is the goal! If users will find it comfortable and intuitive, then I think we've succeeded. Based on the few examples I have seen so far, I think many people will be using your nudge functions. It sounds like you are ready for me to review once again, so I will go ahead and check to see if everything is looking OK to me. I'll write another comment once I do that.
This is the second time I have seen code simultaneously showing two behaviors:
I've come to expect that the CI services (e.g. Travis, Github Actions) are not necessarily representative of the actual computers that people use at home or work. I don't know why. I was crossing my fingers and hoping that the test failure (on Github Actions Windows) was reproducing your experience, but it seems that is not reliable. |
The failure under macOS is unrelated to 'ggrepel'. |
@slowkow I have added the complementary "normal" version (for Please, let me know what you think... whether you would like me to add this feature or not. I am not in a hurry to release 'ggpmisc' so I will wait until you have reviewed my pull request and we have reached a consensus about names to use for the parameters and functions in the two packages, as it would be best to be consistent. |
@slowkow Please, do not yet review this pull request. I have found some bugs that are easier to see in the absence of repulsion. I will let you know, hopefully next week, when these are all fixed. |
In some cases center_x or center_y could remain as NULL instead of being set to mean.
As position_nudge_repel() and position_nudge_repel_t() compute summaries from data or fit splines or polynomials, there is a need to obey grouping and facets. As the grouping may be best ignored in some cases, I replaced the compute_layer function by a compute_panel function and included in this function the code to support grouping. There was also a problem in position_nudge_repel_t() with the computation of the angles, which required taking into consideration the range of the data, for this to work consistently across groups, we get the information from scales, which is a parameter of the panel function, but not of the layer function. For what I have tested, things seem to work reasonably well. I haven't thoroughly tested if the use of the compute_panel function changes the behaviour compared to position_nudge(). It also remains a test of facets with free scales. I would expect this not to work well, but I do not know if it works with position_nudge().
@slowkow There are just a couple of large commits as I refined the code mostly within 'ggpmisc' as without repulsion the nudging was easier to debug and test. Then I copied back my work into 'ggrepel'. I haven't yet added examples with grouping and facets, but I tested them in 'ggpmisc'. I've done quite a lot of progress, but I think more thorough testing of grouping and specially facets is still needed. Anyway, it would be good to get some feedback, when you have some time to play with the code. Merging the two functions, I think would make the function too complex, both in their interfaces and the logic of the code. |
@slowkow An idea occurred to me overnight: if the 'ggprepel' position functions would name the returned variables differently, i.e., keep using |
@slowkow I implemented the change in naming as it was just a 5 min task. It does work. This implementation is "quick and dirty" in that I left the existing code in the geoms unchanged, but added code to rename the columns to the previously used names. This could be improved if you agree to this renaming. |
I like the new naming! Thanks for the great idea. I'll have to spend some time looking through the new examples to build confidence, but it sounds like a very good change. |
I submitted to CRAN 'ggpmisc' 0.3.8 without the position_nudge...() functions, I attach here some examples that are different to those in my pull request, all of them using |
@slowkow We both seem to be quite busy. The new nudge functions in my pull request will require to be supported by a maintainer. If you think they would be a burden in 'ggrepel', they could be in their own package, with myself as maintainer, or I could add them to 'ggpmisc'. At this point described only for use with non-repulsive geoms. When and if you decide to accept the suggested name change for the columns in data 'ggrepel', they would be also usable with the repulsive geoms. However, in the meantime they would be available for use with non-repulsive geoms. |
Add missing ggplot2:: and change direction -> params$direction in one warning message.
@aphalo Thanks for bringing my attention back to this! I hope you're doing well. I apologize for the long delays, but it is difficult to prioritize open-source work over other things that need my attention more urgently. Maybe the best forward is for you to maintain the new code for nudges in ggpmisc (or elsewhere)? Here's what I'm thinking:
By the way, I was hoping to get a closer look at your examples to see how they work without ggrepel, but they don't seem to be working for me. Here's what I get when I knit your Rmd. Sorry if I'm missing something obvious... (before knitting, I git cloned your ggpmisc repo and installed with Thank you again for developing this code and sharing it! 🙏 |
@slowkow No problem at all. I am doing well, thanks, and you? I understand your situation well. I had not touched this code in all this time myself. I have also been busy. I think, based on what Hadley has written, he does not want to add new functionality to ggplot2. So, I guess what he could potentially accept is adding an additional parameter to the existing position functions in ggplot2 to enable the saving of the original x and y coordinates when they are modified by the position functions. It could be good to rise an issue for ggplot2 early enough to agree with Hadley et al. at least on the name to use. I am using x_orig and y_orig but this can be changed. On the other hand, maybe better wait until you have tested that the change in naming I have suggested does not break things. There could be some use for a geom_text() and geom_label() with no repulsion but with the ability to add segments or arrows for nudged text. I started writing these but rapidly stopped as they would have lots of very similar code to that in geom_text_repel() and gem_label_repel() so maybe adding to your geoms a simple switch to disable repulsion, or writing a wrapper with suitable defaults to disable repulsion could be a better solution. I need to think what to do with 'ggpmisc', but probably it would be best to split it into a package with the general purpose functions (including nudging) and another with the statistics-related plot decorations. I will have a look at the examples, I probably have broken something when moving around the code. I'll let you know what I find. Thank you for being open to suggestions and dialogue! And for the wonderful tool that ggrepel is! |
@slowkow About the examples based on 'ggpmisc': I had removed the nudge functions before submitting 'ggpmisc' 0.3.8 to CRAN. I put them back into the main branch of 'ggpmisc' rather recently. The examples using |
@slowkow If you agree I can close this pull request and make a new one with only the name change code. The new position_nudge functions are now in 'ggpmisc' and do not need to be included in 'ggprepel'. There are two possibilities. 1) Change the naming used in 'ggrepel', which would allow position_nudge_repel() to be used with non-repulsive geoms. 2) Not to change the naming used within 'ggrepel' at this point but add code to translate the new naming as used in 'ggpmisc' if found in the data. The second option is the least disruptive, and would need the least testing as the new code, a few lines, would be only be run if the new naming is encountered. Please, let me know what you think. |
@aphalo Thanks for the update! Sorry I haven't been able to find more time for this. I'd appreciate if you could propose these changes:
If you don't think it'll break other packages, then I'm for it. |
@slowkow o.k., I wrote the code some weeks ago but I need to check it carefully before making the new pull request. |
I wrote three new nudge functions. I think they are a step towards "less-dumb" nudging.
position_s_nudge_repel()
does symmetric nudging right/left and up/down on either side of a "divide", by default the divide is the centroid of the data computed withmean()
, but this can be set to another function or a numeric constant.position_r_nudge_repel()
is similar but does the nudging radially away or towards a point, again by default the centroid. The third one isposition_t_nudge_repel()
. It is a bit more sophisticated and the code still needs some polishing. It fits a smoothing spline to the data, computes the first derivative, and uses this to apply the nudging away from the line. I have added examples, in some cases too many examples, to illustrate how they work.Please, let me know what you think.