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!
CodePudding user response:
To expand upon @Greg's excellent answer of paste function. You have a couple of flaws in your logic concerning accessing the data in the data frame.
Also is paste is a vectorized function, it is easier to make a vector of the system commands and then just use the loop just to execute the commands. This saves with dealing with the subscripts.
data<- read.table(header=TRUE, text=" pathname S
user/folder/photo1 A
user/folder/photo2 B
user/folder/photo3 C")
#paste is vectorized function
# create a list of all of the requested system commands
commands <-paste0("exiftool -Comment=", data$S, " ", data$pathname)
#loop through the vectors of command
for (i in commands) {
print(i) #debugging
system(i)
}
CodePudding user response:
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)anddf$S[i]. When you useiindf$pathname, you're iterating withiover 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 theith place of columnS. Since it can't interpret a string like"user/folder/photo1"as anumericindex, the operationdf$S[i]returns anNAvalue...whichpaste()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!
