1

I have two dataframes consisting of X-corrdinates, Y-coordinates and corresponding values. framefinalhas its coordinates rectangular, evenly spaced and I can nicely create a heatmap that covers the whole plotting area (using the code below). However in framefinal2 the same coordinates and values got rotated by 65 degrees, showing a kind of "perspective" orientation. Still I would like to see this as a "rotated heatmap", too, that covers (and possibly interpolates) the whole available plotting region - but my code doesn't really work that way. There are large white spaces between the squares (which gets worse if it is not 25 but hundreds of points).

I am not limited to ggplot - any package / solution will do.

Example_image

My example data

framefinal<-structure(list(x = c(1L, 2L, 3L, 4L, 5L, 1L, 2L, 3L, 4L, 5L, 
1L, 2L, 3L, 4L, 5L, 1L, 2L, 3L, 4L, 5L, 1L, 2L, 3L, 4L, 5L), 
    y = c(1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 
    3L, 3L, 4L, 4L, 4L, 4L, 4L, 5L, 5L, 5L, 5L, 5L), value = c(-0.626453810742332, 
    0.183643324222082, -0.835628612410047, 1.59528080213779, 
    0.329507771815361, -0.820468384118015, 0.487429052428485, 
    0.738324705129217, 0.575781351653492, -0.305388387156356, 
    1.51178116845085, 0.389843236411431, -0.621240580541804, 
    -2.2146998871775, 1.12493091814311, -0.0449336090152309, 
    -0.0161902630989461, 0.943836210685299, 0.821221195098089, 
    0.593901321217509, 0.918977371608218, 0.782136300731067, 
    0.0745649833651906, -1.98935169586337, 0.61982574789471)), class = "data.frame", row.names = c(NA, 
-25L))

framefinal2<-structure(list(x = c(3.9673790505919, 4.3899973123326, 4.8126155740733, 
5.235233835814, 5.6578520975547, 3.06107126355525, 3.48368952529595, 
3.90630778703665, 4.32892604877735, 4.75154431051805, 2.1547634765186, 
2.5773817382593, 3, 3.4226182617407, 3.8452365234814, 1.24845568948195, 
1.67107395122265, 2.09369221296335, 2.51631047470405, 2.93892873644475, 
0.342147902445301, 0.764766164186001, 1.1873844259267, 1.6100026876674, 
2.0326209494081), y = c(0.342147902445301, 1.24845568948195, 
2.1547634765186, 3.06107126355525, 3.9673790505919, 0.764766164186001, 
1.67107395122265, 2.5773817382593, 3.48368952529595, 4.3899973123326, 
1.1873844259267, 2.09369221296335, 3, 3.90630778703665, 4.8126155740733, 
1.6100026876674, 2.51631047470405, 3.4226182617407, 4.32892604877735, 
5.235233835814, 2.0326209494081, 2.93892873644475, 3.8452365234814, 
4.75154431051805, 5.6578520975547), value = c(-0.626453810742332, 
0.183643324222082, -0.835628612410047, 1.59528080213779, 0.329507771815361, 
-0.820468384118015, 0.487429052428485, 0.738324705129217, 0.575781351653492, 
-0.305388387156356, 1.51178116845085, 0.389843236411431, -0.621240580541804, 
-2.2146998871775, 1.12493091814311, -0.0449336090152309, -0.0161902630989461, 
0.943836210685299, 0.821221195098089, 0.593901321217509, 0.918977371608218, 
0.782136300731067, 0.0745649833651906, -1.98935169586337, 0.61982574789471
)), class = "data.frame", row.names = c(NA, -25L))

My Code (so far)

# Looks good, not-rotated:
ggplot(framefinal) +
   geom_tile(aes(x = x, y = y, fill = value)) +
   scale_fill_gradient(low = "black", high = "red")

# Looks not so good, has large white-spaces in between, rotated data:
ggplot(framefinal2) +
   geom_tile(aes(x = x, y = y, fill = value)) +
   scale_fill_gradient(low = "black", high = "red") 

Generally I want a (kind of) "density plot" which fully covers the plotting range where the variable value codes for the color, but the x- and y- values are not at all evenly spaced (as they are rotated).

shghm
  • 239
  • 2
  • 8
  • Not a solution (I guess) but to not leave it unmentioned: `ggplot` objects can be rotated as a whole like this: https://stackoverflow.com/questions/24312713/rotate-a-ggplot2-plot-object#24312791 – I_O May 23 '23 at 09:54
  • Also good to know, but the solution below from you suits my needs much better. – shghm May 23 '23 at 11:51

1 Answers1

2

Voronoi tesselation would be an option which also extends to irregularly spaced points:

with {deldir}

library(deldir)

n_colors <- 100 ## set resolution of color gradient


centroids <- 
  framefinal2 |>
  mutate(z = colorRampPalette(c("green", "white", "red"))(n_colors)
         [framefinal2$value |> cut(n_colors)]
         ) |>
  select(x, y, z)

tile_list <- 
  centroids |>
  deldir() |> ## create Voronoi object
  tile.list() ## extract Voronoi polygons ("tiles")

tile_list |> 
  plot(fillcol = centroids$z,
       showpoints = FALSE, ## hide centroids
       )

## to clip plot to desired area see argument "clipp" of ?plot.tile.list
plot(tile.list(tiles), fillcol = tiles$z)

Voronoi plot

with {ggvoronoi}

library(ggvoronoi)

framefinal2 |>
  ggplot() +
  geom_voronoi(aes(x, y, fill = value)) +
  coord_fixed() ## force 1:1 aspect ratio

enter image description here

I_O
  • 4,983
  • 2
  • 2
  • 15
  • 1
    Thank you! This works nicely! One small comment: The lines ` tile_list |> plot(fillcol = tiles$z, showpoints = FALSE, ## hide centroids ) ` must be ` tile_list |> plot(fillcol = centroids$z, showpoints = FALSE, ## hide centroids ) ` (--> different variable name) But that's just a minor issue! – shghm May 23 '23 at 11:49
  • 1
    Found a convenient `ggplot`ish package; see edited answer plz. – I_O May 23 '23 at 15:19
  • Awesome! This exactly meets my requirements and is very convenient to use. Thank you so much! – shghm May 24 '23 at 06:02