Michael Friendly
2009-Aug-17 19:53 UTC
[R] help simplifying complex graphic arguments to a function
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
baptiste auguie
2009-Aug-18 14:19 UTC
[R] help simplifying complex graphic arguments to a function
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 <friendly at 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 at 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. >-- _____________________________ Baptiste Augui? School of Physics University of Exeter Stocker Road, Exeter, Devon, EX4 4QL, UK http://newton.ex.ac.uk/research/emag