1

I am in the circuit board manufacturing industry, and we measure the temperature at a variety of locations on our circuit boards in an effort to identify if certain components are exceeding their required temperatures.

I have some exposure to .js visualization libraries, RStudio and Shiny. I would like to implement this application into an existing R or ShinyDashboard that I am developing.

What I would like to accomplish is to have a 2d or 3d image of my circuit board, and a heatmap that takes the maximum temperature and shows it on that image. My data sets have columns of temperatures over time for up to 20 different locations.

I know this is a crude description, but I was wondering if anyone has any initial suggestions to accomplish this?

Update1 This is the result of running the first code: enter image description here

Update2 Here is the sample dataset that I would like to base this heatmap off of.
https://docs.google.com/spreadsheets/d/11I19uQyND7YehKrYd-NPiib4bQSHmHmWENFvausigvU/edit?usp=sharing

Gary
  • 2,137
  • 3
  • 23
  • 41

1 Answers1

2

You could use ggplot for something like this, for example:

library(grid)
library(ggplot2)

# Download image
library(jpeg)
download.file("http://www.expresspcb.com/wp-content/uploads/2015/06/PhotoProductionPCB_TL_800.jpg","pcb.jpg")
img <- readJPEG("/home/oskar/pcb.jpg")

## Load image, use this if you can't download image
#library(png)
#img <- readPNG(system.file("img", "Rlogo.png", package="png"))

g   <- rasterGrob(img, interpolate=TRUE,width=1,height=1)

coords <- data.frame("x"=c(0,1),"y"=c(0,1))

# Simulate data
df <- data.frame("x.pos" = c(runif(200),runif(20,min=0.5,max=0.8)),
                 "y.pos" = c(runif(200),runif(20,min=0.5,max=0.8)),
                 "heat"  = c(runif(200),runif(20,min=0.7,max=1)))

# Show overlay of image and heatmap
ggplot(data=df,aes(x=x.pos,y=y.pos,fill=heat)) + 
  annotation_custom(g, xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf) + 
  stat_density2d( alpha=0.2,aes(fill = ..level..), geom="polygon" ) +
  scale_fill_gradientn(colours = rev( rainbow(3) )) +
  scale_x_continuous(expand=c(0,0)) +
  scale_y_continuous(expand=c(0,0))

# Show where max temperature is
dat.max =  df[which.max(df$heat),]

ggplot(data=coords,aes(x=x,y=y)) + 
  annotation_custom(g, xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf) + 
  geom_point(data=dat.max,aes(x=x.pos,y=y.pos), shape=21,size=5,color="black",fill="red") +
  geom_text(data=dat.max,aes(x=x.pos,y=y.pos,label=round(heat,3)),vjust=-1,color="red",size=10)

The ggplot image part is from here

You can also bin the data manually and overlay it on the image like this (run this part after the script above):

# bin data manually

# Manually set number of rows and columns in the matrix containing sums of heat for each square in grid
nrows <- 30
ncols <- 30

# Define image coordinate ranges
x.range <- c(0,1) # x-coord range
y.range <- c(0,1) # x-coord range

# Create matrix and set all entries to 0
heat.density.dat <- matrix(nrow=nrows,ncol=ncols)
heat.density.dat[is.na(heat.density.dat)] <- 0

# Subdivide the coordinate ranges to n+1 values so that i-1,i gives a segments start and stop coordinates
x.seg <- seq(from=min(x.range),to=max(x.range),length.out=ncols+1)
y.seg <- seq(from=min(y.range),to=max(y.range),length.out=nrows+1)

# List to hold found values
a   <- list()
cnt <- 1
for( ri in 2:(nrows+1)){
  for ( ci in 2:(ncols+1)){
    # Get current segments, for example x.vals = [0.2, 0.3]
    x.vals <- x.seg [c(ri-1,ri)]
    y.vals <- y.seg [c(ci-1,ci)]

    # Find which of the entries in the data.frame that has x or y coordinates in the current grid
    x.inds <- which( ((df$x.pos >= min(x.vals)) & (df$x.pos <= max(x.vals)))==T )
    y.inds <- which( ((df$y.pos >= min(y.vals)) & (df$y.pos <= max(y.vals)))==T )

    # Find which entries has both x and y in current grid
    inds <- which( x.inds %in% y.inds )

    # If there's any such coordinates
    if (length(inds) > 0){
      # Append to list
      a[[cnt]] <- data.frame("x.start"=min(x.vals), "x.stop"=max(x.vals),
                             "y.start"=min(y.vals), "y.stop"=max(y.vals),
                             "acc.heat"=sum(df$heat[inds],na.rm = T) )
      # Increment counter variable
      cnt      <- cnt + 1
    }
  }
}

# Construct data.frame from list
heat.dens.df <- do.call(rbind,a)

# Plot again
ggplot(data=heat.dens.df,aes(x=x.start,y=y.start)) + 
  annotation_custom(g, xmin=-Inf, xmax=Inf, ymin=-Inf, ymax=Inf) +
  geom_rect(data=heat.dens.df, aes(xmin=x.start, xmax=x.stop, ymin=y.start, ymax=y.stop, fill=acc.heat), alpha=0.5) +
  scale_fill_gradientn(colours = rev( rainbow(3) )) +
  scale_x_continuous(expand=c(0,0)) +
  scale_y_continuous(expand=c(0,0))

Coordinate conversion from your data to my format can be done like:

sensor.data <- read.csv("~/Sample_Dataset.csv - Sample_Dataset.csv.csv")

# Create position -> coord conversion
pos.names   <- names(sensor.data)[ grep("*Pos",names(sensor.data)) ] # Get column names with "Pos" in them
mock.coords <<- list()
lapply(pos.names, function(name){
  # Create mocup coords between 0-1
  mock.coords[[name]] <<- data.frame("x"=runif(1),"y"=runif(1))
})

# Change format of your data matrix
df.l <- list()
cnt  <- 1

for (i in 1:nrow(sensor.data)){
  for (j in 1:length(pos.names)){
    name        <- pos.names[j]
    curr.coords <- mock.coords[[name]]
    df.l[[cnt]] <- data.frame("x.pos"=curr.coords$x,
                              "y.pos"=curr.coords$x,
                              "heat" =sensor.data[i,j])
    cnt <- cnt + 1
  }
}

# Create matrix
df <- do.call(rbind, df.l)
Community
  • 1
  • 1
RmIu
  • 4,357
  • 1
  • 21
  • 24
  • Oskar, this is fantastic. Thanks for the response! I posted an image of what my output was in my original document. – Gary Oct 14 '15 at 13:11
  • Oskar, can you explain what is going on in the `for` loop in your second code snippet? – Gary Oct 14 '15 at 13:22
  • I'm so glad you liked it! Basically the idea of the for loop is to divide the image into a grid and sum the heat for all points in a particular square in the grid. The *.range variables sets the image x and y coordinate max and min. The *.seg variables contains the boundaries for the segments such that boundary i will be *.seg[c(i-1,i)]. In the for loop we check each square beginning from the top left corner and we check which x and y coordinates are in the current square. Then i sum all the heat values. I'm sorry for the complicated description here, I'll comment the code to make this clearer – RmIu Oct 14 '15 at 13:33
  • Oskar, one more question. You are using `runif` to generate random deviates as the input. How would I go about modifying your code with my own dataset? – Gary Oct 14 '15 at 13:36
  • Could you give me a small mock-up of your dataset? – RmIu Oct 14 '15 at 13:38
  • I've updated my original question with a sample dataset link to a Google Spreadsheet. Along with this dataset, I was wondering where I would set the coordinates of `Position1` through `Position10` in your code snipper? Thanks for your help! – Gary Oct 14 '15 at 13:47
  • Your most welcome! I've updated my answer to show how you could convert your data. Let me know if there's anything more – RmIu Oct 14 '15 at 14:13
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/92274/discussion-between-nick-and-oskar-forsmo). – Gary Oct 14 '15 at 14:22
  • I have posted a new question here: http://stackoverflow.com/questions/33289542/ggplot-heatmap-and-density-plot-errors, and if you could see if you could be of any support, that would be fantastic! – Gary Oct 23 '15 at 01:00