### Install Development Version of dotwhisker Source: https://fsolt.org/dotwhisker/index.html Install the latest development version of the dotwhisker package from GitHub. Ensure the 'remotes' package is installed first. ```r if (!require("remotes")) install.packages("remotes"); remotes::install_github("fsolt/dotwhisker") ``` -------------------------------- ### Initialize Empty List for Estimates Source: https://fsolt.org/dotwhisker/articles/kl2007_examples.html Initializes an empty list to store regression estimates. This is a common starting point before populating it with data. ```R Y1 <- vector(length=0,mode="list") #estimates with law change (in the 4th to 6th columns) Y1$estimate <- coef.matrix[,4:6] ##95% confidence intervals Y1$lo <- coef.matrix[,4:6]-qnorm(0.975)*se.matrix[,4:6] Y1$hi <- coef.matrix[,4:6]+qnorm(0.975)*se.matrix[,4:6] ##90% confidence intervals Y1$lo1 <- coef.matrix[,4:6]-qnorm(0.95)*se.matrix[,4:6] Y1$hi1 <- coef.matrix[,4:6]+qnorm(0.95)*se.matrix[,4:6] ##name the rows of Y1 estimate rownames(Y1$estimate) <- varnames[-7] ##no intercept ``` -------------------------------- ### Install Latest Release Version of dotwhisker Source: https://fsolt.org/dotwhisker/index.html Use this command to install the latest stable release of the dotwhisker package from CRAN. ```r install.packages("dotwhisker") ``` -------------------------------- ### Load Required Packages and Run Regression Source: https://fsolt.org/dotwhisker/articles/dotwhisker-vignette.html Loads the dotwhisker and dplyr packages, then runs a linear regression model using the mtcars dataset. This setup is necessary for generating dot-and-whisker plots. ```r #Package preload library(dotwhisker) library(dplyr) # run a regression compatible with tidy m1 <- lm(mpg ~ wt + cyl + disp + gear, data = mtcars) ``` -------------------------------- ### Example Usage of add_brackets with dwplot Source: https://fsolt.org/dotwhisker/reference/add_brackets.html Demonstrates how to use add_brackets to group predictors in a dot-and-whisker plot. The example first creates a linear model, defines two sets of brackets, generates a dwplot with custom relabeling and theming, and then applies the add_brackets function to the plot. ```R library(dplyr) #> #> Attaching package: 'dplyr' #> The following objects are masked from 'package:stats': #> #> filter, lag #> The following objects are masked from 'package:base': #> #> intersect, setdiff, setequal, union m1 <- lm(mpg ~ wt + cyl + disp, data = mtcars) two_brackets <- list(c("Engine", "Cylinder", "Displacement"), c("Not Engine", "Intercept", "Weight")) {dwplot(m1, show_intercept = TRUE) %>% relabel_predictors("(Intercept)" = "Intercept", wt = "Weight", cyl = "Cylinder", disp = "Displacement") + theme_bw() + xlab("Coefficient") + ylab("") + theme(legend.position="none") + geom_vline(xintercept = 0, colour = "grey50", linetype = 2)} %>% add_brackets(two_brackets) ``` -------------------------------- ### Rescale Regression Results with by_2sd Source: https://fsolt.org/dotwhisker/reference/by_2sd.html Example of using by_2sd to rescale regression results from a linear model for plotting. Requires the broom and dplyr libraries. ```R library(broom) library(dplyr) data(mtcars) m1 <- lm(mpg ~ wt + cyl + disp, data = mtcars) m1_df <- tidy(m1) %>% by_2sd(mtcars) # create data frame of rescaled regression results ``` -------------------------------- ### Advanced Plotting with Multiple Models and Customization Source: https://fsolt.org/dotwhisker/reference/dwplot.html Generate a complex dot-and-whisker plot from multiple models created on the fly. This example includes relabeling predictors, applying a theme, customizing axes and title, and adjusting legend appearance. ```R # Plot regression coefficients from multiple models on the fly mtcars %>% split(.$am) %>% purrr::map(~ lm(mpg ~ wt + cyl + disp, data = .x)) %>% dwplot() %>% relabel_predictors(c(wt = "Weight", cyl = "Cylinders", disp = "Displacement")) + theme_bw() + xlab("Coefficient") + ylab("") + geom_vline(xintercept = 0, colour = "grey60", linetype = 2) + ggtitle("Predicting Gas Mileage, OLS Estimates") + theme(plot.title = element_text(face = "bold"), legend.position = c(.995, .99), legend.justification = c(1, 1), legend.background = element_rect(colour="grey80"), legend.title.align = .5) + scale_colour_grey(start = .4, end = .8, name = "Transmission", breaks = c("Model 0", "Model 1"), labels = c("Automatic", "Manual")) #> Warning: The `legend.title.align` argument of `theme()` is deprecated as of ggplot2 #> 3.5.0. #> ℹ Please use theme(legend.title = element_text(hjust)) instead. #> Warning: A numeric `legend.position` argument in `theme()` was deprecated in ggplot2 #> 3.5.0. #> ℹ Please use the `legend.position.inside` argument of `theme()` instead. ``` -------------------------------- ### Integrating Model Fit Statistics with Relabeling and Brackets Source: https://fsolt.org/dotwhisker/articles/dotwhisker-vignette.html This example demonstrates a workaround to combine model fit statistics with relabeled predictors and added brackets on a dot-whisker plot. It involves separately generating the plot with brackets and then appending the model fit statistics using `gridExtra` and `patchwork`. ```r library(gridExtra) library(patchwork) three_brackets <- list( c("Overall", "Weight", "Weight"), c("Engine", "Cylinders", "Horsepower"), c("Transmission", "Gears", "Manual") ) plot_brackets <- { dwplot(m3, vline = geom_vline( xintercept = 0, colour = "grey60", linetype = 2 )) |> # plot line at zero _behind_ coefs relabel_predictors( c( wt = "Weight", # relabel predictors cyl = "Cylinders", disp = "Displacement", hp = "Horsepower", gear = "Gears", am = "Manual" ) ) + xlab("Coefficient Estimate") + ylab("") + ggtitle("Predicting Gas Mileage") } |> add_brackets(three_brackets, fontSize = 0.3) plot_brackets / tableGrob( dotwhisker:::dw_stats( m3, stats_digits = 2, stats_compare = FALSE ), rows = NULL, theme = ttheme_default(base_size = 3) ) + plot_layout(heights = c(5, -0.5, 1)) # the negative value is used to adjust the space between the plot and the model fits ``` -------------------------------- ### Generate Secret Weapon Plot from Tidy Data Frame Source: https://fsolt.org/dotwhisker/reference/secret_weapon.html This example demonstrates how to generate a 'secret weapon' plot from a tidy data frame of regression results. It first estimates linear models across different groups and then uses `secret_weapon` to visualize the 'carat' coefficient. ```R library(dplyr) library(broom) # Estimate models across many samples, put results in a tidy data frame by_clarity <- diamonds %>% group_by(clarity) %>% do(tidy(lm(price ~ carat + cut + color, data = .))) %>% ungroup %>% rename(model = clarity) # Generate a 'secret weapon' plot of the results of diamond size secret_weapon(by_clarity, "carat") ``` -------------------------------- ### Advanced Customization: Ordering, Relabeling, and Theming Source: https://fsolt.org/dotwhisker/articles/dotwhisker-vignette.html Customizes the plot by ordering models and variables, relabeling predictors, and applying custom ggplot2 themes and layers. This example also adds a vertical line at zero. ```r dwplot(list(m1, m2, m3), vline = geom_vline( xintercept = 0, colour = "grey60", linetype = 2 ), vars_order = c("am", "cyl", "disp", "gear", "hp", "wt"), model_order = c("Model 2", "Model 1", "Model 3") ) |> # plot line at zero _behind_coefs relabel_predictors( c( am = "Manual", cyl = "Cylinders", disp = "Displacement", wt = "Weight", gear = "Gears", hp = "Horsepower" ) ) + theme_bw(base_size = 4) + # Setting `base_size` for fit the theme # No need to set `base_size` in most usage xlab("Coefficient Estimate") + ylab("") + geom_vline(xintercept = 0, colour = "grey60", linetype = 2) + ggtitle("Predicting Gas Mileage") + theme( plot.title = element_text(face = "bold"), legend.position = c(0.007, 0.01), legend.justification = c(0, 0), legend.background = element_rect(colour = "grey80"), legend.title = element_blank() ) ``` -------------------------------- ### Building Tidy Data Frame Directly Source: https://fsolt.org/dotwhisker/articles/dotwhisker-vignette.html Demonstrates how to build a tidy data frame of regression results directly, without first creating model objects, and relabeling predictors. ```r # Run model on subsets of data, save results as tidy df, make a model variable, and relabel predictors by_trans <- mtcars | group_by(am) |> # group data by trans do(broom::tidy(lm(mpg ~ wt + cyl + disp + gear, data = .))) |> # run model on each grp rename(model = am) |> # make model variable relabel_predictors(c( wt = "Weight", # relabel predictors cyl = "Cylinders", disp = "Displacement", gear = "Gear" )) by_trans ``` -------------------------------- ### Initialize List for Estimates Without Law Change Source: https://fsolt.org/dotwhisker/articles/kl2007_examples.html Initializes and populates a list for regression estimates that do not include a law change, calculating confidence intervals. ```R #estimates without law change Y2 <- vector(length=0,mode="list") Y2$estimate <- coef.matrix[,1:3] Y2$lo <- coef.matrix[,1:3]-qnorm(.975)*se.matrix[,1:3] Y2$hi <- coef.matrix[,1:3]+qnorm(.975)*se.matrix[,1:3] Y2$lo1 <- coef.matrix[,1:3]-qnorm(.95)*se.matrix[,1:3] Y2$hi1 <- coef.matrix[,1:3]+qnorm(.95)*se.matrix[,1:3] rownames(Y2$estimate) <- varnames[-7] ``` -------------------------------- ### Prepare Regression Data Matrices Source: https://fsolt.org/dotwhisker/articles/kl2007_examples.html Initializes matrices for point estimates and standard errors, along with R-squared values and variable names, for multiple regression models. This is a precursor to plotting. ```R library(grid) ##point estimates, in a n.variables, n.variables x n.models coef.matrix <- matrix(c(-.039, NA, .048, -.133, .071, -.795, 1.47, -.036, NA, .036, -.142, .07, -.834, 1.70, -.051, NA, .017, .05, .011, -.532, .775, -.037, -.02, .047, -.131,.072, -.783, 1.45, -.034, -.018, -.035, -.139, .071, -.822, 1.68, -.05, -.023, .016,-.049, .013, -.521, .819),nr=7) ## R2 of the models R2<- c(0.910, 0.910, 0.940, 0.910, 0.910, 0.940) ##standard error matrix, n.variables x n.models se.matrix <- matrix(c(.003, NA, .011, .013, .028, .056, .152, .003, NA, .012, .014, .029, .059, .171, .003, NA, .01, .013, .024, .044, .124, .003, .005, .011, .013, .028, .055, .152, .003, .005, .021, .014, .029, .059, .17, .003,.006, .01, .013, .024, .044, .127),nr=7) ##variable names varnames<- c("% of county\nregistration", "Law change", "Log population", "Log median\nfamily income", "% population with\nh.s. education" ,"% population\nAfrican American" ,"Constant") ##exclude intercept coef.matrix<-coef.matrix[-(7,)] se.matrix<-se.matrix[-7,] ## each panel has at most six models, plotted in pairs. ## in each pair, solid circles will be the models with "law change" in the specification ``` -------------------------------- ### Basic Secret Weapon Plot Source: https://fsolt.org/dotwhisker/articles/dotwhisker-vignette.html A basic example of a 'secret weapon' plot, which visualizes regression coefficients with confidence intervals. This is useful for presenting results from a single model. ```R secret_weapon(by.clarity, var = "carat") + xlab("Estimated Coefficient (Dollars)") + ylab("Diamond Clarity") + ggtitle("Estimates for Diamond Size Across Clarity Grades") + theme(plot.title = element_text(face = "bold")) ``` -------------------------------- ### Create and Populate a Graphical Frame Source: https://fsolt.org/dotwhisker/articles/kl2007_examples.html This snippet demonstrates how to create a graphical frame (frameGrob) and populate it with plots and labels using a loop. It's useful for building multi-panel plots. ```R fg <- frameGrob(layout=grid.layout(n.plots*2, 3,widths=unit(widths,"npc"), heights=unit(c(rep(c(hit,hbet),n.plots-1),hit,hlast),"npc"))) ## loop to create panels ## j indexes independent variables j <- 1 for (i in index) { ## i is the vertical slot position #create a dataframe with the data to plot now Y.now <- data.frame(estimate=Y$estimate[j,], lo=Y$lo[j,], hi=Y$hi[j,], lo1=Y$lo1[j,], hi1=Y$hi1[j,]) ##similartly for Y2 if (!is.null(Y2)) Y2.now <- data.frame(estimate=Y2$estimate[j,], lo=Y2$lo[j,], hi=Y2$hi[j,], lo1=Y2$lo1[j,], hi1=Y2$hi1[j,]) else Y2.now <- NULL ## if it is the bottom row, set sp.now to a positive value if (i==max(index)) sp.now <- .1 ##are we drawing a reference line? drawRef <- !is.na(refline[j]) ##place the plot ##the actual plot object is created by the function grid.Vdotplot fg <- placeGrob(fg, grid.Vdotplot(Y.now, Y2.now, sp=c(.1,sp.now),draw=FALSE,lwd.fact=lwd.fact, refline=ifelse(drawRef,refline[j],0)) ,col=2,row=i) ##the independent variables labels fg <- placeGrob(fg,textGrob(x=pos.label.y,label.vec.vars[j] ,rot=rot.label.y,gp=gpar(cex=.75*lwd.fact),just=just.label.y ),col=1,row=i) j <- j+1 } ## if Y2 exists and a legend is specified, draw it using the legendGrob function if (!is.null(Y2)&!is.null(legend)) { fg <- placeGrob(fg,legendGrob(c(21,21),legend,cex=leg.mult,leg.fontsize=leg.fontsize,fill=c("black","white")),col=1,row=i+1) } if (print) { grid.arrange(fg) } else { ## if we are not printing, return the graphical object fg } ``` -------------------------------- ### Format Data and Create Plot with dotwhisker Source: https://fsolt.org/dotwhisker/articles/kl2007_examples.html This snippet demonstrates how to format data into a tidy dataframe and then use the small_multiple function from the dotwhisker package to create a plot with custom themes and labels. It includes data preparation and plot customization steps. ```R # Format data as tidy dataframe model_names <- c("Full Sample\n", "Excluding counties\nw/ partial registration\n", "Full sample w/\nstate year dummies\n") submodel_names <- c("With","Without") model_order <- c(4, 1, 5, 2, 6, 3) results_df <- data.frame(term = rep(varnames[1:6], times = 6), estimate = as.vector(coef.matrix[, model_order]), std.error = as.vector(se.matrix[, model_order]), model = as.factor(rep(model_names, each = 12)), submodel = rep(rep(submodel_names, each = 6), times = 3), stringsAsFactors = FALSE) small_multiple(results_df, show_stats = FALSE) + scale_x_discrete(limits = model_names) + # order the models theme_bw() + ylab("Coefficient Estimate") + geom_hline(yintercept = 0, colour = "grey60", linetype = 2) + theme(axis.text.x = element_text(angle = 45, hjust = 1), legend.position=c(.97, .99), legend.justification=c(1, 1), legend.title = element_text(size=8), legend.background = element_rect(color="gray90"), legend.spacing = unit(-3, "pt"), legend.key.size = unit(10, "pt")) + scale_colour_hue(name = "Law Change\nDummy") + ggtitle("Registration Effects on Turnout\nAnsolabehere and Konisky (2006)\nvia Kastellec and Leoni (2007)") ``` -------------------------------- ### Basic dwplot Usage Source: https://fsolt.org/dotwhisker/articles/dotwhisker-vignette.html Demonstrates the basic usage of the dwplot function with a pre-existing model data frame. ```r dwplot(m1_df) #same as dwplot(m1) ``` -------------------------------- ### Rescaling Coefficients with by_2sd Source: https://fsolt.org/dotwhisker/articles/dotwhisker-vignette.html Shows how to rescale coefficients by two standard deviations using the `by_2sd` function for easier comparison, particularly with dichotomous predictors. The results are then sorted alphabetically by term. ```R # Customize the input data frame m1_df_mod <- m1_df |> # the original tidy data.frame by_2sd(mtcars) |> # rescale the coefficients arrange(term) # alphabetize the variables m1_df_mod # rescaled, with variables reordered alphabetically ``` ```R dwplot(m1_df_mod) ``` -------------------------------- ### Original R Code for Dot Plot with Error Bars Source: https://fsolt.org/dotwhisker/articles/kl2007_examples.html This code generates a dot plot with error bars for a single regression model using base R graphics. It requires manual setup of coefficient and standard error vectors, variable names, and plot margins. ```R #Create vectors for coefficients, standard errors and variable names #we place coefficient as last element in each vector rather than 1st #since it is least important predictor, and thus we place it at the bottom of the graph #note: we exclude the constant, since it is substantively meaningless coef.vec <- c( 1.31, .93, 1.46, .07, .96, .2, .22, -.21, -.32, -.27,.23, 0, -.03, .13, .15, .31, -.10) se.vec <- c( .33, .32, .32, .37, .37, .13, .12, .12, .12, .07, .07, .01, .21, .14, .29, .25, .27) var.names <- c("Argentina", "Chile", "Colombia", "Mexico", "Venezuela", #for longer names, we split into 2 lines using "\n" function "Retrospective Egocentric", "Prospective Egocentric", "Retrospective Sociotropic", "Prospective Sociotropic", "Distance from President", "Ideology", "Age", "Female", "Education", "Academic Sector", "Business Sector", "Government Sector") y.axis <- c(length(coef.vec):1)#create indicator for y.axis, descending so that R orders vars from top to bottom on y-axis oldpar <- par(mar=c(2, 13, 0, 0))#set margins for plot, leaving lots of room on left-margin (2nd number in margin command) for variable names plot(coef.vec, y.axis, type = "p", axes = F, xlab = "", ylab = "", pch = 19, cex = 1.2,#plot coefficients as points, turning off axes and labels. xlim = c(-2,2.5), xaxs = "r", main = "") #set limits of x-axis so that they include mins and maxs of #coefficients + .95% confidence intervals and plot is symmetric; use "internal axes", and leave plot title empty #the 3 lines below create horiztonal lines for 95% confidence intervals, and vertical ticks for 90% intervals segments(coef.vec-qnorm(.975)*se.vec, y.axis, coef.vec+qnorm(.975)*se.vec, y.axis, lwd = 1.5)#coef +/-1.96*se = 95% interval, lwd adjusts line thickness axis(1, at = seq(-2,2,by=.5), labels = NA, tick = T,#draw x-axis and labels with tick marks cex.axis = 1.2, mgp = c(2,.7,0))#reduce label size, moves labels closer to tick marks axis(1, at = seq(-2,2,by=1), labels = c(-2, -1, 0, 1,2), tick = T,#draw x-axis and labels with tick marks cex.axis = 1.2, mgp = c(2,.7,0))#reduce label size, moves labels closer to tick marks axis(2, at = y.axis, label = var.names, las = 1, tick = T, mgp = c(2,.6,0), cex.axis = 1.2) #draw y-axis with tick marks, make labels perpendicular to axis and closer to axis segments(0,0,0,17,lty=2) # draw dotted line through 0 #box(bty = "l") #place box around plot #use following code to place model info into plot region x.height <- .57 text(x.height, 10, expression(R^{2} == .15), adj = 0, cex = 1) #add text for R-squared text(x.height, 9, expression(paste("Adjusted ", R^{2} == ".12", "")), adj = 0, cex = 1)#add text for Adjusted-R-squared text(x.height, 8, "n = 500", adj = 0, cex = 1)#add text for sample size ``` ```R par(oldpar) ``` -------------------------------- ### Filtering with Regular Expressions Source: https://fsolt.org/dotwhisker/articles/dotwhisker-vignette.html Illustrates filtering model estimates using regular expressions, useful for excluding factor levels like country dummies. ```r # Transform cyl to factor variable in the data m_factor <- lm(mpg ~ wt + cyl + disp + gear, data = mtcars |> mutate(cyl = factor(cyl))) # Remove all model estimates that start with cyl* m_factor_df <- broom::tidy(m_factor) | filter(!grepl('cyl*', term)) dwplot(m_factor_df) ``` -------------------------------- ### Create and Customize Regression Graph Source: https://fsolt.org/dotwhisker/articles/kl2007_examples.html This snippet shows how to create a regression graph using plot.reg and then customize its axis labels using editGrob. The graph is not printed until grid.arrange is called. ```R tmp <- plot.reg(Y1,Y2,#the lists #the model labels label.x=c("Full Sample","Excluding counties\nw. partial registration", "Full sample w. \nstate year dummies"), ## reference lines refline=c(0,0,0,0,0,0), ## space left in the bottom (for the x-axis labels) hlast=.15, ## print the graph? print=FALSE, ## line width / character size multiplier lwd.fact=1.3, ## length of the cross- hairs length.arrow=unit(0,"mm"), ## legend ##legend=c("without law change","with law change"), ## widths: variable names, plot size, y-axis widths=c(.6,.4,.3), ## rotation of the variable name labes rot.label.y=0, ## justification of the variable name labels just.label.y="right", ## position (x-axis) of the variable name labels) pos.label.y=0.95, ## size of the symbol pch.size=0.6,expand.factor=.2,expand.factor2=0.1, ##legend legend=c("With law\nchange dummy","Without law\nchange dummy"),leg.mult=.7, ##legend font size leg.fontsize=11, v.grid=TRUE, yaxis.at=list( NULL, NULL, seq(-.1,.1,.05), seq(-.2,.2,.1), seq(-.2,.2,.1), NULL##seq(-1,1,.5) ) ) tmp <- editGrob(tmp,gPath("xaxis","labels"),rot=45,just="right",gp=gpar(lineheight=.75)) ##tmp is the object we have just created,"xaxis" is the name of element in the object with the x-axis ##elements, and "labels" is the actual object in xaxis that we want to rotate ##just is the justification of the text grid.arrange(tmp) ## print the graph ``` -------------------------------- ### Extracting Coefficients for Unsupported Models Source: https://fsolt.org/dotwhisker/articles/dotwhisker-vignette.html Demonstrates how to extract coefficients from model objects not directly supported by `tidy` or `parameters` using `coef()` and converting them into a data frame suitable for `dwplot`. ```R # the ordinal regression model is not supported by tidy m4 <- ordinal::clm(factor(gear) ~ wt + cyl + disp, data = mtcars) m4_df <- coef(summary(m4)) | data.frame() | tibble::rownames_to_column("term") | rename(estimate = Estimate, std.error = Std..Error) m4_df ``` ```R dwplot(m4_df) ``` -------------------------------- ### Display Model Fit Statistics in dwplot Source: https://fsolt.org/dotwhisker/articles/dotwhisker-vignette.html Use `show_stats = TRUE` in `dwplot` to display model fit statistics below the plot. Adjust `stats_size` to control the text size of the statistics. ```r dwplot(m1, show_stats = TRUE, stats_size = 3) ``` -------------------------------- ### Present Regression Results as Normal Distributions Source: https://fsolt.org/dotwhisker/articles/dotwhisker-vignette.html Create dot-and-whisker plots styled as normal distributions by using `style = "distribution"` in `dwplot`. This allows for a visual representation of coefficient estimates and their uncertainty, similar to statistical distributions. ```R by_transmission_brackets <- list( c("Overall", "Weight", "Weight"), c("Engine", "Cylinders", "Horsepower"), c("Transmission", "Gears", "1/4 Mile/t") ) { mtcars %>% split(.$am) %>% purrr::map( ~ lm(mpg ~ wt + cyl + gear + qsec, data = .x)) %>% dwplot(style = "distribution") %>% relabel_predictors( wt = "Weight", cyl = "Cylinders", disp = "Displacement", hp = "Horsepower", gear = "Gears", qsec = "1/4 Mile/t" ) + theme_bw(base_size = 4) + xlab("Coefficient") + ylab("") + geom_vline(xintercept = 0, colour = "grey60", linetype = 2) + theme( legend.position = c(.995, .99), legend.justification = c(1, 1), legend.background = element_rect(colour = "grey80"), legend.title.align = .5 ) + scale_colour_grey( start = .8, end = .4, name = "Transmission", breaks = c("Model 0", "Model 1"), labels = c("Automatic", "Manual") ) + scale_fill_grey( start = .8, end = .4, name = "Transmission", breaks = c("Model 0", "Model 1"), labels = c("Automatic", "Manual") ) + ggtitle("Predicting Gas Mileage by Transmission Type") + theme(plot.title = element_text(face = "bold", hjust = 0.5)) } |> add_brackets(by_transmission_brackets, fontSize = 0.3) ``` -------------------------------- ### Customizing dwplot Appearance with vline and Whisker Arguments Source: https://fsolt.org/dotwhisker/news/index.html Demonstrates how to customize the appearance of a dotwhisker plot by specifying colors for dots and whiskers, and by adding a custom vertical line behind the coefficients. This is useful for aesthetic preferences and highlighting specific values like zero. ```r library(dotwhisker) #> Loading required package: ggplot2 # linear model of interest lm_object <- stats::lm(formula = wt ~ am * cyl, data = mtcars) # creating the plot with dwplot dwplot(x = lm_object, dot_args = list(color = "red"), # color for the dot whisker_args = list(color = "black"), # color for the whisker vline = ggplot2::geom_vline(xintercept = 0, # put vline _behind_ coefs colour = "grey60", linetype = 2, size = 1)) Created on 2018-06-27 by the reprex package (v0.2.0). ``` -------------------------------- ### Create Small Multiple Plot from List of Models Source: https://fsolt.org/dotwhisker/reference/small_multiple.html This snippet demonstrates how to generate a 'small multiple' plot by providing a list of `lm` model objects to the `small_multiple` function. Ensure your models are defined before passing them. ```r m1 <- lm(mpg ~ wt + cyl + disp + gear, data = mtcars) m2 <- update(m1, . ~ . + hp) # Generate a 'small multiple' plot small_multiple(list(m1, m2)) ``` -------------------------------- ### Prepare Data for 'Secret Weapon' Plot Source: https://fsolt.org/dotwhisker/articles/dotwhisker-vignette.html This R code prepares a dataset for creating 'secret weapon' plots by estimating linear models for subsets of data and tidying the results. It groups data by clarity and then applies `lm` and `broom::tidy` to generate model coefficients with confidence intervals. ```R data(diamonds) # Estimate models for many subsets of data, put results in a tidy data.frame by_clarity <- diamonds %>% group_by(clarity) %>% do(broom::tidy(lm(price ~ carat + cut + color, data = .), conf.int = .99)) %>% ungroup() %>% rename(model = clarity) ``` -------------------------------- ### Create Coefficient and Standard Error Matrix Source: https://fsolt.org/dotwhisker/articles/kl2007_examples.html This R code snippet demonstrates how to create a matrix to hold coefficients and standard errors for multiple regression models. It's useful for preparing data for plotting or further analysis. ```r #Create Vectors for coefs and standard errors for each model, and variable names #note that we exclude "margin squared" since it doesn't appear in either model coef.matrix <- matrix(c(-.039, NA, .048, -.133, .071, -.795, 1.47, -.036, NA, .036, -.142, .07, -.834, 1.70, -.051, NA, .017, .05, .011, -.532, .775, -.037, -.02, .047, -.131,.072, -.783, 1.45, -.034, -.018, -.035, -.139, .071, -.822, 1.68, -.05, -.023, .016,-.049, .013, -.521, .819),nr=7) ``` -------------------------------- ### secret_weapon Function Signature Source: https://fsolt.org/dotwhisker/reference/secret_weapon.html This shows the function signature for `secret_weapon`, outlining its parameters and their default values. ```R secret_weapon(x, var = NULL, ci = 0.95, margins = FALSE, by_2sd = FALSE, ...) ``` -------------------------------- ### Prepare Data for Dot-and-Whisker Plot Source: https://fsolt.org/dotwhisker/articles/kl2007_examples.html Formats regression results into a data frame suitable for the `dwplot` function. Ensure the data frame includes columns for term, estimate, standard error, and model. ```R results_df <- data.frame(term = rep(var.names, times = 2), estimate = c(coef.vec.1, coef.vec.2), std.error = c(se.vec.1, se.vec.2), model = c(rep("Model 1", 20), rep("Model 2", 20)), stringsAsFactors = FALSE) ``` -------------------------------- ### Create and Customize Dot-and-Whisker Plot Source: https://fsolt.org/dotwhisker/articles/kl2007_examples.html Generates a basic dot-and-whisker plot and applies several customizations including theme, legend, labels, and a vertical line at zero. This is useful for comparing coefficients across models. ```R p <- dwplot(results_df, show_stats = FALSE) + theme_bw() + theme(legend.justification=c(.02, .993), legend.position=c(.02, .99), legend.title = element_blank(), legend.background = element_rect(color="gray90")) + xlab("Logit Coefficients") + geom_vline(xintercept = 0, colour = "grey60", linetype = 2) + ggtitle("Electoral Incentives and LDP Post Allocation\nPekkanen, Nyblade, and Krause (2006)\nvia Kastellec and Leoni (2007)") ``` -------------------------------- ### Filtering Model Estimates with tidy Source: https://fsolt.org/dotwhisker/articles/dotwhisker-vignette.html Shows how to use the broom::tidy function to filter out specific model estimates, such as the intercept, before plotting. ```r m1_df <- broom::tidy(m1) |> filter(term != "(Intercept)") |> mutate(model = "Model 1") m2_df <- broom::tidy(m2) |> filter(term != "(Intercept)") |> mutate(model = "Model 2") two_models <- rbind(m1_df, m2_df) dwplot(two_models) ``` -------------------------------- ### Dotwhisker Plot for Regression Results Source: https://fsolt.org/dotwhisker/articles/kl2007_examples.html This code demonstrates how to use the dotwhisker package to create a plot of regression results. It requires formatting the data into a tidy dataframe with 'term', 'estimate', and 'std.error' columns. ```R #install.packages("dotwhisker") # uncomment to install from CRAN library(dplyr) library(dotwhisker) library(dplyr) # Format data as tidy dataframe results_df <- data.frame(term=var.names, estimate=coef.vec, std.error=se.vec) ``` -------------------------------- ### Display Model Fit Statistics in small_multiple Source: https://fsolt.org/dotwhisker/articles/dotwhisker-vignette.html Use `show_stats = TRUE` in `small_multiple` to display model fit statistics below each plot in a small multiples arrangement. `stats_size` adjusts the text size. ```r small_multiple(list(m1, m2, m3), show_stats = TRUE, stats_size = 3) ``` -------------------------------- ### Plotting Model Coefficients and Confidence Intervals Source: https://fsolt.org/dotwhisker/articles/kl2007_examples.html Generates a plot comparing coefficient estimates and 95% confidence intervals for two models. Uses different plotting characters and colors to distinguish models. Includes manual legend creation. ```R layout(matrix(c(2,1),1,2), #in order to add variable categories and braces to left side of plot, widths = c(1.5, 5))#we use layout command, create a small second panel on left side. #using c(2,1) in matrix command tells R to create right panel 1st #layout.show(2) #can use this command to check results of layout command (but it must be commented out when creating PDF). oldpar <- par(mar=c(2,8,.5,1), lheight = .8)#set margins for regression plot plot(coef.vec.1, y.axis+adjust, type = "p", axes = F, xlab = "", ylab = "", pch = 19, cex = 1.2, #plot model 1 coefs using black points (pch = 19, default = black), adding the "adjust amount" to the y.axis indicator to move points up xlim = c(min((coef.vec.1-qnorm(.975)*se.vec.1 -.1), (coef.vec.2-qnorm(.975)*se.vec.2 -.1), na.rm = T), #set xlims at mins and maximums (from both models) of confidence intervals, plus .1 to leave room at ends of plots max((coef.vec.1+qnorm(.975)*se.vec.1 -.1), (coef.vec.2+qnorm(.975)*se.vec.2 -.1), na.rm = T)), #use na.rm=T since vectors have missing values ylim = c(min(y.axis), max(y.axis)), main = "") axis(1,at = seq(-4,1, by = 1), label = seq(-4,1, by = 1), mgp = c(2,.8,1), cex.axis = 1.3)#add x-axis and labels; "pretty" creates a sequence of equally spaced nice values that cover the range of the values in 'x'-- in this case, integers axis(2, at = y.axis, label = var.names, las = 1, tick = T, cex.axis =1.3)#add y-axis and labels; las = 1 makes labels perpendicular to y-axis #axis(3,pretty(coef.vec.1, 3))#same as x-axis, but on top axis abline(h = y.axis, lty = 2, lwd = .5, col = "light grey")#draw light dotted line at each variable for dotplot effect #box(bty="l")#draw box around plot segments(coef.vec.1-qnorm(.975)*se.vec.1, y.axis+adjust, coef.vec.1+qnorm(.975)*se.vec.1, y.axis+adjust, lwd = 1.3)#draw lines connecting 95% confidence intervals abline(v=0, lty = 2) # draw dotted line through 0 for reference line for null significance hypothesis testing #add 2nd model #because we are using white points and do want the lines to go "through" points rather than over them #we draw lines first and the overlay points segments(coef.vec.2-qnorm(.975)*se.vec.2, y.axis-adjust, coef.vec.2+qnorm(.975)*se.vec.2, y.axis-adjust, lwd = 1.3)#draw lines connecting 95% confidence intervals points(coef.vec.2, y.axis-adjust, pch = 21, cex = 1.2, bg = "white" ) #add point estimates for 2nd model; pch = 21 uses for overlay points, and "white" for white color #add legend (manually) to identify which dots denote model 1 and which denote model 2 #legend(-4.5, 20, c("Model 1", "Model 2"), pch = c(19,21),bty = "n") points(-4, 19.5, pch = 19, cex = 1.2) text(-3.7, 19.5, "Model 1", adj = 0,cex = 1.2)#left-justify text using adj = 0 points(-4, 18.5, pch = 21,cex = 1.2) text(-3.7, 18.5, "Model 2", adj = 0,cex = 1.2)#left-justify text using adj = 0 par(oldpar) #Create Variable Categories and Braces to go in 2nd plot oldpar <- par(mar=c(2,0,.5,0)) #set margins--- bottom (1st number) and top (3rd number) must be the same as in 1st plot plot(seq(0,1,length=length(var.names)), y.axis, type = "n", axes = F, xlab = "", ylab = "")#call empty plot using type="n" #use a sequence of length 20 so that x and y have same length left.side <- .55#use this to manipulate how far segments are from y-axis ``` -------------------------------- ### BibTeX Citation for dotwhisker Source: https://fsolt.org/dotwhisker/authors.html Use this BibTeX entry to cite the dotwhisker R package in academic publications. ```bibtex @Manual{ title = {dotwhisker: Dot-and-Whisker Plots of Regression Results}, author = {Frederick Solt and Yue Hu}, year = {2024}, note = {R package version 0.8.3}, url = {https://fsolt.org/dotwhisker/}, } ``` -------------------------------- ### Creating a Tidy Data Frame of Regression Results Source: https://fsolt.org/dotwhisker/articles/dotwhisker-vignette.html Converts a regression model object into a tidy data frame using `broom::tidy()`. This data frame can then be used as input for `dwplot`. ```r # regression compatible with tidy m1_df <- broom::tidy(m1) # create data.frame of regression results m1_df # a tidy data.frame available for dwplot ``` -------------------------------- ### Create a Graphical Tree (gTree) for Plotting Source: https://fsolt.org/dotwhisker/articles/kl2007_examples.html This function constructs a gTree object, which is a graphical object containing various components like grids, reference lines, confidence intervals, estimates, and axes. It defines the viewport for the plot area and can optionally draw the plot. ```R gplot <- with(Y, ## gTree is a graphical object with the whole plot gTree( children=gList( hgrid,vgrid, refline, ci1a, ci1b, estimates.grob(x,Y), ## if Y2 ci2a, ci2b ,points2 ## box/rectangle around the plot area ##,rectGrob(gp=gpar(lwd=.5*lwd.fact)) ## plot x axis if sp2>0 (we name it xaxis, so we can refer to it later) ,if(sp[2]!=0) xaxisGrob(at=x.o,label=label.x,name="xaxis", gp=gpar(cex=0.8*lwd.fact,lwd=0.6*lwd.fact)) ## plot x axis with no labels if it is not the bottom plot ,if((sp[2]==0)&(!v.grid)) xaxisGrob(at=x.o,label=rep("",length(x.o)), gp=gpar(cex=0.8*lwd.fact,lwd=0.6*lwd.fact)) ## plot y-axis if sp1>0 ,if(sp[1]!=0) yaxisGrob(at=aty,label=labely, gp=gpar(cex=0.7*lwd.fact,lwd=0.6*lwd.fact),main=FALSE,name="yaxis")), ## definition of the viewport (plot area) vp=viewport(width=unit(1, "npc"), height=unit(1, "npc"), ##xscale=c(1,nrow(Y)), xscale=c(1-.5,nrow(Y)+.5), yscale=r.y ##yscale=c(-.1,.1) ,clip=FALSE) )) if (draw==TRUE) { ##draw the plot grid.newpage() fg <- frameGrob(layout=grid.layout(2,2,widths=unit(c(sp[1],1-sp[1]),"npc"),heights=unit(c(1-sp[2],sp[2]),"npc"))) fg <- placeGrob(fg,gplot,col=2,row=1) grid.arrange(fg) } else { gplot } } ```