-
Notifications
You must be signed in to change notification settings - Fork 8
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
type_ridge()
#252
base: main
Are you sure you want to change the base?
type_ridge()
#252
Conversation
This is really cool and I'm just starting to work through the examples. Quick first comment: For
|
A similar quick comment. I'd like to be able to do tinyplot(Species ~ Sepal.Length | Species, data = iris, type = "ridge") so that colors vary by the y-axis entries. Accounting for this kind Lines 122 to 132 in c4d4e2c
Lines 209 to 211 in c4d4e2c
(In the specific case of |
Great minds think alike, I was playing with the same thing. 🤓 More generally, faceting does not seem to work, yet. Also, browsing the ggridges vignette, it would be really nice to have color gradients that help to compare the x-axis values across density curves. Are you planning to add this? |
This would be quite a lot of work, no? Off the top of my head, I guess it would require either looping over the sequence of x values and drawing mini polygons (similar to this), or converting the polygon to an appropriate matrix and then rasterising it. Perhaps there's a simpler solution. But I think that gradient fill support is probably out of scope for this PR. We can revisit the idea once we manage to fix #243, since the logic would probably carry over to regular density plots too. Edit: To clarify, I think that this would be very cool. But I worry that supporting x gradient fill will require quite a lot of additional work. |
I added support for facets and fixed the grid problem. I think that any fancier Unfortunately, I don't have the bandwidth for this right now. I can do minor fixes on PR review, but any major change will have to wait. We can merge this close to "as-is" (perhaps with an "experimental" tag), or we can wait a few weeks (months?) until I have more time. library(tinyplot)
dat = transform(airquality, Late = ifelse(Day > 15, "Late", "Early"))
tinyplot(Month ~ Ozone,
facet = ~Late,
data = dat,
type = "ridge",
grid = TRUE,
col = "white",
bg = "light blue") |
Great, thanks @vincentarelbundock. I want to take a stab at tweaking a few things so have cloned your fork locally and will test things. I'll push any changes that look good and then we can merge. Will probably be a few days. |
In the meantime, I'll have a look at how hard it would be to add a |
OK, quick proof of concept: To implement this I used a fixed grid of 1000 rectangles across the full range of the x variable. In the
For the Instead one could also use |
This looks amazing! |
OK, I have now a version which uses
Personally, I would still go for the more general code. What do you think? Should I modify |
Cool. I don't have a view so I'll let Grant trace the path forward. |
Grant, what do you think about this? First complete the PR without color gradients and then make a new separate PR afterwards - or integrate my proposed changes into the existing PR? If the latter, I would also export some of the |
Would the latter be easier? I don't mind and still have to integrate my own changes for this PR. (I also noticed some weird behaviour when P.S. Sorry for being slow on this. I've been solo parenting the last few days and also juggling an important deadline at work.. |
Go for it. For posterity, I also played with some dens = density(Nile)
x = dens$x
y = dens$y
# How many y "bins"?
# (higher numbers mean a smoother looking density function)
nx = 1000L
# create a length(x) * ny matrix along the color gradient
m = matrix(
rep(hcl.colors(length(x), "Viridis"), nx),
ncol = length(x),
byrow = TRUE
)
# Use an internal tinyplot function for rescaling/normalizing
y = tinyplot:::rescale_num(y, to = c(1, ny))
y2 = round(y)
# idea: "blank" out the matrix cells above the top edge of the distribution
# note that raster plots rowwise, so we have to do this a bit back-to-front
for (i in seq_along(y2)) m[1:(nrow(m)-y2[i]+1), i] = NA
plot(y, type = "n")
plot(as.raster(m), add = TRUE)
# lines(y2)
lines(y) Created on 2024-11-17 with reprex v2.1.1 GM: Slight edits to make this example look and read better. |
…nel', add more examples
Grant, I've pushed now my relatively slow version using Meanwhile I'm not convinced anymore that However, for a large number of breaks, your raster-based idea seems to be much faster. By definition this will break things down into a regular raster grid which might be somewhat less precise than the I also adapted your code so that we rescale the raster rather rescaling the density:
|
OK, I couldn't go to sleep before finishing the |
Amazing @zeileis. Get some sleep and I'll dig into this as soon as I can.
…On Sun, Nov 17, 2024, 18:46 Achim Zeileis ***@***.***> wrote:
OK, I couldn't go to sleep before finishing the rasterImage()-based
solution. This is now the new default but you can select via type_ridge(gradient
= TRUE, raster = FALSE) vs. the default raster = TRUE. More later, need
to get some sleep now...
—
Reply to this email directly, view it on GitHub
<#252 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ACOO73IBBDRODDQHHH6VU4L2BFIHZAVCNFSM6AAAAABRVRQT5KVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDIOBRHAZDAMJTGI>
.
You are receiving this because you commented.Message ID:
***@***.***>
|
…ples, better processing of defaults
OK, some more updates. I tweaked the color gradient. By default, it uses Example: On the left via raster, on the right via polygon.
If you want to play around with the two implementations, you can explicitly set Additionally, I have implemented the option to use group-specific quantiles (at
Finally, all I think that this covers all features that I had in mind. Suggestions for improvement are very welcome. Also, let me know if I added something that you don't feel is so useful. |
nothing to add but just wanted to say that these last few plots look insanely cool |
This comment was marked as outdated.
This comment was marked as outdated.
Thanks for the kind words! The examples are essentially stolen from the Re: polygon with NAs inserted. Yes, that's what my code had been doing all along. Separate polygons would have been hopeless. But even the single segmented polygon becomes quite slow - and it can even create awkward artefacts if the segments are too narrow. Try
|
Ah, sorry. I should have read your code to start with. Too many balls in the air at the moment... |
No worries, I know that feeling. And take your time with looking at the code - just do it when you have the capacity for it. Now that I have implemented the things that I wanted to implement, I will sleep well 😴 |
@zeileis I took a stab at improving the polygon logic and now think that it's at point we're we can safely default to it for everything instead of rasters. The new polygon version (which is the now default) is slightly faster than the raster equivalent for gradients and doesn't leave any artifacts either. I can post some examples here, but I think the best thing is for you to clone and test locally. Let me know if you agree. Thanks! |
Thank you so much, most of this looks great. But we need to be more careful about dropping polygon intervals that are empty. In this case we need to make sure that the intervals remain aligned with the color palette (see the left panel below). Another small issue is that in the case without gradient but with breaks, we should keep the default light gray shading. Currently, this is dropped (see right panel below).
|
Thanks @zeileis. I believe that I've managed to plug those two cases now: pkgload::load_all("~/Documents/Projects/tinyplot_vincent/")
#> ℹ Loading tinyplot
set.seed(0)
d <- data.frame(y = rep(1:4, each = 100), x = c(
rnorm(100, mean = 5, sd = 2),
rnorm(100, mean = 2, sd = 1),
rnorm(100, mean = 8, sd = 1),
rnorm(50, mean = 1, sd = 0.5), rnorm(50, mean = 9, sd = 0.5)
))
tinyplot(y ~ x, data = d, type = type_ridge(bw = 0.5, gradient = TRUE, breaks = -1:6 * 2)) tinyplot(y ~ x, data = d, type = type_ridge(bw = 0.5, breaks = -1:6 * 2)) Bonus: Replicating a fun example from the ggridges package/vignette. Note that this is a case where data(lincoln_weather, package = "ggridges")
op = tpar(las = 1, mgp = c(3, 0, 0))
tinyplot(
Month ~ `Max Temperature [F]`, data = lincoln_weather,
type = type_ridge(gradient = "plasma", scale = 3),
# grid = grid(nx = NA, ny = 12),
draw = abline(h = 0:11, col = "lightgray"),
axes = "l",
main = "Temperatures in Lincoln NE",
ylab = NA
) tpar(op) Created on 2024-11-22 with reprex v2.1.1 |
One mistake I made (and corrected) in I don't know if this is a concern here, but I'm just flagging this in case |
Still to do / fix:
|
I just realised another issue: Back when we first implemented gradient legends, we agreed that low values would correspond to light colors and high values to dark colours. See #122 (comment)
However, we're doing the opposite here for Do we just want to live with this inconsistency, or reverse the palette direction? |
This all looks great!! Some comments/thoughts:
|
Just quickly on this topic: I have some mock-up code that yields the below result. What we should do is pick one of these cases as the default for
tinyplot(Month ~ Temp | Late, data = airq, type = "ridge")
tinyplot(Month ~ Temp | Late, data = airq, type = type_ridge(), fill = "by")
tinyplot(Month ~ Temp | Late, data = airq, type = "ridge", fill = 0.7)
tinyplot(Month ~ Temp | Late, data = airq, type = type_ridge(), fill = 1, col = "white") My own order of preference is probably 3, 4, 1, 2. But interested to hear what you both think. |
I would recommend a slightly different variation of 3. Maybe you can try that with your code? The idea is to borrow the strategy for lightening colors as we do in the spineplots from #233 (comment)
|
#71
This is pretty easy to implement (says the guy who couldn't figure it out for 3 hours).