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 usei
indf$pathname
, you're iterating withi
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 thei
th place of columnS
. Since it can't interpret a string like"user/folder/photo1"
as anumeric
index, the operationdf$S[i]
returns anNA
value...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!