Forestplot ❤️ dplyr

The forestplot package now loves the tidyverse! The image is CC by Julie Jablonski.

Since initial publishing my forestplot package, dplyr and tidyverse have become evermore dominant in how we think about data and data management. I have therefore just published a 2.0 version that uses many of the awesome select & group features that make tidyverse such a compelling tool.

Basic change

Below is how you can work with some standard meta-analysis data. I assume that you have a base dataset with all your studies and then one additional with the summary data. We can easily append rows for header or empty rows without all the manual fuzz the previous solution required:

# Cochrane data
base_data <- tibble(mean  = c(0.578, 0.165, 0.246, 0.700, 0.348, 0.139, 1.017), 
                    lower = c(0.372, 0.018, 0.072, 0.333, 0.083, 0.016, 0.365),
                    upper = c(0.898, 1.517, 0.833, 1.474, 1.455, 1.209, 2.831),
                    study = c("Auckland", "Block", "Doran", "Gamsu", "Morrison", "Papageorgiou", "Tauesch"),
                    deaths_steroid = c("36", "1", "4", "14", "3", "1", "8"),
                    deaths_placebo = c("60", "5", "11", "20", "7", "7", "10"),
                    OR = c("0.58", "0.16", "0.25", "0.70", "0.35", "0.14", "1.02"))
 
summary <- tibble(mean  = 0.531, 
                  lower = 0.386,
                  upper = 0.731,
                  study = "Summary",
                  OR = "0.53",
                  summary = TRUE)
 
header <- tibble(study = c("", "Study"),
                 deaths_steroid = c("Deaths", "(steroid)"),
                 deaths_placebo = c("Deaths", "(placebo)"),
                 OR = c("", "OR"),
                 summary = TRUE)
 
empty_row <- tibble(mean = NA_real_)
 
cochrane_output_df <- bind_rows(header,
                                base_data,
                                empty_row,
                                summary)
 
cochrane_output_df %>% 
  forestplot(labeltext = c(study, deaths_steroid, deaths_placebo, OR), 
             is.summary = summary,
             clip = c(0.1, 2.5), 
             hrzl_lines = list("3" = gpar(lty = 2), 
                               "11" = gpar(lwd = 1, columns = 1:4, col = "#000044")),
             xlog = TRUE,
             col = fpColors(box = "royalblue",
                            line = "darkblue", 
                            summary = "royalblue",
                            hrz_lines = "#444444"))

Just as a comparison you can see the old code for achieving the same thing:

cochrane_from_rmeta <- structure(list(mean  = c(NA, NA, 0.578, 0.165, 0.246, 0.700, 0.348, 0.139, 1.017, NA, 0.531), 
                                      lower = c(NA, NA, 0.372, 0.018, 0.072, 0.333, 0.083, 0.016, 0.365, NA, 0.386),
                                      upper = c(NA, NA, 0.898, 1.517, 0.833, 1.474, 1.455, 1.209, 2.831, NA, 0.731)),
                                 .Names = c("mean", "lower", "upper"), 
                                 row.names = c(NA, -11L), 
                                 class = "data.frame")
 
tabletext <- cbind(c("", "Study", "Auckland", "Block", "Doran", "Gamsu", "Morrison", "Papageorgiou", "Tauesch", NA, "Summary"),
                   c("Deaths", "(steroid)", "36", "1", "4", "14", "3", "1", "8", NA, NA),
                   c("Deaths", "(placebo)", "60", "5", "11", "20", "7", "7", "10", NA, NA),
                   c("", "OR", "0.58", "0.16", "0.25", "0.70", "0.35", "0.14", "1.02", NA, "0.53"))
 
forestplot(tabletext, 
           hrzl_lines = list("3" = gpar(lty = 2), 
                             "11" = gpar(lwd = 1, columns = 1:4, col = "#000044")),
           cochrane_from_rmeta,
           is.summary = c(TRUE,TRUE,rep(FALSE,8),TRUE),
           clip = c(0.1,2.5), 
           xlog = TRUE,
           col = fpColors(box = "royalblue",
                          line = "darkblue", 
                          summary = "royalblue",
                          hrz_lines = "#444444"))

The difference perhaps doesn’t seem huge here but the ability to work directly with data.frame objects instead of matrices or lists makes life much easier.

Grouped coefficients

One of the features that was rather unique when I started working on this package was the ability to have multiple bands per row. This allows us to package data more densely and thus makes it easier to compare results.

data(dfHRQoL)
dfHRQoL %>%
  group_by(group) %>%
  forestplot(legend_args = fpLegend(pos = list(x = .25, y = 0.95), 
                                    gp = gpar(col = "#CCCCCC", fill = "#F9F9F9")),
             fn.ci_norm = c(fpDrawNormalCI, fpDrawCircleCI),
             boxsize = .25, # We set the box size to better visualize the type
             line.margin = .2, # We need to add this to avoid crowding
             clip = c(-.125, 0.075),
             col = fpColors(box = c("blue", "darkred")),
             xlab = "EQ-5D index")

Do I have to change everything?

The major version number change is due to the API change, although most of you should not have to change anything with this update. The core things to look for are:

  • In loops/functions, you have to manually call print (or plot) on the returned object. This as the 2.0 returns an object that is only plotted once print is called on it – the same way ggplot works. This should be automatic unless you’ve encapsulated the forestplot function.
  • If you have provided a data.frame as your first input argument then there may be issues as this will result that the tidyselect syntax is used.

A few tips for beginners on how to engage with the open source community

Engaging the open source community can be challenging at start. The image is CC by Ryan Roberts.

I have been writing code in multiple languages since 1994 and for almost a decade now I have been active within the open source community, primarily in R but I’ve also published some JavaSript/TypeScript packages. Publishing something that other people enjoy is a pure joy and the fact that some of my packages have download counts in the thousands is something that truly warms my heart.

Throughout the years I have though noticed that there is newcomers to the open source community often struggle with how to get help, which is why I decided to write this post. I’ll start out with some basics and then a little more advanced topics such as how to write an issue or a pull request. Continue reading

Easiest flowcharts eveR?

A guide to flowcharts using my Gmisc package. The image is CC by Michael Staats.

A flowchart is a type of diagram that represents a workflow or process. The building blocks are boxes and the arrows that connect them. If you have submitted any research paper the last 10 years you have almost inevitably been asked to produce a flowchart on how you generated your data. While there are excellent click-and-draw tools I have always found it to be much nicer to code my charts. In this post I will go through some of the abilities in my Gmisc package that makes this process smoother. Continue reading

News in htmlTable 2.0

A short intro to the new features in htmlTable 2.0. The image is a blend based on a CC image by Ken Xu.

The htmlTable 2.0 package was just released on CRAN! It is my most downloaded package with 160 000+ downloads/month and this update is something that I have been wanting to do for a long time. For those of you that never encountered htmlTable it is a package that takes a `matrix`/`data.frame` and outputs a nicely formatted HTML table. When I created the package there weren’t that many alternatives and knitr was this new thing that everyone was excited about, magrittr with its ubiquitous `%>%` pipe had not even entered the scene. The current update should make it easier to streamline table look, separate layout from content and use tidyverse functionality. Continue reading

Surgery for sacroiliac joint dysfunction, is this a thing?

A nice surprise article from JBJS on low back pain. The image is CC by Wanderer99.

Low back pain has a history of failed trials supporting surgery (see Brox et al. and Fairbank et al.) and I was therefore thrilled when I encountered Dengler et al.’s randomized controlled trial (RCT) on sacroiliac joint dysfunction. They showed that arthrodesis outperformed conservative management both early and up to two years after surgery. Continue reading

Long-awaited updates to htmlTable

Lets celebrate 2019 with some updates to my most popular package ever, the htmlTable. The image is CC by Thomas Hawk

One of the most pleasant surprises has been the popularity of my htmlTable-package with more than 100k downloads per month. This is all thanks to more popular packages relying on it and the web expanding beyond its original boundaries. As a thank you I have taken the time to update and fix some features (the 1.13 release) – enjoy! Continue reading

Collum screw – to parallel or not

Two bridges

Bridging the knowledge gap in how to operate femoral neck fractures. The image is CC by Nick Mealey.

One of the first surgeries young orthopaedic surgeons learn is the femoral neck fracture fixation with collum screws. A common theme is how to position the screws and as a young surgeon one often believe that everything depends on the screw position, especially if they’re parallel or not. It was therefore quite fun and liberating to read Nyholm et al’s study with the subtitle: “Minimal Effect of Implant Position on Risk of Reoperation” Continue reading

A screw finally worth writing about

Nice to see a good RCT that actually addresses an issue that I care about. The image is CC by AraĆ­ Moleri Riva-Zucchelli,

I finally found something exciting to write about, the suture button/tightrope. I must admit that I’ve been “suture button curious” for a while now and seeing Andersen et al’s exciting RCT with almost 100 patients was truly interesting. The suture button outperformed the 4-cortical syndesmotic screws in all measurements, both patient reported and radiographic. The study is simply almost too good to be true. Continue reading

And I thought we were done with the mid-shaft clavicle….

Bicycle

Bicycle injuries are by far the most common mechanism of injury for clavicle injuries. The image is CC by Hiroyuki Takeda.

So… just a few days after my previous clavicle post, Ahrens et al released their multi-center study on 300 patients randomized to surgery. They found that operated clavicles have less pain early on, but that after 9 months they perform the same. The study was excellently performed with 20 centers, adequate patient selection, random block permutation for treatment allocation, and reasonable treatment options. Continue reading

The clavicle fracture – can the madness finally come to an end?

Finding the right path can be harder than we want to think . The image is CC by Gwenole Camus.

In the early 2000s Nowak et al. [1] shattered the belief that mid-shaft clavicle fractures always healed fine; even after 10 years almost half of the patients had remaining symptoms*. This common injury had also by in others [2] (with shorter follow-ups) been hinted as problematic, and within short we were showered with fancy new implants. After ≥ 7 RCTs [3], [4] on the subjects it seems that these new implants failed do deliver. Can we finally start questioning if surgery is the solution? Continue reading