A REDCap-to-R Pipeline for Interactive Body Map Visualizations in Cutaneous Oncology
OVERVIEW
- Optimal Data Visualizations of patient-level data can facilitate data analysis and hypothesis generation
- Real World Data Collection efforts with an Electronic Data Capture (EDC) system like REDCap can be enhanced with well-designed pipelines using advanced data analytical software such as
R
- The objective of this post is to generate an interactive data visualization of positional data, e.g. skin cancer lesions on a body map, that is captured in REDCap and processed in R
- In this post, we will walk through the steps to generate the following Interactive Body Map
- Of note, all clinical data is simulated. Any relation to actual patients is purely coincidental
LOAD PACKAGES
- We will be making use of the following packages
library(tidyverse)
library(png)
library(kableExtra)
library(janitor)
library(plotly)
library(readxl)MAP DEVELOPMENT
Will will first import a
.pngfile of a body map
You can use which ever image you choose because we will be generating an image-specific coordinate system for our mapping
- We are using a very generic one below for the sake of simplicity
Import Body Map
- We will use the
readPNG()function from thepngpackage
- This is the image we will be using:
body_map.png <- readPNG("body_map_dmm.png")Develop Coordinate System Body Map
Overview
First we will create a coordinate system that we will map back to our Electronic Data Capture System Data Dictionary (e.g. we are using REDCap for our data collection efforts).
We will create a series of “look up” tables that correspond to both the data stored in our EDC and the x and y coordinates of our map.
For example, in our system (which you’ll see in a moment) a lesion in the right anterior medial shoulder will have the four digit combo of “1, 1, 1, 1” for the variables,
spec_loc_laterality,spec_loc_ant_post,spec_med_lat,spec_loc_ue, respectively.Thus, we can create a specific coordinate system based on that combination.
In the graph below, “1, 1, 1, 1” for those four variables will have an x and y coordinate of 14.5 and 77, respectively if we use the image above and the x and ylim of 0 to 100
- Of note, 0 to 100 is an arbitrary selection, but works well for this pipeline
We will create a coordinate system for all of the possible location of the lesions. To do so, we can then either write code or use a “look up” table to create an “x” and “y” vector in our body map data frame that links the four digit code to a two digit x and y coordinate system
To assist with the mapping, I would recommend generating a body map that has a grid of points overlaying the image of choice (you’ll see that below), and make use of the
plotlypackage to allow for interactivity with the map to ease the generation of the look up tablesBelow, I created two examples with different densities of dots that can be used to help
Create Grid
- Let’s create a canvas that is 100 x 100 in dimensions
Density: 1 x 1
- This will serve as a our map
grid <- data.frame(x = rep(1:100, each = 100),
y = rep(1:100, len = 100))Let’s overlay our body map with our grid
df.grid <- data.frame()
grid.plotly <- ggplot(df.grid) +
xlim(0, 100) +
ylim(0, 100) +
theme_bw() +
theme(panel.border = element_blank(),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.line = element_blank()) +
theme(axis.text.x = element_blank(),
axis.text.y = element_blank(),
axis.ticks = element_blank(),
axis.title = element_blank()) +
annotation_raster(body_map.png,
ymin = 0,
xmin = 0,
xmax = 100,
ymax = 100) +
geom_point(data = grid,
aes(x = x,
y = y))
ggplotly(grid.plotly)Of note, the density of the above plot is quite thick (indeed it looks like a solid sheet of black), but if you use the functionality of plotly, you can zoom in to see where each area of the body map correlates to you x and y coordinates. To appreciate the points overlayed on the body map image, use the “+” symbol above to zoom in.
Density: 2 x 2
- This graph will be slightly less dense and may be more usable
grid.2 <- data.frame(x = rep((seq(from = 2, to = 100, by = 2)), each = 100),
y = rep((seq(from = 2, to = 100, by = 2)), len = 100))Let’s overlay our body map with our grid
df.grid <- data.frame()
grid.plotly <- ggplot(df.grid) +
xlim(0, 100) +
ylim(0, 100) +
theme_bw() +
theme(panel.border = element_blank(),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.line = element_blank()) +
theme(axis.text.x = element_blank(),
axis.text.y = element_blank(),
axis.ticks = element_blank(),
axis.title = element_blank()) +
annotation_raster(body_map.png,
ymin = 0,
xmin = 0,
xmax = 100,
ymax = 100) +
geom_point(data = grid.2,
aes(x = x,
y = y))
ggplotly(grid.plotly)