Search code examples
rpngrgbtidyversepurrr

Save multiple PNG from RGB data frame in R


I have a data frame which looks like below. I want to save two png files out of this data frame, named based on sample column, 1.png and 2.png both being 2x3 pixel large using the rgb values on respective columns.

As far as I can tell, I need to prepare a 3d array for each channel and then use writePNG function for each array to save as PNG file, but I got stuck after nesting rgb values for each sample.

Any help would be appreciated (help in tidyverse and purrr way will be appreciated more ;)

Data frame:

| sample| pixel| red| green| blue|
|------:|-----:|---:|-----:|----:|
|      1|     1| 255|     0|    0|
|      1|     2| 255|    32|    0|
|      1|     3| 255|    64|    0|
|      1|     4| 255|    96|    0|
|      1|     5| 255|   128|    0|
|      1|     6| 255|   159|    0|
|      2|     1| 255|   191|    0|
|      2|     2| 255|   223|    0|
|      2|     3| 255|   255|    0|
|      2|     4| 255|   255|   42|
|      2|     5| 255|   255|  128|
|      2|     6| 255|   255|  213|

Here's the code to generate this data frame:

test_df <- data_frame(sample=rep(1:2,each=6), pixel=rep(1:6,2)) %>% 
  bind_cols(as_data_frame(t(col2rgb(heat.colors(12))))) 

Solution

  • For instance:

    library(purrr)
    
    test_df %>% 
      split(.$sample) %>% 
      setNames(paste0(names(.), ".png")) %>% 
      map(~ array(c(.x$red, .x$green, .x$blue), c(2, 3, 3)) / 255) %>%
      iwalk(png::writePNG)
    

    Or in a more "step-by-step" fashion:

    test_df %>% 
      split(.$sample) %>% 
      setNames(paste0(names(.), ".png")) %>% 
      map(`[`, 3:5) %>% 
      map(as.matrix) %>% 
      map(`/`, 255) %>% 
      map(array, c(2, 3, 3)) %>% 
      iwalk(png::writePNG)
    

    Or without the tidyverse:

    z <- split(test_df, test_df$sample)
    mapply(function(x, y) {
      png::writePNG(array(as.matrix(x[3:5]), c(2, 3, 3)) / 255, paste0(y, ".png"))
    }, z, names(z))