| 1 | | #' Split Plot |
| 2 | | #' |
| 3 | | #' A plotting function aimed at automating some common visualization tasks in order to ease data exploration. |
| 4 | | #' @param y a formula (see note), or the primary variable(s) to be shown on the y axis (unless \code{x} is not specified). |
| 5 | | #' When not a formula, this can be one or more variables as objects, or names in \code{data}. |
| 6 | | #' @param data a \code{data.frame} to pull variables from. If variables aren't found in \code{data}, they will be looked |
| 7 | | #' for in the environment. |
| 8 | | #' @param su a subset to all variables, applied after they are all retrieved from \code{data} or the environment. |
| 9 | | #' @param type determines the type of plot to make, between \code{"bar"}, \code{"line"}, \code{"density"}, or |
| 10 | | #' \code{"scatter"}. If \code{"density"}, \code{x} is ignored. Anything including the first letter of each is accepted |
| 11 | | #' (e.g., \code{type='l'}). |
| 12 | | #' @param split how to split any continuous variables (those with more than \code{lim} levels as factors). Default is |
| 13 | | #' \code{"median"}, with \code{"mean"}, \code{"standard deviation"}, \code{"quantile"}, or numbers as options. If |
| 14 | | #' numbers, the variable is either cut at each value in a vector, or broken into roughly equal chunks. Entering an |
| 15 | | #' integer (e.g., \code{split = 3L}) that is greater than 1 will force splitting into segments. Otherwise variables will |
| 16 | | #' be split by value if you enter a single value for split and there are at least two data less than or equal to and |
| 17 | | #' greater than the split, or if you enter more than 1 value for split. If a numeric split is not compatible with |
| 18 | | #' splitting by value or segment, splitting will default to the median. |
| 19 | | #' @param levels a list with entries corresponding to variable names, used to rename and/or reorder factor levels. To |
| 20 | | #' reorder a factor, enter a vector of either numbers or existing level names in the new order (e.g., |
| 21 | | #' \code{levels =} \code{list(var =} \code{c(3,2,1))}). To rename levels of a factor, enter a character vector the same |
| 22 | | #' length as the number of levels. To rename and reorder, enter a list, with names as the first entry, and order as the |
| 23 | | #' second entry (e.g., \code{levels =} \code{list(var =} \code{list(c('a','b','c'),} \code{c(3,2,1)))}). This happens |
| 24 | | #' after variables are split, so names and orders should correspond to the new split levels of split variables. For |
| 25 | | #' example, if a continuous variable is median split, it now has two levels ('Under Median' and 'Over Median'), which are |
| 26 | | #' the levels reordering or renaming would apply to. Multiple variables entered as \code{y} can be renamed and sorted |
| 27 | | #' with an entry titled \code{'mv'}. |
| 28 | | #' @param sort specified the order of character or factor \code{x} levels. By default, character or factor \code{x} levels |
| 29 | | #' are sorted alphabetically. \code{FALSE} will prevent this (preserving entered order). \code{TRUE} or \code{'d'} will |
| 30 | | #' sort by levels of \code{y} in decreasing order, and anything else will sort in increasing order. |
| 31 | | #' @param error string; sets the type of error bars to show in bar or line plots, or turns them off. If \code{FALSE}, no |
| 32 | | #' error bars will be shown. Otherwise, the default is \code{"standard error"} (\code{'^s'}), with \code{"confidence |
| 33 | | #' intervals"} (anything else) as an option. |
| 34 | | #' @param error.color color of the error bars. Default is \code{'#585858'}. |
| 35 | | #' @param error.lwd line weight of error bars. Default is 2. |
| 36 | | #' @param lim numeric; checked against the number of factor levels of each variable. Used to decide which variables should |
| 37 | | #' be split, which colors to use, and when to turn off the legend. Default is \code{9}. If set over \code{20}, \code{lim} |
| 38 | | #' is treated as infinite (set to \code{Inf}). |
| 39 | | #' @param lines logical or a string specifying the type of lines to be drawn in scatter plots. By default (and whenever |
| 40 | | #' \code{cov} is not missing, or if \code{lines} matches \code{'^li|^lm|^st'}), a prediction line is fitted with |
| 41 | | #' \code{\link[stats]{lm}}. For (potentially) bendy lines, \code{'loess'} (matching \code{'^loe|^po|^cu'}) will use |
| 42 | | #' \code{\link[stats]{loess}}, and \code{'spline'} (\code{'^sm|^sp|^in'}) will use \code{\link[stats]{smooth.spline}}. |
| 43 | | #' If \code{y} is not numeric and has only 2 levels, \code{'probability'} (\code{'^pr|^log'}) will draw probabilities |
| 44 | | #' estimated by a logistic regression (\code{glm(y ~} \code{x, binomial)}). \code{'connected'} (\code{'^e|^co|^d'}) will |
| 45 | | #' draw lines connecting all points, and \code{FALSE} will not draw any lines. |
| 46 | | #' @param colors sets a color theme or manually specifies colors. Default theme is \code{"pastel"}, with \code{"dark"} and |
| 47 | | #' \code{"bright"} as options; these are passed to \code{\link{splot.color}}. If set to \code{"grey"}, or if \code{by} |
| 48 | | #' has more than 9 levels, a grey scale is calculated using \code{\link[grDevices]{gray}}. See the \code{col} parameter |
| 49 | | #' in \code{\link[graphics]{par}} for acceptable manual inputs. To set text and axis colors, \code{col} sets outside |
| 50 | | #' texts (title, sud, labx, laby, and note), \code{col.sub} or \code{col.main} sets the frame titles, and \code{col.axis} |
| 51 | | #' sets the axis text and line colors. To set the color of error bars, use \code{error.color}. For histograms, a vector of |
| 52 | | #' two colors would apply to the density line and bars separately (e.g., for \code{color =} \code{c('red','green')}, the |
| 53 | | #' density line would be red and the histogram bars would be green). See the \code{color.lock} and \code{color.offset} |
| 54 | | #' arguments for more color controls. |
| 55 | | #' @param colorby a variable or list of arguments used to set colors and the legend, alternatively to \code{by}. If |
| 56 | | #' \code{by} is not missing, \code{colorby} will be reduced to only the unique combinations of \code{by} and \code{colorby}. |
| 57 | | #' For example, if \code{by} is a participant ID with multiple observations per participant, and \code{by} is a condition |
| 58 | | #' ID which is the same for all observations from a given participant, \code{colorby} would assign a single color to each |
| 59 | | #' participant based on their condition. A list will be treated as a call to \code{\link{splot.color}}, so arguments can be |
| 60 | | #' entered positionally or by name. Data entered directly into splot can be accessed by position name preceded by a |
| 61 | | #' period. For example, \code{splot(rnorm(100),} \code{colorby=.y)} would draw a histogram, with bars colored by the value |
| 62 | | #' of \code{y} (\code{rnorm(100)} in this case). |
| 63 | | #' @param ... passes additional arguments to \code{\link[graphics]{par}} or \code{\link[graphics]{legend}}. Arguments before |
| 64 | | #' this can be named partially; those after must by fully named. |
| 65 | | #' @param colorby.leg logical; if \code{FALSE}, a legend for \code{colorby} is never drawn. Otherwise, a legend for |
| 66 | | #' \code{colorby} will be drawn if there is no specified \code{by}, or for non-scatter plots (overwriting the usual legend). |
| 67 | | #' @param color.lock logical; if \code{FALSE}, colors will not be adjusted to offset lines from points or histogram bars. |
| 68 | | #' @param color.offset how much points or histogram bars should be offset from the initial color used for lines. Default is |
| 69 | | #' 1.1; values greater than 1 lighten, and less than 1 darken. |
| 70 | | #' @param color.summary specifies the function used to collapse multiple colors for a single display. Either a string |
| 71 | | #' matching one of \code{'mean'} (which uses \code{\link{splot.colormean}} to average RGB values), \code{'median'} ( |
| 72 | | #' which treats codes as ordered, and selects that at the rounded median), or \code{'mode'} (which selects the most |
| 73 | | #' common code), or a function which takes color codes in its first argument, and outputs a single color code as a |
| 74 | | #' character. |
| 75 | | #' @param opacity a number between 0 and 1; sets the opacity of points, lines, and bars. Semi-opaque lines will sometimes |
| 76 | | #' not be displayed in the plot window, but will show up when the plot is written to a file. |
| 77 | | #' @param dark logical; if \code{TRUE}, sets text and axis colors to \code{"white"}. Defaults to the \code{splot.dark} |
| 78 | | #' option. |
| 79 | | #' @param x secondary variable, to be shown in on the x axis. If not specified, \code{type} will be set to \code{'density'}. |
| 80 | | #' If \code{x} is a factor or vector of characters, or has fewer than \code{lim} levels when treated as a factor, |
| 81 | | #' \code{type} will be set to \code{'line'} unless specified. |
| 82 | | #' @param by the 'splitting' variable within each plot, by which the plotted values of \code{x} and \code{y} will be |
| 83 | | #' grouped. |
| 84 | | #' @param between a single object or name, or two in a vector (e.g., \code{c(b1, b2)}), the levels of which will determine |
| 85 | | #' the number of plot windows to be shown at once (the cells in a matrix of plots; levels of the first variable as rows, |
| 86 | | #' and levels of the second as columns). |
| 87 | | #' @param cov additional variables used for adjustment. Bar and line plots include all \code{cov} variables in their |
| 88 | | #' regression models (via \code{\link[stats]{lm}}, e.g., \code{lm(y ~ 0 + x + cov1 + cov2)}) as covariates. Scatter plots |
| 89 | | #' with lines include all \code{cov} variables in the regression model to adjust the prediction line (e.g., |
| 90 | | #' \code{lm(y ~ x + x^2)}). |
| 91 | | #' \code{\link[graphics]{par}} options \code{col}, \code{mfrow}, \code{oma}, \code{mar}, \code{mgp}, \code{font.main}, |
| 92 | | #' \code{cex.main}, \code{font.lab}, \code{tcl}, \code{pch}, \code{lwd}, and \code{xpd} are all set within the function, |
| 93 | | #' but will be overwritten if they are included in the call. For example, \code{col} sets font colors in this case |
| 94 | | #' (as opposed to \code{colors} which sets line and point colors). The default is \code{'#303030'} for a nice dark grey, |
| 95 | | #' but maybe you want to lighten that up: \code{col='#606060'}. After arguments have been applied to |
| 96 | | #' \code{\link[graphics]{par}}, if any have not been used and match a \code{\link[graphics]{legend}} argument, these will |
| 97 | | #' be applied to \code{\link[graphics]{legend}}. |
| 98 | | #' @param line.type a character setting the style of line (e.g., with points at joints) to be drawn in line plots. Default |
| 99 | | #' is \code{'b'} if \code{error} is \code{FALSE}, and \code{'l'} otherwise. See the \code{line} argument of |
| 100 | | #' \code{\link[graphics]{plot.default}} for options. \code{line.type='c'} can look nice when there aren't a lot of |
| 101 | | #' overlapping error bars. |
| 102 | | #' @param mv.scale determines whether to center and scale multiple \code{y} variables. Does not center or scale by default. |
| 103 | | #' Anything other than \code{'none'} will mean center each numeric \code{y} variable. Anything matching \code{'^t|z|sc'} |
| 104 | | #' will also scale. |
| 105 | | #' @param mv.as.x logical; if \code{TRUE}, variable names are displayed on the x axis, and \code{x} is treated as \code{by}. |
| 106 | | #' @param save logical; if \code{TRUE}, an image of the plot is saved to the current working directory. |
| 107 | | #' @param format the type of file to save plots as. Default is \code{cairo_pdf}; see |
| 108 | | #' \code{\link[grDevices]{Devices}} for options. |
| 109 | | #' @param dims a vector of 2 values (\code{c(width, height)}) specifying the dimensions of a plot to save in inches or |
| 110 | | #' pixels depending on \code{format}. Defaults to the dimensions of the plot window. |
| 111 | | #' @param file.name a string with the name of the file to be save (excluding the extension, as this is added depending on |
| 112 | | #' \code{format}). |
| 113 | | #' @param myl sets the range of the y axis (\code{ylim} of \code{\link{plot}} or \code{\link[graphics]{barplot}}). |
| 114 | | #' If not specified, this will be calculated from the data. |
| 115 | | #' @param mxl sets the range of the x axis (\code{xlim} of \code{\link{plot}}). If not specified, this will be |
| 116 | | #' calculated from the data. |
| 117 | | #' @param autori logical; if \code{FALSE}, the origin of plotted bars will be set to 0. Otherwise, bars are adjusted such |
| 118 | | #' that they extend to the bottom of the y axis. |
| 119 | | #' @param xlas,ylas numeric; sets the orientation of the x- and y-axis labels. See \code{\link[graphics]{par}}. |
| 120 | | #' @param xaxis,yaxis logical; if \code{FALSE}, the axis will not be drawn. |
| 121 | | #' @param breaks determines the width of histogram bars. See \code{\link[graphics]{hist}}. |
| 122 | | #' @param density.fill logical; \code{FALSE} will turn off polygon fills when they are displayed, \code{TRUE} will replace |
| 123 | | #' histograms with polygons. |
| 124 | | #' @param density.opacity opacity of the density polygons, between 0 and 1. |
| 125 | | #' @param density.args list of arguments to be passed to \code{\link[stats]{density}}. |
| 126 | | #' @param leg sets the legend inside or outside the plot frames (when a character matching \code{'^i'}, or a character |
| 127 | | #' matching \code{'^o'} or a number respectively), or turns it off (when \code{FALSE}). When inside, a legend is drawn in |
| 128 | | #' each plot frame. When outside, a single legend is drawn either to the right of all plot frames, or within an empty |
| 129 | | #' plot frame. By default, this will be determined automatically, tending to set legends outside when there are multiple |
| 130 | | #' levels of \code{between}. A number will try and set the legend in an empty frame within the grid of plot frames. If |
| 131 | | #' there are no empty frames, the legend will just go to the side as if \code{leg='outside'}. |
| 132 | | #' @param lpos sets the position of the legend within its frame (whether inside or outside of the plot frames) based on |
| 133 | | #' keywords (see \code{\link[graphics]{legend}}. By default, when the legend is outside, \code{lpos} is either |
| 134 | | #' \code{'right'} when the legend is in a right-hand column, or \code{'center'} when in an empty plot frame. When the |
| 135 | | #' legend is inside and \code{lpos} is not specified, the legend will be placed automatically based on the data. Set to |
| 136 | | #' \code{'place'} to manually place the legend; clicking the plot frame will set the top left corner of the legend. |
| 137 | | #' @param lvn level variable name. Logical: if \code{FALSE}, the names of by and between variables will not be shown |
| 138 | | #' before their level (e.g., for a sex variable with a "female" level, "sex: female" would become "female" above each |
| 139 | | #' plot window). |
| 140 | | #' @param leg.title sets the title of the legend (which is the by variable name by default), or turns it off with |
| 141 | | #' \code{FALSE}. |
| 142 | | #' @param leg.args a list passing arguments to the \code{\link[graphics]{legend}} call. |
| 143 | | #' @param title logical or a character: if \code{FALSE}, the main title is turned off. If a character, this will be shown |
| 144 | | #' as the main title. |
| 145 | | #' @param labx,laby logical or a character: if \code{FALSE}, the label on the x axis is turned off. If a character, this |
| 146 | | #' will be shown as the axis label. |
| 147 | | #' @param lty logical or a vector: if \code{FALSE}, lines are always solid. If a vector, changes line type based on each |
| 148 | | #' value. Otherwise loops through available line types, see \code{\link[graphics]{par}}. |
| 149 | | #' @param lwd numeric; sets the weight of lines in line, density, and scatter plots. Default is 2. See |
| 150 | | #' \code{\link[graphics]{par}}. |
| 151 | | #' @param sub affects the small title above each plot showing \code{between} levels; text replaces it, and \code{FALSE} |
| 152 | | #' turns it off. |
| 153 | | #' @param note logical; if \code{FALSE}, the note at the bottom about splits and/or lines or error bars is turned off. |
| 154 | | #' @param font named numeric vector: \code{c(title,sud,leg,leg.title,note)}. Sets the font of the title, su display, legend |
| 155 | | #' levels and title, and note. In addition, \code{font.lab} sets the x and y label font, \code{font.sub} sets the font of |
| 156 | | #' the little title in each panel, \code{font.axis} sets the axis label font, and \code{font.main} sets the between level/n |
| 157 | | #' heading font; these are passed to \code{\link[graphics]{par}}. See the input section. |
| 158 | | #' @param cex named numeric vector: \code{c(title,sud,leg,note,points)}. Sets the font size of the title, su display, legend, |
| 159 | | #' note, and points. In addition, \code{cex.lab} sets the x and y label size, \code{cex.sub} sets the size of the little |
| 160 | | #' title in each panel, \code{cex.axis} sets the axis label size, and \code{cex.main} sets the between level/n heading size; |
| 161 | | #' these are passed to \code{\link[graphics]{par}}. See the input section. |
| 162 | | #' @param sud affects the heading for subset and covariates/line adjustments (su display); text replaces it, and |
| 163 | | #' \code{FALSE} turns it off. |
| 164 | | #' @param ndisp logical; if \code{FALSE}, n per level is no longer displayed in the subheadings. |
| 165 | | #' @param labels logical; if \code{FALSE}, sets all settable text surrounding the plot to \code{FALSE} (just so you don't |
| 166 | | #' have to set all of them if you want a clean frame). |
| 167 | | #' @param labels.filter a regular expression string to be replaced in label texts with a blank space. Default is |
| 168 | | #' \code{'_'}, so underscores appearing in the text of labels are replace with blank spaces. Set to |
| 169 | | #' \code{FALSE} to prevent all filtering. |
| 170 | | #' @param labels.trim numeric or logical; the maximum length of label texts (in number of characters). Default is 20, with |
| 171 | | #' any longer labels being trimmed. Set to \code{FALSE} to prevent any trimming. |
| 172 | | #' @param points logical; if \code{FALSE}, the points in a scatter plot are no longer drawn. |
| 173 | | #' @param points.first logical; if \code{FALSE}, points are plotted after lines are drawn in a scatter plot, placing lines |
| 174 | | #' behind points. This does not apply to points or lines added in \code{add}, as that is always evaluated after the main |
| 175 | | #' points and lines are drawn. |
| 176 | | #' @param byx logical; if \code{TRUE} (default) and \code{by} is specified, regressions for bar or line plots compare |
| 177 | | #' levels of \code{by} for each level of \code{x}. This makes for more intuitive error bars when comparing levels of |
| 178 | | #' \code{by} within a level of \code{x}; otherwise, the model is comparing the difference between the first level of |
| 179 | | #' \code{x} and each of its other levels. |
| 180 | | #' @param drop named logical vector: \code{c(x,by,bet)}. Specifies how levels with no data should be treated. All are |
| 181 | | #' \code{TRUE} by default, meaning only levels with data will be presented, and the layout of \code{between} levels |
| 182 | | #' will be minimized. \code{x} only applies to bar or line plots. \code{by} relates to levels presented in the legend. |
| 183 | | #' If \code{bet} is \code{FALSE}, the layout of \code{between} variables will be strict, with levels of \code{between[1]} |
| 184 | | #' as rows, and levels of \code{between[2]} as columns -- if there are no data at an intersection of levels, the |
| 185 | | #' corresponding panel will be blank. See the input section. |
| 186 | | #' @param prat panel ratio, referring to the ratio between plot frames and the legend frame when the legend is out. A |
| 187 | | #' single number will make all panels of equal width. A vector of two numbers will adjust the ratio between plot panels |
| 188 | | #' and the legend panel. For example, \code{prat=c(3,1)} makes all plot panels a relative width of 3, and the legend frame |
| 189 | | #' a relative width of 1. |
| 190 | | #' @param check.height logical; if \code{FALSE}, the height of the plot frame will not be checked before plotting is |
| 191 | | #' attempted. The check tries to avoid later errors, but may prevent plotting when a plot is possible. |
| 192 | | #' @param model logical; if \code{TRUE}, the summary of an interaction model will be printed. This model won't always align |
| 193 | | #' with what is plotted since variables may be treated differently, particularly in the case of interactions. |
| 194 | | #' @param options a list with named arguments, useful for setting temporary defaults if you plan on using some of the same |
| 195 | | #' options for multiple plots (e.g., \code{opt = list(}\code{type = 'bar',} \code{colors = 'grey',} |
| 196 | | #' \code{bg = '#999999');} \code{splot(x ~ y,} \code{options = opt)}). |
| 197 | | #' use \code{\link{quote}} to include options that are to be evaluated within the function (e.g., |
| 198 | | #' \code{opt =} \code{list(su =} \code{quote(y > 0))}). |
| 199 | | #' @param add evaluated within the function, so you can refer to the objects that are returned, to variable names (those |
| 200 | | #' from an entered data frame or entered as arguments), or entered data by their position, preceded by '.' (e.g., |
| 201 | | #' \code{mod =} \code{lm(.y~.x)}). Useful for adding things like lines to a plot while the parameters are still |
| 202 | | #' those set by the function (e.g., \code{add =} \code{abline(v =} \code{mean(x),} \code{xpd = FALSE)} for a vertical |
| 203 | | #' line at the mean of x). |
| 204 | | #' |
| 205 | | #' @return A list containing data and settings is invisibly returned, which might be useful to check for errors. |
| 206 | | #' Each of these objects can also be pulled from within \code{add}: |
| 207 | | #' \tabular{ll}{ |
| 208 | | #' \code{dat} \tab a \code{data.frame} of processed, unsegmented data.\cr |
| 209 | | #' \code{cdat} \tab a \code{list} of \code{list}s of \code{data.frame}s of processed, segmented data.\cr |
| 210 | | #' \code{txt} \tab a \code{list} of variable names. used mostly to pull variables from \code{data} or the environment.\cr |
| 211 | | #' \code{ptxt} \tab a \code{list} of processed variable and level names. Used mostly for labeling.\cr |
| 212 | | #' \code{seg} \tab a \code{list} containing segmentation information (such as levels) for each variable.\cr |
| 213 | | #' \code{ck} \tab a \code{list} of settings.\cr |
| 214 | | #' \code{lega} \tab a \code{list} of arguments that were or would have been passed to \code{\link[graphics]{legend}}.\cr |
| 215 | | #' \code{fmod} \tab an \code{lm} object if \code{model} is \code{TRUE}, and the model succeeded. |
| 216 | | #' } |
| 217 | | #' |
| 218 | | #' @section Input: |
| 219 | | #' \strong{formulas} |
| 220 | | #' |
| 221 | | #' When \code{y} is a formula (has a \code{~}), other variables will be pulled from it: |
| 222 | | #' |
| 223 | | #' \code{y ~ x * by * between[1] * between[2] + cov[1] + cov[2] + cov[n]} |
| 224 | | #' |
| 225 | | #' If \code{y} has multiple variables, \code{by} is used to identify the variable (it becomes a factor with variable names |
| 226 | | #' as levels), so anything entered as \code{by} is treated as \code{between[1]}, \code{between[1]} is moved to |
| 227 | | #' \code{between[2]}, and \code{between[2]} is discarded with a message. |
| 228 | | #' |
| 229 | | #' \strong{named vectors} |
| 230 | | #' |
| 231 | | #' Named vector arguments like \code{font}, \code{cex}, and \code{drop} can be set with a single value, positionally, or |
| 232 | | #' with names. If a single value is entered (e.g., \code{drop = FALSE}), this will be applied to each level (i.e., |
| 233 | | #' \code{c(x = FALSE, by = FALSE, bet = FALSE)}). If more than one value is entered, these will be treated positionally |
| 234 | | #' (e.g., \code{cex =} \code{c(2, 1.2)} would be read as \code{c(title = 2, sud = 1.2, leg = .9, note = .7, points = 1)}). |
| 235 | | #' If values are named, only named values will be set, with other defaults retained (e.g., \code{cex =} \code{c(note = 1.2)} |
| 236 | | #' would be read as \code{c(title = 1.5, sud = .9, leg = .9, note = 1.2, points = 1)}). |
| 237 | | #' |
| 238 | | #' @note |
| 239 | | #' \strong{x-axis levels text} |
| 240 | | #' |
| 241 | | #' If the text of x-axis levels (those corresponding to the levels of \code{x}) are too long, they are hidden before |
| 242 | | #' overlapping. To try and avoid this, by default longer texts are trimmed (dictated by \code{labels.trim}), and at some |
| 243 | | #' point the orientation of level text is changed (settable with \code{xlas}), but you may still see level text missing. |
| 244 | | #' To make these visible, you can reduce \code{labels.trim} from the default of 20 (or rename the levels of that variable), |
| 245 | | #' make the level text vertical (\code{xlas = 3}), or expand your plot window if possible. |
| 246 | | #' |
| 247 | | #' \strong{missing levels, lines, and/or error bars} |
| 248 | | #' |
| 249 | | #' By default (if \code{drop = TRUE}), levels of \code{x} with no data are dropped, so you may not see every level of your |
| 250 | | #' variable, at all or at a level of \code{by} or \code{between}. Sometimes error bars cannot be estimated (if, say, there |
| 251 | | #' is only one observation at the given level), but lines are still drawn in these cases, so you may sometimes see levels |
| 252 | | #' without error bars even when error bars are turned on. Sometimes (particularly when \code{drop['x']} is \code{FALSE}), |
| 253 | | #' you might see floating error bars with no lines drawn to them, or what appear to be completely empty levels. This |
| 254 | | #' happens when there is a missing level of \code{x} between two non-missing levels, potentially making an orphaned level |
| 255 | | #' (if a non-missing level is surrounded by missing levels). If there are no error bars for this orphaned level, by default |
| 256 | | #' nothing will be drawn to indicate it. If you set \code{line.type} to \code{'b'} (or any other type with points), a point |
| 257 | | #' will be drawn at such error-bar-less, orphaned levels. |
| 258 | | #' |
| 259 | | #' \strong{unexpected failures} |
| 260 | | #' |
| 261 | | #' splot tries to clean up after itself in the case of an error, but you may still run into errors that break things before |
| 262 | | #' this can happen. If after a failed plot you find that you're unable to make any new plots, or new plots are drawn over |
| 263 | | #' old ones, you might try entering \code{dev.off()} into the console. If new plots look off (splot's |
| 264 | | #' \code{\link[graphics]{par}} settings didn't get reset), you may have to close the plot window to reset |
| 265 | | #' \code{\link[graphics]{par}} (if you're using RStudio, Plots > "Remove Plot..." or "Clear All..."), or restart R. |
| 266 | | #' |
| 267 | | #' @examples |
| 268 | | #' # simulating data |
| 269 | | #' n <- 2000 |
| 270 | | #' dat <- data.frame(sapply(c("by", "bet1", "bet2"), function(c) sample(0:1, n, TRUE))) |
| 271 | | #' dat$x <- with( |
| 272 | | #' dat, |
| 273 | | #' rnorm(n) + by * -.4 + by * bet1 * -.3 + by * bet2 * |
| 274 | | #' .3 + bet1 * bet2 * .9 - .8 + rnorm(n, 0, by) |
| 275 | | #' ) |
| 276 | | #' dat$y <- with( |
| 277 | | #' dat, |
| 278 | | #' x * .2 + by * .3 + bet2 * -.6 + bet1 * bet2 * .8 + x * |
| 279 | | #' by * bet1 * -.5 + x * by * bet1 * bet2 * -.5 |
| 280 | | #' + rnorm(n, 5) + rnorm(n, -1, .1 * x^2) |
| 281 | | #' ) |
| 282 | | #' |
| 283 | | #' # looking at the distribution of y between bets split by by |
| 284 | | #' splot(y, by = by, between = c(bet1, bet2), data = dat) |
| 285 | | #' |
| 286 | | #' # looking at quantile splits of y in y by x |
| 287 | | #' splot(y ~ x * y, dat, split = "quantile") |
| 288 | | #' |
| 289 | | #' # looking at y by x between bets |
| 290 | | #' splot(y ~ x, dat, between = c(bet1, bet2)) |
| 291 | | #' |
| 292 | | #' # sequentially adding levels of split |
| 293 | | #' splot(y ~ x * by, dat) |
| 294 | | #' splot(y ~ x * by * bet1, dat) |
| 295 | | #' splot(y ~ x * by * bet1 * bet2, dat) |
| 296 | | #' |
| 297 | | #' # same as the last but entered by name |
| 298 | | #' splot(y, x = x, by = by, between = c(bet1, bet2), data = dat) |
| 299 | | #' |
| 300 | | #' # zooming in on one of the windows |
| 301 | | #' splot(y ~ x * by, dat, bet1 == 1 & bet2 == 0) |
| 302 | | #' |
| 303 | | #' # comparing an adjusted lm prediction line with a loess line |
| 304 | | #' # this could also be entered as y ~ poly(x,3) |
| 305 | | #' splot(y ~ x + x^2 + x^3, dat, bet1 == 1 & bet2 == 0 & by == 1, add = { |
| 306 | | #' lines(x[order(x)], loess(y ~ x)$fitted[order(x)], lty = 2) |
| 307 | | #' legend("topright", c("lm", "loess"), lty = c(1, 2), lwd = c(2, 1), bty = "n") |
| 308 | | #' }) |
| 309 | | #' |
| 310 | | #' # looking at different versions of x added to y |
| 311 | | #' splot(cbind( |
| 312 | | #' Raw = y + x, |
| 313 | | #' Sine = y + sin(x), |
| 314 | | #' Cosine = y + cos(x), |
| 315 | | #' Tangent = y + tan(x) |
| 316 | | #' ) ~ x, dat, myl = c(-10, 15), lines = "loess", laby = "y + versions of x") |
| 317 | | #' |
| 318 | | #' @export |
| 319 | | #' @importFrom grDevices grey dev.copy dev.size dev.off cairo_pdf adjustcolor colors col2rgb |
| 320 | | #' @importFrom graphics axis axTicks hist legend lines text mtext plot barplot par points arrows strwidth layout plot.new |
| 321 | | #' locator strheight polygon abline |
| 322 | | #' @importFrom stats density median quantile sd lm glm confint update loess smooth.spline formula as.formula predict |
| 323 | | #' var binomial |
| 324 | | |
| 325 | | splot <- function( |
| 326 | | y, data = NULL, su = NULL, type = "", split = "median", levels = list(), sort = NULL, |
| 327 | | error = "standard", error.color = "#585858", error.lwd = 2, lim = 9, lines = TRUE, ..., |
| 328 | | colors = "pastel", colorby = NULL, colorby.leg = TRUE, color.lock = FALSE, color.offset = 1.1, |
| 329 | | color.summary = "mean", opacity = 1, dark = getOption("splot.dark", FALSE), x = NULL, |
| 330 | | by = NULL, between = NULL, cov = NULL, line.type = "l", mv.scale = "none", mv.as.x = FALSE, |
| 331 | | save = FALSE, format = cairo_pdf, dims = dev.size(), file.name = "splot", myl = NULL, |
| 332 | | mxl = NULL, autori = TRUE, xlas = 0, ylas = 1, xaxis = TRUE, yaxis = TRUE, breaks = "sturges", |
| 333 | | density.fill = TRUE, density.opacity = .4, density.args = list(), leg = "outside", |
| 334 | | lpos = "auto", lvn = TRUE, leg.title = TRUE, leg.args = list(), title = TRUE, labx = TRUE, |
| 335 | | laby = TRUE, lty = TRUE, lwd = 2, sub = TRUE, ndisp = TRUE, note = TRUE, |
| 336 | | font = c(title = 2, sud = 1, leg = 1, leg.title = 2, note = 3), |
| 337 | | cex = c(title = 1.5, sud = .9, leg = .9, note = .7, points = 1), sud = TRUE, labels = TRUE, |
| 338 | | labels.filter = "_", labels.trim = 20, points = TRUE, points.first = TRUE, byx = TRUE, |
| 339 | | drop = c(x = TRUE, by = TRUE, bet = TRUE), prat = c(1, 1), check.height = TRUE, model = FALSE, |
| 340 | | options = NULL, add = NULL) { |
| 341 | | # parsing input and preparing data |
| 342 | 33x | if (check.height && dev.size()[2] < 1.7) { |
| 343 | ! | stop("the plot window seems too short; increase the height of the plot window, or set check.height to FALSE", |
| 344 | ! | call. = FALSE |
| 345 | | ) |
| 346 | | } |
| 347 | 33x | if (!missing(options) && is.list(options) && length(options) != 0) { |
| 348 | ! | a <- as.list(match.call())[-1] |
| 349 | ! | options <- tryCatch(options, error = function(e) NULL) |
| 350 | ! | if (is.null(options)) stop("could not find options") |
| 351 | ! | return(do.call(splot, c(a[names(a) != "options"], options[!names(options) %in% names(a)]), envir = parent.frame())) |
| 352 | | } |
| 353 | ! | if (!labels) title <- sud <- sub <- labx <- laby <- note <- FALSE |
| 354 | 33x | opt_saf <- getOption("stringsAsFactors") |
| 355 | 33x | on.exit(options(stringsAsFactors = opt_saf)) |
| 356 | 33x | options(stringsAsFactors = FALSE) |
| 357 | 33x | ck <- list( |
| 358 | 33x | ff = list(bet = FALSE, cov = FALSE), |
| 359 | 33x | t = if (grepl("^b|^l", type, TRUE)) 1 else if (grepl("^d", type, TRUE)) 2 else 3, |
| 360 | 33x | b = grepl("^b", type, TRUE), |
| 361 | 33x | tt = !missing(type) && !grepl("^b|^l", type, TRUE), |
| 362 | 33x | d = !missing(data) && !is.null(data), |
| 363 | 33x | su = !missing(su), |
| 364 | 33x | c = !missing(cov), |
| 365 | 33x | co = missing(colors), |
| 366 | 33x | cb = !missing(colorby), |
| 367 | 33x | cblegm = missing(colorby.leg), |
| 368 | 33x | cbleg = is.logical(colorby.leg) && colorby.leg, |
| 369 | 33x | poly = missing(density.fill) || (!is.logical(density.fill) || density.fill), |
| 370 | 33x | polyo = !missing(density.fill) || !missing(density.opacity), |
| 371 | 33x | e = grepl("^s", error, TRUE), |
| 372 | 33x | el = !(is.logical(error) && !error), |
| 373 | 33x | sp = if (!is.character(split)) { |
| 374 | ! | 4 |
| 375 | 33x | } else if (grepl("^mea|^av", split, TRUE)) { |
| 376 | 2x | 1 |
| 377 | 33x | } else if (grepl("^q", split, TRUE)) { |
| 378 | ! | 2 |
| 379 | | } else { |
| 380 | 31x | ifelse(grepl("^s", split, TRUE), 3, 4) |
| 381 | | }, |
| 382 | 33x | ly = !(is.logical(laby) && !laby) || is.character(laby), |
| 383 | 33x | lys = is.character(laby), |
| 384 | 33x | lx = !(is.logical(labx) && !labx) || is.character(labx), |
| 385 | 33x | line = substitute(lines), |
| 386 | 33x | lty = is.logical(lty), |
| 387 | 33x | ltym = missing(lty), |
| 388 | 33x | ltm = missing(line.type), |
| 389 | 33x | leg = if (is.logical(leg) && !leg) 0 else if (!is.character(leg) || grepl("^o", leg, TRUE)) 1 else 2, |
| 390 | 33x | legm = missing(leg), |
| 391 | 33x | legt = !(is.logical(leg.title) && !leg.title), |
| 392 | 33x | lp = is.character(lpos) && grepl("^a", lpos, TRUE), |
| 393 | 33x | lpm = is.character(lpos) && grepl("^p|^m", lpos, TRUE), |
| 394 | 33x | mod = !missing(x) && model, |
| 395 | 33x | note = !is.character(note), |
| 396 | 33x | mv = FALSE, |
| 397 | 33x | mlvn = missing(lvn), |
| 398 | 33x | opacity = !missing(opacity) && opacity <= 1 && opacity > 0, |
| 399 | 33x | mai = FALSE |
| 400 | | ) |
| 401 | ! | if (ck$lpm) lpos <- "center" |
| 402 | 33x | if (ck$d && !is.data.frame(data)) { |
| 403 | ! | data <- if (!is.matrix(data) && !is.list(data)) { |
| 404 | ! | as.data.frame(as.matrix(data)) |
| 405 | | } else { |
| 406 | ! | as.data.frame(data) |
| 407 | | } |
| 408 | | } |
| 409 | 33x | ck$ltck <- (is.logical(ck$line) && ck$line) || !grepl("^F", ck$line) |
| 410 | ! | if (!ck$ltck && ck$note) note <- FALSE |
| 411 | 33x | ck$ltco <- if (ck$ltck) if (is.logical(ck$line) || ck$c || grepl("^li|^lm|^st", ck$line, TRUE)) "li" else if (grepl("^loe|^po|^cu", ck$line, TRUE)) "lo" else if (grepl("^sm|^sp|^in", ck$line, TRUE)) "sm" else if (grepl("^e|^co|^d", ck$line, TRUE)) "e" else if (grepl("^pr|^log", ck$line, TRUE)) "pr" else "li" else "li" |
| 412 | 33x | if (any(!missing(font), !missing(cex), !missing(drop))) { |
| 413 | ! | dop <- formals(splot)[c("font", "cex", "drop")] |
| 414 | ! | oco <- function(s, d) { |
| 415 | ! | od <- d <- eval(d) |
| 416 | ! | if (length(s) != length(d)) { |
| 417 | ! | n <- NULL |
| 418 | ! | if (!is.null(n <- names(s)) || length(s) != 1) if (!is.null(n)) d[n] <- s[n] else d[seq_along(s)] <- s else d[] <- s |
| 419 | ! | s <- d |
| 420 | | } |
| 421 | ! | s <- s[names(od)] |
| 422 | ! | names(s) <- names(od) |
| 423 | ! | if (any(n <- is.na(s))) s[n] <- od[n] |
| 424 | ! | s |
| 425 | | } |
| 426 | ! | if (!missing(font)) font <- oco(font, dop$font) |
| 427 | ! | if (!missing(cex)) cex <- oco(cex, dop$cex) |
| 428 | ! | if (!missing(drop)) drop <- oco(drop, dop$drop) |
| 429 | | } |
| 430 | 33x | dn <- if (ck$d) names(data) else "" |
| 431 | 33x | if (any(grepl("~", c(substitute(y), if (paste(deparse(substitute(y)), collapse = "") %in% ls(envir = globalenv())) y), fixed = TRUE))) { |
| 432 | 24x | f <- as.character(as.formula(y))[-1] |
| 433 | 24x | y <- as.formula(y)[[2]] |
| 434 | 24x | bl <- function(x) { |
| 435 | 24x | cs <- strsplit(x, "")[[1]] |
| 436 | 24x | rs <- lapply(c("(", ")", "[", "]"), grep, cs, fixed = TRUE) |
| 437 | 24x | l <- vapply(rs, length, 0) |
| 438 | 24x | cr <- TRUE |
| 439 | 24x | if (any(l != 0)) { |
| 440 | ! | if (l[1] != l[2] || l[3] != l[4]) stop("invalid parentheses or brackets in ", x) |
| 441 | ! | cr <- !seq_along(cs) %in% c( |
| 442 | ! | unlist(lapply(seq_len(l[1]), function(r) do.call(seq, lapply(rs[1:2], "[[", r)))), |
| 443 | ! | unlist(lapply(seq_len(l[3]), function(r) do.call(seq, lapply(rs[3:4], "[[", r)))) |
| 444 | | ) |
| 445 | | } |
| 446 | 24x | cs[cr] <- sub("*", "_VAR_", sub("+", "_COV_", cs[cr], fixed = TRUE), fixed = TRUE) |
| 447 | 24x | paste(cs, collapse = "") |
| 448 | | } |
| 449 | 24x | f <- strsplit(bl(f[-1]), " _COV_ ", fixed = TRUE)[[1]] |
| 450 | 24x | if (any(grepl(" _VAR_ ", f, fixed = TRUE))) { |
| 451 | 7x | r <- strsplit(f[1], " _VAR_ ", fixed = TRUE)[[1]] |
| 452 | 7x | if (length(r)) x <- r[1] |
| 453 | 7x | if (length(r) > 1) by <- r[2] |
| 454 | 7x | if (length(r) > 2) { |
| 455 | 4x | ck$ff$bet <- TRUE |
| 456 | 4x | between <- r[3] |
| 457 | | } |
| 458 | 2x | if (length(r) > 3) between <- c(r[3], r[4]) |
| 459 | 7x | f <- f[!grepl(" _VAR_ ", f, fixed = TRUE)] |
| 460 | | } else { |
| 461 | 17x | x <- f[1] |
| 462 | 17x | f <- f[-1] |
| 463 | | } |
| 464 | 24x | if (length(f)) { |
| 465 | ! | cov <- f |
| 466 | ! | ck$c <- ck$ff$cov <- TRUE |
| 467 | | } |
| 468 | | } |
| 469 | 33x | txt <- list( |
| 470 | 33x | split = "none", |
| 471 | 33x | y = substitute(y), |
| 472 | 33x | x = substitute(x), |
| 473 | 33x | by = substitute(by), |
| 474 | 33x | bet = as.list(substitute(between)), |
| 475 | 33x | cov = as.list(substitute(cov)), |
| 476 | 33x | su = deparse(substitute(su)) |
| 477 | | ) |
| 478 | 33x | txt[c("bet", "cov")] <- lapply(c("bet", "cov"), function(l) { |
| 479 | 66x | paste(if (!ck$ff[[l]] && length(txt[[l]]) > 1) txt[[l]][-1] else txt[[l]]) |
| 480 | | }) |
| 481 | 33x | txt <- lapply(txt, function(e) if (is.call(e)) paste(deparse(e), collapse = "\n") else e) |
| 482 | ! | if (length(txt$bet) > 2) txt$bet <- txt$bet[1:2] |
| 483 | 33x | tdc <- function(x, l = NULL) { |
| 484 | 90x | if (!is.call(x)) { |
| 485 | 89x | if ((is.null(l) && length(x) != 1) || (!is.null(l) && length(x) == l)) { |
| 486 | ! | return(x) |
| 487 | | } |
| 488 | | } |
| 489 | 60x | if (is.character(x)) x <- parse(text = x) |
| 490 | 90x | tx <- tryCatch(eval(x, data, parent.frame(2)), error = function(e) NULL) |
| 491 | 90x | if (is.character(tx) && length(tx) < 2) { |
| 492 | ! | x <- parse(text = tx) |
| 493 | ! | tx <- tryCatch(eval(x, data, parent.frame(2)), error = function(e) NULL) |
| 494 | ! | } else if (is.null(tx)) tx <- tryCatch(eval(x, data, parent.frame(3)), error = function(e) NULL) |
| 495 | ! | if (is.null(tx) || any(class(tx) %in% c("name", "call", "expression", "function"))) stop("could not find ", x, call. = FALSE) |
| 496 | 90x | if (!is.null(l) && is.null(ncol(tx))) { |
| 497 | 56x | if (length(tx) != l) { |
| 498 | ! | tx <- rep_len(tx, l) |
| 499 | ! | if (is.call(x)) x <- deparse(x) |
| 500 | ! | warning(x, " is not the same length as y", call. = FALSE) |
| 501 | | } |
| 502 | | } |
| 503 | ! | if (!is.null(dim(tx)) && !is.matrix(tx) && !is.data.frame(tx)) tx <- as.matrix(tx) |
| 504 | 90x | tx |
| 505 | | } |
| 506 | 33x | if (!missing(data) && !any(class(data) %in% c("matrix", "data.frame"))) { |
| 507 | ! | data <- if (is.character(data)) eval(parse(text = data)) else eval(data, globalenv()) |
| 508 | | } |
| 509 | 33x | dat <- data.frame(y = tdc(txt$y), check.names = FALSE) |
| 510 | 32x | if (ncol(dat) == 1) names(dat) <- "y" |
| 511 | 33x | nr <- nrow(dat) |
| 512 | 33x | lvs <- function(x, s = FALSE) if (is.factor(x)) base::levels(x) else if (s) sort(unique(x[!is.na(x)])) else unique(x[!is.na(x)]) |
| 513 | 33x | for (n in names(txt)[-c(1, 2, 7)]) { |
| 514 | 132x | l <- length(txt[[n]]) |
| 515 | 81x | if (l == 0) next |
| 516 | 51x | if (l == nr) { |
| 517 | ! | dat[, n] <- txt[[n]] |
| 518 | ! | txt[[n]] <- n |
| 519 | 5x | } else if (l == 1) dat[, n] <- tdc(txt[[n]], nr) else for (i in seq_along(txt[[n]])) dat[, paste0(n, ".", i)] <- tdc(txt[[n]][[i]], nr) |
| 520 | | } |
| 521 | ! | if (length(txt$y) == nr) txt$y <- "y" |
| 522 | 33x | if (missing(x) && !is.null(dat$y) && !is.numeric(dat$y)) { |
| 523 | 1x | dat$x <- dat$y |
| 524 | 1x | sl <- grepl("^(y|by|bet[.12]{,2})$", colnames(dat)) |
| 525 | 1x | dat$y <- if (sum(sl) == 1) dat[, sl] else do.call(paste, dat[, sl]) |
| 526 | 1x | dat$y <- table(dat$y)[dat$y] |
| 527 | ! | if (sum(sl) != 1) dat <- dat[, c("y", "x", colnames(dat)[!colnames(dat) %in% c("y", "x")])] |
| 528 | 1x | if (ck$t != 2) txt[c("y", "x")] <- c("count", txt$y) |
| 529 | 1x | ck$el <- FALSE |
| 530 | 1x | if (missing(type)) { |
| 531 | 1x | ck$b <- TRUE |
| 532 | 1x | ck$t <- 1 |
| 533 | 1x | ck[c("b", "t", "tt")] <- list(TRUE, 1, FALSE) |
| 534 | | } |
| 535 | 1x | if (missing(autori)) autori <- FALSE |
| 536 | | } |
| 537 | 33x | if (NCOL(dat$x) > 1) { |
| 538 | ! | ck$c <- TRUE |
| 539 | ! | txt$cov <- c(txt$x, txt$cov) |
| 540 | ! | dat$cov <- cbind(dat$cov, dat$x[, -1]) |
| 541 | ! | dat$x <- dat$x[, 1] |
| 542 | | } |
| 543 | 33x | ck$orn <- nr |
| 544 | 33x | su <- substitute(su) |
| 545 | 33x | if (ck$su && length(su) != nr) { |
| 546 | ! | tsu <- tryCatch(eval(su, if (ck$d) data), error = function(e) NULL) |
| 547 | ! | if (is.null(tsu) || length(tsu) != nr) { |
| 548 | ! | odat <- dat |
| 549 | ! | colnames(odat) <- sub("^y\\.", "", colnames(dat)) |
| 550 | ! | tsu <- tryCatch(eval(su, odat), error = function(e) NULL) |
| 551 | | } |
| 552 | ! | if (!is.null(tsu)) { |
| 553 | ! | tsu[is.na(tsu)] <- FALSE |
| 554 | ! | su <- tsu |
| 555 | | } |
| 556 | ! | if (is.logical(tsu) && sum(tsu) == 0 || length(tsu) == 0) { |
| 557 | ! | ck$su <- FALSE |
| 558 | ! | warning("su excludes all rows, so it was ignored.", .call = FALSE) |
| 559 | | } |
| 560 | | } |
| 561 | 33x | tsu <- vapply(dat, is.numeric, TRUE) |
| 562 | 33x | ck$omitted <- list( |
| 563 | 33x | na = apply(dat, 1, function(r) any(is.na(r))), |
| 564 | 33x | inf = apply(dat[, tsu, drop = FALSE], 1, function(r) any(is.infinite(r))) |
| 565 | | ) |
| 566 | ! | if (ck$su) ck$omitted$su <- !su |
| 567 | 33x | ck$omitted$all <- !Reduce("|", ck$omitted) |
| 568 | 33x | if (any(!ck$omitted$all)) { |
| 569 | ! | if (any(ck$omitted$all)) { |
| 570 | ! | odat <- dat[ck$omitted$all, , drop = FALSE] |
| 571 | ! | dat <- odat |
| 572 | ! | dn <- colnames(dat) |
| 573 | ! | if ("x" %in% dn && length(unique(dat$x)) == 1) { |
| 574 | ! | ck$t <- 2 |
| 575 | ! | dat$x <- NULL |
| 576 | ! | warning("after omitting, x only had 1 level, so it was dropped") |
| 577 | | } |
| 578 | ! | if ("by" %in% dn && length(unique(dat$by)) == 1) { |
| 579 | ! | txt$by <- dat$by <- NULL |
| 580 | ! | warning("after omitting, by only had 1 level, so it was dropped") |
| 581 | | } |
| 582 | ! | if (ck$d) data <- data[ck$omitted$all, , drop = FALSE] |
| 583 | | } else { |
| 584 | ! | stop("this combination of variables/splits has no complete cases") |
| 585 | | } |
| 586 | | } |
| 587 | 33x | dn <- colnames(dat) |
| 588 | 33x | nr <- nrow(dat) |
| 589 | 33x | if (sum(grepl("^y", dn)) > 1) { |
| 590 | | # setting up multiple y variables |
| 591 | 1x | dn <- grep("^y\\.", dn) |
| 592 | 1x | ck$mvn <- colnames(dat)[dn] |
| 593 | 1x | ck$mvnl <- length(ck$mvn) |
| 594 | 1x | if (any(tcn <- grepl("(V\\d+$|c\\(|y\\.(\\d+$|.*\\.))", ck$mvn))) { |
| 595 | 1x | ncn <- substitute(y) |
| 596 | 1x | if (length(ncn) > 1 && length(ncn <- as.character(ncn[-1])) == length(dn)) { |
| 597 | ! | ck$mvn[tcn] <- paste0("y.", ncn[tcn]) |
| 598 | | } |
| 599 | | } |
| 600 | 1x | ck$mv <- TRUE |
| 601 | 1x | if (ck$mlvn) lvn <- FALSE |
| 602 | 1x | if (!missing(by)) { |
| 603 | ! | txt$bet <- c(txt$by, txt$bet) |
| 604 | ! | if (length(txt$bet) > 2) { |
| 605 | ! | warning("multiple y variables moves by to between, so the second level of between was dropped") |
| 606 | ! | txt$bet <- txt$bet[1:2] |
| 607 | ! | dat <- dat[-grep("bet", colnames(dat))[2]] |
| 608 | | } |
| 609 | ! | if (length(txt$bet) > 1) { |
| 610 | ! | dat$bet.1 <- if (is.factor(dat$by)) dat$by else as.character(dat$by) |
| 611 | ! | dat$bet.2 <- if (is.factor(dat$bet)) dat$bet else as.character(dat$bet) |
| 612 | ! | dat$bet <- NULL |
| 613 | | } else { |
| 614 | ! | dat$bet <- if (is.factor(dat$by)) dat$by else as.character(dat$by) |
| 615 | | } |
| 616 | | } |
| 617 | 1x | td <- dat |
| 618 | ! | if (any(ckn <- duplicated(ck$mvn))) ck$mvn[ckn] <- paste0(ck$mvn[ckn], "_", seq_len(sum(ckn))) |
| 619 | 1x | by <- sub("^y\\.", "", ck$mvn) |
| 620 | ! | if (any(by == "")) by[by == ""] <- seq_len(sum(by == "")) |
| 621 | 1x | by <- factor(rep(by, each = nr), levels = by) |
| 622 | 1x | cncls <- vapply(dat[, dn], function(v) is.numeric(v) || is.integer(v) || is.factor(v), TRUE) |
| 623 | 1x | if (any(cncls) && any(!cncls)) { |
| 624 | ! | for (cnc in which(!cncls)) { |
| 625 | ! | dat[, cnc] <- as.numeric(factor(dat[, cnc], lvs(dat[, cnc]))) |
| 626 | | } |
| 627 | | } |
| 628 | 1x | dat <- data.frame(y = unlist(dat[, dn], use.names = FALSE)) |
| 629 | ! | if (ncol(td) > length(dn)) dat <- cbind(dat, do.call(rbind, lapply(seq_along(dn), function(i) td[, -dn, drop = FALSE]))) |
| 630 | 1x | if (mv.as.x) { |
| 631 | ! | txt$by <- txt$x |
| 632 | ! | txt$x <- if (missing(labx)) "variable" else if (labx == txt$by) paste0(labx, ".1") else labx |
| 633 | ! | dat$by <- dat$x |
| 634 | ! | dat$x <- by |
| 635 | | } else { |
| 636 | 1x | txt$by <- "variable" |
| 637 | 1x | dat$by <- by |
| 638 | | } |
| 639 | 1x | if (missing(leg.title) && !mv.as.x) ck$legt <- FALSE |
| 640 | ! | if (!missing(levels) && "mv" %in% names(levels)) names(levels)[names(levels) == "mv"] <- txt[[if (mv.as.x) "x" else "by"]] |
| 641 | 1x | dn <- colnames(dat) |
| 642 | 1x | if (!missing(mv.scale) && mv.scale != "none") { |
| 643 | ! | tv <- if (mv.as.x) dat$x else dat$by |
| 644 | ! | for (g in levels(as.factor(tv))) { |
| 645 | ! | svar <- tv == g |
| 646 | ! | cvar <- scale(dat[svar, 1], scale = grepl("^t|z|sc", mv.scale, TRUE)) |
| 647 | ! | if (any(is.na(cvar))) cvar <- dat[svar, 1] - mean(dat[svar, 1], na.rm = TRUE) |
| 648 | ! | dat[svar, 1] <- cvar |
| 649 | | } |
| 650 | | } |
| 651 | 1x | nr <- nrow(dat) |
| 652 | | } else { |
| 653 | 32x | ck$mv <- FALSE |
| 654 | | } |
| 655 | 33x | if (!"x" %in% dn) { |
| 656 | 8x | ck$t <- 2 |
| 657 | 1x | if (!missing(type) && !grepl("^d", type, TRUE)) warning("x must be included to show other types of splots") |
| 658 | | } |
| 659 | 13x | if (!ck$cb && !"by" %in% dn) ck$leg <- 0 |
| 660 | 33x | if (lim > 20 || (is.logical(lim) && !lim)) { |
| 661 | ! | lim <- Inf |
| 662 | ! | if (ck$legm && !ck$cb) ck$leg <- 0 |
| 663 | ! | if (missing(error)) ck$el <- FALSE |
| 664 | | } |
| 665 | 1x | if (ck$ltm && !ck$el) line.type <- "b" |
| 666 | 33x | if (ck$ltym && is.logical(lines) && !lines) { |
| 667 | ! | ck$lty <- FALSE |
| 668 | ! | lty <- 1 |
| 669 | | } |
| 670 | 33x | if (!is.numeric(dat$y)) { |
| 671 | ! | txt$yax <- lvs(dat$y) |
| 672 | ! | if (!is.logical(dat$y) && !is.factor(dat$y)) dat$y <- factor(dat$y, lvs(dat$y)) |
| 673 | ! | dat$y <- as.numeric(dat$y) |
| 674 | | } |
| 675 | 33x | if ("by" %in% dn && is.character(dat$by) && all(!grepl("[^0-9]", dat$by))) { |
| 676 | ! | dat$by <- gsub(" ", "0", base::format(dat$by, justify = "right"), fixed = TRUE) |
| 677 | | } |
| 678 | 33x | odat <- dat |
| 679 | | # splitting and parsing variables |
| 680 | 33x | splt_type <- function(x, s) { |
| 681 | 5x | if (s == 1) { |
| 682 | 2x | "mean" |
| 683 | 3x | } else if (s == 3) { |
| 684 | ! | "standard deviation" |
| 685 | 3x | } else if (s == 2) { |
| 686 | ! | "quantile" |
| 687 | 3x | } else if (s == 4 && is.double(split) && (length(split) != 1 || all(c( |
| 688 | 3x | sum(split >= x, na.rm = TRUE), |
| 689 | 3x | sum(split <= x, na.rm = TRUE) |
| 690 | 3x | ) > 1))) { |
| 691 | ! | paste(split, collapse = ", ") |
| 692 | 3x | } else if (s == 4 && is.numeric(split) && split > 1) { |
| 693 | ! | split <- min(length(x), round(split), na.rm = TRUE) |
| 694 | ! | paste0("segments (", split, ")") |
| 695 | | } else { |
| 696 | 3x | "median" |
| 697 | | } |
| 698 | | } |
| 699 | 33x | splt <- function(x, s) { |
| 700 | 5x | if (s == 1) { |
| 701 | 2x | factor(x >= mean(x, na.rm = TRUE) * 1, labels = c("Below Average", "Above Average")) |
| 702 | 3x | } else if (s == 3) { |
| 703 | ! | m <- mean(x, na.rm = TRUE) |
| 704 | ! | s <- sd(x, TRUE) |
| 705 | ! | cut(x, c(-Inf, m - s, m + s, Inf), labels = c("-1 SD", "Mean", "+1 SD")) |
| 706 | 3x | } else if (s == 2) { |
| 707 | ! | cut(x, c(-Inf, quantile(x, na.rm = TRUE)[c(2, 4)], Inf), |
| 708 | ! | labels = c("2nd Quantile", "Median", "4th Quantile") |
| 709 | | ) |
| 710 | 3x | } else if (s == 4 && is.double(split) && (length(split) != 1 || all(c( |
| 711 | 3x | sum(split >= x, na.rm = TRUE), |
| 712 | 3x | sum(split <= x, na.rm = TRUE) |
| 713 | 3x | ) > 1))) { |
| 714 | ! | cut(x, c(-Inf, split, Inf), paste0("<=", c(split, "Inf")), ordered_result = TRUE) |
| 715 | 3x | } else if (s == 4 && is.numeric(split) && split > 1) { |
| 716 | ! | n <- length(x) |
| 717 | ! | split <- min(n, round(split), na.rm = TRUE) |
| 718 | ! | factor(paste("seg", rep(seq_len(split), each = round(n / split + .49))[order(order(x))])) |
| 719 | | } else { |
| 720 | 3x | factor(x >= median(x, TRUE) * 1, labels = c("Under Median", "Over Median")) |
| 721 | | } |
| 722 | | } |
| 723 | 33x | seg <- list( |
| 724 | 33x | x = list(e = !missing(x), s = FALSE, i = 2), |
| 725 | 33x | f1 = list(e = FALSE, s = FALSE, l = "", ll = 1), |
| 726 | 33x | f2 = list(e = FALSE, s = FALSE, l = "", ll = 1), |
| 727 | 33x | by = list(e = FALSE, s = FALSE, l = "", ll = 1) |
| 728 | | ) |
| 729 | 33x | if (seg$x$e && ck$t != 2) { |
| 730 | 22x | if ((ck$t == 1 || is.character(dat$x) || is.factor(dat$x) || |
| 731 | 22x | (missing(type) && length(unique(dat$x)) < lim))) { |
| 732 | 11x | dat$x <- if (!is.character(dat$x) && !is.factor(dat$x) && length(unique(dat$x)) > lim) { |
| 733 | 4x | seg$x$s <- TRUE |
| 734 | ! | if (missing(type)) ck$t <- 1 |
| 735 | 4x | txt$split <- splt_type(dat$x, ck$sp) |
| 736 | 4x | splt(dat$x, ck$sp) |
| 737 | | } else { |
| 738 | 6x | if (missing(type)) ck$t <- 1 |
| 739 | 7x | as.factor(dat$x) |
| 740 | | } |
| 741 | | } |
| 742 | | } |
| 743 | 33x | if (ck$t == 1 || (is.character(dat$x) || is.factor(dat$x))) { |
| 744 | 12x | seg$x$l <- lvs(dat$x) |
| 745 | ! | if (length(seg$x$l) == 1) ck$t <- 3 |
| 746 | | } |
| 747 | 33x | svar <- NULL |
| 748 | 33x | cvar <- if (any(grepl("^c", dn))) which(grepl("^c", dn)) else NULL |
| 749 | 33x | if (any(grepl("^b", dn))) { |
| 750 | 18x | svar <- which(grepl("^b", dn)) |
| 751 | 18x | for (i in svar) { |
| 752 | 33x | e <- if (grepl("bet", dn[i])) if (!seg$f1$e) "f1" else "f2" else "by" |
| 753 | 33x | seg[[e]]$e <- TRUE |
| 754 | 33x | seg[[e]]$i <- i |
| 755 | 33x | seg[[e]]$l <- lvs(dat[, i]) |
| 756 | 33x | if (is.factor(dat[, i]) && drop[[dn[i]]]) { |
| 757 | 1x | seg[[e]]$l <- seg[[e]]$l[seg[[e]]$l %in% dat[, i]] |
| 758 | | } |
| 759 | 33x | seg[[e]]$ll <- length(seg[[e]]$l) |
| 760 | 33x | if (seg[[e]]$ll > lim && !(is.character(dat[, i]) || is.factor(dat[, i]))) { |
| 761 | 1x | txt$split <- splt_type(dat[, i], ck$sp) |
| 762 | 1x | dat[, i] <- splt(dat[, i], ck$sp) |
| 763 | 1x | seg[[e]]$s <- TRUE |
| 764 | 1x | seg[[e]]$l <- lvs(dat[, i]) |
| 765 | 1x | seg[[e]]$ll <- length(seg[[e]]$l) |
| 766 | | } |
| 767 | 33x | if (!is.factor(dat[, i])) { |
| 768 | 31x | dat[, i] <- if (is.character(dat[, i])) { |
| 769 | 1x | factor(dat[, i], lvs(dat[, i])) |
| 770 | | } else { |
| 771 | 30x | as.factor(dat[, i]) |
| 772 | | } |
| 773 | | } |
| 774 | | } |
| 775 | | } |
| 776 | 16x | if (seg$by$l[1] == "") seg$by$l <- "NA" |
| 777 | 33x | fmod <- NULL |
| 778 | 33x | vs <- c(y = txt$y, x = txt$x, by = txt$by, bet = txt$bet, cov = txt$cov) |
| 779 | 33x | colnames(odat) <- vs |
| 780 | 33x | if (ck$t != 2 && model) { |
| 781 | 2x | tryCatch( |
| 782 | | { |
| 783 | 2x | mod <- formula(paste( |
| 784 | 2x | vs["y"], "~", vs["x"], |
| 785 | 2x | if (seg$by$e) paste0("*", vs["by"]), |
| 786 | 2x | if (seg$f1$e) paste0("*", vs[grep("^bet", names(vs))[1]]), |
| 787 | 2x | if (seg$f2$e) paste0("*", vs["bet2"]), |
| 788 | 2x | if (length(cvar)) paste0("+", paste0(vs["cov"], collapse = "+")) |
| 789 | | )) |
| 790 | 2x | fmod <- lm(mod, odat) |
| 791 | 2x | if (model) { |
| 792 | 2x | s <- summary(fmod) |
| 793 | 2x | s$call <- mod |
| 794 | 2x | print(s) |
| 795 | | } |
| 796 | | }, |
| 797 | 2x | error = function(e) warning(paste("summary model failed:", e$message), call. = FALSE) |
| 798 | | ) |
| 799 | | } |
| 800 | 33x | if (!missing(levels)) { |
| 801 | ! | tryCatch( |
| 802 | | { |
| 803 | ! | lc <- c("y", "x", "by", "f1", "f2") |
| 804 | ! | ns <- c(txt$y, txt$x, txt$by, txt$bet, lc) |
| 805 | ! | lc <- c(lc[seq_len(length(ns) - length(lc))], lc) |
| 806 | ! | for (n in names(levels)) { |
| 807 | ! | if (any(cns <- ns %in% n)) { |
| 808 | ! | sl <- lc[cns <- which(cns)[1]] |
| 809 | ! | if (sl == "y") { |
| 810 | ! | sl <- list(i = 1) |
| 811 | ! | vfac <- txt$yax |
| 812 | ! | dat$y <- factor(dat$y, labels = vfac) |
| 813 | | } else { |
| 814 | ! | sl <- seg[[sl]] |
| 815 | ! | vfac <- lvs(dat[, sl$i]) |
| 816 | | } |
| 817 | ! | vl <- length(vfac) |
| 818 | ! | ln <- levels[[n]] |
| 819 | ! | lo <- NULL |
| 820 | ! | if (is.list(ln)) { |
| 821 | ! | if (length(ln) > 1) lo <- levels[[n]][[2]] |
| 822 | ! | ln <- ln[[1]] |
| 823 | | } |
| 824 | ! | if (is.numeric(ln)) ln <- vfac[ln] |
| 825 | ! | if (vl == length(ln)) { |
| 826 | ! | vl <- list(dat[, sl$i]) |
| 827 | ! | if (all(ln %in% vfac)) { |
| 828 | ! | vl$levels <- ln |
| 829 | | } else { |
| 830 | ! | if (!is.null(lo)) { |
| 831 | ! | vl$labels <- ln[lo] |
| 832 | ! | vl$levels <- vfac[lo] |
| 833 | | } else { |
| 834 | ! | vl$labels <- ln |
| 835 | | } |
| 836 | | } |
| 837 | ! | dat[, sl$i] <- do.call(factor, vl) |
| 838 | ! | if ("l" %in% names(sl)) seg[[lc[cns]]]$l <- levels(dat[, sl$i]) |
| 839 | | } else { |
| 840 | ! | warning(n, " has ", vl, " levels but you provided ", length(ln), call. = FALSE) |
| 841 | | } |
| 842 | ! | if (sl$i == 1) { |
| 843 | ! | txt$yax <- lvs(dat$y) |
| 844 | ! | dat$y <- as.numeric(dat$y) |
| 845 | | } |
| 846 | | } |
| 847 | | } |
| 848 | | }, |
| 849 | ! | error = function(e) warning("setting levels failed: ", e$message, call. = FALSE) |
| 850 | | ) |
| 851 | | } |
| 852 | 33x | dsf <- list(c1 = "", sep = rep.int("^^", nr), c2 = "") |
| 853 | 11x | if (seg$f1$e) dsf$c1 <- dat[, seg$f1$i] |
| 854 | 5x | if (seg$f2$e) dsf$c2 <- dat[, seg$f2$i] |
| 855 | 33x | cdat <- split(dat, dsf) |
| 856 | 33x | if (seg$by$e) { |
| 857 | 17x | cdat <- lapply(cdat, function(s) { |
| 858 | 37x | if (length(unique(s$by)) > 1) { |
| 859 | 37x | split(s, factor(as.character(s$by), lvs(s$by))) |
| 860 | | } else { |
| 861 | ! | s <- lapply(seg$by$l, function(l) if (sum(s$by == l)) s else NULL) |
| 862 | ! | names(s) <- seg$by$l |
| 863 | ! | s |
| 864 | | } |
| 865 | | }) |
| 866 | 17x | if (all((seg$n <- vapply(cdat, length, 0)) == seg$by$ll)) { |
| 867 | 17x | seg$n <- vapply(cdat, function(s) vapply(s, NROW, 0), numeric(seg$by$ll)) |
| 868 | | } else { |
| 869 | ! | drop["by"] <- FALSE |
| 870 | | } |
| 871 | | } else { |
| 872 | 16x | seg$n <- vapply(cdat, nrow, 0) |
| 873 | | } |
| 874 | 33x | if (seg$by$e && ck$t != 3 && drop["by"]) { |
| 875 | 10x | seg$by$l <- if (is.null(rownames(seg$n))) { |
| 876 | ! | structure(seg$n > 1, names = seg$by$l) |
| 877 | | } else { |
| 878 | 10x | vapply(rownames(seg$n), function(r) any(seg$n[r, ] > 1), TRUE) |
| 879 | | } |
| 880 | 10x | if (!any(seg$by$l)) { |
| 881 | ! | if (ck$t == 2) stop("no level of by has more than 1 observation") |
| 882 | ! | warning("no level of by has more than 1 observation so it was treated as colorby", call. = FALSE) |
| 883 | ! | seg$by$e <- FALSE |
| 884 | ! | seg$by$l <- "" |
| 885 | ! | seg$by$ll <- 1 |
| 886 | ! | if (ck$cb) { |
| 887 | ! | colorby <- substitute(colorby) |
| 888 | ! | colorby[[2]] <- dat$by |
| 889 | | } else { |
| 890 | ! | colorby <- dat$by |
| 891 | | } |
| 892 | ! | ck$cb <- TRUE |
| 893 | ! | dat <- dat[, -seg$by$i] |
| 894 | ! | cdat <- split(dat, dsf) |
| 895 | ! | seg$n <- vapply(cdat, nrow, 0) |
| 896 | | } else { |
| 897 | 10x | seg$by$l <- names(seg$by$l[seg$by$l]) |
| 898 | 10x | seg$by$ll <- length(seg$by$l) |
| 899 | | } |
| 900 | | } |
| 901 | 33x | if (!is.null(nrow(seg$n))) { |
| 902 | 17x | cdat <- cdat[apply(seg$n, 2, function(r) any(r > 1))] |
| 903 | 17x | if (nrow(seg$n) > 1) seg$n <- colSums(seg$n[, names(cdat), drop = FALSE]) |
| 904 | | } |
| 905 | 1x | if (ck$mv) seg$n <- seg$n / length(ck$mvn) |
| 906 | 33x | seg$ll <- length(seg$n) |
| 907 | 2x | if (ck$mlvn && seg$by$e && (seg$by$s || !any(grepl("^[0-9]", seg$by$l)))) lvn <- FALSE |
| 908 | 33x | ptxt <- c(txt[-c(1, 7)], l = lapply(seg[1:4], "[[", "l")) |
| 909 | 10x | if (missing(labels.trim) && seg$ll == 1 && length(ptxt$l.x) < 2 && (seg$by$ll == 1 || ck$mv)) labels.trim <- 40 |
| 910 | 33x | if (is.numeric(labels.trim) || is.character(labels.filter)) { |
| 911 | 32x | vs <- c("y", "x", "by", "bet", "cov", "l.x", "l.f1", "l.f2", "l.by") |
| 912 | 32x | ptxt <- lapply(vs, function(n) { |
| 913 | 288x | n <- as.character(ptxt[[n]]) |
| 914 | 288x | if (length(n) != 0 && all(n != "NULL" & n != "")) { |
| 915 | 144x | names(n) <- n |
| 916 | 144x | if (is.character(labels.filter)) n <- gsub(labels.filter, " ", n, perl = TRUE) |
| 917 | ! | if (any(is.na(iconv(n)))) stop("labels appear to be misencoded -- check them with the iconv function") |
| 918 | ! | if (is.numeric(labels.trim)) if (any(ln <- nchar(n) > (labels.trim + 3))) n[ln] <- sub("$", "...", strtrim(n[ln], labels.trim)) |
| 919 | | } |
| 920 | 288x | n |
| 921 | | }) |
| 922 | 32x | names(ptxt) <- vs |
| 923 | | } |
| 924 | ! | if (is.character(labx)) ptxt$x <- labx else if (ck$t == 2) ptxt$x <- ptxt$y |
| 925 | ! | if (is.character(laby)) ptxt$y <- laby else if (ck$t == 2) ptxt$y <- "Density" |
| 926 | 33x | ck$ileg <- seg$by$e && ck$leg > 1 |
| 927 | 33x | ptxt$leg <- ptxt$l.by |
| 928 | 33x | fdat <- dat |
| 929 | 33x | names(fdat) <- paste0(".", names(dat)) |
| 930 | 33x | fdat <- if (!is.null(data)) if (nrow(data) == nr) cbind(data, fdat, odat) else data else cbind(fdat, odat) |
| 931 | | # figuring out colors |
| 932 | 33x | csf <- if (is.function(color.summary)) { |
| 933 | ! | color.summary |
| 934 | 33x | } else if (grepl("^av|mea", color.summary, TRUE)) { |
| 935 | 33x | splot.colormean |
| 936 | 33x | } else if (grepl("^mode", color.summary, TRUE)) { |
| 937 | ! | function(x) names(which.max(table(x))) |
| 938 | | } else { |
| 939 | ! | function(x) lvs(x)[round(median(as.numeric(factor(x, lvs(x)))))] |
| 940 | | } |
| 941 | 33x | colors <- substitute(colors) |
| 942 | 33x | seg$cols <- if (ck$co) colors else if (any(paste(colors) %in% names(fdat))) NULL else tryCatch(tdc(colors), error = function(e) NULL) |
| 943 | 1x | if (is.null(seg$cols)) seg$cols <- eval(colors, fdat, parent.frame(1)) |
| 944 | 33x | ptxt$cbo <- substitute(colorby) |
| 945 | ! | if (length(ptxt$cbo) > 1 && ptxt$cbo[[1]] == "list") ptxt$cbo <- ptxt$cbo[[2]] |
| 946 | 33x | if (!is.character(ptxt$cbo)) ptxt$cbo <- deparse(ptxt$cbo) |
| 947 | 33x | if (length(seg$cols) == 1) { |
| 948 | 32x | if (grepl("^bri|^dar|^pas", seg$cols, TRUE) && (ck$cb || (seg$by$ll > 1 && seg$by$ll < 10))) { |
| 949 | 20x | seg$cols <- splot.color(seed = seg$cols) |
| 950 | 12x | } else if (ck$co || grepl("^gra|^grey", seg$cols, TRUE)) seg$cols <- splot.color(seg$by$ll, seed = "grey") |
| 951 | | } |
| 952 | 33x | cl <- length(seg$cols) |
| 953 | 33x | seg$lcols <- seg$cols |
| 954 | 33x | ck[c("cbn", "cbb")] <- tg <- FALSE |
| 955 | 33x | chl <- if (ck$cblegm) FALSE else ck$cbleg |
| 956 | 33x | if (ck$cb) { |
| 957 | 3x | sca <- names(formals(splot.color)) |
| 958 | 3x | colorby <- substitute(colorby) |
| 959 | 3x | cba <- if (any(paste(colorby) %in% names(fdat))) NULL else tryCatch(tdc(colorby), error = function(e) NULL) |
| 960 | 2x | if (is.null(cba)) cba <- eval(substitute(colorby), fdat) |
| 961 | ! | if (is.null(cba) || (is.character(cba) && length(cba) == 1)) cba <- tdc(colorby) |
| 962 | 3x | if (!is.list(cba) || is.data.frame(cba)) { |
| 963 | 3x | cba <- list(x = cba) |
| 964 | ! | } else if (is.null(names(cba))) { |
| 965 | ! | names(cba) <- names(formals(splot.color))[seq_along(cba)] |
| 966 | ! | } else if (any(names(cba) == "")) { |
| 967 | ! | tn <- names(cba) == "" |
| 968 | ! | names(cba)[tn] <- sca[seq_len(sum(tn))] |
| 969 | | } |
| 970 | 3x | if (!is.null(ncol(cba$x)) && ncol(cba$x) > 1) { |
| 971 | ! | if (!"by" %in% names(cba)) cba$by <- cba$x[, 2] |
| 972 | ! | cba$x <- cba$x[, 1] |
| 973 | | } |
| 974 | 3x | cba$flat <- TRUE |
| 975 | 3x | cn <- names(cba) |
| 976 | 3x | ck$cbb <- "by" %in% cn |
| 977 | 3x | if (ck$mv && length(cba$x) * ck$mvnl == nr) { |
| 978 | ! | cba$x <- rep(cba$x, ck$mvnl) |
| 979 | ! | if (ck$cbb) cba$by <- rep(cba$by, ck$mvnl) |
| 980 | | } |
| 981 | 3x | if (ck$cbb) { |
| 982 | ! | cba$by <- if (is.numeric(cba$by) && length(unique(cba$by)) > lim) { |
| 983 | ! | ptxt$cbos <- if (missing(leg.title)) colorby else leg.title |
| 984 | ! | ptxt$cbos <- if (is.call(ptxt$cbos)) { |
| 985 | ! | deparse(ptxt$cbos[[if (cn[2] == "by" && length(ptxt$cbos) > 2) 3 else 2]]) |
| 986 | | } else { |
| 987 | ! | deparse(ptxt$cbos) |
| 988 | | } |
| 989 | ! | splt(cba$by, ck$sp) |
| 990 | | } else { |
| 991 | ! | factor(cba$by, lvs(cba$by)) |
| 992 | | } |
| 993 | ! | if (seg$by$e && seg$by$ll <= lim && length(cba$by) == nr && !identical(as.character(dat$by), as.character(cba$by))) { |
| 994 | ! | cba$by <- dat$by:cba$by |
| 995 | ! | cbbl <- sub(":.*", "", lvs(cba$by)) |
| 996 | ! | colorby[[3]] <- as.name(paste0(ptxt$by, ":", colorby[[3]])) |
| 997 | ! | seg$lcols <- seg$cols <- splot.color(cbbl, seed = seg$cols) |
| 998 | ! | if (!ck$b && ck$line) { |
| 999 | ! | if (length(lty) < seg$by$ll) lty <- seq_len(seg$by$ll) |
| 1000 | ! | ck[c("lty", "ltym")] <- FALSE |
| 1001 | ! | lty <- rep(lty, table(cbbl)) |
| 1002 | ! | seg$lty <- unique(lty) |
| 1003 | | } |
| 1004 | | } else { |
| 1005 | ! | lby <- length(lvs(cba$by)) |
| 1006 | ! | if (!color.lock && cl < lby) seg$cols <- splot.color(as.list(rep.int(round(lby / cl + .49), cl)), seed = seg$cols) |
| 1007 | | } |
| 1008 | | } |
| 1009 | 3x | if (length(cba$x) == ck$orn) cba$x <- cba$x[ck$omitted$all] |
| 1010 | ! | if (ck$cbb && length(cba$by) == ck$orn) cba$by <- cba$by[ck$omitted$all] |
| 1011 | 3x | if (seg$by$e || !"seed" %in% cn) { |
| 1012 | 3x | cba$seed <- seg$cols |
| 1013 | 3x | if ("seed" %in% cn) { |
| 1014 | ! | warning("colorby's seed is ignored because by is specified -- use colors to set seeds", call. = FALSE) |
| 1015 | | } |
| 1016 | | } |
| 1017 | 3x | cn <- names(cba) |
| 1018 | 3x | ckn <- cken <- is.numeric(cba$x) |
| 1019 | 3x | if ((ck$t == 1 || any(seg$by$e, seg$f1$e)) && length(cba$x) == nr) { |
| 1020 | 1x | seg$cbxls <- lvs(cba$x) |
| 1021 | 1x | if (ck$t != 3 && (!seg$by$e || seg$by$ll > lim)) { |
| 1022 | 1x | cba$x <- vapply(split(cba$x, if (seg$by$e) dat$by else dat$x), function(x) { |
| 1023 | 2x | if (ckn) { |
| 1024 | 2x | mean(x, na.rm = TRUE) |
| 1025 | | } else { |
| 1026 | ! | names(which.max(table(x))) |
| 1027 | | } |
| 1028 | 1x | }, if (ckn) 0 else "") |
| 1029 | 1x | if (!ckn || length(seg$cbxls) <= lim) { |
| 1030 | ! | cba$x <- if (ckn) { |
| 1031 | ! | cba$x <- round(cba$x, 3) |
| 1032 | ! | factor(cba$x, sort(unique(cba$x))) |
| 1033 | | } else { |
| 1034 | ! | factor(cba$x, seg$cbxls) |
| 1035 | | } |
| 1036 | ! | ckn <- FALSE |
| 1037 | | } |
| 1038 | 1x | if (ck$cbb && length(cba$by) == nr) { |
| 1039 | ! | cba$by <- factor(vapply(split(cba$by, if (seg$by$e) dat$by else dat$x), function(x) { |
| 1040 | ! | names(which.max(table(x))) |
| 1041 | ! | }, ""), lvs(cba$by)) |
| 1042 | ! | if (length(cba$x) != length(cba$by)) { |
| 1043 | ! | cba$by <- NULL |
| 1044 | ! | ck$cbb <- FALSE |
| 1045 | ! | warning("colorby's by was dropped as it was not the same length as x after being aligned with the formula's x", |
| 1046 | ! | call. = FALSE |
| 1047 | | ) |
| 1048 | | } |
| 1049 | | } |
| 1050 | 1x | if (ckn && !ck$b && ck$t == 1 && length(cba$x) == 2) { |
| 1051 | ! | cba$x <- c(mean(cba$x), cba$x) |
| 1052 | ! | if (ck$cbb) cba$by <- factor(c(lvs(cba$by)[which.max(tabulate(cba$by))], as.character(cba$by)), lvs(cba$by)) |
| 1053 | | } |
| 1054 | ! | } else if (!ck$cbb) { |
| 1055 | ! | if (ck$t == 3) { |
| 1056 | ! | cba$by <- dat$by |
| 1057 | | } else { |
| 1058 | ! | cba$x <- data.frame(cba$x, dat$by) |
| 1059 | ! | if (ck$b && seg$ll != 1) cba$x$x <- dat$x |
| 1060 | ! | cba$x <- unlist(lapply(split(cba$x, dsf), function(x) { |
| 1061 | ! | lapply( |
| 1062 | ! | split(x[, 1], x[, -1]), function(x) if (!length(x)) NA else if (ckn) mean(x, na.rm = TRUE) else names(which.max(table(x))) |
| 1063 | | ) |
| 1064 | ! | }), TRUE, FALSE) |
| 1065 | ! | if (length(cba$x) == seg$by$ll) names(cba$x) <- seg$by$l else seg$ill <- names(cba$x) |
| 1066 | ! | if (!ckn) cba$x <- factor(cba$x, seg$cbxls) |
| 1067 | ! | cba$by <- factor(rep_len(seg$by$l, length(cba$x)), seg$by$l) |
| 1068 | ! | if (ck$cblegm) ck$cbleg <- FALSE |
| 1069 | | } |
| 1070 | | } |
| 1071 | | } |
| 1072 | 3x | if (ck$cbb) { |
| 1073 | ! | if (length(cba$by) == nr && length(cba$x) == seg$by$ll) { |
| 1074 | ! | tn <- lapply(split(cba$by, dat$by), unique) |
| 1075 | ! | if (all(vapply(tn, length, 0) == 1)) { |
| 1076 | ! | cba$by <- unlist(tn, use.names = FALSE) |
| 1077 | | } else { |
| 1078 | ! | cba$by <- NULL |
| 1079 | ! | warning("colorby's by was dropped as its levels within levels of by are not unique", call. = FALSE) |
| 1080 | | } |
| 1081 | | } |
| 1082 | ! | if (ck$cbleg) { |
| 1083 | ! | chl <- TRUE |
| 1084 | ! | if (missing(leg.title)) { |
| 1085 | ! | leg.title <- substitute(colorby) |
| 1086 | ! | leg.title <- if (is.call(leg.title) && length(leg.title) > 2) { |
| 1087 | ! | deparse(leg.title[[if (cn[2] == "by") 3 else 2]]) |
| 1088 | | } else { |
| 1089 | ! | deparse(leg.title) |
| 1090 | | } |
| 1091 | | } |
| 1092 | ! | ptxt$leg <- lvs(cba$by) |
| 1093 | | } |
| 1094 | | } else { |
| 1095 | 3x | if (ck$cbleg && (ck$t == 1 || !seg$by$e)) { |
| 1096 | 3x | chl <- TRUE |
| 1097 | 3x | tg <- ckn |
| 1098 | 3x | ll <- all(ck$t != 1 || (length(seg$x$l) > 2 || seg$by$ll > 2)) |
| 1099 | 3x | if (ll) { |
| 1100 | ! | if (is.call(cba$x)) cba$x <- tdc(cba$x) |
| 1101 | 2x | ll <- length(unique(cba$x)) > 2 |
| 1102 | | } |
| 1103 | 3x | if (missing(leg.title) && length(ptxt$cbo) == 1) leg.title <- ptxt$cbo |
| 1104 | 3x | ptxt$leg <- if (ckn) formatC(c(min(cba$x), if (ll) mean(cba$x), max(cba$x)), 2, format = "f") else lvs(cba$x) |
| 1105 | ! | } else if (!seg$by$e) ck$leg <- 0 |
| 1106 | | } |
| 1107 | ! | if (!ckn && length(cba$x) > lim && !"shuffle" %in% cn) cba$shuffle <- TRUE |
| 1108 | 3x | sca <- cn %in% sca |
| 1109 | ! | if (any(!sca)) warning(paste0("unused colorby arguments: ", paste(cn[!sca], collapse = ", ")), call. = FALSE) |
| 1110 | 3x | seg$cols <- do.call(splot.color, cba[sca]) |
| 1111 | 1x | if (!is.null(names(cba$x))) names(seg$cols) <- names(cba$x) |
| 1112 | 3x | if (!chl || ck$cbb) { |
| 1113 | ! | ck$cbn <- TRUE |
| 1114 | ! | ptxt$cbn <- paste0("Colored by ", if (ckn || cken) "value of " else "levels of ", ptxt$cbo, ". ") |
| 1115 | | } |
| 1116 | 3x | if (seg$by$e && !ck$cbb) { |
| 1117 | ! | if (length(seg$cols) == length(ptxt$leg)) { |
| 1118 | ! | seg$lcols <- seg$cols |
| 1119 | ! | } else if (ckn && ck$cbb) { |
| 1120 | ! | seg$lcols <- seg$cols[c(which.min(cba$x), which.max(cba$x))] |
| 1121 | | } |
| 1122 | | } |
| 1123 | 3x | if (chl) { |
| 1124 | ! | if (ck$legm && !ck$leg) ck$leg <- 1 + seg$ll > 1 |
| 1125 | 3x | if ((ck$ltym || length(lty) == length(seg$cbxls)) && (!seg$by$e || seg$by$ll > length(ptxt$leg))) { |
| 1126 | 3x | ck[c("lty", "ltym")] <- FALSE |
| 1127 | 3x | if (!is.numeric(lty)) lty <- 1 |
| 1128 | 3x | seg$lty <- rep_len(lty, seg$by$ll) |
| 1129 | 3x | if (!ck$ltym) lty <- seq_along(seg$cbxls) |
| 1130 | 3x | if (ck$ltym && seg$by$e && !ckn) { |
| 1131 | ! | cbl <- cba[[if (ck$cbb) "by" else "x"]] |
| 1132 | ! | for (g in seq_along(seg$cbxls)) seg$lty[cbl == seg$cbxls[[g]]] <- lty[[g]] |
| 1133 | | } |
| 1134 | 3x | lty <- unique(seg$lty) |
| 1135 | | } |
| 1136 | 3x | if (tg) { |
| 1137 | 3x | l <- length(seg$cols) |
| 1138 | 3x | seg$lcols <- seg$cols[order(cba$x)[c(1, if (ll) round(mean(seq_len(l))), l)]] |
| 1139 | ! | } else if (seg$by$e && length(seg$cols) == seg$by$ll && length(ptxt$leg) == seg$by$ll) seg$lcols <- seg$cols |
| 1140 | | } else { |
| 1141 | ! | ptxt$leg <- if (length(seg$cols) == seg$by$ll && !is.null(names(seg$cols))) names(seg$cols) else seg$by$l |
| 1142 | ! | if (length(ptxt$leg) == length(seg$cols)) seg$lcols <- seg$cols else if (all(ptxt$leg %in% names(seg$cols))) seg$lcols <- seg$cols[ptxt$leg] else if (seg$by$e && length(seg$cols) == nr) seg$lcols <- vapply(split(seg$cols, dat$by), csf, "") |
| 1143 | | } |
| 1144 | | } else { |
| 1145 | 30x | if (!color.lock && cl < seg$by$ll) { |
| 1146 | ! | seg$cols <- |
| 1147 | ! | splot.color(as.list(rep.int(round(seg$by$ll / cl + .49), cl)), seed = seg$cols) |
| 1148 | | } |
| 1149 | 30x | if (ck$t != 2 && !any(length(seg$cols) == c(nr, seg$by$ll)) && (!ck$b || seg$by$e)) { |
| 1150 | 13x | seg$cols <- rep_len(seg$cols, seg$by$ll) |
| 1151 | | } |
| 1152 | | } |
| 1153 | 33x | if (seg$by$e && !all(seg$by$l %in% names(seg$cols))) { |
| 1154 | 17x | if (length(seg$cols) == seg$by$ll) { |
| 1155 | 13x | names(seg$cols) <- seg$by$l |
| 1156 | 13x | if (!ck$cbb && !chl) seg$lcols <- seg$cols |
| 1157 | 4x | } else if (length(seg$lcols) == seg$by$ll) { |
| 1158 | ! | names(seg$lcols) <- seg$by$l |
| 1159 | 4x | } else if (length(ptxt$leg) == seg$by$ll) { |
| 1160 | 4x | if (length(seg$lcols) == nr) { |
| 1161 | ! | seg$lcols <- split(seg$lcols, dat$by) |
| 1162 | | } else { |
| 1163 | 4x | seg$lcols <- rep_len(seg$lcols, seg$by$ll) |
| 1164 | 4x | if (any(grepl(names(cdat)[1], names(seg$lcols), fixed = TRUE))) { |
| 1165 | ! | for (g in names(cdat)) names(seg$lcols) <- sub(paste0(g, "."), "", names(seg$lcols), fixed = TRUE) |
| 1166 | ! | if (all(seg$by$l %in% names(seg$lcols))) seg$lcols <- seg$lcols[seg$by$l] |
| 1167 | | } else { |
| 1168 | 4x | names(seg$lcols) <- seg$by$l |
| 1169 | | } |
| 1170 | | } |
| 1171 | | } |
| 1172 | 17x | if (ck$b && length(seg$cols) == nr) { |
| 1173 | ! | seg$cols <- unlist(lapply( |
| 1174 | ! | split(data.frame(seg$cols, dat$by), dat$x), |
| 1175 | ! | function(d) vapply(split(d[, 1], d[, 2], drop = TRUE), csf, "") |
| 1176 | ! | ), use.names = FALSE) |
| 1177 | | } |
| 1178 | | } |
| 1179 | 33x | if (ck$opacity && (ck$t != 3 || !points)) { |
| 1180 | ! | if (is.list(seg$cols)) { |
| 1181 | ! | lapply(seg$cols, adjustcolor, opacity) |
| 1182 | | } else { |
| 1183 | ! | seg$cols[] <- adjustcolor(seg$cols, opacity) |
| 1184 | | } |
| 1185 | | } |
| 1186 | 15x | if (lvn && length(ptxt$by)) ptxt$l.by[] <- paste0(paste0(ptxt$by, ": "), ptxt$l.by) |
| 1187 | 33x | if (length(seg$cols) == nr) { |
| 1188 | 3x | if (any(seg$by$e && !ck$b, seg$f1$e, seg$f2$e)) { |
| 1189 | 1x | seg$scols <- split(if (seg$by$e && !ck$b) data.frame(seg$cols, dat$by) else seg$cols, dsf) |
| 1190 | 1x | if (!ck$b) { |
| 1191 | ! | if (seg$by$e) seg$scols <- lapply(seg$scols, function(d) split(d[, 1], d[, 2])) |
| 1192 | ! | if (ck$t == 1) { |
| 1193 | ! | seg$scols <- lapply(seg$scols, function(bl) { |
| 1194 | ! | vapply(bl, function(bll) { |
| 1195 | ! | if (length(bll)) csf(bll) else "" |
| 1196 | | }, "") |
| 1197 | | }) |
| 1198 | | } |
| 1199 | | } |
| 1200 | | } |
| 1201 | 30x | } else if (seg$ll != 1 && "ill" %in% names(seg)) { |
| 1202 | ! | seg$scols <- lapply(names(cdat), function(n) seg$cols[grepl(n, names(seg$cols), fixed = TRUE)]) |
| 1203 | ! | names(seg$scols) <- names(cdat) |
| 1204 | | } |
| 1205 | 33x | if (ck$t == 2 && seg$by$ll > 1 && !all(seg$by$l %in% names(seg$cols))) { |
| 1206 | 4x | seg$cols <- if (length(seg$lcols) == seg$by$ll) { |
| 1207 | 4x | seg$lcols |
| 1208 | 4x | } else if (length(seg$cols) == 1) { |
| 1209 | ! | splot.color(seq_len(seg$by$ll), seed = seg$cols) |
| 1210 | | } else { |
| 1211 | ! | rep_len(seg$cols, seg$by$ll) |
| 1212 | | } |
| 1213 | ! | if (is.null(names(seg$cols))) names(seg$cols) <- seg$by$l |
| 1214 | | } |
| 1215 | | # figuring out parts of the plot |
| 1216 | 33x | ylab <- if (ck$ly) ptxt$y else "" |
| 1217 | 33x | xlab <- if (ck$lx && length(ptxt$x)) ptxt$x else "" |
| 1218 | 33x | main <- if (is.logical(title) && title) { |
| 1219 | 31x | paste0(if (ck$t == 2) { |
| 1220 | 9x | paste("Density of", ptxt$x) |
| 1221 | | } else { |
| 1222 | 22x | paste( |
| 1223 | 22x | ptxt$y, |
| 1224 | 22x | "by", ptxt$x |
| 1225 | | ) |
| 1226 | 31x | }, if (seg$by$e && !ck$mv) paste(" at levels of", ptxt$by), if (length(ptxt$bet) != 0) { |
| 1227 | 10x | paste( |
| 1228 | 10x | " between", |
| 1229 | 10x | paste(ptxt$bet, collapse = " & ") |
| 1230 | | ) |
| 1231 | | }) |
| 1232 | 33x | } else if (is.character(title)) title else "" |
| 1233 | 33x | if (!is.character(note)) { |
| 1234 | 32x | if (!is.logical(note) || note) { |
| 1235 | 32x | ck$er <- ck$t == 1 && ck$el |
| 1236 | 32x | ck$spm <- txt$split != "none" |
| 1237 | 32x | if (ck$er && all(vapply(cdat, function(d) { |
| 1238 | 4x | if (!is.data.frame(d)) all(vapply(d, function(dd) !anyDuplicated(dd$x), TRUE)) else !anyDuplicated(d$x) |
| 1239 | 32x | }, TRUE))) { |
| 1240 | ! | ck[c("el", "er")] <- FALSE |
| 1241 | | } |
| 1242 | 32x | if (any(ck$cbn, ck$spm, ck$er, ck$t == 3 && ck$ltck)) { |
| 1243 | 21x | if (ck$spm) { |
| 1244 | 5x | tv <- unique(c( |
| 1245 | 5x | if (seg$x$s) ptxt$x, |
| 1246 | 5x | if (seg$by$s) ptxt$by, |
| 1247 | 5x | if (seg$f1$s) ptxt$bet[1], |
| 1248 | 5x | if (seg$f2$s) ptxt$bet[2], |
| 1249 | 5x | if ("cbos" %in% names(ptxt)) ptxt$cbos |
| 1250 | | )) |
| 1251 | 5x | tv <- sub(", (?=[A-z0-9]+$)", if (length(tv) > 2) ", & " else " & ", paste(tv, collapse = ", "), perl = TRUE) |
| 1252 | | } |
| 1253 | 21x | note <- paste0( |
| 1254 | 21x | if (ck$spm) paste0(tv, " split by ", txt$split, ". "), |
| 1255 | 21x | if (ck$er) paste("Error bars show", ifelse(ck$e, "standard error. ", "95% confidence intervals. ")), |
| 1256 | 21x | if (ck$cbn) ptxt$cbn, |
| 1257 | 21x | if (ck$t == 3 && ck$ltck) { |
| 1258 | 11x | paste0("Line type: ", switch(ck$ltco, |
| 1259 | 11x | li = "lm", |
| 1260 | 11x | lo = "loess", |
| 1261 | 11x | sm = "spline", |
| 1262 | 11x | e = "connected", |
| 1263 | 11x | pr = "probability" |
| 1264 | | ), ".") |
| 1265 | | } |
| 1266 | | ) |
| 1267 | | } |
| 1268 | | } else { |
| 1269 | ! | note <- "" |
| 1270 | | } |
| 1271 | | } |
| 1272 | 33x | ck$sud <- (!is.logical(sud) || sud) && (is.character(sud) || ck$su || ck$c) |
| 1273 | 33x | ck$sub <- (!is.logical(sub) || sub) && (is.character(sub) || seg$ll > 1 || ndisp) |
| 1274 | 33x | pdo <- list(...) |
| 1275 | 33x | l2m <- function(l) { |
| 1276 | 1x | tl <- round(l^.5) |
| 1277 | 1x | c(tl + all(l > c(tl^2, tl * (tl - 1))), tl) |
| 1278 | | } |
| 1279 | 33x | seg$dim <- if (any(ckl <- c("mfrow", "mfcol") %in% names(pdo))) { |
| 1280 | ! | pdo[[if (ckl[1]) "mfrow" else "mfcol"]] |
| 1281 | 33x | } else if (!seg$f1$e) { |
| 1282 | 22x | c(1, 1) |
| 1283 | 33x | } else if (!seg$f2$e) { |
| 1284 | 6x | if (seg$f1$ll > 2) l2m(seg$f1$ll) else c(2, 1) |
| 1285 | | } else { |
| 1286 | 5x | c(seg$f1$ll, seg$f2$ll) |
| 1287 | | } |
| 1288 | 33x | seg$l <- t(data.frame(strsplit(names(cdat), ".^^.", fixed = TRUE))) |
| 1289 | 33x | if (seg$f1$e) { |
| 1290 | 11x | rownames(seg$l) <- match(seg$l[, 1], seg$f1$l) |
| 1291 | 11x | seg[c("f1", "f2")] <- lapply(c("f1", "f2"), function(n) { |
| 1292 | 22x | nl <- seg[[n]] |
| 1293 | 16x | if (nl$e) nl$l <- unique(seg$l[, if (n == "f1") 1 else 2]) |
| 1294 | 16x | if (nl$e) nl$ll <- length(nl$l) |
| 1295 | 22x | nl |
| 1296 | | }) |
| 1297 | | } |
| 1298 | 33x | nc <- seg$dim[1] * seg$dim[2] |
| 1299 | 13x | if (length(ptxt$leg) == 1 && ptxt$leg == "NA") ck$leg <- 0 |
| 1300 | 33x | if (ck$leg == 1 && ck$legm && (dev.size(units = "in")[1] < 2 || |
| 1301 | 33x | (all(seg$dim == 1) && (ck$t != 1 || seg$by$ll < 9)))) { |
| 1302 | 10x | ck$leg <- 2 |
| 1303 | | } |
| 1304 | 10x | if (ck$leg == 1) if (is.logical(leg) || is.character(leg)) leg <- nc + 1 |
| 1305 | 33x | dop <- par(no.readonly = TRUE) |
| 1306 | 33x | if (drop["bet"] && !any(ckl) && any(nc - seg$ll >= seg$dim)) { |
| 1307 | 1x | seg$dim <- l2m(seg$ll) |
| 1308 | 1x | nc <- seg$dim[1] * seg$dim[2] |
| 1309 | | } |
| 1310 | 33x | seg$dmat <- matrix(seq_len(nc), seg$dim[2], seg$dim[1]) |
| 1311 | 33x | if (!drop["bet"] && seg$f2$e) { |
| 1312 | ! | seg$lc <- vapply(seg$f1$l, function(l) seg$f2$l %in% seg$l[seg$l[, 1] == l, 2], logical(seg$f2$ll)) |
| 1313 | | } else { |
| 1314 | 33x | seg$lc <- seg$dmat == 0 |
| 1315 | 33x | seg$lc[seq_len(seg$ll)] <- TRUE |
| 1316 | | } |
| 1317 | 33x | if (nc > seg$ll) { |
| 1318 | ! | if (any(ckl)) { |
| 1319 | ! | tm <- lapply(dim(seg$lc), seq_len) |
| 1320 | ! | mm <- matrix(FALSE, seg$dim[2], seg$dim[1]) |
| 1321 | ! | mm[tm[[1]], tm[[2]]] <- seg$lc |
| 1322 | ! | seg$lc <- mm |
| 1323 | | } |
| 1324 | ! | if (!drop["bet"]) { |
| 1325 | ! | seg$dmat[seg$lc] <- seq_len(seg$ll) |
| 1326 | ! | seg$dmat[!seg$lc] <- seq_len(sum(!seg$lc)) + seg$ll |
| 1327 | | } |
| 1328 | | } |
| 1329 | 33x | ck$legcol <- FALSE |
| 1330 | 33x | if (lpos == "auto") "topright" |
| 1331 | 33x | lega <- list(x = lpos, col = seg$lcols, cex = cex["leg"], text.font = font["leg"], bty = "n", x.intersp = .5, xjust = .5, legend = ptxt$leg) |
| 1332 | 33x | if (ck$legt && (is.character(leg.title) && length(leg.title) == 1 || length(ptxt$by) == 1)) { |
| 1333 | 19x | lega$title <- if (is.character(leg.title)) leg.title else ptxt$by |
| 1334 | | } |
| 1335 | 33x | l <- length(lega$legend) |
| 1336 | 33x | seg$lwd <- rep_len(if (is.numeric(lwd)) lwd else 2, seg$by$ll) |
| 1337 | 30x | if (!"lty" %in% names(seg)) seg$lty <- rep_len(if (!ck$ltym && !ck$lty) lty else if (ck$cbleg && ck$cbb && seg$by$ll == length(cba$by)) as.numeric(cba$by) else if (ck$lty && lty) seq_len(6) else 1, seg$by$ll) |
| 1338 | 31x | if (length(seg$cols) == length(seg$lcols)) names(seg$lcols) <- names(seg$cols) |
| 1339 | 17x | if (seg$by$e || ck$cbb) names(seg$lwd) <- names(seg$lty) <- if (length(seg$lcols) == seg$by$ll) names(seg$lcols) else if (all(c(length(seg$lwd), length(seg$lty)) == length(seg$cols))) names(seg$cols) else seg$by$l |
| 1340 | 33x | lega$lwd <- if (seg$by$ll == l) seg$lwd else rep_len(if (is.numeric(lwd)) lwd else 2, l) |
| 1341 | 33x | lega$lty <- if (seg$by$ll == l) seg$lty else rep_len(if (!ck$ltym && !ck$lty) lty else if (ck$lty && lty) seq_len(6) else 1, l) |
| 1342 | ! | if (!missing(leg.args)) lega[names(leg.args)] <- leg.args |
| 1343 | 33x | if (any(tck <- !names(lega) %in% names(formals(legend)))) { |
| 1344 | ! | warning("dropped items from leg.args: ", paste(names(lega)[tck], collapse = ", "), call. = FALSE) |
| 1345 | ! | lega <- lega[!tck] |
| 1346 | | } |
| 1347 | 33x | if ((ck$legm || !ck$leg) && missing(leg.args) && (sum(strheight(lega$legend, "i")) * cex["leg"] * 1.5 / if ("ncol" %in% names(pdo)) { |
| 1348 | ! | pdo$ncol |
| 1349 | | } else { |
| 1350 | 33x | 1 |
| 1351 | 33x | }) > dev.size()[2]) { |
| 1352 | ! | ck$leg <- 0 |
| 1353 | ! | if (ck$ltym) seg$lty[] <- 1 |
| 1354 | | } |
| 1355 | 33x | if (ck$leg == 1) { |
| 1356 | ! | if (ck$legm && nc > seg$ll) leg <- which(!seg$lc)[1] |
| 1357 | 10x | if (nc > seg$ll && leg <= nc) { |
| 1358 | ! | if (seg$lc[leg] && !drop["bet"]) { |
| 1359 | ! | mm <- which(!seg$lc) |
| 1360 | ! | leg <- mm[which.min(abs(mm - leg))] |
| 1361 | | } |
| 1362 | ! | if (seg$lc[leg]) { |
| 1363 | ! | seg$lc[] <- TRUE |
| 1364 | ! | seg$lc[leg] <- FALSE |
| 1365 | ! | seg$lc[seg$lc][bsq <- seq_len(nc - seg$ll - 1) + seg$ll] <- FALSE |
| 1366 | ! | seg$dmat[seg$lc] <- seq_len(seg$ll) |
| 1367 | ! | seg$dmat[leg] <- seg$ll + 1 |
| 1368 | ! | seg$lc[leg] <- TRUE |
| 1369 | ! | seg$dmat[!seg$lc] <- bsq + 1 |
| 1370 | | } else { |
| 1371 | ! | seg$lc[leg] <- TRUE |
| 1372 | ! | seg$dmat[leg] <- seg$ll + 1 |
| 1373 | ! | seg$dmat[!seg$lc] <- seq_len(sum(!seg$lc)) + seg$ll + 1 |
| 1374 | | } |
| 1375 | ! | if (ck$lp) lega$x <- "center" |
| 1376 | 10x | } else if (ck$lp) lega$x <- "right" |
| 1377 | 10x | if (nc == seg$ll || leg > nc) { |
| 1378 | 10x | seg$dmat[seg$dmat == seg$ll + 1] <- nc + 1 |
| 1379 | 10x | seg$dmat <- rbind(seg$dmat, rep.int(seg$ll + 1, seg$dim[1])) |
| 1380 | 10x | ck$legcol <- TRUE |
| 1381 | | } |
| 1382 | | } |
| 1383 | 33x | seg[c("dmat", "lc")] <- lapply(seg[c("dmat", "lc")], t) |
| 1384 | 33x | seg$prat <- if (missing(prat) && ck$legcol) { |
| 1385 | 10x | lw <- max(.4, if (ck$legt) strwidth(lega$title, "i"), strwidth(ptxt$leg, "i") / if (seg$ll > 1) 1.3 else 1.7) + |
| 1386 | 10x | if (all(seg$dim == 1)) .5 else .2 |
| 1387 | 10x | fw <- (dev.size(units = "in")[1] - lw) / seg$dim[2] |
| 1388 | 10x | c(fw, max(fw / 10, lw)) |
| 1389 | | } else { |
| 1390 | 23x | prat |
| 1391 | | } |
| 1392 | 33x | op <- list( |
| 1393 | 33x | oma = c( |
| 1394 | 33x | sum(is.character(note) && note != "", ck$lx) + .15, ck$ly * .9, |
| 1395 | 33x | max(sum((main != "") * 1.8 + if (sum(seg$dim) > 2) .5 else 0, ck$sud), 1), .5 |
| 1396 | | ), |
| 1397 | 33x | mar = c( |
| 1398 | 33x | if (ck$lx) 2 else 1.5, if (ck$ly) 3 else 2.4, (ck$sud && (ck$su || ck$c)) * |
| 1399 | 33x | ifelse(seg$ll > 1, 2, 0) + (ck$sub && sum(seg$dim) > 2) * 1.3, 0 |
| 1400 | | ), |
| 1401 | 33x | mgp = c(3, .3, 0), |
| 1402 | 33x | font.main = 1, |
| 1403 | 33x | font.lab = 2, |
| 1404 | 33x | cex.main = 1, |
| 1405 | 33x | cex.lab = 1, |
| 1406 | 33x | cex.axis = 1, |
| 1407 | 33x | tcl = -.2, |
| 1408 | 33x | pch = 19, |
| 1409 | 33x | xpd = NA |
| 1410 | | ) |
| 1411 | 33x | if (length(pdo) != 0) { |
| 1412 | ! | if (any(cpdo <- (npdo <- names(pdo)) %in% names(dop))) { |
| 1413 | ! | ck$mai <- "mai" %in% npdo |
| 1414 | ! | op[npdo[cpdo]] <- pdo[cpdo] |
| 1415 | ! | if ("font.sub" %in% names(op)) op$font.main <- op$font.sub |
| 1416 | ! | if ("cex.sub" %in% names(op)) op$cex.main <- op$cex.sub |
| 1417 | ! | if ("col.sub" %in% names(op)) op$col.main <- op$col.sub |
| 1418 | | } |
| 1419 | ! | pdo <- pdo[!cpdo] |
| 1420 | | } |
| 1421 | 33x | if (!"horiz" %in% names(pdo) && !"ncol" %in% names(leg.args)) lega$ncol <- 1 |
| 1422 | 33x | if (length(pdo) != 0) { |
| 1423 | ! | if (any(cpdo <- (npdo %in% names(formals(legend)) & !npdo %in% names(leg.args)))) lega[npdo[cpdo]] <- pdo[cpdo] |
| 1424 | ! | if (any(!cpdo)) { |
| 1425 | ! | warning("unused argument", if (sum(!cpdo) == 1) ": " else "s: ", |
| 1426 | ! | paste(names(pdo)[!cpdo], collapse = ", "), |
| 1427 | ! | call. = FALSE |
| 1428 | | ) |
| 1429 | | } |
| 1430 | | } |
| 1431 | 33x | expand_color_code <- function(e) { |
| 1432 | 363x | if (is.character(e) && all(grepl("^#[0-9a-f]{3}$", e, TRUE))) paste0(e, substring(e, 2)) else e |
| 1433 | | } |
| 1434 | 33x | pdo <- lapply(pdo, expand_color_code) |
| 1435 | 33x | op <- lapply(op, expand_color_code) |
| 1436 | 33x | if (dark) { |
| 1437 | ! | op$fg <- op$col <- op$col.axis <- op$col.main <- op$col.sub <- op$col.sub <- "white" |
| 1438 | ! | if (is.null(op$bg) && par("bg") == "white") warning("foreground and background are both white") |
| 1439 | | } |
| 1440 | 33x | par(op) |
| 1441 | 33x | on.exit(par(dop)) |
| 1442 | 33x | layout(seg$dmat, c(rep.int(seg$prat[1], seg$dim[2]), if (ck$legcol) seg$prat[if (length(seg$prat) > 1) 2 else 1])) |
| 1443 | 33x | success <- FALSE |
| 1444 | 33x | ck$scol <- "scols" %in% names(seg) |
| 1445 | 33x | for (i in names(cdat)) { |
| 1446 | 53x | tryCatch( |
| 1447 | | { |
| 1448 | | # plotting |
| 1449 | 53x | cl <- (if ("list" %in% class(cdat[[i]])) vapply(cdat[[i]], NROW, 0) else nrow(cdat[[i]])) > 0 |
| 1450 | 53x | if (any(!cl)) { |
| 1451 | ! | cdat[[i]] <- cdat[[i]][cl] |
| 1452 | ! | if (length(cdat[[i]]) == 0) next |
| 1453 | | } |
| 1454 | 1x | if (ck$scol) seg$cols <- seg$lcols <- seg$scols[[i]] |
| 1455 | 53x | cl <- strsplit(i, ".^^.", fixed = TRUE)[[1]] |
| 1456 | 53x | ptxt$sub <- if (is.character(sub)) { |
| 1457 | ! | sub |
| 1458 | 53x | } else if (ck$sub) { |
| 1459 | 52x | if (seg$ll > 1 || (!missing(ndisp) && ndisp)) { |
| 1460 | 30x | paste0( |
| 1461 | 30x | if (seg$f1$e) { |
| 1462 | 30x | paste0( |
| 1463 | 30x | if (lvn || (ck$mlvn && grepl("^[0-9]", cl[1]))) paste0(ptxt$bet[1], ": "), cl[1], |
| 1464 | 30x | if (seg$f2$e) paste0(", ", if (lvn || (ck$mlvn && grepl("^[0-9]", cl[2]))) paste0(ptxt$bet[2], ": "), cl[2]) |
| 1465 | | ) |
| 1466 | 30x | }, if ((length(names(cdat)) > 1 || !missing(ndisp)) && ndisp) paste(", n =", seg$n[i]) |
| 1467 | | ) |
| 1468 | | } else { |
| 1469 | | "" |
| 1470 | | } |
| 1471 | | } |
| 1472 | 53x | if (!is.null(sort) && ck$t != 2 && any(class(if (seg$by$e) cdat[[i]][[1]][, "x"] else cdat[[i]][, "x"]) %in% |
| 1473 | 53x | c("factor", "character"))) { |
| 1474 | 1x | nsl <- grepl("^[Ff]", as.character(sort)) |
| 1475 | 1x | sdir <- grepl("^[DdTt]", as.character(sort)) |
| 1476 | 1x | td <- if (seg$by$e) do.call(rbind, cdat[[i]]) else cdat[[i]] |
| 1477 | 1x | td[, "x"] <- as.character(td[, "x"]) |
| 1478 | 1x | cdat[[i]] <- do.call(rbind, lapply( |
| 1479 | 1x | if (nsl) { |
| 1480 | 1x | lvs(td[, "x"]) |
| 1481 | | } else { |
| 1482 | ! | names(sort(vapply(split(td[, "y"], td[, "x"]), mean, 0, na.rm = TRUE), sdir)) |
| 1483 | | }, |
| 1484 | 1x | function(l) td[td[, "x"] == l, , drop = FALSE] |
| 1485 | | )) |
| 1486 | 1x | seg$x$l <- ptxt$l.x <- lvs(cdat[[i]][, "x"]) |
| 1487 | 1x | cdat[[i]][, "x"] <- factor(cdat[[i]][, "x"], seg$x$l) |
| 1488 | ! | if (seg$by$e) cdat[[i]] <- split(cdat[[i]], cdat[[i]][, "by"]) |
| 1489 | | } |
| 1490 | 53x | if (ck$t == 1) { |
| 1491 | | # bar and line |
| 1492 | 20x | flipped <- FALSE |
| 1493 | 20x | if (missing(byx) && ck$mv && any(vapply(cdat[[i]], function(d) { |
| 1494 | ! | any(vapply( |
| 1495 | ! | split(d$y, as.character(d$x)), |
| 1496 | ! | function(dl) if (length(dl) == 1) 0 else var(dl), 0 |
| 1497 | ! | ) == 0) |
| 1498 | 20x | }, TRUE))) { |
| 1499 | ! | byx <- FALSE |
| 1500 | | } |
| 1501 | 20x | if (byx && lim < Inf && seg$by$e && (is.list(cdat[[i]]) && length(cdat[[i]]) > 1)) { |
| 1502 | 14x | flipped <- TRUE |
| 1503 | 14x | cdat[[i]] <- do.call(rbind, cdat[[i]]) |
| 1504 | 14x | cdat[[i]][c("x", "by")] <- cdat[[i]][c("by", "x")] |
| 1505 | ! | if (is.numeric(cdat[[i]]$x)) cdat[[i]]$x <- as.character(cdat[[i]]$x) |
| 1506 | 14x | cdat[[i]] <- split(cdat[[i]], cdat[[i]]$by)[lvs(cdat[[i]]$by)] |
| 1507 | | } |
| 1508 | 20x | dl <- if (cl <- "list" %in% class(cdat[[1]])) length(cdat[[i]]) else 1 |
| 1509 | 20x | mot <- paste0("y~0+", paste(names(if (cl) cdat[[i]][[1]] else cdat[[i]])[c(2, cvar)], collapse = "+")) |
| 1510 | 20x | m <- pe <- ne <- matrix(NA, seg$by$ll, max(c(1, length(seg$x$l))), dimnames = list(seg$by$l, seg$x$l)) |
| 1511 | 14x | if (flipped) m <- pe <- ne <- t(m) |
| 1512 | 20x | rn <- if (nrow(m) == 1) 1 else rownames(m) |
| 1513 | 20x | cn <- if (seg$by$e && flipped) seg$by$l else colnames(m) |
| 1514 | 20x | for (l in seq_len(dl)) { |
| 1515 | 34x | ri <- rn[l] |
| 1516 | 34x | td <- if (cl) cdat[[i]][[ri]] else cdat[[i]] |
| 1517 | ! | if (is.null(td)) next |
| 1518 | 34x | if (nrow(td) > 1 && length(unique(td$x)) > 1) { |
| 1519 | 34x | mo <- lm(mot, data = td) |
| 1520 | 34x | ccn <- sub("^x", "", names(mo$coef)) |
| 1521 | 34x | sus <- which(ccn %in% cn) |
| 1522 | 34x | su <- ccn[sus] |
| 1523 | 34x | m[ri, su] <- mo$coef[sus] |
| 1524 | 34x | if (nrow(td) > 2 && anyDuplicated(td$x)) { |
| 1525 | 33x | if (ck$e) { |
| 1526 | 33x | e <- suppressWarnings(summary(update(mo, ~ . - 0))$coef[sus, 2]) |
| 1527 | 33x | e <- e[c(2, seq_along(e)[-1])] |
| 1528 | 33x | pe[ri, su] <- m[l, su] + e |
| 1529 | 33x | ne[ri, su] <- m[l, su] - e |
| 1530 | | } else { |
| 1531 | ! | e <- confint(mo)[sus, ] |
| 1532 | ! | pe[ri, su] <- e[, 2] |
| 1533 | ! | ne[ri, su] <- e[, 1] |
| 1534 | | } |
| 1535 | | } |
| 1536 | | } else { |
| 1537 | ! | if (nrow(td) == 0) next |
| 1538 | ! | mo <- lapply(split(td, td["x"]), function(s) if (nrow(s) == 0) NA else mean(s[is.finite(s[, "y"]), "y"])) |
| 1539 | ! | m[ri, ] <- unlist(mo[colnames(m)]) |
| 1540 | | } |
| 1541 | | } |
| 1542 | 20x | re <- if (flipped) list(m = t(m), ne = t(ne), pe = t(pe)) else list(m = m, ne = ne, pe = pe) |
| 1543 | 20x | if (ck$ltm && all(apply(is.na(re$m), 2, any))) { |
| 1544 | ! | drop["x"] <- FALSE |
| 1545 | ! | line.type <- "b" |
| 1546 | | } |
| 1547 | 20x | dx <- !apply(is.na(re$m), 2, all) |
| 1548 | 20x | if (drop["x"]) re <- lapply(re, function(s) s[, dx, drop = FALSE]) |
| 1549 | 20x | m <- re$m |
| 1550 | 20x | ne <- re$ne |
| 1551 | 20x | pe <- re$pe |
| 1552 | ! | if (all(mna <- is.na(m))) next |
| 1553 | 20x | re <- lapply(re, function(s) { |
| 1554 | 60x | na <- is.na(s) |
| 1555 | 60x | s[na] <- m[na] |
| 1556 | 60x | s[!mna] |
| 1557 | | }) |
| 1558 | 19x | if (ck$el) ck$el <- all(round(re$m - re$ne, 8) != 0) |
| 1559 | 20x | lb <- min(re$m) - if (!ck$el) round((max(re$m) - min(re$m)) / 10) else max(abs(re$m - re$ne)) * 1.2 |
| 1560 | 2x | if (ck$b && !ck$el) lb <- lb - (max(re$m) - min(re$m)) * .1 |
| 1561 | 20x | dm <- dim(m) |
| 1562 | 20x | ylim <- if (missing(myl)) c(lb, max(re$m) + if (ck$el) max(abs(re$m - re$pe)) else 0) else myl |
| 1563 | 20x | if (ck$leg == 2 && ck$lp) { |
| 1564 | 3x | if (!seg$by$e && ncol(m) == 2) { |
| 1565 | 1x | lega$x <- "top" |
| 1566 | | } else { |
| 1567 | 2x | lega$x <- apply(m, 2, function(r) { |
| 1568 | 4x | na <- !is.na(r) |
| 1569 | ! | if (any(na)) max(r[na]) else -Inf |
| 1570 | | }) |
| 1571 | 2x | stw <- ncol(m) |
| 1572 | 2x | oyl <- if (stw %% 2) 3 else 2 |
| 1573 | 2x | lega$x <- c("topleft", "top", "topright")[if (oyl == 2) -2 else 1:3][which.min(vapply(split(lega$x, rep(seq_len(oyl), |
| 1574 | 2x | each = stw / oyl |
| 1575 | 2x | )[seq_len(stw)]), mean, 0, na.rm = TRUE))] |
| 1576 | ! | if (is.na(lega$x)) lega$x <- "topright" |
| 1577 | | } |
| 1578 | | } |
| 1579 | ! | if (any(is.na(ylim))) next |
| 1580 | 20x | oyl <- axTicks(2, c(ylim[1], ylim[2], par("yaxp")[3])) |
| 1581 | 20x | rn <- if (nrow(m) == 1) colnames(m) else rownames(m) |
| 1582 | 20x | colnames(m) <- if (drop["x"] && sum(dx) == ncol(m)) ptxt$l.x[dx] else ptxt$l.x |
| 1583 | 20x | stw <- strwidth(colnames(m), "i") |
| 1584 | 20x | if ((missing(xlas) || xlas > 1) && sum(stw) > par("fin")[1] - sum(par("omi")[c(2, 4)]) - dm[2] * .1 && par("fin")[1] > 2.5) { |
| 1585 | ! | xlas <- 3 |
| 1586 | ! | if (missing(mxl)) mxl <- c(1, dm[2]) |
| 1587 | ! | mh <- c(par("fin")[2] / 2, max(stw)) |
| 1588 | ! | par(mai = if (ck$mai) op$mai else c(min(mh) + .25, par("mai")[-1])) |
| 1589 | ! | if (mh[1] < mh[2] && missing(labels.trim)) { |
| 1590 | ! | mh <- round(mh[1] / .1) |
| 1591 | ! | n <- colnames(m) |
| 1592 | ! | ln <- nchar(n) > mh |
| 1593 | ! | colnames(m)[ln] <- sub("$", "...", strtrim(n[ln], mh)) |
| 1594 | | } |
| 1595 | | } |
| 1596 | 2x | if (min(re$ne, na.rm = TRUE) >= 0) autori <- FALSE |
| 1597 | 20x | rck <- !is.list(seg$cols) && all(rn %in% names(seg$cols)) |
| 1598 | ! | if (rck && length(seg$cols) < dm[1]) seg$cols <- rep_len(seg$cols, dm[1]) |
| 1599 | 1x | if (!rck && ck$ltm && !ck$el) line.type <- "b" |
| 1600 | 20x | if (ck$b) { |
| 1601 | 7x | if (autori) { |
| 1602 | 5x | a <- if (missing(myl)) lb else myl[1] |
| 1603 | 5x | a <- a * -1 |
| 1604 | 5x | m <- m + a |
| 1605 | 5x | ne <- ne + a |
| 1606 | 5x | pe <- pe + a |
| 1607 | 5x | ayl <- oyl + a |
| 1608 | 5x | aj <- lapply(re, "+", a) |
| 1609 | 5x | ylim <- if (missing(myl)) { |
| 1610 | 5x | if (!ck$el) { |
| 1611 | ! | ylim + a |
| 1612 | | } else { |
| 1613 | 5x | c( |
| 1614 | 5x | min(aj$m) - max(abs(aj$m - aj$ne)) * 1.2, |
| 1615 | 5x | max(aj$m) + max(abs(aj$m - aj$pe)) * if (ck$leg == 2 && seg$by$ll > 1) seg$by$ll^.3 + .7 else 1.2 |
| 1616 | | ) |
| 1617 | | } |
| 1618 | | } else { |
| 1619 | ! | myl + a |
| 1620 | | } |
| 1621 | | } |
| 1622 | 4x | if (dm[1] != 1) rownames(m) <- ptxt$l.by[rn] |
| 1623 | 7x | lega[c("lwd", "lty")] <- NULL |
| 1624 | 7x | lega[c("pch", "pt.cex", "x.intersp", "y.intersp", "adj")] <- list(15, 2, 1, 1.2, c(0, .35)) |
| 1625 | 7x | p <- barplot(m, |
| 1626 | 7x | beside = TRUE, col = if (rck) seg$cols[rn] else seg$cols, axes = FALSE, axisnames = FALSE, |
| 1627 | 7x | border = NA, ylab = NA, xlab = NA, ylim = ylim, main = if (ck$sub) ptxt$sub else NA, |
| 1628 | 7x | xpd = if ("xpd" %in% names(pdo)) pdo$xpd else if (autori) NA else FALSE |
| 1629 | | ) |
| 1630 | | } else { |
| 1631 | 13x | p <- matrix(rep.int(seq_len(dm[2]), dm[1]), nrow = dm[1], byrow = TRUE) |
| 1632 | 13x | plot(NA, |
| 1633 | 13x | ylim = ylim, xlim = if (missing(mxl)) c(1 - stw[1] / 3, dm[2] + stw[length(stw)] / 3) else mxl, ylab = NA, xlab = NA, |
| 1634 | 13x | main = if (ck$sub) ptxt$sub else NA, axes = FALSE |
| 1635 | | ) |
| 1636 | 13x | for (a in if (dm[1] == 1) 1 else if (all(rn %in% names(seg$cols))) rn else seq_len(dm[1])) { |
| 1637 | 23x | graphics::lines(m[a, ], col = seg$cols[[a]], lty = seg$lty[[a]], lwd = seg$lwd[[a]], type = line.type) |
| 1638 | | } |
| 1639 | | } |
| 1640 | ! | if (ck$ileg) lega$legend <- rn |
| 1641 | 20x | if (xaxis) axis(1, colMeans(p), colnames(m), FALSE, las = xlas, cex = par("cex.axis"), fg = par("col.axis")) |
| 1642 | 20x | a2a <- list(2, las = ylas, cex = par("cex.axis"), fg = par("col.axis")) |
| 1643 | 20x | if (ck$b && autori) { |
| 1644 | 5x | a2a$at <- ayl |
| 1645 | 5x | a2a$labels <- formatC(oyl, 2, format = "f") |
| 1646 | | } |
| 1647 | 20x | if (yaxis) do.call(axis, a2a) |
| 1648 | 20x | if (ck$el) { |
| 1649 | 18x | te <- round(Reduce("-", list(ne, pe)), 8) |
| 1650 | 18x | te[is.na(te)] <- 0 |
| 1651 | 18x | te <- te == 0 |
| 1652 | ! | if (any(te)) ne[te] <- pe[te] <- NA |
| 1653 | 18x | arrows(p, ne, p, pe, lwd = error.lwd, col = error.color, angle = 90, code = 3, length = .05) |
| 1654 | | } |
| 1655 | 33x | } else if (ck$t == 2) { |
| 1656 | | # density |
| 1657 | ! | if (!is.list(density.args)) density.args <- list() |
| 1658 | 14x | fdan <- names(formals(stats::density.default)) |
| 1659 | 14x | dan <- names(density.args) |
| 1660 | 14x | if (any(mdan <- !dan %in% fdan)) { |
| 1661 | ! | warning(paste("unused density argument(s):", paste(dan[mdan], collapse = ", ")), call. = FALSE) |
| 1662 | ! | density.args <- density.args[!mdan] |
| 1663 | | } |
| 1664 | 14x | density.args$give.Rkern <- FALSE |
| 1665 | 14x | if (!missing(mxl)) { |
| 1666 | ! | if (!"from" %in% dan) density.args$from <- mxl[1] |
| 1667 | ! | if (!"to" %in% dan) density.args$to <- mxl[2] |
| 1668 | | } |
| 1669 | 10x | if (!"n" %in% dan) density.args$n <- 512 |
| 1670 | 14x | n <- density.args$n |
| 1671 | 14x | m <- list() |
| 1672 | 14x | dl <- if (cl <- "list" %in% class(cdat[[i]])) length(cdat[[i]]) else 1 |
| 1673 | 14x | rnl <- logical(dl) |
| 1674 | 14x | rn <- if (is.data.frame(cdat[[i]])) names(ptxt$l.by) else names(cdat[[i]]) |
| 1675 | 14x | dx <- dy <- numeric(n * seg$by$ll) |
| 1676 | 14x | for (l in seq_len(dl)) { |
| 1677 | 22x | tryCatch( |
| 1678 | | { |
| 1679 | 22x | density.args$x <- (if (cl) cdat[[i]][[l]] else cdat[[i]])[, "y"] |
| 1680 | 22x | m[[l]] <- do.call(stats::density, density.args) |
| 1681 | 22x | dx[seq_len(n) + n * (l - 1)] <- m[[l]]$x |
| 1682 | 22x | dy[seq_len(n) + n * (l - 1)] <- m[[l]]$y |
| 1683 | 22x | rnl[l] <- TRUE |
| 1684 | | }, |
| 1685 | 22x | error = function(e) NULL |
| 1686 | | ) |
| 1687 | | } |
| 1688 | 14x | names(m) <- rn <- rn[rnl] |
| 1689 | 14x | if (seg$by$ll > 1 || (ck$polyo && ck$poly)) { |
| 1690 | 8x | plot(NA, |
| 1691 | 8x | xlim = if (missing(mxl)) range(c(dx, dx)) else mxl, ylim = if (missing(myl)) c(0, max(dy)) else myl, |
| 1692 | 8x | main = if (ck$sub) ptxt$sub else NA, ylab = NA, xlab = NA, axes = FALSE, xpd = if ("xpd" %in% names(pdo)) pdo$xpd else FALSE |
| 1693 | | ) |
| 1694 | 8x | for (l in if (seg$by$ll > 1 && all(rn %in% names(seg$cols))) rn else seq_along(m)) { |
| 1695 | 16x | if (ck$poly) polygon(m[[l]], col = adjustcolor(seg$cols[[l]], density.opacity), border = NA) |
| 1696 | 16x | if (!is.logical(lines) || lines) graphics::lines(m[[l]], col = seg$cols[[l]], lwd = seg$lwd[[l]], lty = seg$lty[[l]]) |
| 1697 | | } |
| 1698 | ! | if (ck$ileg) lega$legend <- rn |
| 1699 | | } else { |
| 1700 | 6x | col <- if (length(seg$lcols) > 2) "#555555" else seg$lcols[1] |
| 1701 | 6x | if (ck$leg) { |
| 1702 | ! | lega[c("lwd", "lty")] <- NULL |
| 1703 | ! | lega[c("pch", "pt.cex", "x.intersp", "y.intersp", "adj")] <- list(15, 2, 1, 1.2, c(0, .35)) |
| 1704 | | } |
| 1705 | 6x | y <- (if (cl) cdat[[i]][[1]] else cdat[[i]])[, "y"] |
| 1706 | 6x | hp <- hist(y, breaks, plot = FALSE) |
| 1707 | 6x | if (ck$cb && length(seg$cols) == nr) { |
| 1708 | ! | nb <- length(hp$counts) |
| 1709 | ! | seg$cols <- vapply(split(seg$cols[order(y)], sort(rep_len(seq_len(nb), nr))), csf, "") |
| 1710 | ! | if (!ckn) seg$cols <- adjustcolor(seg$cols, 1, color.offset, color.offset, color.offset) |
| 1711 | 6x | } else if (!color.lock && (ck$co || length(seg$cols) == 1)) { |
| 1712 | 6x | seg$cols[2] <- adjustcolor(seg$cols[1], 1, color.offset, color.offset, color.offset) |
| 1713 | | } |
| 1714 | 6x | hist( |
| 1715 | 6x | y, breaks, FALSE, |
| 1716 | 6x | border = if ("border" %in% names(pdo)) pdo$border else par("bg"), main = if (ck$sub) ptxt$sub else NA, |
| 1717 | 6x | ylab = NA, xlab = NA, axes = FALSE, col = if (length(seg$cols) == 2) seg$cols[2] else seg$cols, |
| 1718 | 6x | xlim = if (missing(mxl)) range(hp$breaks) else mxl, |
| 1719 | 6x | ylim = if (missing(myl)) c(0, max(c(dy, hp$density))) else myl |
| 1720 | | ) |
| 1721 | 6x | if (!is.logical(lines) || lines) { |
| 1722 | 6x | graphics::lines(m[[1]], col = col, lwd = lwd, xpd = if ("xpd" %in% names(pdo)) { |
| 1723 | ! | pdo$xpd |
| 1724 | | } else { |
| 1725 | 6x | FALSE |
| 1726 | | }) |
| 1727 | | } |
| 1728 | | } |
| 1729 | 2x | if (ck$lp && ck$leg == 2) lega$x <- if (mean(dx) > mean(range(dx))) "topleft" else "topright" |
| 1730 | 14x | if (xaxis) axis(1, las = xlas, cex = par("cex.axis"), fg = par("col.axis")) |
| 1731 | 14x | if (yaxis) axis(2, las = ylas, cex = par("cex.axis"), fg = par("col.axis")) |
| 1732 | | } else { |
| 1733 | | # scatter |
| 1734 | 19x | dl <- if (cl <- "list" %in% class(cdat[[i]])) length(cdat[[i]]) else 1 |
| 1735 | 19x | rn <- if (is.data.frame(cdat[[i]])) seg$by$l else names(cdat[[i]]) |
| 1736 | 19x | td <- if (cl) do.call(rbind, cdat[[i]]) else cdat[[i]] |
| 1737 | 19x | cx <- td[, "x"] |
| 1738 | 19x | cy <- td[, "y"] |
| 1739 | 19x | xch <- if (is.numeric(cx) || is.logical(cx)) cx else as.numeric(factor(cx)) |
| 1740 | 19x | a2a <- list(cex = par("cex.axis"), fg = par("col.axis")) |
| 1741 | 19x | if (length(ptxt$l.x) != 0) { |
| 1742 | ! | a2a$tick <- FALSE |
| 1743 | ! | a2a$at <- seq_along(ptxt$l.x) |
| 1744 | ! | a2a$labels <- ptxt$l.x |
| 1745 | ! | if (missing(xlas) || xlas > 1) { |
| 1746 | ! | xlas <- 3 |
| 1747 | ! | par(mai = if (ck$mai) { |
| 1748 | ! | op$mai |
| 1749 | | } else { |
| 1750 | ! | c( |
| 1751 | ! | min(c(par("fin")[2] / 2, max(strwidth(ptxt$l.x, "i")))) + .25, par("mai")[-1] |
| 1752 | | ) |
| 1753 | | }) |
| 1754 | | } |
| 1755 | | } |
| 1756 | 19x | plot( |
| 1757 | 19x | NA, |
| 1758 | 19x | xlim = if (missing(mxl)) range(xch, na.rm = TRUE) else mxl, |
| 1759 | 19x | ylim = if (missing(myl)) { |
| 1760 | 19x | c(min(cy, na.rm = TRUE), max(cy, na.rm = TRUE) + max(cy, na.rm = TRUE) * |
| 1761 | 19x | if (ck$leg == 1 && seg$by$ll < lim) seg$by$ll / 20 else 0) |
| 1762 | | } else { |
| 1763 | ! | myl |
| 1764 | | }, |
| 1765 | 19x | main = if (ck$sub) ptxt$sub else NA, ylab = NA, xlab = NA, axes = FALSE |
| 1766 | | ) |
| 1767 | 19x | if (yaxis) { |
| 1768 | 19x | do.call(axis, c(list(2, las = ylas), c( |
| 1769 | 19x | a2a[c("cex", "fg")], |
| 1770 | 19x | if ("yax" %in% names(txt)) list(at = seq_along(txt$yax), labels = txt$yax, tick = FALSE) |
| 1771 | | ))) |
| 1772 | | } |
| 1773 | 19x | if (xaxis) do.call(axis, c(list(1, las = xlas), a2a)) |
| 1774 | 19x | if (ck$leg > 1) { |
| 1775 | 5x | up <- xch[cy >= quantile(cy, na.rm = TRUE)[4]] |
| 1776 | 5x | mr <- quantile(xch, na.rm = TRUE) |
| 1777 | 5x | if (ck$lp) lega$x <- if (sum(up < mr[2]) > sum(up > mr[4])) "topright" else "topleft" |
| 1778 | ! | if (ck$ileg) lega$legend <- rn |
| 1779 | | } |
| 1780 | 19x | padj <- if (color.lock || ck$cb || (missing(color.offset) && !ck$ltck)) 1 else color.offset |
| 1781 | 19x | ckcn <- all(rn %in% names(seg$cols)) |
| 1782 | 19x | ckln <- all(rn %in% names(seg$lcols)) |
| 1783 | 19x | if (!ckln) { |
| 1784 | 4x | if (ckcn) { |
| 1785 | ! | seg$lcols <- seg$cols |
| 1786 | | } else { |
| 1787 | 4x | seg$lcols[] <- if (opacity != 1) { |
| 1788 | ! | adjustcolor("#555555", opacity) |
| 1789 | | } else { |
| 1790 | 4x | "#555555" |
| 1791 | | } |
| 1792 | | } |
| 1793 | | } |
| 1794 | 19x | lwd <- rep_len(if (is.numeric(lwd)) lwd else 2, dl) |
| 1795 | 19x | for (l in if (ckcn) rn else seq_len(dl)) { |
| 1796 | 34x | td <- if (cl) cdat[[i]][[l]] else cdat[[i]] |
| 1797 | ! | if (is.null(td)) next |
| 1798 | 34x | x <- td[, "x"] |
| 1799 | 34x | y <- td[, "y"] |
| 1800 | 34x | col <- if (ckcn) seg$cols[[l]] else seg$cols |
| 1801 | 32x | if (opacity != 1 || padj != 1) col <- adjustcolor(col, opacity, padj, padj, padj) |
| 1802 | 34x | if (points && points.first) points(x, y, col = col, cex = cex["points"]) |
| 1803 | 34x | if (ck$ltck) { |
| 1804 | 34x | lt <- if (ck$ltco == "pr" && length(unique(y)) != 2) "li" else ck$ltco |
| 1805 | 34x | fit <- if (lt == "e") { |
| 1806 | ! | y |
| 1807 | | } else { |
| 1808 | 34x | tryCatch( |
| 1809 | | { |
| 1810 | 34x | if (ck$c) { |
| 1811 | ! | lm(y ~ x + as.matrix(td[, cvar, drop = FALSE]))$fitted |
| 1812 | 34x | } else if (lt == "pr") { |
| 1813 | ! | yr <- range(y) |
| 1814 | ! | y <- factor(y, labels = c(0, 1)) |
| 1815 | ! | fit <- predict(glm(y ~ x, binomial)) |
| 1816 | ! | fit <- exp(fit) / (1 + exp(fit)) |
| 1817 | ! | if (!all(yr == c(0, 1))) fit <- (fit - mean(fit)) * (yr[2] - yr[1]) + mean(yr) |
| 1818 | ! | if (max(fit) > yr[2]) fit - (max(fit) - yr[2]) else fit |
| 1819 | | } else { |
| 1820 | 34x | predict(switch(lt, |
| 1821 | 34x | li = lm, |
| 1822 | 34x | lo = loess, |
| 1823 | 34x | sm = smooth.spline |
| 1824 | 34x | )(y ~ x)) |
| 1825 | | } |
| 1826 | | }, |
| 1827 | 34x | error = function(e) { |
| 1828 | ! | warning("error estimating line: ", e$message, call. = FALSE) |
| 1829 | ! | NULL |
| 1830 | | } |
| 1831 | | ) |
| 1832 | | } |
| 1833 | 34x | if (!is.null(fit)) { |
| 1834 | 34x | if (lt == "e") { |
| 1835 | ! | xo <- x |
| 1836 | 34x | } else if (lt == "sm") { |
| 1837 | ! | xo <- fit$x |
| 1838 | ! | fit <- fit$y |
| 1839 | | } else { |
| 1840 | 34x | or <- order(x) |
| 1841 | 34x | xo <- x[or] |
| 1842 | 34x | fit <- fit[or] |
| 1843 | | } |
| 1844 | 34x | graphics::lines(xo, fit, col = seg$lcols[[l]], lty = seg$lty[[l]], lwd = seg$lwd[[l]]) |
| 1845 | | } |
| 1846 | | } |
| 1847 | ! | if (points && !points.first) points(x, y, col = col, cex = cex["points"]) |
| 1848 | | } |
| 1849 | | } |
| 1850 | 53x | if (ck$leg == 2) { |
| 1851 | 10x | if (ck$lpm) { |
| 1852 | ! | message("click to place the legend") |
| 1853 | ! | lega[c("x", "y")] <- locator(1) |
| 1854 | ! | if (is.null(lega$x)) { |
| 1855 | ! | warning("placing the legend with locator(1) failed") |
| 1856 | ! | lega$y <- NULL |
| 1857 | ! | lega$x <- if (seg$ll > 1) "topright" else "right" |
| 1858 | | } |
| 1859 | | } |
| 1860 | 10x | tf <- par("font") |
| 1861 | 10x | par(font = font["leg.title"]) |
| 1862 | 10x | do.call(legend, lega) |
| 1863 | 10x | par(font = tf) |
| 1864 | | } |
| 1865 | 53x | success <- TRUE |
| 1866 | 53x | if (!missing(add)) { |
| 1867 | 1x | add_attempt <- tryCatch(eval(substitute(add), fdat), error = function(e) list(failed = TRUE)) |
| 1868 | 1x | if (is.list(add_attempt) && isTRUE(add_attempt$failed)) { |
| 1869 | ! | tryCatch( |
| 1870 | ! | eval(substitute(add), parent.frame(1)), |
| 1871 | ! | error = function(e) warning("error from add: ", e$message, call. = FALSE) |
| 1872 | | ) |
| 1873 | | } |
| 1874 | | } |
| 1875 | | }, |
| 1876 | 53x | error = function(e) { |
| 1877 | ! | dev.off() |
| 1878 | ! | stop(e) |
| 1879 | | } |
| 1880 | | ) |
| 1881 | | } |
| 1882 | ! | if (!success) stop("failed to make any plots with the current input", call. = FALSE) |
| 1883 | 33x | if (ck$leg == 1) { |
| 1884 | 10x | if (all(par("mfg")[1:2] != 0)) { |
| 1885 | 10x | plot.new() |
| 1886 | 1x | if (ck$b) lega[c("pch", "pt.cex", "x.intersp", "y.intersp", "adj")] <- list(15, 2, 1, 1.2, c(0, .35)) |
| 1887 | 10x | if (ck$lpm) { |
| 1888 | ! | message("click to place the legend") |
| 1889 | ! | lega[c("x", "y")] <- locator(1) |
| 1890 | ! | if (is.null(lega$x)) { |
| 1891 | ! | warning("placing the legend with locator(1) failed") |
| 1892 | ! | lega$y <- NULL |
| 1893 | ! | lega$x <- if (seg$ll > 1) "topright" else "right" |
| 1894 | | } |
| 1895 | | } |
| 1896 | 10x | tf <- par("font") |
| 1897 | 10x | par(font = font["leg.title"]) |
| 1898 | 10x | do.call(legend, lega) |
| 1899 | 10x | par(font = tf) |
| 1900 | | } else { |
| 1901 | ! | warning("legend positioning failed", call. = FALSE) |
| 1902 | | } |
| 1903 | | } |
| 1904 | 33x | if (ck$sud && any(ck$su, ck$c, is.character(sud))) { |
| 1905 | ! | mtext( |
| 1906 | ! | if (is.character(sud)) { |
| 1907 | ! | sud |
| 1908 | | } else { |
| 1909 | ! | gsub(", (?=[A-z0-9 ]+$)", ifelse(length(ptxt$cov) > 2, ", & ", " & "), gsub("^ | $", "", paste0(if (ck$su) { |
| 1910 | ! | paste("Subset:", paste0(txt$su[1], if (length(txt$su) != 1) "...")) |
| 1911 | ! | }, if (ck$su && ck$c) ", ", if (ck$c) { |
| 1912 | ! | paste(if (ck$t == 1) "Covariates:" else "Line adjustment:", paste(ptxt$cov, collapse = ", ")) |
| 1913 | ! | })), TRUE, TRUE) |
| 1914 | | }, |
| 1915 | ! | 3, 0, TRUE, |
| 1916 | ! | cex = cex["sud"], font = font["sud"] |
| 1917 | | ) |
| 1918 | | } |
| 1919 | 33x | mtext(main, 3, if (ck$sud) 1.5 else .5, TRUE, cex = cex["title"], font = font["title"]) |
| 1920 | 33x | mtext(ylab, 2, -.2, TRUE, cex = par("cex.lab"), font = par("font.lab")) |
| 1921 | 33x | mtext(xlab, 1, 0, TRUE, cex = par("cex.lab"), font = par("font.lab")) |
| 1922 | 22x | if (is.character(note)) mtext(note, 1, ck$lx, TRUE, adj = if (ck$ly) 0 else .01, font = font["note"], cex = cex["note"]) |
| 1923 | 33x | if (save || (missing(save) && any(!missing(format), !missing(file.name), !missing(dims)))) { |
| 1924 | 1x | tryCatch( |
| 1925 | | { |
| 1926 | 1x | if (is.character(format) || is.name(format)) { |
| 1927 | 1x | t <- as.character(format) |
| 1928 | 1x | format <- eval(parse(text = t)) |
| 1929 | | } else { |
| 1930 | ! | t <- deparse(substitute(format)) |
| 1931 | | } |
| 1932 | 1x | if (is.function(format)) t <- sub("^[^:]*::", "", t) |
| 1933 | 1x | tt <- if (any(grepl("cairo", t, TRUE))) { |
| 1934 | ! | paste0(".", tolower(strsplit(t, "_|Cairo")[[1]][2])) |
| 1935 | 1x | } else if (t == "postscript") ".ps" else paste0(".", t) |
| 1936 | ! | if (missing(dims) && grepl("jpeg|png|tiff|bmp|bit", t, TRUE)) dims <- dev.size(units = "px") |
| 1937 | 1x | fn <- paste0(if (main == "" || !missing(file.name)) { |
| 1938 | 1x | sub("\\.[^.]+$", "", file.name) |
| 1939 | | } else { |
| 1940 | ! | gsub("\\s+", "_", gsub("^ +| +$| ", "", main)) |
| 1941 | 1x | }, tt) |
| 1942 | 1x | dev.copy(format, fn, width = dims[1], height = dims[2]) |
| 1943 | 1x | dev.off() |
| 1944 | ! | if (file.exists(fn)) message("image saved: ", fn) else warning("failed to save image") |
| 1945 | | }, |
| 1946 | 1x | error = function(e) warning("unable to save image: ", e$message, call. = FALSE) |
| 1947 | | ) |
| 1948 | | } |
| 1949 | 33x | invisible(list(dat = dat, cdat = cdat, txt = txt, ptxt = ptxt, seg = seg, ck = ck, lega = lega, fmod = fmod)) |
| 1950 | | } |