Search code examples
javascriptpythonvisualizationsurvey

How to overlay xy coordinates onto an image


I have a dataset (n=256) with xy coordinates (>10) that have been exported from an online survey that map onto an image of a body. Essentially, participants (n=256) were asked to indicate on the body map where they experience pain (a max of 10 xy pairs).

Now I'm trying to work out how to map these coordinates on top of the image (597 × 597 - image below) to give a "heat map" visual.

I have tried some online tutorials but they are all limited to overlaying coordinates onto a google maps image.

A subset of my data looks like this:

ID  x1  y1  x2  y2  x3  y3  x4  y4  x5  y5  x6  y6  x7  y7  x8  y8  x9  y9  x10 y10
1   467 77  135 121 104 265 135 170 155 481 441 406 488 221 462 279 541 300 390 299
2   495 464 476 520 486 488 129 436 130 468 156 262                             
3   133 274 147 259 117 256                                                     
4   132 140 451 89  493 101 123 92  157 89  123 240 155 243 440 296 509 297     
5   156 495 490 499 442 495 121 502 158 269                                     
6   123 244 151 256 158 151 175 189 434 419 472 496 431 353 394 189 414 149     
7   134 100 135 127 112 163                                                     
8   95  304 509 301 118 271 149 269                                             
9   136 247 148 148 165 194 153 506 153 320 127 200 132 123 439 533             
10  130 135 177 324 162 294 141 255                                             
11  146 249 490 307 507 165 411 167 397 239 490 406 444 400 427 300 204 284 54  277
12  440 415 482 415 535 336 454 530 439 527                                     
13  105 497 177 162 186 195 481 492 420 149 421 191 165 272 158 314 435 283 435 311
14  153 254 100 382 145 76  145 133 197 230 437 195 452 302 466 146             
15  379 331 553 328 218 292 54  296 52  333 227 334 385 299 545 301 443 416     
16  135 113 467 87  131 251 149 262 222 323 209 286 151 139 133 172             
17  121 110 139 109 107 143 172 145 451 137 480 136 140 162 123 165 123 131 147 135
18  131 129 172 152 105 157 151 250 122 252 168 288 110 298 487 136 451 137     
19  448 406 500 153 437 158 472 93  459 307 446 303 431 281 422 293 489 500 452 495
20  145 252 468 143 429 161 506 166 175 145 131 145 466 69  138 74              
21  480 289 116 279 124 271 458 285 472 148 124 148 148 141 157 265             
22  84  313 427 304 503 309 182 309 510 172 412 168 126 232 128 133 493 414 444 414
23  452 110 431 163 440 310 461 316 145 278 137 73  459 90  144 118 159 145 145 313
24  118 277 153 276 130 125 126 84  139 525 458 83  444 507 485 504 441 284 492 274
25  503 287 389 296 546 293 426 287 488 404 447 498 491 497 135 177 500 151 433 151
26  476 397 448 400 109 142 83  228 126 527                                     
27  150 499 123 503 500 219 432 222 511 177 417 182 451 411 491 411 452 532 489 539
28  441 410 485 408 208 273 63  273 177 158                                     
29  163 410 148 409 435 415 444 404                                             
30  446 403 485 407 480 532 447 527 561 341 501 301 113 260 374 336             
31  441 407 153 409 133 255                                                     
32                                                                              
33  148 133 492 416 441 412 115 264 498 449 477 443 451 423 495 356 178 150 99  276
34  499 454 497 468 494 479 495 491 144 535 148 524 122 519 134 128 151 257 124 259
35  454 192 457 288 489 503 558 288 323 294 141 272 133 205 131 168 131 508 141 135

body map

Ideally, I'd like to use python, R, or js so I can understand it and use in my script (currently using R).

Massive thanks in advance

Edit Here is the csv bodymap


Solution

  • Is this real data? Or fake/example data? I'm having trouble getting the data points to 'line up' with the expected body parts e.g. pretty sure lots of people have lower back pain, should the 'thigh' cluster be higher up?

    Here is one potential approach:

    Load the example data:

    library(tidyverse)
    library(grid)
    library(png)
    
    df <- structure(list(ID = c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 
                                13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 
                                29, 30, 31, 32, 33, 34, 35),
                         x1 = c(467, 495, 133, 132, 156, 
                                123, 134, 95, 136, 130, 146, 440, 105, 153, 379, 135, 121, 131, 
                                448, 145, 480, 84, 452, 118, 503, 476, 150, 441, 163, 446, 441, 
                                NA, 148, 499, 454), 
                         y1 = c(77, 464, 274, 140, 495, 244, 100, 
                                304, 247, 135, 249, 415, 497, 254, 331, 113, 110, 129, 406, 252, 
                                289, 313, 110, 277, 287, 397, 499, 410, 410, 403, 407, NA, 133, 
                                454, 192), 
                         x2 = c(135, 476, 147, 451, 490, 151, 135, 509, 148, 
                                177, 490, 482, 177, 100, 553, 467, 139, 172, 500, 468, 116, 427, 
                                431, 153, 389, 448, 123, 485, 148, 485, 153, NA, 492, 497, 457), 
                         y2 = c(121, 520, 259, 89, 499, 256, 127, 301, 148, 324, 307, 
                                415, 162, 382, 328, 87, 109, 152, 153, 143, 279, 304, 163, 276, 
                                296, 400, 503, 408, 409, 407, 409, NA, 416, 468, 288), 
                         x3 = c(104, 
                                486, 117, 493, 442, 158, 112, 118, 165, 162, 507, 535, 186, 145, 
                                218, 131, 107, 105, 437, 429, 124, 503, 440, 130, 546, 109, 500, 
                                208, 435, 480, 133, NA, 441, 494, 489),
                         y3 = c(265, 488, 256, 
                                101, 495, 151, 163, 271, 194, 294, 165, 336, 195, 76, 292, 251, 
                                143, 157, 158, 161, 271, 309, 310, 125, 293, 142, 219, 273, 415, 
                                532, 255, NA, 412, 479, 503), 
                         x4 = c(135, 129, NA, 123, 121, 
                                175, NA, 149, 153, 141, 411, 454, 481, 145, 54, 149, 172, 151, 
                                472, 506, 458, 182, 461, 126, 426, 83, 432, 63, 444, 447, NA, 
                                NA, 115, 495, 558), 
                         y4 = c(170, 436, NA, 92, 502, 189, NA, 269, 
                                506, 255, 167, 530, 492, 133, 296, 262, 145, 250, 93, 166, 285, 
                                309, 316, 84, 287, 228, 222, 273, 404, 527, NA, NA, 264, 491, 
                                288),
                         x5 = c(155, 130, NA, 157, 158, 434, NA, NA, 153, NA, 397, 
                                439, 420, 197, 52, 222, 451, 122, 459, 175, 472, 510, 145, 139, 
                                488, 126, 511, 177, NA, 561, NA, NA, 498, 144, 323), 
                         y5 = c(481, 
                                468, NA, 89, 269, 419, NA, NA, 320, NA, 239, 527, 149, 230, 333, 
                                323, 137, 252, 307, 145, 148, 172, 278, 525, 404, 527, 177, 158, 
                                NA, 341, NA, NA, 449, 535, 294), 
                         x6 = c(441, 156, NA, 123, NA, 
                                472, NA, NA, 127, NA, 490, NA, 421, 437, 227, 209, 480, 168, 
                                446, 131, 124, 412, 137, 458, 447, NA, 417, NA, NA, 501, NA, 
                                NA, 477, 148, 141), 
                         y6 = c(406, 262, NA, 240, NA, 496, NA, NA, 
                                200, NA, 406, NA, 191, 195, 334, 286, 136, 288, 303, 145, 148, 
                                168, 73, 83, 498, NA, 182, NA, NA, 301, NA, NA, 443, 524, 272), 
                         x7 = c(488, NA, NA, 155, NA, 431, NA, NA, 132, NA, 444, NA, 
                                165, 452, 385, 151, 140, 110, 431, 466, 148, 126, 459, 444, 491, 
                                NA, 451, NA, NA, 113, NA, NA, 451, 122, 133), 
                         y7 = c(221, NA, 
                                NA, 243, NA, 353, NA, NA, 123, NA, 400, NA, 272, 302, 299, 139, 
                                162, 298, 281, 69, 141, 232, 90, 507, 497, NA, 411, NA, NA, 260, 
                                NA, NA, 423, 519, 205), 
                         x8 = c(462, NA, NA, 440, NA, 394, NA, 
                                NA, 439, NA, 427, NA, 158, 466, 545, 133, 123, 487, 422, 138, 
                                157, 128, 144, 485, 135, NA, 491, NA, NA, 374, NA, NA, 495, 134, 
                                131), 
                         y8 = c(279, NA, NA, 296, NA, 189, NA, NA, 533, NA, 300, 
                                NA, 314, 146, 301, 172, 165, 136, 293, 74, 265, 133, 118, 504, 
                                177, NA, 411, NA, NA, 336, NA, NA, 356, 128, 168),
                         x9 = c(541, 
                                NA, NA, 509, NA, 414, NA, NA, NA, NA, 204, NA, 435, NA, 443, 
                                NA, 123, 451, 489, NA, NA, 493, 159, 441, 500, NA, 452, NA, NA, 
                                NA, NA, NA, 178, 151, 131), 
                         y9 = c(300, NA, NA, 297, NA, 149, 
                                NA, NA, NA, NA, 284, NA, 283, NA, 416, NA, 131, 137, 500, NA, 
                                NA, 414, 145, 284, 151, NA, 532, NA, NA, NA, NA, NA, 150, 257, 
                                508), 
                         x10 = c(390, NA, NA, NA, NA, NA, NA, NA, NA, NA, 54, NA, 
                                 435, NA, NA, NA, 147, NA, 452, NA, NA, 444, 145, 492, 433, NA, 
                                 489, NA, NA, NA, NA, NA, 99, 124, 141),
                         y10 = c(299, NA, NA, 
                                 NA, NA, NA, NA, NA, NA, NA, 277, NA, 311, NA, NA, NA, 135, NA, 
                                 495, NA, NA, 414, 313, 274, 151, NA, 539, NA, NA, NA, NA, NA, 
                                 276, 259, 135)), 
                    row.names = c(NA, -35L), spec = structure(list(
                      cols = list(ID = structure(list(), class = c("collector_double", 
                                                                   "collector")), 
                                  x1 = structure(list(), class = c("collector_double", 
                                                                   "collector")), 
                                  y1 = structure(list(), class = c("collector_double", 
                                                                   "collector")), 
                                  x2 = structure(list(), class = c("collector_double", 
                                                                   "collector")), 
                                  y2 = structure(list(), class = c("collector_double", 
                                                                   "collector")), 
                                  x3 = structure(list(), class = c("collector_double", 
                                                                   "collector")), 
                                  y3 = structure(list(), class = c("collector_double", 
                                                                   "collector")),
                                  x4 = structure(list(), class = c("collector_double", 
                                                                   "collector")), 
                                  y4 = structure(list(), class = c("collector_double", 
                                                                   "collector")), 
                                  x5 = structure(list(), class = c("collector_double", 
                                                                   "collector")), 
                                  y5 = structure(list(), class = c("collector_double", 
                                                                   "collector")), 
                                  x6 = structure(list(), class = c("collector_double", 
                                                                   "collector")), 
                                  y6 = structure(list(), class = c("collector_double", 
                                                                   "collector")), 
                                  x7 = structure(list(), class = c("collector_double", 
                                                                   "collector")),
                                  y7 = structure(list(), class = c("collector_double", 
                                                                   "collector")), 
                                  x8 = structure(list(), class = c("collector_double", 
                                                                   "collector")), 
                                  y8 = structure(list(), class = c("collector_double", 
                                                                   "collector")), 
                                  x9 = structure(list(), class = c("collector_double", 
                                                                   "collector")),
                                  y9 = structure(list(), class = c("collector_double", 
                                                                   "collector")), 
                                  x10 = structure(list(), class = c("collector_double", 
                                                                    "collector")), 
                                  y10 = structure(list(), class = c("collector_double", 
                                                                    "collector"))), 
                      default = structure(list(), class = c("collector_guess", "collector")), delim = "\t"), class = "col_spec"),
                    problems = "<pointer: 0x7fb2892c4200>", 
                    class = c("spec_tbl_df", "tbl_df", "tbl", "data.frame"))
    

    Load the image, generate the plot, and combine using grid:

    img <- readPNG("~/Desktop/l78U8.png")
    #> Warning in readPNG("~/Desktop/l78U8.png"): libpng warning: iCCP: extra
    #> compressed data
    
    g1 <- df %>%
      pivot_longer(-ID, names_pattern = "([a-z]+)(\\d+)",
                   names_to = c("coord", "num")) %>%
      pivot_wider(id_cols = c(ID, num), names_from = coord, values_from = value) %>%
      ggplot(aes(x = x, y = y, fill = after_stat(density))) +
      geom_hex() +
      scale_fill_viridis_c(option = "C") +
      theme_void() +
      theme(legend.position = "none") +
      coord_equal(expand = TRUE)
    
    grid.draw(gList(rasterGrob(img, width = unit(0.8,"npc"), height = unit(1.2, "npc")), 
                    ggplotGrob(g1)))
    #> Warning: Removed 89 rows containing non-finite values (`stat_binhex()`).
    

    Created on 2023-05-18 with reprex v2.0.2


    Edit

    Changing the alpha makes the plot look a bit nicer (i.e. geom_hex(alpha = 0.75)):

    example.png