loon.ggplotJust as ggplot2 provides a layered implementation of a
grammar of graphics, loon.ggplot provides
a layered implementation of a grammar of interactive
graphics.
With loon.ggplot, data analysts can easily switch
between the elegant and beautiful static graphics of
ggplot2 and the powerful direct manipulation interactive
graphics of loon, using each where it is most
natural.
airqualityTo provide a working example, consider the airquality
dataset:
data("airquality")
summary(airquality)
#> Ozone Solar.R Wind Temp
#> Min. : 1.00 Min. : 7.0 Min. : 1.700 Min. :56.00
#> 1st Qu.: 18.00 1st Qu.:115.8 1st Qu.: 7.400 1st Qu.:72.00
#> Median : 31.50 Median :205.0 Median : 9.700 Median :79.00
#> Mean : 42.13 Mean :185.9 Mean : 9.958 Mean :77.88
#> 3rd Qu.: 63.25 3rd Qu.:258.8 3rd Qu.:11.500 3rd Qu.:85.00
#> Max. :168.00 Max. :334.0 Max. :20.700 Max. :97.00
#> NA's :37 NA's :7
#> Month Day
#> Min. :5.000 Min. : 1.0
#> 1st Qu.:6.000 1st Qu.: 8.0
#> Median :7.000 Median :16.0
#> Mean :6.993 Mean :15.8
#> 3rd Qu.:8.000 3rd Qu.:23.0
#> Max. :9.000 Max. :31.0
#> It has missing data (in Ozone and Solar.R)
and the variable Month appears as numerical. The date
information can easily be made more meaningful:
airquality$Date <- with(airquality,
as.Date(paste("1973", Month, Day, sep = "-")))
# Could also look up the day of the week for each date and add it
airquality$Weekday <- factor(weekdays(airquality$Date),
levels = c("Monday", "Tuesday", "Wednesday",
"Thursday", "Friday",
"Saturday", "Sunday"))Month can also be turned it into a factor so that its
levels are three character month abbreviations (arranged to match
calendar order):
airquality$Month <- factor(month.abb[airquality$Month],
levels = month.abb[unique(airquality$Month)])The data now look like
head(airquality, n = 3)
#> Ozone Solar.R Wind Temp Month Day Date Weekday
#> 1 41 190 7.4 67 May 1 1973-05-01 Tuesday
#> 2 36 118 8.0 72 May 2 1973-05-02 Wednesday
#> 3 12 149 12.6 74 May 3 1973-05-03 ThursdayWith its mix of continuous and categorical variables (some with
missing data) this transformed data will be used to illustrate
loon.ggplot’s grammar of interactive graphics.
ggplot() becomes
l_ggplot()The interactive grammar begins by simply replacing
ggplot() by l_ggplot(), wherever it appears in
the layered grammar. The same arguments (e.g., data,
mapping, etc.) and clauses (e.g. geoms, scales,
coordinates, etc.) are used, but now to create an interactive plot.
For example,
lgp <- l_ggplot(airquality,
mapping = aes(x = Wind, y = Ozone)) +
ggtitle("Air quality in New York (1973)") +
geom_point(size = 3) Like ggplot(), l_ggplot() produces a data
structure containing the information needed to create a plot. No plot is
actually yet displayed; rather lgp has the
potential to produce a plot on demand.
A look at its class
suggests that it is both an l_ggplot and a
ggplot (and gg).
In ggplot2 identical methods have been written for
both the plot() and the
print() function to show the ggplot on the
current device.
In loon.ggplot these functions are different for an
l_ggplot:
plot(lgp) displays the lgp structure as a
static ggplot on the current deviceprint(lgp) displays it as an interactive
loon plot in the R session.For example, to see the ggplot:
And to see the interactive loon plot, simply print
it:
which will look something like
(as rendered in
grid graphics).
Each plot (the static ggplot and the interactive
loon plot) presents the same information, but in slightly
different form (e.g., different choices on title placement, white space
padding, etc.)
Though the data information is identical in both plots, the
loon plot appears a little more spartan. This is because an
interactive plot is dynamic and can change in real time by direct
interaction; it is enough that the analyst appreciates the data content
of the plot without too much concern over display details. In contrast,
the ggplot is more often meant to be shared in print and so
demands more flexibility to lay out its plot elements in an elegant
display.
Note: In ggplot2, every time a
ggplot is printed, a new plot is produced on the current
device. Similarly, in loon.ggplot, every time an
l_ggplot is printed, a new (interactive)
loon plot is produced; every time it is plotted, a new
(static) ggplot is produced.
At times, as when creating this document, it will be handy to have
programmatic access to the loon plot. This can be done by
assigning the loon plot to a variable, say lp,
in any one of several different ways:
When it was first printed it, the interactive plot would have returned a string. For example, it was
#> [1] ".l0.ggplot.plot"
which is of the form ".lXX.ggplot.plot" where
XX is a non-negative integer.
This is the tcltk “path” to the loon plot
and uniquely identifies the loon structure. The data
structure is now accessed through l_getFromPath(pathname)
as below.
Of course, this requires you to have noticed and recovered the string
pathname for that plot when it first appeared. Fortunately, that is not
necessary. In the title bar of the window containing the
loon plot, the string following "path: " can
also be used. In the present case, this will be of the form
".lXX.ggplot" (as before but without the additional
".plot" suffix). The call
will return the interactive plot as before.
Finally, if you have the foresight to know that you would like to
have programmatic access to the interactive plot from the start, you
could assign it to a variable when it was first created.
There are two ways to do this.
One is
The other uses a powerful function called loon.ggplot()
(more on this below):
Note: that unlike l_getFromPath(),
either of the above calls will produce a new
interactive plot from lgp and assign it to
lp.
Using l_ggplot() in place of ggplot()
extends the graphics grammar of ggplot2 to produce
interactive loon plots.
use l_ggplot() in place of ggplot()
plotting an l_ggplot produces a static
ggplot display
printing an l_ggplot produces an
interactive loon display.
the l_ggplot is not the interactive
loon plot; it is an enhanced ggplot
and can be augmented just as any other
ggplot.
Changes to the ggplot have no effect on the
interactive plot
For example,
behaves like any other
ggplot with no effect on the
interactive plot.
Changes to the interactive loon plot have no
effect on the static l_ggplot
Make whatever changes you like interactively to the loon
plot lp, or programmatically as below:
# Change glyph aesthetics of ALL points
lp["color"] <- "lightgrey"
lp["glyph"] <- "ctriangle" # closed triangle
lp["size"] <- 10 # proportional to area in loon
# Dynamically change the scaling (magnify or zoom in and out)
for (mag in rep(c(0.8, 1, 1.2), times = 5)){
lp["zoomX"] <- mag
lp["zoomY"] <- mag
Sys.sleep(0.1) # slow down to see effect
}
# Settle on
lp["zoomX"] <- 1.2
lp["zoomY"] <- 1.2
#
# Or, similarly, change the location/origin of the plot
xlocs <- seq(min(lp["x"]),
median(lp["x"]),
length.out = 10)
ylocs <- seq(min(lp["y"]),
median(lp["y"]),
length.out = 10)
# Dynamically change the origin
for (i in 1:length(xlocs)){
lp["panX"] <- xlocs[i]
lp["panY"] <- ylocs[i]
Sys.sleep(0.1) # slow down to see effect
}
# and back
xlocs <- rev(xlocs)
ylocs <- rev(ylocs)
# dynamically
for (i in 1:length(xlocs)){
lp["panX"] <- xlocs[i]
lp["panY"] <- ylocs[i]
Sys.sleep(0.1) # slow down to see effect
}
# Perhaps settle on
lp["panX"] <- 7
lp["panY"] <- 0And now observe the effect on each of the plots:
No change. It is unaffected by any change to the interactive plot.
Reflects the changes made on the interactive plot.
loon.ggplot()Earlier, loon.ggplot() was recommended as a means to
produce an interactive plot from an l_ggplot and to assign
it to a variable, as in
This makes it seem essentially equivalent to print(lgp),
but it is not.
Instead, loon.ggplot() is a powerful two way
bridge between ggplots and loon
plots.
When called on
an l_ggplot (an extended ggplot)
loon.ggplot(lgp) produces an interactive loon
plot.
an interactive loon plot, it produces a static
ggplot
This is a ggplot with parameters to make it look like
the loon plot it was built from (e.g. the title is centred
in this ggplot).
loon.ggplot() gives us a second way to produce a
static version of an interactive loon plot.
plot(lp) produces a grid graphic object
(or grob)
loon.ggplot(lp) produces a ggplot
graphic object.
Either reproduces the current loon plot as a static
snapshot of its present appearence (ideally wysiwyg).
Which is preferred depends on the use intended for the static plot .
The ggplot version is easier to work with and trivial to
adapt using the grammar.
Alternatively, a new interactive plot can be generated from
an ordinary ggplot as
and this interactive plot is turned into a new static
ggplot with the same function, as shown below
Note that the grey polygon emphasizing the confidence region does not
appear. It is there, but as a hidden layer of the
loon plot that can be revealed at any time (via the
loon inspector or programmatically). The polygon is not
shown by default because colours do not (yet) have an alpha channel (for
transparency) in tcltk.
The bridge: loon.ggplot() turns
ggplots to interactive loon plots and
loon plots to ggplots.
loon.ggplot extends the grammar of graphics by adding
several new clauses.
+ linking()+ linking(linkingGroup = NULL, linkingKey = NULL, linkedStates = NULL, sync = NULL)
loon implements a group-key-state linking
model.
Interactive plots having the same linkingGroup are
linked, in that each plot changes its display in
response to display changes of the linkedStates of any plot
in the same linkingGroup. Only those
linkedStates in common are changed and display elements are
matched by values of the linkingKey.
linkingGroup
A string naming the group or NULL for no
linking.
linkedStates
A character vector of the states to be linked.
By default these include selected, active,
and others peculiar to each type of plot such as color and
size.
linkingKey
A length n character vector of unique strings, one for
each observation; default is "0", "1", …,
"n-1".
sync
Specifies the direction of synchronizing the linked states
at the time the plot is created.
The value "pull" pulls the values of the linked states from
plots in the linking group and assigns them to the new plot; value
"push" pushes the newly created plot’s linked states values
out to the others in the linking group.
Default is "pull" unless some aesthetics matching the
linked states are specified in the plot creation; then the default will
be "push".
Each plot will propogate, and respond to, only changes in those
states named in its linkedStates. Display
elements associated with the unique set of keys in each plot’s
linkingKey will change together.
+ hover()+ hover(itemLabel = NULL, showItemLabels = NULL)
Provides a pop up display as the mouse hovers over a plot element in the interactive plot.
itemLabel
A character vector of length n with a string to be used
to pop up when the mouse hovers above that element.
showItemLabels
A single logical value: TRUE if pop up labels are to
appear on hover, FALSE (the default) if they are
not.
+ selection()+ selection(selected = NULL, selectBy = NULL, selectionLogic = NULL)
Set which elements (i.e., observations) are "selected".
These are shown highlighted in the plot.
selected
a logical or a logical vector of length n that determines which observations are selected (TRUE and hence appear highlighted in the plot) and which are not. Default is FALSE and no points are highlighted.
selectBy
A string determining how selection will occur in the interactive
plot. Default is "sweeping" where a rectangular region is
reshaped or “swept” out to select observations.; alternately
"brushing" will indicate that a fixed rectangular region is
moved about the display to select observations.
selectionLogic
One of "select" (the default), "deselect",
and "invert". The first highlights observations as
selected, the second downlights them, and the third inverts them
(downlighting highlighted observations and highlighting downlighted
ones).
+ active()+ active(active = NULL, activeGeomLayers = NULL)
Set active and/or activeGeomLayers
active
a logical, or a logical vector of length n, determining
which observations are active (hence appear in the plot) and which are
inactive (FALSE and hence do not appear). Default is
TRUE.
activeGeomLayers
determine which geom layer is interactive by its
geom_... position in the grammar of the expression.
Currently, only geom_point() and
geom_histogram() can be set as the active geom layer(s) so
far. (N.B. more than one geom_point() layer can be set as
an active layer, but only one geom_histogram() can be set
as an active geom layer and it can be the only active layer)
+ zoom()+ zoom(layerId = NULL, scaleToFun = NULL)
Change the visible plot region by scaling to different elements of the display.
layerId
numerical; which layer to scale the plot by. If the layerId is set as
NULL (default), the region of the interactive graphics loon
will be determined by the ggplot object
(i.e. coord_cartesian, xlim, etc); else one
can use scaleToFun to modify the region of the
layer.
scaleToFun
scale function to be used. If NULL (default), based on
different layers, different scale functions will be applied. For
example, if the layer is the main graphic model,
i.e. l_plot l_hist, then the default
scaleToFun is l_scaleto_plot; else if the
layer is a general l_layer widget, the default
scaleToFun would be loon::l_scaleto_layer.
If it is not NULL, any of the following
scaleToFun functions could be used
| scale to | Subfunction |
|---|---|
| plot | l_scaleto_plot |
| world | l_scaleto_world |
| active | l_scaleto_active |
| selected | l_scaleto_selected |
| layer | l_scaleto_layer |
Alternatively, scaleToFun can be any function whose
arguments match those of the functions above.
+ interactivity()+ interactivity(linkingGroup, linkingKey, linkedStates, sync, # linking
active, activeGeomLayers, # active
selected, selectBy, selectionLogic, # selection
layerId, scaleToFun, # zoom
itemLabel, showItemLabels, # hover
... )Set interactive components (e.g. linking, selection, etc) in one clause. All named arguments are as described in the other clauses.
... other named arguments to modify loon plot states.
See l_info_states().l_ggplot() becomes
ggplot()We began with the recommendation that to have interactive
ggplots, all we need do is replace ggplot() in
the grammar by l_ggplot() with all the usual arguments. All
clauses of the ggplot grammar can be used as before
plus the new interactive clauses. The
result is an l_ggplot that prints as an
interactive loon plot and plots
as a ggplot2 plot. The advantage is that using
l_ggplot() makes it clear that an ordinary
ggplot is not being produced and emphasizes the
loon-ggplot duality.
The final trick is that, with loon.ggplot, one does not
actually need to use l_ggplot(), simply use
ggplot(). If no clauses are interactive, then an
ordinary ggplot will be produced; if an interactive clause
is added, an l_ggplot will be produced.
For example, the following produces an ordinary
ggplot.
ggp <- ggplot(airquality, mapping = aes(Solar.R, Temp)) +
geom_point(size = 3) +
ggtitle("Air quality in New York (1973)")
# which is an ordinary ggplot and prints as one
ggp
Adding an interactive clause, turns the result into an
l_ggplot.
lggp <- ggp +
linking(linkingGroup = "airquwqality") +
selection(selected = airquality$Solar.R < 100) +
zoom(layerId = 1, scaleToFun = l_scaleto_selected) +
geom_smooth()
# which is an interactive loon and prints as one
lggp
#> [1] ".l3.ggplot.plot"
#> attr(,"class")
#> [1] "l_plot" "loon"
# but plots as a ggplot
plot(lggp)Note that the interactive effects do not appear in
plot(l_ggp); this is because this is still an
l_ggplot. To print the interactive plot, a handle to it
must be found, for example using
l_getFromPath(".l3.ggplot")
l_ggp <- l_getFromPath(".l3.ggplot")
# Alternatively, the loon plot could have been captured when first
# created by using loon.ggplot(lggp) in stead of print(lggp) as follows
#
# l_ggp <- loon.ggplot(lggp)
#
# Either way, it will look like the following as a grid graphics plot
plot(l_ggp)
# and as below when presented as a ggplot
loon.ggplot(l_ggp)The final trick is that either
l_ggplot() or ggplot() cam be produce
interactive plots using the ggplot grammar.
It’s largely a matter of taste.