【问题标题】:How to use paste in for loop in R? to change exif data of photos如何在R中的for循环中使用粘贴?更改照片的 exif 数据
【发布时间】:2021-12-11 08:28:41
【问题描述】:

我正在使用 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 标签根据 S 列值进行更改。我有这样的代码:

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

粘贴函数中的输入(即 x 和 i)应在遍历列表时更改。

感谢您的帮助!

【问题讨论】:

  • 试试:paste0("exiftool -Comment=", x, " ", i)
  • 像任何其他字符值一样构建字符串:sysCommand &lt;- paste0("exiftool -Comment=", x, " ", i)。您可能需要确保 x 没有任何空格,否则您可能会得到一个无效的命令。
  • 您也可以使用glue 包:sysCommand &lt;- glue::glue("exiftool -Comment={x} {i}")。你只需要“拥抱”({ })字符串中的变量名。

标签: r metadata system paste exif


【解决方案1】:

扩展@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)  
}

【讨论】:

  • 很好地矢量化解决方案!
【解决方案2】:

你哪里出错了

您对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?"

解决方案 1:使用paste() 正确

正如 cmets 正确建议的那样,正确使用 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 对象,也是一个普通字符串。

注意

正如我在comment 中提到的那样

您使用for(i in df$pathname)df$S[i] 错误地提取了数据。当您在df$pathname 中使用i 时,您将在字符串 "user/folder/photo1""user/folder/photo2" 等上使用i 进行迭代。相比之下,df$S[i] 期望在[ ] 中有一个数字,因为它试图在S 列的第i 处取值。由于它无法将像"user/folder/photo1" 这样的字符串解释为numeric 索引,因此操作df$S[i] 返回一个NA 值...其中paste() interprets as the string "NA"

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

也就是说,您的 correction 可以很好地解决此问题

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

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

【讨论】:

  • 谢谢,我尝试了这两种解决方案,但我得到了 NA 以供评论
  • 您使用for(i in df$pathname)df$S[i] 错误地提取了数据。当您使用i in df$pathname 时,您将使用i字符串 "user/folder/photo1""user/folder/photo2" 等进行迭代。相比之下,df$S[i] 期望在[ ] 中有一个数字,因为它试图在S 列的第i 处取值。由于它无法将像"user/folder/photo1" 这样的字符串解释为numeric 索引,因此操作df$S[i] 返回一个NA 值...其中paste()interprets as the string "NA"
  • 感谢您的粘贴课程。这似乎有效:for(i in 1:length(df$pathname)){ #for each photo x &lt;- df$S[i] pathname &lt;- df$pathname[i] syscommand &lt;- paste("exiftool -Comment=", site, " ", pathname, sep = "") system(syscommand) }
  • @Rogue 很高兴我能帮上忙!
猜你喜欢
  • 1970-01-01
  • 2020-09-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多