Skip to content

Commit

Permalink
Merge pull request #118 from TidierOrg/you-wouldnt-pirate-a-type
Browse files Browse the repository at this point in the history
Massive refactor.

Adds:

calculations in @aes macro
broadcasting support
fill scales (not complete)
ggsave for GGPlotGrids
Removes:

All type piracy
A lot of complexity from the calculation system (now handled entirely by DataFrames.jl)
closes #71
closes #100
closes #108
closes #97
  • Loading branch information
rdboyes authored Sep 17, 2024
2 parents de5808b + c0b754e commit a8c6589
Show file tree
Hide file tree
Showing 35 changed files with 592 additions and 1,016 deletions.
9 changes: 5 additions & 4 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
name = "TidierPlots"
uuid = "337ecbd1-5042-4e2a-ae6f-ca776f97570a"
authors = [
"Randall Boyes <[email protected]> and contributors",
]
version = "0.7.8"
authors = ["Randall Boyes <[email protected]> and contributors"]
version = "0.8.0"

[deps]
CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
CategoricalArrays = "324d7699-5711-5eae-9e2f-1d82baa6b597"
ColorSchemes = "35d6a980-a343-548e-a6ea-1d62b119f2f4"
ColorTypes = "3da002f7-5984-5a60-b8a6-cbb66c0b333f"
Colors = "5ae59095-9a9b-59fe-a467-6f913c188581"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
FixedPointNumbers = "53c48c17-4a7d-5ca2-90c5-79b7896eea93"
Format = "1fa38f19-a742-5d3f-a2b9-30dd87b9d5f8"
GLM = "38e38edf-8417-5370-95a0-9cbb8c7f171a"
Infiltrator = "5903a43b-9cc3-4c30-8d17-598619ec4e9b"
KernelDensity = "5ab0869b-81aa-558d-bb23-cbf5423bbe9b"
Loess = "4345ca2d-374a-55d4-8d30-97f9976e7612"
Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
Expand Down
98 changes: 50 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,20 @@ TidierPlots.jl is a 100% Julia implementation of the R package [ggplot2](https:/
1. **Stick as closely to tidyverse syntax and behaviour as possible:** Whereas other
meta-packages introduce Julia-centric idioms for working with
plots, this package’s goal is to reimplement ggplot
in Julia. This currently just means that `TidierPlots.jl` gives the option for specifying `aes` with the macro `@es` to allow unquoted column references, but the use of macros may need to expand as more features are added.
in Julia. This currently just means that `TidierPlots.jl` gives the option for specifying `aes` with the macro `@es` to allow unquoted column references, but the use of macros may need to expand as more features are added.

2. **Stay as compatible as possible with Makie.jl** This package is meant
to be a thin wrapper around Makie's SpecApi syntax to help introduce R users to plotting in
Julia.
to be a thin wrapper around Makie's SpecApi syntax to help introduce R users to plotting in
Julia.

3. **To Extend ggplot using julia-specific features where appropriate** as long as this does
not confict with the first two goals. The package aims to behave exactly like ggplot
unless told otherwise. Additional options and parameters that are not present in ggplot
may be added, but options that are present in R's ggplot should behave the way they do in R.
unless told otherwise. Additional options and parameters that are not present in ggplot
may be added, but options that are present in R's ggplot should behave the way they do in R.

## Installation

For the "stable" version, access the Pkg interface by pressing `]` at the `julia>` prompt, then type `add TidierPlots`.
For the "stable" version, access the Pkg interface by pressing `]` at the `julia>` prompt, then type `add TidierPlots`.

For the development version:

Expand Down Expand Up @@ -62,7 +62,7 @@ Makie Themes:
Colour Scales:

- `scale_color_manual()` - set `values = c(c1, c2, c3, ...)`, accepts anything that can be parsed as a color by Colors.jl (named colors, hex values, etc.)
- `scale_color_[discrete|continuous|binned]()` - set `palette =` a [ColorSchemes.jl palette](https://juliagraphics.github.io/ColorSchemes.jl/stable/catalogue/) as a string or symbol. Also accepts ColorSchemes.jl color scheme objects.
- `scale_color_[discrete|continuous|binned]()` - set `palette =` a [ColorSchemes.jl palette](https://juliagraphics.github.io/ColorSchemes.jl/stable/catalogue/) as a string or symbol. Also accepts ColorSchemes.jl color scheme objects.

Additional Elements:

Expand All @@ -77,7 +77,7 @@ Use the function `TidierPlots_set(option::String, value::Bool)` to control displ
- "plot_show" (default true). Enables `ggplot`-like behaviour where plots are displayed when created.
- "plot_log" (default true). Prints a text summary of the properties of the ggplot

You will likely want to disable both of these if you are working in a notebook environment. In [Pluto.jl](https://github.com/fonsp/Pluto.jl), you can get interactive plots (scroll, zoom, labels, etc.) using `WGLMakie` by including `WGLMakie.activate!()` as the first cell after your imports.
You will likely want to disable both of these if you are working in a notebook environment. In [Pluto.jl](https://github.com/fonsp/Pluto.jl), you can get interactive plots (scroll, zoom, labels, etc.) using `WGLMakie` by including `WGLMakie.activate!()` as the first cell after your imports.

## Differences from ggplot2

Expand All @@ -87,30 +87,33 @@ The goal of this package is to allow you to write code that is as similar to ggp
- Option 2: `@aes` (or `@es`) macro, aes as in ggplot, e.g. `@aes(x = x, y = y)` or `@aes(x, y)`
- Option 3 (Deprecated): `aes` function, column names as strings, e.g. `aes(x = "x", y = "y")` or `aes("x", "y")`

If you use Option 1, you get experimental support for calculations inside aes, including `+`, `-`, `*`, `/` and function application. Functions can be applied to columns with the `>>` operator, or wrapped for aes use with the `aesthetics_function()` command. The following geom_point specifications are equivalent:
If you use Option 1, functions can be applied to columns with the `=>` operator to form a `Pair{Symbol, Function}`, similar to how `DataFrames.jl` functions work.

```julia
my_fn(x) = x ./ 10
my_aes_fn = aesthetics_function(my_fn)

geom_point(aes(x = :x/10))
geom_point(aes(x = :x >> my_fn))
geom_point(aes(x = my_aes_fn(:x)))
geom_point(aes(x = :x => my_fn))
```

Functions can take multiple columns as input (up to two, currently). The following `geom_point` specifications are equivalent, and result in `x / y` (where `x` and `y` are the names of columns in a DataFrame) being plotted as the x axis of the graph:
Functions can take multiple columns as input. The following `geom_point` specification results in `x / y` (where `x` and `y` are the names of columns in a DataFrame) being plotted as the x axis of the graph:

```julia
my_new_fn(x, y) = x ./ y
my_new_aes_fn = aesthetics_function(my_new_fn)

geom_point(aes(x = :x/:y))
geom_point(aes(x = my_new_aes_fn(:x, :y)))
geom_point(aes(x = [:x, :y] => my_new_fn))
```

## Why would I use this instead of ggplot2?
With Option 2, functions will be interpreted using `TidierData.jl`:

**Right now, you probably wouldn't.** This package is still early in development, and is not ready for production use. However, there are a couple of advantages already and the list will hopefully get longer over time.
```julia
# Macro aes equivalents to the above examples
geom_point(@aes(x = x / 10))
geom_point(@aes(x = x / y))
```

## Why would I use this instead of ggplot2?

**Right now, you probably wouldn't.** This package is still early in development, and is not ready for production use. However, there are a couple of advantages already and the list will hopefully get longer over time.

### Easier Factor Handling

Expand Down Expand Up @@ -147,21 +150,21 @@ beautiful_makie_theme = Attributes(
fonts=(;regular="CMU Serif"),
)

ggplot(df) +
ggplot(df) +
geom_point(aes(x = :x, y = :y, size = :size, color = :x), alpha = 0.8) +
scale_x_log10() +
scale_y_log10() +
scale_x_log10() +
scale_y_log10() +
labs(x = "x", y = "y") +
lims(y = c(.1, 100)) +
scale_color_continuous(palette = "Hiroshige", name = "") +
theme(
xminorticksvisible=true,
xminorgridvisible=true,
yminorticksvisible=true,
yminorgridvisible=true,
xminorticks=IntervalsBetween(9),
xminorticksvisible=true,
xminorgridvisible=true,
yminorticksvisible=true,
yminorgridvisible=true,
xminorticks=IntervalsBetween(9),
yminorticks=IntervalsBetween(9),
backgroundcolor = :transparent,
backgroundcolor = :transparent,
xgridstyle=:dash,
ygridstyle=:dash
) + beautiful_makie_theme
Expand All @@ -170,37 +173,37 @@ ggplot(df) +

### Built-in Support for Plot Layouts

Combine plots with a `{patchwork}`-inspired syntax to create complex layouts (adapted from [beautiful.makie.org](https://beautiful.makie.org/examples/2d/histogram/hists_on_the_sides)):
Combine plots with a `{patchwork}`-inspired syntax to create complex layouts (adapted from [beautiful.makie.org](https://beautiful.makie.org/examples/2d/histogram/hists_on_the_sides)):

```julia
Random.seed!(123)
n = 200
df = DataFrame(x = randn(n) / 2, y = randn(n))

top = ggplot(df) +
geom_histogram(aes(x = :x), color = (:orangered, 0.5), strokewidth = 0.5) +
lims(x = c(-4, 4)) +
theme(xticklabelsvisible = false, xgridvisible = false) +
top = ggplot(df) +
geom_histogram(aes(x = :x), color = (:orangered, 0.5), strokewidth = 0.5) +
lims(x = c(-4, 4)) +
theme(xticklabelsvisible = false, xgridvisible = false) +
beautiful_makie_theme

right = ggplot(df) +
geom_histogram(aes(:y), color = (:dodgerblue, 0.5),
direction = :x, strokewidth = 0.5) +
lims(y = c(-3, 3)) +
right = ggplot(df) +
geom_histogram(aes(:y), color = (:dodgerblue, 0.5),
direction = :x, strokewidth = 0.5) +
lims(y = c(-3, 3)) +
theme(yticklabelsvisible = false, ygridvisible = false) +
beautiful_makie_theme

middle = ggplot(df) + geom_point(aes(:x, :y), size = 10) +
lims(x = c(-4, 4), y = c(-3, 3)) + labs(x = "x", y = "y") +
middle = ggplot(df) + geom_point(aes(:x, :y), size = 10) +
lims(x = c(-4, 4), y = c(-3, 3)) + labs(x = "x", y = "y") +
beautiful_makie_theme

blank = ggplot() +
blank = ggplot() +
theme(xticklabelsvisible = false, xgridvisible = false, yticklabelsvisible = false,
ygridvisible = false, xtickcolor = :transparent, ytickcolor = :transparent,
bottomspinevisible = false, topspinevisible = false, rightspinevisible = false,
ygridvisible = false, xtickcolor = :transparent, ytickcolor = :transparent,
bottomspinevisible = false, topspinevisible = false, rightspinevisible = false,
leftspinevisible = false) + beautiful_makie_theme

top + blank + middle + right +
top + blank + middle + right +
plot_layout(ncol = 2, nrow = 2, widths = c(3, 1), heights = c(1, 2))
```

Expand All @@ -213,21 +216,20 @@ Add basic support for any Makie plot using `geom_template(name, required_aes, ma
```julia
geom_raincloud = geom_template("geom_raincloud", ["x", "y"], :RainClouds)

ggplot(penguins) +
ggplot(penguins) +
geom_raincloud(aes(x = :species, y = :bill_depth_mm/10, color = :species), size = 4) +
scale_y_continuous(labels = "{:.1f} cm") +
scale_y_continuous(labels = "{:.1f} cm") +
labs(title = "Bill Depth by Species", x = "Species", y = "Bill Depth") +
theme_minimal()
```
![](assets/raincloud.png)

See the [documentation](https://tidierorg.github.io/TidierPlots.jl/latest) for more information and examples.
See the [documentation](https://tidierorg.github.io/TidierPlots.jl/latest) for more information and examples.

# What's New

See [NEWS.md](https://github.com/TidierOrg/TidierPlots.jl/blob/main/NEWS.md) for the latest updates.

# What's Missing

Lots! Please feel free to file an issue and/or submit a pull request to add additional ggplot-based features. If it is in ggplot, we want to add it.

Lots! Please feel free to file an issue and/or submit a pull request to add additional ggplot-based features. If it is in ggplot, we want to add it.
13 changes: 3 additions & 10 deletions src/TidierPlots.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ using Reexport
# Needed for color scales to work correctly
using Colors
using ColorSchemes
using FixedPointNumbers

# Needed for label functions
using Format
Expand All @@ -33,32 +34,29 @@ using KernelDensity
include("structs.jl")

include("addplots.jl")
include("aes_ops.jl")
include("aes.jl")
include("attributes.jl")
include("broadcasting.jl")
include("draw.jl")
include("extract_aes.jl")
include("facets.jl")
include("geom.jl")
include("ggplot.jl")
include("ggsave.jl")
include("grouping.jl")
include("labs.jl")
include("label_functions.jl")
include("legend.jl")
include("patchwork.jl")
include("scales_colour.jl")
include("scales_numeric.jl")
include("themes.jl")
include("transforms.jl")
include("show.jl")
include("util.jl")

include("geoms/geom_template.jl")

include("geoms/geom_bar.jl")
include("geoms/geom_boxplot.jl")
include("geoms/geom_contour.jl")
include("geoms/geom_density.jl")
include("geoms/geom_errorbar.jl")
include("geoms/geom_hvline.jl")
Expand Down Expand Up @@ -120,12 +118,7 @@ export scale_x_pseudolog10, scale_y_pseudolog10, scale_x_symlog10, scale_y_symlo
export scale_x_reverse, scale_y_reverse, scale_x_sqrt, scale_y_sqrt
export scale_colour_continuous, scale_colour_discrete, scale_colour_manual, scale_colour_binned
export scale_color_continuous, scale_color_discrete, scale_color_manual, scale_color_binned

# transforms

export cat_inseq, cat_inorder, number_on_axis, as_is, discard, verbatim, kernel_density_2d
export as_color
export aesthetics_function
export scale_fill_continuous, scale_fill_discrete, scale_fill_manual, scale_fill_binned

# default options

Expand Down
16 changes: 12 additions & 4 deletions src/addplots.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,21 @@ function get_options(geom_list)
end

function Base.:+(x::GGPlot, y::Union{Geom, Vector{Geom}, Aesthetics, AxisOptions, FacetOptions, Attributes}...)::GGPlot

themes = [i for i in y if i isa Attributes]
theme = length(themes) == 0 ? Makie.theme_ggplot2() : themes[end]

facet = [i for i in y if i isa FacetOptions]
facet_options = length(facet) == 0 ? nothing : facet[end]

color = [i.color_palette for i in y if i isa AxisOptions &&
!isnothing(i.color_palette)]
color_palette = length(color) == 0 ? nothing : color[end]

fill = [i.fill_palette for i in y if i isa AxisOptions &&
!isnothing(i.fill_palette)]
fill_palette = length(fill) == 0 ? nothing : fill[end]

result = GGPlot(
vcat(x.geoms, # if there are geoms or lists of geoms, append them to the ggplot's geoms
[i for i in y if i isa Geom],
Expand All @@ -27,11 +35,11 @@ function Base.:+(x::GGPlot, y::Union{Geom, Vector{Geom}, Aesthetics, AxisOptions
[get_options(i) for i in y if i isa Vector{Geom}]...,
[i.opt for i in y if i isa AxisOptions]...),
theme,
merge(x.column_transformations,
[i.column_transformations for i in y if i isa AxisOptions]...),
merge(x.legend_options,
[i.legend_options for i in y if i isa AxisOptions]...),
facet_options
facet_options,
color_palette,
fill_palette
)

return result
Expand Down
Loading

0 comments on commit a8c6589

Please sign in to comment.