如何在 R 中使用 paste in for 循环?更改照片的 EXIF 数据

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

我正在使用 R 版本 4.0.4 (2021-02-15) 的 MacOS Big Sur 11.6

我正在尝试在 for 循环中使用 paste(),但我需要 paste 函数中的值随着每次迭代而改变。

我有这样一个数据框:

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

我正在尝试将 EXIF Comment 标签添加到我照片的元数据中。我希望 Comment 标记根据 S 列值进行更改。我有这样的代码:

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

粘贴函数中的输入(即 x 和 i)在遍历列表时应该发生变化。

感谢您的帮助!

你错在哪里

您对 paste() 的解释有误。此函数接受 R objects 并连接它们的字符串表示形式。

如此给出

name <- "Rogue"

然后是代码

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

将简单地连接字符串对象 "Hi name!"" How are you?" 以产生

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

要在 name 中进行替换,必须使用 name 对象

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

获得

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

解决方法一:paste()正确使用

正如评论所正确暗示的那样,paste() 的正确使用应该是

  # ...

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

  # ...

注意包含参数 sep = "",从而避免像 "exiftool -Comment= A 1" 中那样的额外空格。每个结果应如下所示:

[1] "exiftool -Comment=A 1"

备注

paste0() 函数会自动省略多余的空格,因此不需要 sep = "".

解决方案 2:glue

要使事情按您预期的方式工作,您可以使用 glue 程序包。

  # ...

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

  # ...

注意用{ }“拥抱”每个变量名。每个结果应该看起来像

exiftool -Comment=A 1

一个 glue 也是普通字符串的对象。

备注

正如我在

中提到的

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".

您的原始代码 在访问 df 中的数据时也 存在逻辑错误。漂亮的 by @dave2e 为这个错误提供了一个干净的向量化解决方案。

也就是说,您的 很好地解决了这个问题

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)
} 

并且它保留了原始循环的结构。我很高兴听到它有效!

扩展@Greg 对粘贴功能的出色解释。与访问数据框中的数据相关的逻辑存在一些缺陷。

另外paste()是一个向量化的函数,它更容易将系统命令制作一个向量然后只使用循环来执行命令。这避免了处理下标。

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)  
}