Title: | Mutable and dynamic data models |
---|---|
Description: | The base R data.frame, like any vector, is copied upon modification. This behavior is at odds with that of GUIs and interactive graphics. To rectify this, plumbr provides a mutable, dynamic tabular data model. Models may be chained together to form the complex plumbing necessary for sophisticated graphical interfaces. Also included is a general framework for linking datasets; an typical use case would be a linked brush. |
Authors: | Michael Lawrence, Hadley Wickham |
Maintainer: | Michael Lawrence <[email protected]> |
License: | GPL (>= 2) |
Version: | 0.6.9 |
Built: | 2024-10-27 02:47:53 UTC |
Source: | https://github.com/ggobi/plumbr |
These functions extract, subset and replace data in a
mutaframe. For the most part, these behave much like
those for data.frame
.
x |
A mutaframe |
name |
Name of the column to extract |
i |
The row indices |
j |
The column indices |
... |
Arguments passed to methods |
value |
The replacement column |
drop |
If |
The subset function, [
, does not copy the data; it
establishes a dynamic filter.
Replacing an existing variable will pass the replacement data up the reverse pipeline, towards the root. When defining a new variable, the variable is stored in the current mutaframe; not at the root.
The selected column
The selected column
A dynamic, filtering mutaframe
Plumbr data structures send only single event for data changes: data_changed. This has a two arguments, i and j. Either both are NULL, indicating a change in the shape of the underlying data, or they give the the locations of changed data values.
add_listener(mf, callback)
add_listener(mf, callback)
mf |
muta frame |
callback |
function with arguments i and j |
Coerces a mutaframe to a data.frame
## S3 method for class 'mutaframe' as.data.frame(x, row.names = rownames(x), optional = FALSE, ...)
## S3 method for class 'mutaframe' as.data.frame(x, row.names = rownames(x), optional = FALSE, ...)
x |
a mutaframe |
row.names |
character vector of rownames, defaults
to rownames of |
optional |
see |
... |
see |
a data.frame
Coerces a mutaframe to a list
## S3 method for class 'mutaframe' as.list(x, ...)
## S3 method for class 'mutaframe' as.list(x, ...)
x |
a mutaframe |
... |
ignored |
a list, with one element for each mutaframe column
Coerce an object to a mutaframe. Supported types include
data.frame
, or anything coercible to one.
as.mutaframe(x, ...) ## S3 method for class 'mutaframe' as.mutaframe(x, ...) ## S3 method for class 'data.frame' as.mutaframe(x, ...) ## Default S3 method: as.mutaframe(x, ...)
as.mutaframe(x, ...) ## S3 method for class 'mutaframe' as.mutaframe(x, ...) ## S3 method for class 'data.frame' as.mutaframe(x, ...) ## Default S3 method: as.mutaframe(x, ...)
x |
the object to coerce |
... |
arguments passed to methods |
a mutaframe
Get the 'changed' signal
changed(mf)
changed(mf)
mf |
a mutaframe |
If any event is a shape_changed
event, return it.
Otherwise, take the unique elements of the union of all
element changes.
combine_data_events(events)
combine_data_events(events)
events |
a list of event parameters |
a unified event
Implement a selection model against a dataset/pipeline
DataSelection(data, column = 1L)
DataSelection(data, column = 1L)
data |
|
column |
Column index of selection variable in data |
An ItemSelection
reflecting the selection
in the data
Michael Lawrence
A utility for creating linking functions that operate in both directions (full duplex).
duplex_data_linker(delegate, from_data, to_data = from_data)
duplex_data_linker(delegate, from_data, to_data = from_data)
delegate |
The linking function that performs the
mapping, such as |
from_data |
A |
to_data |
A |
The generated linker function takes two arguments:
from_selection
and new_selection
. If
new_selection
is specified, new_selection
is mapped from to_data
to from_data
.
Otherwise, from_selection
is mapped from
from_data
to to_data
.
A two-way linking function as described in the details.
Michael Lawrence
Is a mutaframe currently paused?
is_paused(mf)
is_paused(mf)
mf |
a mutaframe |
Tests whether an object is a mutaframe
is.mutaframe(x)
is.mutaframe(x)
x |
an object to check |
TRUE
if x
is an instance of a class that
inherits from mutaframe
; otherwise, FALSE
ItemSelection
class implements
Selection
for the very common case of selecting
items in a dataset, optionally with weights.
The ItemSelection
class implements
Selection
for the very common case of selecting
items in a dataset, optionally with weights.
ItemSelection(delegate = NULL)
: Constructs an
ItemSelection
object with the underlying selection provided
by delegate
, which may be a function or any other R
object. If it is not a function, delegate
must support the
coercions described in the next section. A good example would be a
logical vector. However, delegate
is usually a function
that is invoked whenever the selection is stored or retrieved. If
the function is called with no arguments, it should return the
selection. Otherwise, the argument is the new selection status,
and the function should store it. This is the same semantic as
active bindings. This dynamic
functionality allows proxying of other Selection
objects or
external sources, such as a selection model from a GUI toolkit.
Any R object can represent the underlying selection, so for simplicity
we recommend that the client interpret the selection through
coercion. Each of these simply delegate to the underlying
selection object, which will need to support all of them for
consistency. The following coercions are supported, where x
is
a ItemSelection
instance:
which(x)
: integer indices of the selected items.
as.logical(x)
: TRUE
where selected.
as.integer(x)
: usually 0L (unselected) or 1L
(selected), but in general it is a weighting of the selection.
as.numeric(x)
: similar to as.integer
, except
with real values.
as.factor(x)
: ordinarily this will have two
levels, FALSE
and TRUE
, although it could have more,
which confers support for multinary selections.
All operations mentioned in Selection
are
supported: add
, subtract
, toggle
, intersect
.
Michael Lawrence
Selection
for the rest of the details.
## Assume we have a dataset: data(Cars93, package="MASS") mf <- mutaframe(Cars93) mf$.color <- "gray" ## First step is to create a base selection sel <- ItemSelection() ## Now, link that selection to other cases in same dataset by some variable linked_sel <- sel$link(match_any_linker(Cars93["Manufacturer"])) ## Finally, scale that linked selection to the data linked_sel$scale(function(x, d) { d[as.logical(x), ".color"] <- "red" }, mf) ## To test, select some cases cases <- rep(FALSE, nrow(mf)) cases[seq(1, 10, 2)] <- TRUE sel$replace(cases)
## Assume we have a dataset: data(Cars93, package="MASS") mf <- mutaframe(Cars93) mf$.color <- "gray" ## First step is to create a base selection sel <- ItemSelection() ## Now, link that selection to other cases in same dataset by some variable linked_sel <- sel$link(match_any_linker(Cars93["Manufacturer"])) ## Finally, scale that linked selection to the data linked_sel$scale(function(x, d) { d[as.logical(x), ".color"] <- "red" }, mf) ## To test, select some cases cases <- rep(FALSE, nrow(mf)) cases[seq(1, 10, 2)] <- TRUE sel$replace(cases)
Linking functions return a logical vector, with the
TRUE
elements indicating rows in the data that are
linked.
match_any_linker(from_data, to_data = from_data)
match_any_linker(from_data, to_data = from_data)
from_data |
A |
to_data |
A |
The match_any_linker
function links rows in
from_data
to rows in to_data
that share the
same key.
By convention, a key is defined as the combination of the
values in every column of from_data
and
to_data
. Thus, from_data
and to_data
should contain only the columns necessary for key
generation. They should not be an entire dataset.
a logical vector, indicating which from_data
rows
are linked
Michael Lawrence
Create a mutaframe, a mutable data.frame
mutaframe(..., row.names = NULL)
mutaframe(..., row.names = NULL)
... |
Objects to coerce to a mutaframe and combine column-wise |
row.names |
optional, the character vector of row names |
a mutaframe
The mutalist is a mutable list. Modifications to a mutalist occur by a reference semantic. Otherwise, it should act like an ordinary R list and provides a similar API. If anything is found missing, please inform the authors.
mutalist(...) ## S3 method for class 'mutalist' length(x) ## S3 replacement method for class 'mutalist' names(x, ...) <- value ## S3 method for class 'mutalist' names(x) ## S3 method for class 'mutalist' x[[i, j, ...]] ## S3 replacement method for class 'mutalist' x[[i, j, ...]] <- value ## S3 replacement method for class 'mutalist' x$name <- value ## S3 method for class 'mutalist' x[i, j, ..., drop] ## S3 replacement method for class 'mutalist' x[i, j, ...] <- value ## S3 method for class 'mutalist' head(x, n = 6L, ...) ## S3 method for class 'mutalist' tail(x, n = 6L, ...) ## S3 method for class 'mutalist' c(x, ..., recursive = FALSE) ## S3 method for class 'mutalist' lapply(X, FUN, ...) ## S3 method for class 'mutalist' as.list(x, ...) ## S3 method for class 'mutalist' as.data.frame(x, ...) ## S3 method for class 'mutalist' unlist(x, recursive = TRUE, use.names = TRUE) mutalist2env(x, envir = new.env(hash, parent, size), parent = parent.frame(), hash = FALSE, size = 29L) ## S3 method for class 'mutalist' rev(x) ## S3 method for class 'mutalist' rep(x, ...) ## S3 method for class 'mutalist' print(x, ...)
mutalist(...) ## S3 method for class 'mutalist' length(x) ## S3 replacement method for class 'mutalist' names(x, ...) <- value ## S3 method for class 'mutalist' names(x) ## S3 method for class 'mutalist' x[[i, j, ...]] ## S3 replacement method for class 'mutalist' x[[i, j, ...]] <- value ## S3 replacement method for class 'mutalist' x$name <- value ## S3 method for class 'mutalist' x[i, j, ..., drop] ## S3 replacement method for class 'mutalist' x[i, j, ...] <- value ## S3 method for class 'mutalist' head(x, n = 6L, ...) ## S3 method for class 'mutalist' tail(x, n = 6L, ...) ## S3 method for class 'mutalist' c(x, ..., recursive = FALSE) ## S3 method for class 'mutalist' lapply(X, FUN, ...) ## S3 method for class 'mutalist' as.list(x, ...) ## S3 method for class 'mutalist' as.data.frame(x, ...) ## S3 method for class 'mutalist' unlist(x, recursive = TRUE, use.names = TRUE) mutalist2env(x, envir = new.env(hash, parent, size), parent = parent.frame(), hash = FALSE, size = 29L) ## S3 method for class 'mutalist' rev(x) ## S3 method for class 'mutalist' rep(x, ...) ## S3 method for class 'mutalist' print(x, ...)
... |
elements to include in the list or arguments passed to methods |
x |
a mutalist |
value |
replacement value |
i |
element indices |
j |
unused |
name |
element name |
drop |
unused |
n |
number of elements in subset |
recursive |
whether to perform recursively |
X |
a mutalist |
FUN |
a function to apply over the elements |
use.names |
whether to preserve the names |
envir |
environment to populate |
parent |
parent for new environment, if created |
hash |
whether to hash the new environment |
size |
initial size of hash table |
a new mutalist
Michael Lawrence
Notify listeners that data has changed.
notify_listeners(mf, i, j)
notify_listeners(mf, i, j)
mf |
mutaframe |
i , j
|
row and column indices |
When a mutaframe is paused, it accumulates events without passing them on. When unpaused, it accumulates all events into a single event and passes it on.
pause(mf)
pause(mf)
mf |
mutaframe |
This is a performance optimisation for when you expect many changes: pause the mutaframe, perform all the changes and then unpause.
Generate binding for proxies.
proxy_bindings(mf, j = names(mf))
proxy_bindings(mf, j = names(mf))
mf |
mutaframe to inherit from |
j |
columns to generate bindings for |
Generate binding for raw values
raw_binding(mf, name, data)
raw_binding(mf, name, data)
mf |
mutaframe |
name |
name |
data |
vector to store |
named list of binding functions
Generate binding for raw values
raw_bindings(mf, data)
raw_bindings(mf, data)
mf |
mutaframe |
data |
list of values |
named list of binding functions
ItemSelection
class implements
Selection
for the selection of 1D and 2D regions
in plot/data space.
The ItemSelection
class implements
Selection
for the selection of 1D and 2D regions
in plot/data space.
RegionSelection(delegate = NULL)
: Constructs
an RegionSelection
object with the underlying selection
provided by delegate
, which may be a function or any other
R object. If it is not a function, delegate
must support
coercion to a matrix
as described in the next section.
However, delegate
is usually a function that is invoked
whenever the selection is stored or retrieved. If the function is
called with no arguments, it should return the
selection. Otherwise, the argument is the new selection status,
and the function should store it. This is the same semantic as
active bindings. This dynamic
functionality allows proxying of other Selection
objects or
external sources, such as a selection model from a GUI toolkit.
Any R object can represent the underlying selection, so for simplicity
we recommend that the client interpret the selection through
coercion. Currently, there is only one supported coercion of
RegionSelection
:
as.matrix(x)
: returns a matrix with a column for each
dimension and a row for each point. In the 2D case, the points
describe one or more polygons. As with the polygon
function, polygons are separated by rows of NA
, and the last
point is connected with the first. In the 1D case, the single column
might encode, for example, selections of factor levels in an area
plot.
We will probably need to add more coercions as use cases arise. This is still very preliminary.
For now, RegionSelection
only supports the add
operation
described in the documentation for Selection
.
Michael Lawrence
Selection
for the rest of the details.
## forthcoming
## forthcoming
add_listener
.Remove a listener, identified by the ID returned by
add_listener
.
remove_listener(mf, id)
remove_listener(mf, id)
mf |
mutaframe |
id |
value returned by |
A virtual base class for data models that store a selection, which might be of items, regions, or whatever. Clients can register handlers for selection changes and can create proxy models to transform selections, link across datasets and map selections to actions on the data.
This design is preliminary and subject to change.
Internally, the selection may be stored as any object, including as a
function that is invoked whenever the selection is stored or
retrieved. The function allows dynamic mapping of selections. Due to
this generality, the client should not access the selection
directly. Instead, it should explicitly coerce the selection object to
an interpretable representation. The set of supported coercions
depends on the subclass. For example,
ItemSelection
has a as.logical
method that
coerces it to a logical vector, where an element is TRUE
if the
corresponding element in the dataset is selected.
Whenever the selection is changed, the changed
signal is
emitted. The signal has zero arguments. See the objectSignals
package for details on using signals.
Eventually, a selection leads to the execution of some action by the
application. In interactive graphics, that action usually involves
scaling/transforming the selection to a modification on the data. The
x$scale(scaler, data)
method tries to facilitate these
operaitons. All it does is create a handler for the changed
signal on x
that passes x
and data
to the function
scaler
, which implements the change.
Since any type of object can represent a selection, setting the
selection has very few constraints. There are several ways to modify
the selection. Not all of them will be supported by every subclass. In
the code snippets below, x
represents a Selection
object
and selection
represents the primary representation of a
selection, like a logical vector.
x$replace(selection)
: this is supported by all
implementations.
x$add(selection)
: the result contains the
union of the original selection and selection
.
x$subtract(selection)
: the result
contains the original selection except that indicated
by selection
.
x$intersect(selection)
: the result
contains the intersection of the original selection and
selection
.
x$toggle(selection)
:
The intersection of the original selection and selection
is
deselected, that only in selection
is selected.
In interactive graphics, it is often necessary to link selections
within and across datasets. The x$link(linker)
method creates a
new Selection
object that proxies x
and maps the
selection in x
through linker
. Changes to the selection
in x
will propagate via linker
to changes in the
proxy. Analogously, the linker
will pass modifications to the
proxy down to x
.
The linker
may be provided as an integer vector, like that
returned by match
, but it is usually a function, as that
allows very general linking strategies. As an example, let us consider
a simple linker between two datasets based on key matching. We assume
that the keys, source_keys
and dest_keys
, are in the
enclosure of our linker function.
function(source_selection, new_dest_value) { if (missing(new_dest_value)) dest_keys else source_keys }
The linker
function takes one or two arguments, depending on
whether the selection is being retrieved or stored. When the selection
is being retrieved, source_selection
is passed as the only
argument. The duty of the linker
is then to retrieve the
underlying selection from source_selection
(through coercion,
see above) and figure out which keys in the destination selection
match the selected source keys. The new_dest_value
argument is
provided whenever the selection is being stored/set. In that case, the
analogous operation is performed, in the opposite direction. The
symmetry here is fairly obvious, and duplex_data_linker
is a utility for facilitating the implementation of such two-way
linking functions.
Michael Lawrence
The ItemSelection
and RegionSelection
subclasses, which have examples.
Is the event a shape changed event?
shape_changed(i, j)
shape_changed(i, j)
i |
col index |
j |
row index |
Unpause (reply) events.
unpause(mf)
unpause(mf)
mf |
mutaframe |
Make valid variable names
variable_names(var_names)
variable_names(var_names)
var_names |
variable names |