#adapted (taken) from dotchart with some minor addition of confidence intervals and to interface with statsBy, describeBy and cohen.d #July 17, 2016 #input is the mean + standard errors, and (optionally, alpha) #August 12, added the ability to find (and save) the stats using describe or describeBy #Modified Oct, 4, 2019 to include cohen.d values #corrected April 27, 2022 to correctly call Item in dictionary "error.dots" <- function (x=NULL,var=NULL, se=NULL, group=NULL,sd=FALSE, effect=NULL, stats=NULL, head = 12, tail = 12, sort=TRUE,decreasing=TRUE,main=NULL, alpha=.05,eyes=FALSE, items=FALSE, min.n = NULL,max.labels =40, labels = NULL, label.width=NULL, select=NULL, groups = NULL, gdata = NULL, cex = par("cex"), pt.cex = cex, pch = 21, gpch = 21, bg = par("bg"), fg=par("fg"), color = par("fg"), gcolor = par("fg"), lcolor = "gray", xlab = NULL, ylab = NULL,xlim=NULL,add=FALSE,order=NULL, ...) { opar <- par("mai", "mar", "cex", "yaxs") on.exit(par(opar)) par(cex = cex, yaxs = "i") #first, see if the data come from a psych object with sd and n or se if(length(class(x)) > 1) { cohen.d <- fa.ci <- NULL #strange fix to R compiler names <- cs(statsBy,describe,describeBy, fa.ci,bestScales,cohen.d, cor.ci,corr.test) value <- inherits(x,names,which=TRUE) # value <- class(x)[2] if(any(value > 1) ) { obj <- names[which(value > 0)]} else {obj <- "other"} } else {obj <- "other"} # if(length(class(x)) > 1 ) {if (class(x)[1] == "psych") {obj <- class(x)[2] des <- NULL switch(obj, statsBy = {if(is.null(min.n)) { if(!is.null(effect)) { #convert means to effect sizes compared to a particular group x$mean[,var] <- ( x$mean[,var] -x$mean[effect,var])/x$sd[effect,var] } se <- x$sd[,var]/sqrt(x$n[,var]) x <- x$mean[,var] } else {se <- x$sd[,var] n.obs <- x$n[,var] x <- x$mean[,var] if(!is.null(effect)) { #convert means to effect sizes compared to a particular group x <- ( x$mean[,var] -x$mean[effect,var])/x$sd[effect,var] } if(sd) {se <- x$sd[,var] } else {se <- se/sqrt(n.obs)} x <- subset(x,n.obs > min.n) se <- subset(se,n.obs > min.n) n.obs <- subset(n.obs, n.obs > min.n) }}, describe = {if(sd) {se <- x$sd} else {se <- x$se} if(is.null(labels)) labels <- rownames(x) x <- x$mean names(x) <- labels }, describeBy = {des <- x if(is.null(xlab)) xlab <- var var <- which(rownames(des[[1]]) == var) x <- se <- rep(NA,length(des)) for(grp in 1:length(x)) { x[grp] <- des[[grp]][["mean"]][var] if(sd) {se[grp] <- des[[grp]][["sd"]][var]} else {se[grp] <- des[[grp]][["se"]][var]} } names(x) <- names(des) if(is.null(xlab)) xlab <- var }, fa.ci ={se = x$cis$sds if(is.null(labels)) labels <-rownames(x$cis$means) x <-x$cis$means }, bestScales = {if(!missing(items)) { se=x$items[[items]][,3,drop=FALSE] browser() rn=rownames(x$items[[items]]) x <- x$items[[items]][,2,drop=FALSE] names(x) <- rn des <- NULL } else {se <- x$stats$se rn <- rownames(x$stats) x <- x$stats$mean names(x) <-rn des <- NULL} }, cohen.d = {des <- x$cohen.d[,"effect"] se <- x$se if(!is.null(x$dict)) { cn <- colnames(x$dict)[which(colnames(x$dict) %in% c("Content","Items","content","item","Item"))] # names <- x$dict %in% c("Content","Items","content","item","Items") names <- x$dict[,cn] # names <- x$dict[,"Content"] } else {names <- rownames(x$cohen.d)} x <- des names(x) <- names sd <- TRUE #use these values for the confidence intervals }, reliability ={ x <- x$splits if (sort) { if(is.null(order)) {ord <- order(x,decreasing=!decreasing) } else {ord<- order} } else {ord <- n.var:1} x <- x[ord] names <- rownames(x) se <- NULL }, cor.ci = {des=x$means se = x$sds names= rownames(x$ci) x <- x$means names(x) <- names if(missing(main)) {main="Confidence intervals of correlations"} if(missing(xlab) ) {xlab="Correlation"} }, corr.test = {des= x$ci$r if(x$sym) {se= x$se[lower.tri(x$se)]} else { se = as.vector(x$se)} names= rownames(x$ci) x <- x$ci$r names(x) <- names if(missing(main)) {main="Confidence intervals of correlations"} if(missing(xlab) ) {xlab="Correlation"} }, other = {} #an empty operator )#end switch if (obj=="other"){ if(is.null(group)) { #the case of just one observation per condition if(is.null(stats)) { if(is.null(dim(x))) {se <- rep(0,length(x)) des <- x labels=NULL } else { des <- describe(x) x <-des$mean if(sd) { se <- des$sd} else {se <- des$se} names(x) <- rownames(des)} } else { #the normal case is to find the means and se x <- stats$mean se <- stats$se names(x) <- rownames(stats) des <- NULL } } else { if(is.null(xlab)) xlab <- var des <- describeBy(x,group=group) x <- se <- rep(NA,length(des)) names(x) <- names(des) var <- which(rownames(des[[1]]) == var) for(grp in 1:length(des)) { x[grp] <- des[[grp]][["mean"]][var] if(sd) { se[grp] <- des[[grp]][["sd"]][var]} else {se[grp] <- des[[grp]][["se"]][var]} }} } n.var <- length(x) # if(!is.null(se) && !sd) {ci <- qnorm((1-alpha/2))*se} else {ci <- NULL} if (sort) { if(is.null(order)) {ord <- order(x,decreasing=!decreasing) } else {ord<- order} } else {ord <- n.var:1} x <- x[ord] se <- se[ord] temp <- temp.se <- rep(NA,min(head+tail,n.var)) if((head+tail) < n.var) { if (head > 0 ){ temp[1:head] <- x[1:head] temp.se[1:head] <- se[1:head] names(temp) <- names(x)[1:head] } if(tail > 0 ) {temp[(head + 1):(head + tail)] <- x[(length(x)-tail+1):length(x)] temp.se[(head + 1):(head + tail)] <- se[(length(x)-tail+1):length(x)] names(temp)[(head + 1):(head + tail)] <- names(x)[(length(x)-tail+1):length(x)] } x <- temp se <- temp.se } if(missing(main)) {if(sd) {main <- "means + standard deviation"} else {main="Confidence Intervals around the mean"}} if(is.null(labels)) labels <- names(x) if(sd) {ci <- se} else {ci <- qnorm((1-alpha/2))*se} # if(!is.null(se)) {ci <- qnorm((1-alpha/2))*se} else {ci <- NULL} if(!is.null(ci) && is.null(xlim)) xlim <- c(min(x - ci,na.rm=TRUE),max(x + ci,na.rm=TRUE)) labels <- substr(labels,1,max.labels) if(eyes) { #get ready to draw catseyes ln <- seq(-3,3,.1) rev <- (length(ln):1) } if (!is.numeric(x)) stop("'x' must be a numeric vector or matrix") n <- length(x) if (is.matrix(x)) { if (is.null(labels)) labels <- rownames(x) if (is.null(labels)) labels <- as.character(1L:nrow(x)) labels <- rep_len(labels, n) if (is.null(groups)) groups <- col(x, as.factor = TRUE) glabels <- levels(groups) } else { if (is.null(labels)) labels <- names(x) glabels <- if (!is.null(groups)) levels(groups) if (!is.vector(x)) { warning("'x' is neither a vector nor a matrix: using as.numeric(x)") x <- as.numeric(x) } } if(!add) plot.new() linch <- if (!is.null(labels)) max(strwidth(labels, "inch"),label.width, na.rm = TRUE) else 0 if (is.null(glabels)) { ginch <- 0 goffset <- 0 } else { ginch <- max(strwidth(glabels, "inch"),label.width, na.rm = TRUE) goffset <- 0.4 } if (!(is.null(labels) && is.null(glabels))) { nmai <- par("mai") nmai[2L] <- nmai[4L] + max(linch + goffset, ginch) + 0.1 par(mai = nmai) } if (is.null(groups)) { o <- 1L:n if(!is.null(select)) o <- o[select] y <- o x <- x[o] if(!is.null(ci)) ci <- ci[o] ylim <- c(0, n + 1) } else { o <- sort.list(as.numeric(groups), decreasing = TRUE) x <- x[o] groups <- groups[o] color <- rep_len(color, length(groups))[o] lcolor <- rep_len(lcolor, length(groups))[o] offset <- cumsum(c(0, diff(as.numeric(groups)) != 0)) y <- 1L:n + 2 * offset ylim <- range(0, y + 2) } plot.window(xlim = xlim, ylim = ylim, log = "") lheight <- par("csi") if (!is.null(labels)) { linch <- max(strwidth(labels, "inch"), na.rm = TRUE) loffset <- (linch + 0.1)/lheight labs <- labels[o] mtext(labs, side = 2, line = loffset, at = y, adj = 0, col = color, las = 2, cex = cex, ...) } abline(h = y, lty = "dotted", col = lcolor) points(x, y, pch = pch, col = color, bg = bg, cex = pt.cex/cex) if(!is.null(ci)) {if(!eyes) { segments(x - ci, y, x+ci, y,bg=bg,col=fg,...) # col = par("fg"), lty = par("lty"), lwd = par("lwd")) } } if (!is.null(groups)) { gpos <- rev(cumsum(rev(tapply(groups, groups, length)) + 2) - 1) ginch <- max(strwidth(glabels, "inch"), na.rm = TRUE) goffset <- (max(linch + 0.2, ginch, na.rm = TRUE) + 0.1)/lheight mtext(glabels, side = 2, line = goffset, at = gpos, adj = 0, col = gcolor, las = 2, cex = cex, ...) if (!is.null(gdata)) { abline(h = gpos, lty = "dotted") points(gdata, gpos, pch = gpch, col = gcolor, bg = bg, cex = pt.cex/cex, ...) } } if(eyes) { for (e in 1:(min(head+tail,n.var))) {catseye(x[e],y[e],ci[e]/qnorm(1-alpha/2),alpha=alpha,density=density) }} if(!add) axis(1) if(!add) box() title(main = main, xlab = xlab, ylab = ylab, ...) result <- list(des =des,order=ord) invisible(result) #report the order if sort } #modified from catseyes in error.bars "catseye" <- function(x,y,ci,alpha,density=density,col=col) { SCALE=.7 ln <- seq(-3,3,.1) rev <- (length(ln):1) norm <- dnorm(ln) # clim <- qnorm(alpha/2) #norm <- dt(ln,n-1) clim <- qnorm(alpha/2) #clim <- ci norm <- c(norm,-norm[rev]) ln <- seq(-3,3,.1) cln <- seq(clim,-clim,.1) cnorm <- dnorm(cln) cnorm <- c(0,cnorm,0,-cnorm,0) #this closes the probability interval # polygon(norm*SCALE*ci+x,c(ln,ln[rev])+y) polygon(c(ln,ln[rev])*ci+x, norm*SCALE+y) # polygon(cnorm*SCALE+x,c(clim,cln,-clim,-cln,clim)*ci+y,density=density,col=col) }