Hi
The "standard" approach of having graphics parameter arguments separate
from the data arguments is a little bit creaky. The relationship
between the nth data value and the nth graphics parameter only really
exists in your head. It would feel much safer if you could specify the
graphics parameters as part of the data frame that contains the data, so
you had more confidence about the relationship between values (c.f.
ggplot2). I think the standard approach really only works in practice
because it is simple enough to think about two one-dimensional vectors
matching up.
But as soon as you go to something like a matrix, it gets too hard to
keep straight in your head, all confidence drains away and you can see
the frailty of this approach. If you have something hierarchical, like
a tree of nodes, god help you.
This is where the second approach mentioned by Baptiste may provide a
way out. If you provide some sort of rational naming scheme for the
graphical elements that you are drawing, then you can address the
components of a more complex graphic by name. Here's some code that
provides a trivial example ...
library(grid)
nrow <- 3
ncol <- 5
# Draw a table of cells
# grid.newpage()
pushViewport(viewport(width=.9, height=.9,
layout=grid.layout(nrow, ncol)))
for (i in 1:nrow) {
for (j in 1:ncol) {
pushViewport(viewport(layout.pos.row=i,
layout.pos.col=j))
grid.rect(name=paste("rect", i, j, sep="-"))
grid.text(paste("cell", i, j, sep="-"),
name=paste("text", i, j, sep="-"))
upViewport()
}
}
upViewport()
# Modify all cells in row 2
grid.gedit("rect-2", gp=gpar(fill="grey"))
# Modify all labels in col 4
grid.gedit("text-.-4", gp=gpar(col="light grey"))
# Modify just cell 3,2
grid.edit("rect-3-2", gp=gpar(fill="black"))
grid.edit("text-3-2", gp=gpar(col="white"))
If you don't want to impose the effort of calling grid.edit() on your
users, you can wrap this all up within your function. The basic idea
would be to create a grob (or gTree) representing the table, with all
component grobs rationally labelled, then apply user-supplied edits to
the grob before returning the entire, modified object (or you can just
draw the modified grob). Here's a trivial example of this idea ...
tableGrob <- function(nr, nc, edits=NULL) {
cells <- vector("list", nr*nc)
for (i in 1:nr) {
for (j in 1:nc) {
cellvp <- viewport(layout.pos.row=i,
layout.pos.col=j)
grobs <- gList(rectGrob(name=paste("rect", i, j, sep="-"),
vp=cellvp),
textGrob(paste("cell", i, j, sep="-"),
name=paste("text", i, j, sep="-"),
vp=cellvp))
cells[[(i - 1)*nc + j]] <- gTree(children=grobs)
}
}
table <- gTree(children=do.call("gList", cells),
vp=viewport(layout=grid.layout(nr, nc)))
if (!is.null(edits)) {
for (i in 1:length(edits))
table <- editGrob(table,
gPath=names(edits)[i],
grep=TRUE, global=TRUE,
gp=edits[[i]])
}
table
}
# Unmodified table
# grid.newpage()
grid.draw(tableGrob(3, 5))
# Modified table
grid.newpage()
grid.draw(tableGrob(3, 5,
edits=list("rect-2"=gpar(fill="grey"),
"text-.-4"=gpar(col="light grey"),
"rect-3-2"=gpar(fill="black"),
"text-3-2"=gpar(col="white"))))
For large tables this may draw slowly (c.f. ggplot2), but you'd need to
try it to find out.
Hope that is of some help.
Paul
p.s. We might want to move any further discussion on to R-devel.
baptiste auguie wrote:
I'm facing a similar challenge with a grid.table function (see example
below). I envisaged two routes,
1- rather than assigning a list of properties for each cell, I would
define a matrix for each property. For instance, the default could be,
fill = matrix("grey90", nrow(values), ncol(values)) # or an array if
more than 2D
col = matrix("red", nrow(values), ncol(values))
col.text = matrix("black", nrow(values), ncol(values))
The drawing of each cell would look like,
for( ii in 1:nrow){
for (jj in 1:ncol){
grid.rect(gp=gpar(fill = fill[ii, jj], col = col[ii, jj]))
grid.text(values[ii, jj], gp = gpar(col= col.text[ii, jj]))
# or whatever grid function you need
}
}
2- Create the table with default values, but give a name to each grob
and allow for subsequent editing of individual gpar() properties.
(as suggested in sec. 7.3.10 "avoiding argument explosion" of Paul
Murrell's R graphics book)
Perhaps a structure like a gTree can help (in my case I wanted the
table header to have different settings than the rest of the table).
Best,
baptiste
source("http://gridextra.googlecode.com/svn/trunk/R/tableGrob.r")
tc = textConnection("
carat VeryLongWordIndeed color clarity depth
14513 1.35 Ideal J VS2 61.4
28685 0.30 Good G VVS1 64.0
50368 0.75 Ideal F SI2 59.2")
d = read.table(tc,head=T)
close(tc)
grid.newpage()
g = grid.table(d)
grid.ls(g) # not much of a clue which is which... let's edit a random
one (numbers probably session-dependent)
grid.edit("GRID.tableGrob.556::table.header.left::GRID.cellGrob.718::GRID.rect.717",
gp=gpar(fill="red"))
2009/8/17 Michael Friendly <frien...@yorku.ca>:
I'm working on a package to produce graphic displays of 2- and 3-way tables
and need some help/advice on how to simplify the specification of a complex
argument that gives the drawing details for each cell of the table.
Prototypes of two functions, 'tableplot' and 'cellgram' are given below.
The essential idea is that for a given table ('values'), the cells can be
be of different 'types' (integers: 1, 2, ..., max(types)). An argument
'patterns' is a list of max(types) lists, where patterns[[k]] is presently
a list of 10 graphic parameters specifying shapes, colors, fill, background
color,
etc. that are passed as arguments to the cellgram function.
The difficulty is that it's too hard to remember the order and meaning
of the cellgram arguments to be passed in the patterns argument to
tableplot,
and often quite a few of these will take their default values, but-- as
presently written-- all must be specified.
The actual implementation of these functions use grid graphics, and I know
from other grid-based packages that use named specifications like
gp = gpar(shape=1, shape.col="black", shape.lty=2, ...)
are commonly used, but I'm unable to see how to re-write these functions to
take advantage of that form. To be specific, I need to re-write cellgram
to take two arguments
cellgram(cell.values, cell.pattern)
and then be able to extract the current arguments from cell.pattern within
this function.
tableplot <- function(
values, # Matrix of values to plot; can be a matrix, or an
array of 3 dimensions.
types, # Matrix of pattern designations (i.e., what pattern for
each cell).
# patterns, # List of lists; each list specifies one pattern.
patterns = list(list(0, "black", 1, "white", "white", 0, 0.5, "grey80",
"no", 1)),
...){
#
#---Draw cellgrams.
for (i in 1:dim(values)[1]){
for (j in 1:dim(values)[2]){
pattern = patterns[[types[i,j]]]
cellgram(cell = values[i,j,],
shape = pattern[[1]],
shape.col = pattern[[2]],
shape.lty = pattern[[3]],
cell.fill = pattern[[4]],
back.fill = pattern[[5]],
label = pattern[[6]],
label.size= pattern[[7]],
ref.col = pattern[[8]],
ref.grid = pattern[[9]],
scale.max = pattern[[10]]
)
}
}
}
cellgram = function(
#-- Arguments that may be vectorized:
cell, #-- cell value(s)
shape=0, #-- shape of cell value(s); 0=circle, 1=diamond,
2=square, 3=cross
shape.col="black", #-- color of shape(s), outline only
shape.lty=1, #-- line type used for shape(s)
#-- Arguments that can not be vectorized:
scale.max=1, shape.lwd=1, #-- line width of shape(s)
cell.fill="white", #-- inside color of smallest shape in a cell
back.fill="white", #-- background color of cell
label=0, #-- how many cell values will be printed; max is 4
label.size=0.7, #-- size of text labels
ref.col="grey80", #-- color for ref lines
ref.grid=TRUE, #-- to draw ref lines or not
neg.col="white", #-- fill color for negative cell value
frame.col="black", #-- color of frame around cell
frame.lwd="0.5" #-- line width of frame around cell
t.col="black", #-- color of cell labels [[ should be: label.col]]
)
{
# ...
}
--
Michael Friendly Email: friendly AT yorku DOT ca Professor, Psychology
Dept.
York University Voice: 416 736-5115 x66249 Fax: 416 736-5814
4700 Keele Street http://www.math.yorku.ca/SCS/friendly.html
Toronto, ONT M3J 1P3 CANADA
______________________________________________
R-help@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-help
PLEASE do read the posting guide http://www.R-project.org/posting-guide.html
and provide commented, minimal, self-contained, reproducible code.
--
Dr Paul Murrell
Department of Statistics
The University of Auckland
Private Bag 92019
Auckland
New Zealand
64 9 3737599 x85392
p...@stat.auckland.ac.nz
http://www.stat.auckland.ac.nz/~paul/
______________________________________________
R-help@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-help
PLEASE do read the posting guide http://www.R-project.org/posting-guide.html
and provide commented, minimal, self-contained, reproducible code.