Code Monkey home page Code Monkey logo

tinyplot's Introduction

tinyplot

CRAN version R-universe status badge Dependencies ci Docs

What

A lightweight extension of the base R graphics system, with support for automatic grouping, legends, facets, and various other enhancements.

The stable version of tinyplot is available on CRAN.

install.packages("tinyplot")

Or, you can grab the latest development version from R-universe.

install.packages("tinyplot", repos = "https://grantmcdermott.r-universe.dev")

Why

R users are spoiled for choice when it comes to visualization frameworks. The options, of course, include lattice and ggplot2; say nothing of the bewildering array of extensions built around, on top of, and in between these amazing packages.1

Given the wealth of options, it is perhaps understandable that even avid R users can overlook the base R graphics system. This is unfortunate, because base R offers very powerful and flexible plotting facilities.2 The downside of this power and flexibility is that base R plotting can require a lot of manual tinkering. A case in point is plotting grouped data with an appropriate legend. Doing so with the generic plot() function can require several function calls or a loop, fiddling with your plot regions, and then generating the legend manually.

The tinyplot package aims to remove this overhead. It provides a lightweight extension of the base R graphics system that preserves the same core logic and syntax, but with numerous convenience features to make base R plotting a more user-friendly experience. For example, the core tinyplot() function—or its shorthand alias plt()—makes it easy to plot grouped datasets and generate automatic legends in a single function call. Or, you can display groups in separate facets without having to worry about manually setting plot regions. While tinyplot offers these and various other enhancements, it tries as far as possible to be a drop-in replacement for the equivalent base plotting function. Users should generally be able to swap out a valid plot() call for tinyplot()/plt() without any changes to the expected output.

It is worth emphasizing that tinyplot requires only base R. It has zero recursive dependencies and we have been careful to keep its installation size down to a minimum. tinyplot should thus provide an attractive and lightweight option for package developers (or regular R users!) who would like to produce convenient and sophisticated plots, combined with a minimal footprint.

Quickstart

The tinyplot website includes a detailed introductory tutorial, with numerous examples. But here are some quickstart examples of the package in action.

library(tinyplot)

Grouped scatterplot with automatic legend:

# with(iris, tinyplot(x = Petal.Length, y = Sepal.Length, by = Species)) # atomic
tinyplot(Sepal.Length ~ Petal.Length | Species, data = iris)             # formula

If you would prefer to save on a few keystrokes, you can use the shorthand plt() alias instead instead of typing out tinyplot() in full. Here’s the same plot with this shorthand alias, plus a few aesthetic tweaks:

plt(
  Sepal.Length ~ Petal.Length | Species, 
  data = iris,
  palette = "dark", pch = 16,
  grid = TRUE, frame = FALSE
)

Grouped grouped density plot with automatic legend:

plt(
  ~ Petal.Length | Species,
  data = iris,
  type = "density",
  palette = "dark", fill = "by",
  grid = TRUE,
  main = "Distribution of petal lengths by species"
)

Grouped scatterplot with (continuous) gradient legend, combined with facet layout:

plt(
  Sepal.Length ~ Petal.Length | Sepal.Length, data = iris,
  facet = ~Species, facet.args = list(bg = "grey90"),
  pch = 19,
  main = "Faceted Species!",
  grid = TRUE, frame = FALSE
)

Hopefully, these have been enough to pique your interest. Head over to the intro tutorial for many more examples, including range plots and customization.

Footnotes

  1. Both lattice and ggplot2 are built on top of the grid package, which was incoporated into the base R distribution way back in R 2.0.0. ggplot2, in particular, is arguably the most important and influential (high-level) graphics library of the last two decades, across any programming language.

  2. Just type demo(graphics) or demo(persp) into your R console to get an idea. Or, take a look at these two excellent tutorials.

tinyplot's People

Contributors

grantmcdermott avatar vincentarelbundock avatar etiennebacher avatar zeileis avatar

Stargazers

Raphael Saldanha avatar Xfly avatar Tokhir Dadaev avatar William Humberto Herrera Rey avatar Shimeng Huang avatar  avatar John Blischak avatar Michael Schramm avatar Kai Aragaki avatar Insang Song avatar Clemens Brunner avatar Jon Clayden avatar Paul Hoffman avatar Hugh Graham avatar mikefc avatar Aurélien Ginolhac avatar Steven Pawley avatar Muhamad Said Fathurrohman avatar Ladislas Nalborczyk avatar Matti Vuorre avatar Amarullazo avatar Allister Hodge avatar B. Ogan Mancarcı avatar Xiangjiao avatar  avatar Alexandros Gotinakos avatar Ethan Bass avatar  avatar Tomás Barcellos avatar Danilo Freire avatar  avatar nyubachi avatar Francisco Rodriguez-Sanchez avatar  avatar Jinhwan Kim avatar Shinya Uryu avatar  avatar  avatar Kazuharu Yanagimoto avatar Brancen Gregory avatar zhihu xu avatar RS-eco avatar JING YANG avatar Kyle Mitchell avatar Fernando A. avatar Mwavu avatar John Coene avatar Najko Jahn avatar Daniel Tinoco avatar Konrad avatar wang minjie avatar  avatar James E. Pustejovsky avatar Masatoshi Katabuchi avatar David Hodge avatar  avatar Matt Kerlogue avatar Petr Bouchal avatar EconMaett avatar Ashish Damania avatar Markus Kainu avatar Md. Aminul Islam Shazid avatar James J Balamuta avatar James Goldie avatar Chris Hartgerink avatar Liang Zhang avatar Jeffrey Stevens avatar Yusuke Sasaki avatar Kurtis Pivert, MS, CAP avatar Jonathan Kennel avatar PierGianLuca avatar Reynard avatar  avatar Elian H. Thiele-Evans avatar Nicholas avatar  avatar Larefly avatar Jiří Moravec avatar Giuseppe Carteny avatar E. David Aja avatar  avatar sp7der avatar Jose Toledo avatar James Inlow avatar Chad Allison avatar Keshav Motwani avatar Laurent Bergé avatar Gina Reynolds avatar Emanuele Aliverti avatar Hongyuan Jia avatar  avatar Nicholas Knoblauch avatar Yongfu Liao avatar Haibing Yan avatar Matěj avatar Maciej Beręsewicz avatar vjvelascorios avatar Oscar Davalos avatar  avatar antx avatar

Watchers

timelyportfolio avatar James Cloos avatar  avatar Robert Nuske avatar  avatar  avatar EconMaett avatar  avatar Alexandros Gotinakos avatar

tinyplot's Issues

R CMD check warning about S3 method

Running R CMD check generates the following warning:

checking S3 generic/method consistency ... WARNING
  plot2:
    function(x, y, ...)
  plot2.formula:
    function(formula, data, type, xlim, ylim, main, sub, xlab, ylab, ann,
             axes, frame.plot, asp, grid, palette, palette.args,
             legend.position, legend.args, pch, ...)
  
  See sectionGeneric functions and methodsin theWriting R
  Extensionsmanual.

The current dispatch calling looks right to me. Maybe it's a roxygen2 issue? Or do I have to explicitly allow all arguments in each method?

cex arg not working

Compare:

plot(0:10, cex = 3, main = "plot")

library(plot2)
plot2(0:10, cex = 3, main = "plot2")

Created on 2023-07-27 with reprex v2.0.2

Same for cex.main, cex.sub, and cex.axis, although these probably require separate attention via ... passing.

Custom legend not working with type = "density" plots

library(plot2)

# fails silently
with(
  mtcars,
  plot2(
    x = mpg, by = cyl,
    type = "density", 
    legend = list(x = "bottom!")
  )
)

# fails outright
with(
  mtcars,
  plot2(
    x = mpg, by = cyl,
    type = "density", 
    legend = "bottom!"
  )
)
#> Error in if (names(fargs[["legend"]])[1] == "") names(fargs[["legend"]])[1] = "x": argument is of length zero

Interestingly, this isn't a problem if we feed it a density object directly.

# this works, though
with(
  mtcars,
  plot2(
    x = density(mpg), by = cyl,
    legend = "bottom!"
  )
)

Package name: plot2 vs. baseplot vs. tinyplot vs. ?

I have been wondering whether plot2() is the best and/or most intuitive name for the package and its main function. It's certainly "ok" but I'm not excited about it. So I wanted to ask what you think, Grant @grantmcdermott and Vincent @vincentarelbundock, maybe others have thoughts/ideas as well.

A somewhat longer but more verbose alternative might be baseplot(). Short alternatives would be draw() or viz(). Currently, I think I slightly prefer baseplot() over plot2(). Other opinions or better alternatives?

Should we support `symbols(2)`?

Mostly for bubble charts. You can imagine the background colour below being assigned by group (as opposed to random recycling here).

with(
    data.frame(state.x77),
    {
        symbols(
            x = Income, y = Life.Exp,
            circ = sqrt(Population / pi),
            inches = .25,
            fg = "white",
            bg = palette.colors(n = 4, alpha = 0.5)[-1],
            main = "Life expectancy among US states (1977)"
        )
    }
)

Created on 2023-04-17 with reprex v2.0.2

Known issues

  • #16
  • "col" argument could be smarter (e.g., inherit from by) [Closing b/c I think we have a better solution with #5 now.]
  • #27
  • #26
  • #17
  • #65

ribbon legends could be improved

tl;dr the current ribbon legend is just a line without shading/fill. Contrary to #46 (comment), I now realise that this could be addressed quite elegantly by specifying a large square with fill (basically, pch = 22 + pt.cex > 1) and some other tweaks.

Manual example of what I have in mind:

pred = predict(lm(mpg~wt+factor(cyl),mtcars), interval = "confidence")
m = cbind(mtcars, pred)

library(plot2)

with(
  m,
  plot2(
    wt, fit, ymin = lwr, ymax = upr, by = cyl, type = "ribbon",
    legend = FALSE # turn off default line type legend
    )
)

# manual example of a nicer legend
legend(
  "topright",
  legend = c(4,6,8),
  title = "cyl", bty = "n", border = NA, 
  col = palette()[1:3],
  pch = 22,
  pt.bg = adjustcolor(palette()[1:3], alpha = 0.2),
  pt.cex = 3.5,
  x.intersp = 0.75, y.intersp = 1.25,
  lty = 1,
  pt.lwd = 0,
  seg.len = 1.5
)

Created on 2023-08-11 with reprex v2.0.2

Update: seg.len above should probably be 1.25 but otherwise these settings seem to work well.

Unable to add further elements on the plot2() plot.

Seems like currently images produced by plot2() with a legend hijack plot.window() coordinates.
As a result adding additional points using the coordinate system of the original data no longer works:

library(plot2)

plot2(Sepal.Width ~ Sepal.Length | Species, iris)

points(6,3)  # point not visible

Instead we have to specify elements in the range of 0-1:

points(0.5,0.5)  # adds a point to the middle of the plot

I think this is because when you set a legend you start a new window on top of the existing one.
Maybe the par() settings are not correctly returned afterwards?

Supported plot types

This issue is meant to list all of tinyplot's supported plot types, as well as track other candidate plot types that aren't supported yet. Please feel free to suggest more below, or call out anything that's missing.

(Repo admins are encouraged to edit this comment and add them directly.)

Direct plot equivalents

  • "p" (points)
  • "l" (lines)
  • "b" (points + lines)
  • "c" (empty points)
  • "o" (overplotted points)
  • "s"/"S" (stair steps)
  • "h" (histogram-like vertical lines)
  • #156

Other base plot types

  • "density"
  • "ridgeline" #71
  • "histogram" #72
  • "barplot"
  • #153
  • "spineplot" #2
  • #126
  • "polypath"
  • "segments"
  • "curve"
  • "text"
  • "jitter" (alias "j"?)
  • "rug" (alias "r"?)
  • "coplot"

Range and interval types

  • "ribbon"
  • "area"
  • "errorbar"
  • "pointrange"

Misc types

  • "bubble"

Composite types

  • "lollipop" (alias "i"?) = "h" + "p"
  • "fit" (alias "f"?) for model fit = "p" +"ribbon"
    • Aside: Need to figure out how/whether to support multiple model types and how/whether to opt out of plotting SEs.
    • lm
    • glm
    • loess
    • gam
  • "densrug" = "density" + "rug"

User-supplied functions

Would be cool if we could support arbitrary type = function(x, y) calls from users. Would probably have to be limited to points, since these are the most flexible plot elements. Maybe not worth the potential headache of catching edge cases (or just issue a caveat emptor disclaimer and be done with it). But listing here for consideration.

  • function(...)

Wishlist: Proper formula processing

Currently, the plot2.formula() method does not actually do a full formula processing (building terms and model.frame etc.) but just extracts the variables from the data argument. However, this means that transformations cannot be used in the formula, e.g., plot2(log(y) ~ x, ...).

Also, I wouldn't recommend doing the | handling in the formula by using string operations (grep etc.). This leads to problems with things like plot(y ~ x | factor(z > 2 | z < -2), ...). I have been there, done that, and eventually implemented the Formula package which avoids all string-based operations.

API change: single `legend` and `palette` arguments

TL;DR Roll the legend.position and legend.args arguments into a single legend = legend(...) / legend = <keyword> argument. Similarly, roll the palette and palette.args arguments into a single palette = <function> / palette = <keyword> argument.

Problem

An API/design decision I'd like to nail down before a CRAN submission is the best way to handle the legend and palette arguments. At the moment, each of these is broken into two user-facing arguments. Respectively:

  • legend: legend.position (accepts a keyword string like "right!" or "topleft") and legend.args (accepts a list of arguments, passed down to legend())
  • palette: palette (accepts a string for like "Viridis" that matches at least one output from palette.pals() or hcl.pals()) and palette.args (accepts a list of arguments, passed down to palette.colors() or hcl.colors()).

The current setup has some advantages, but I don't really like that we require two separate arguments for each "aspect". Other concerns include the fact that we don't use all arguments available to, say, legend creation and existing arguments don't necessarily align with their base equivalents. (An example: the "title" argument in legend.args is equivalent to the "legend" argument in the base legend() function.) Finally, the current API also places some constraints on user-specified inputs. For example, if a user wanted to supply a bespoke colour-generating palette as a function.

Proposal

An alternative would be to roll each of these into a single argument that accepts a function (and possibly a string for a convenient shorthand to change a leading/key feature). Specifically:

  • legend: would accept a regular legend function, e.g. legend = legend(bty = "o", legend = "my legend title"), where we splice in any remaining defaults. We could also catch/allow users to pass a key position string as an alternative convenience option, e.g. "bottom!". (One slight concern/inconsistency is whether the keyword behaviour should be reserved for position, as I've suggested here, or whether it should be the legend title...)
  • palette: would accept a palette-producing function like palette(), or palette.cols(), hcl.colors(). The trick here is that the leading "n" argument would usually (always?) be redundant, since we calculate n internally based on the number of groups in by. Similar to the current behaviour, we could catch/allow users to pass down a known palette string as a convenience option e.g., "Tableau 10".

Decision?

Personally I'm leaning towards the proposed setup, despite be a breaking change. I would love to hear other opinions before putting in a PR, though.

Support "area" plots

(Where, following ggplot2, area plots are just defined as a special case of ribbon plots with ymin = 0 and ymax = y.)

While we can manually achieve this by doing something like...

with(
    airquality,
    plot2(
        x = seq_along(Temp), 
        y = Temp,
        ymin = rep(0, length(Temp)),
        ymax = Temp,
        type = "ribbon"
    )
)

... it would be cool if it could be automatically inferred when (a) type = "ribbon" and (b) ymin and ymax aren't specified. So, something like the following would offer a concise way to get the previous plot above.

with(
    airquality,
    plot2(Temp, type = "ribbon")
)

Partial palette name matching could be smarter

We currently allow for palette matching regardless of case (e.g., "r4" <-> "R4").

But, unlike, the main palette() and hcl.colors() functions, we don't do a good job of partial matching (e.g., with an unambiguous first word like palette("tableau") or hcl.colors(5, "zissou")). It's probably a good idea just to use the same charmatch approach that the latter two functions have adopted internally.

Should ribbons be (x-axis) ordered automatically?

Ribbons only make sense in the context of ordered data. So something like the following should work automatically.

library(plot2)

mod = lm(mpg ~ wt, mtcars)
pred = predict(mod, interval = "confidence")

mtcars2 = cbind(mtcars, pred)

with(
  mtcars2,
  plot2(
    x = wt, y = mpg,
    ymin = lwr, ymax = upr,
    type = "ribbon"
  )
)

Created on 2023-08-07 with reprex v2.0.2

Website

In keeping with plot2's goal to be an attractive lightweight plotting option, the website should help to offload content that is adding unnecessary bulk to the package itself. A case in point is the fact that all the images in the README are currently pushing the installation tarball to several MiBs.

Serving content external to the package also means that we shouldn't use conventional R vignettes (which would get bundled with the installation tarball). So I'm not sure how well this workflow jibes with standard R website builders like pkgdown and altdoc.

Some options:

  • Build a docs/ folder locally and serve it through github-pages. (Can perhaps still be automated with GH Actions?)
  • Use a Quarto book format like @vincentarelbundock did for marginaleffects link. Vincent, curious about your experiences here.
  • A gallery-type website (see #59).

Facet support

I'm working towards basic facet support in https://github.com/grantmcdermott/plot2/tree/facets.

Quick example of what's possible so far.

devtools::load_all("~/Documents/Projects/plot2")
#> ℹ Loading plot2

data("penguins", package = "palmerpenguins")

par(pch = 19)

# basic
with(
  penguins,
  plot2(
    x = flipper_length_mm, y = body_mass_g,
    facet = species
  )
)

# fancier version with addtional args (incl. interaction with "by" arg)
with(
  penguins,
  plot2(
    x = flipper_length_mm, y = body_mass_g,
    facet = species, by = sex,
    main = "penguins",
    grid = TRUE, frame = FALSE
  )
)

Created on 2023-10-27 with reprex v2.0.2

Some outstanding issues that I'd like to resolve:

  1. At the moment, the facet arg is specified as a standard, single atomic var. Do we instead want to enable facet support via a (potentially two-sided) formula? This might be the most natural way to enable ggplot2-style facet grid, a la facet = rows ~ cols... Or, do you want to support both methods somehow?
  2. At the moment, facets are arranged by window columns (i.e., in a single row). Should we try to do something smarter here, like automatically fold into "square" rows and columns?
  3. At the moment, the axes are printed for all facets. Do we want to only print these for the exterior facets to avoid duplication?
  4. Do we want to enable additional customization of facet options, probably via a list argument? This could include some of the things that I've mentioned above, e.g. controlling the number of cols/rows, printing of axes, etc. But I'm worried that it creates an additional maintenance burden.

Nested grouping

It would be nice if we could support nested grouping. (Or, put differently, allow colours to vary/repeat across units.) This would mostly be useful for line plots where we want to avoid joining the end of one line with the start of another. The idea is similar to how ggplot2 allows you to specific aes(col = var1, group = var2) separately.

Here is an illustration using the following dataset. The setting is a difference-in-differences research design with staggered treatment. So we have treatment cohorts (first_treat) superimposed on individual units (id).

  1. First, points. (Fine.)
plot2(y ~ time | first_treat, dat)

  1. Second, lines. (Not fine, because we have lines rejoining across units in the same cohort.)
plot2(y ~ time | first_treat, dat, type = "l")

Of course, we could group (colour) by the individual IDs. This stops the rejoining, but means that we lose the colouring by treatment group (which is the interesting thing from a causal inference perspective).

plot2(y ~ time | id, dat, type = "l", legend = FALSE)

I don't have a solution right now, but it probably requires a new argument like bycol. On the formula side, we could potentially represent this via a / nesting interaction. So the call would become plot2(y ~ time | first_treat / id, dat, type = "l"), i.e. units are nested within first treatment cohorts.

Facet titles are not sorted correctly

... leading to wrong titles above the different facet plots. (If the data are not already sorted by the facet variable(s).)

library(plot2)
library(ggplot2) ## for comparison

with(mtcars, plot2(x = wt, y = mpg, facet = cyl))

Note the "6" and "4" facet titles should be swapped.

Compare:

ggplot(mtcars, aes(x = wt, y = mpg)) +
  geom_point(shape = 1) + 
  facet_wrap(~cyl) + 
  theme_linedraw() +
  theme(panel.grid = element_blank())

Created on 2024-01-21 with reprex v2.0.2

A bad bug, but a simple thing to fix. PR incoming shortly.

Ridgeline plot

Hi @grantmcdermott, I was looking for how to make ridgeline plots in base R and I came across this package that has a single function to do it: https://github.com/R-CoderDotCom/ridgeline

It is MIT licensed so it should be fine to take the code. I'm not making a PR for this because I don't know what kind of implementation you'd prefer (formula, arguments, etc.) and because the code of the plot2.* functions is quite dense. I'm just putting this here so that you or someone else don't have to start from scratch if you choose/have time to implement this.

Global palette should be respected

library(plot2)

par(pch = 16)

# Default "R4" palette
plot2(Sepal.Length ~ Petal.Length | Species, iris)

# Specifying different palette as part of plot call works...
plot2(Sepal.Length ~ Petal.Length | Species, iris, palette = "Tableau 10")

... But setting global palette doesn't
palette("Tableau 10")
plot2(Sepal.Length ~ Petal.Length | Species, iris)

Created on 2023-07-25 with reprex v2.0.2

Facet to-do list

Moved from #83.

  • Smarter arrangements of facets (e.g., "square" window arrangements) instead of current single row default. (#91)
  • Smarter axes options (e.g., only print the axis ticks for the "outer" facets). (#91)
  • Should we allow free axis scaling/limits for individual facets? Decision: No. (For now at least; see discussion and MWE in reply below.)
  • Extra facets.args argument to control the above three options (maybe others). (#91)
  • #100
  • If yes to the above, should we integrate formula support directly into the plot.formula method somehow... maybe via a second vertical bar, e.g. y ~ x | by | facetrow ~ facetcol? Decision: No.
  • Alternatively, maybe y ~ x | by, facet = TRUE could treat by as a facet variable and override the default grouping behaivour (distinct from facet = "by", which treats them as equivalent). Decision: No.
  • Should the larger legend cex be adjusted to match the smaller facet elements (or, vice versa)? (#91)
  • Consistent facet margins that can be adjusted by the user. (#94)
  • #98
  • #103

Relevant comments:

#83 (comment) (@zeileis)

Layout: In my first attempt to add both faceting and categorical variables #12, I had just added an argument mfrow so that we could set mfrow = TRUE or mfrow = c(1, 4) etc. Maybe this would be feasible for letting the users decide whether they want square layouts or not.

Axes: It would be good to separate out the functions for drawing the panels and drawing the axes now. This might let us add the support for categorical variables from #12 that we had further discussed in #2. My suggestion was that there should be workhorse functions a la plot2_xclass_yclass_plottype(x, y, by, axes = TRUE, ...).

From reading your description above (but not studying the code in detail), I thought that it might maybe help to have accompanying functions axis2_xlass_yclass_plottype(...) that could be inserted into the workflow for drawing the axes?

#83 (comment) (@vincentarelbundock)

Personally, I would avoid putting too much burden on the formula. Faceting does feel like a different functionality which should mostly (exclusively?) be called with its own argument. Also, focusing on the facet argument eventually allow us to do more complex things like multiple variables with a two-sided formula indicating rows and columns.

The mfrow feature seems important and powerful. Is that name too generic? Will people think it does something else, like allow multiple plot2() calls? If it only applies to facet, maybe it should be facet_mfrow. I don't have a strong view...

Combine multiple `type`s in a list?

Would be cool if we could do something like:

plot2(1:10, type = list("p", "l"))

Obviously, for this particular case we could just use type = o as a workaround. But I'm thinking more as convenience feature for combining, say, points with ribbon plots. (And, as opposed, to calling another plot with add= TRUE.)

... OTOH are there many cases where this would be useful? Especially given that, for some of the canonical "combination" plots that I can think of (e.g., regression best-fit line with raw data points), the different types actually require different y values.

Wishlist: Refined methods for densities/histograms

In addition to the plot2.density method from PR #18, it would be nice to have a formula-based interface:

plot2(~ x | by, type = c("density", "histogram"), ...)

where it can be selected which display is used for x in each by group.

In addition to just lines it would be nice to support semi-transparent shaded areas below the lines as well (via polygon()). The area might also be opaque if we order the by groups (e.g., by median) from highest to lowest, which would yield a kind of ridge lines plot.

GM edit: adding task items to track.

bg arg not working

Compare:

plot(0:10, pch = 21, col = "black", bg = "red", main = "plot")

library(plot2)
plot2(0:10, pch = 21, col = "black", bg = "red", main = "plot2")

Created on 2023-07-27 with reprex v2.0.2

We probably want to support bg = "by" too, so that we can support something like plot2(..., col = "white", bg = "by").

Dates should work properly

dat = data.frame(
    y = rnorm(24),
    ds = seq(as.Date("2021-01-01"), as.Date("2022-12-01"), by = "month")
)

plot(y ~ ds, dat)

library(plot2)
plot2(y ~ ds, dat)

Created on 2023-09-06 with reprex v2.0.2

Support pointrange character / factor x-axis

One special case that we might want to think about separate support for is point-range plots (following @vincentarelbundock's PR in #35). In particular, we probably want to handle the x-axis carefully if we are passing a vector of characters or factors, e.g. coefficient names.

At present, we have to manually convert the x axis to a numeric first...

library(plot2)
par(pch = 19)

mod = lm(mpg ~ hp + factor(cyl), mtcars)
coefs = data.frame(names(coef(mod)), coef(mod), confint(mod))
coefs = setNames(coefs, c("x", "y", "ymin", "ymax"))

with(
    coefs,
    plot2(
        x = 1:4, # <<-- Problem: has to be numeric ATM
        y = y,
        ymin = ymin,
        ymax = ymax,
        type = "pointrange"
    )
)

... whereas, we'd ideally just be able to pass it the x variable directly and it would handle labels appropriately.

# aspirational code example that doesn't currently work
with(
    coefs,
    plot2(
        x = x,
        y = y,
        ymin = ymin,
        ymax = ymax,
        type = "pointrange"
    )
)

Created on 2023-06-19 with reprex v2.0.2

Should be easy to do. But just flagging so that we don't inadvertently impose/override with unexpected behaviour upstream.

Originally posted by @grantmcdermott in #2 (comment)

Should we support a "fill" argument as an alias for "bg"?

My sense is that bg is not well known among non-dedicated base plot users.

From a practical perspective, I often find myself mistyping by = "by" (instead of bg = "by") by mistake :-/

Having the fill = "by" fallback is something I'm likely to mistype less often.

Wishlist: Support factors as x or y

Thanks:

Grant @grantmcdermott, the project looks really nice and useful, thanks!

Wishlist:

I just wanted to put on the wishlist that in addition to numeric x and y, it would be great if the factor-based plot() flavors were also supported in plot2(). Especially the plots where y is a factor are in my opinion underappreciated in base R. (It's not surprising that I would say that because I implemented these...)

Plots:

In plot(x, y, ...) and plot(y ~ x, ...) we have the following:

x
Numeric
 
Factor
y   Numeric Scatterplot Parallel boxplots
Factor Spinogram
(Spineplot with histogram-style breaks in x)
Spineplot
(Flavor of mosaic plot)

Examples:

data("SwissLabor", package = "AER")
plot(income ~ age, data = SwissLabor)            ## Numeric ~ Numeric
plot(income ~ foreign, data = SwissLabor)        ## Numeric ~ Factor
plot(participation ~ age, data = SwissLabor)     ## Factor ~ Numeric
plot(participation ~ foreign, data = SwissLabor) ## Factor ~ Factor

It would be great if the plots above would work with plot2() as well - and if the coloring and grouping etc. would also be supported.

Wishlist: Global options

Allow users to set things outside of par. Palette defaults would be an obvious candidate. (Not true: Should be via palette as per #44). Maybe default legend position. Other "theme" options like grid and frame.plot. Any others?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.