Search code examples
rmetadatasystempasteexif

How to use paste in for loop in R? to change exif data of photos


I am working on a MacOS Big Sur 11.6 with R version 4.0.4 (2021-02-15)

I am trying to use paste() within a for loop but I need the values within the paste function to change with each iteration.

I have a data frame like this:

            pathname S
1 user/folder/photo1 A
2 user/folder/photo2 B
3 user/folder/photo3 C

and I am trying to add an EXIF Comment tag to the metadata of my photos. I would like the Comment tag to change based on the S column value. I have code like this:

for(i in df$pathname){
  x <- df$S[i]
  sysCommand <- paste("exiftool -Comment=x i")
  system(sysCommand)
}

The inputs (i.e. x and i) within the paste function should change as it goes through the list.

Thank you for your help!


Solution

  • Where You Went Wrong

    Your interpretation of paste() is flawed. This function takes R objects and concatenates their string representations.

    So given

    name <- "Rogue"
    

    then the code

    paste("Hi name!", "  How are you?")
    

    will simply concatenate the string objects "Hi name!" and " How are you?" to yield

    [1] "Hi name!  How are you?"
    

    To substitute in the name, one must use the name object

    paste("Hi ", name, "!", "  How are you?")
    #            ^^^^
    

    to obtain

    [1] "Hi Rogue!  How are you?"
    

    Solution 1: Use paste() Correctly

    As the comments rightly suggest, the proper use of paste() would be

      # ...
    
      sysCommand <- paste("exiftool -Comment=", x, " ", i, sep = "")
      #                                                    ^^^^^^^^
      #                                                    Avoid unwanted spaces.
    
      # ...
    

    with care to include the argument sep = "", and thus avoid extra spaces like those in "exiftool -Comment= A 1". Each result should look like this:

    [1] "exiftool -Comment=A 1"
    

    Note

    The paste0() function omits extra spaces automatically, so it has no need for sep = "".

    Solution 2: The glue Package

    To make things work the way you expected, you could use the glue package.

      # ...
    
      sysCommand <- glue::glue("exiftool -Comment={x} {i}")
    
      # ...
    

    with care to "embrace" every variable name with { }. Each result should look like

    exiftool -Comment=A 1
    

    a glue object that is also a normal string.

    Note

    As I mention in my comment

    You've extracted your data incorrectly with for(i in df$pathname) and df$S[i]. When you use i in df$pathname, you're iterating with i over the strings "user/folder/photo1", "user/folder/photo2", and so forth. By contrast, df$S[i] expects a number within the [ ], as it attempts to take the value in the ith place of column S. Since it can't interpret a string like "user/folder/photo1" as a numeric index, the operation df$S[i] returns an NA value...which paste() interprets as the string "NA".

    your original code also erred logically when accessing the data in df. The nifty answer by @dave2e offers a cleanly vectorized solution to this error.

    That said, your correction does well in fixing this issue

    for(i in 1:length(df$pathname)){
      #for each photo
      x <- df$S[i]
      pathname <- df$pathname[i]
    
      syscommand <- paste("exiftool -Comment=", site, " ", pathname, sep = "")   
      system(syscommand)
    } 
    

    and it retains the structure of your original loop. I'm happy to hear it works!