【问题标题】:Trouble using mutate within a for loop在 for 循环中使用 mutate 时遇到问题
【发布时间】:2023-03-06 04:07:02
【问题描述】:

我正在尝试编写一个名为 complete 的函数,该函数采用文件目录(其中包含标题为 1-332 的 csv 文件)和文件标题作为数字打印出硫酸盐中没有 NA 的行数或硝酸盐柱。我正在尝试使用 mutate 添加一个标题为 nobs 的列,如果两个列都不是 na 则返回 1 ,然后将 nobs 的总和作为我的答案,但是我收到一条错误消息,指出找不到对象 nob。我怎样才能解决这个问题?有问题的特定文件目录在此代码块中下载。

library(tidyverse)
if(!file.exists("rprog-data-specdata.zip")) {
  temp <- tempfile()
  download.file("https://d396qusza40orc.cloudfront.net/rprog%2Fdata%2Fspecdata.zip",temp)
  unzip(temp)
  unlink(temp)
}

complete <- function(directory, id = 1:332){
  #create a list of files
  files_full <- list.files(directory, full.names = TRUE)
  #create an empty data frame
  dat <- data.frame()
  for(i in id){
    dat <- rbind(dat, read.csv(files_full[i]))
  }
  mutate(dat, nob = ifelse(!is.na(dat$sulfate) & !is.na(dat$nitrate), 1, 0))
  x <- summarise(dat, sum = sum(nob))

return(x)
}

当运行以下代码时,nobs 应该是 117,但我收到一条错误消息

complete("specdata", 1)

错误:找不到对象“nob””

【问题讨论】:

  • 永远不要在for 循环中调用rbind。它导致二次复制。见R Inferno - Circle 2: Growing Objects
  • 很抱歉没有说明错误信息是什么。当我运行 complete("specdata", 1) 时,它显示“错误:找不到对象 'nob'”
  • 在这种情况下,有什么比 rbind 更好的选择?
  • mutate 行应该类似于dat &lt;- mutate(dat, ...,以便创建新列并可用于下一行。
  • 添加 dat

标签: r for-loop dplyr


【解决方案1】:

我认为下面的功能应该可以满足您的需求。而不是一个循环,我更喜欢在这个设置中映射(或应用)。但是,如果没有错误消息或我可以在我的机器上运行的示例,很难说你的代码哪里出错了。

快乐编码, 丹尼尔

library(tidyverse)
complete <- function(directory, id = 1:332){
  #create a list of files
  files_full <- list.files(directory, full.names = TRUE)

  # cycle over each file to get the number of nonmissing rows
  purrr::map_int(
    files_full,
    ~ read.csv(.x) %>% # read in datafile 
      dplyr::select(sulfate, nitrate) %>% # select two columns of interest
      tidyr::drop_na %>% # drop missing observations
      nrow() # get the number of rows with no missing data
  ) %>%
    sum() # sum the total number of rows not missing among all files
}

【讨论】:

  • 感谢您的示例!对于未来的问题,我应该如何更好地展示我的代码以在其他人的机器上创建可重现的示例?
  • @nzhanggh 太好了!如果您提供一个代表(reprex.tidyverse.org),它会非常有帮助!另外,如果答案对您有用,您可以单击绿色复选标记,表示问题已回答:)
  • @nzhanggh - 不要将此作为 Coursera R 编程课程的家庭作业答案,否则您将违反 Coursera 荣誉准则,该准则规定您不能将其他人的工作作为自己的工作提交.
  • @len greski 别担心我不会!
【解决方案2】:

如前所述,避免在循环中构建对象。相反,请考虑从每个 csv 构建数据帧列表,然后调用 rbind 一次。事实上,您甚至可以考虑使用base R(即tinyverse)来满足您的所有需求:

complete <- function(directory, id = 1:332){
  # create a list of files
  files_full <- list.files(directory, full.names = TRUE)

  # create a list of data frames
  df_list <- lapply(files_full[id], read.csv)

  # build a single data frame with nob column
  dat <- transform(do.call(rbind, df_list), 
                   nob = ifelse(!is.na(sulfate) & !is.na(nitrate), 1, 0)
         )

  return(sum(dat$nob))
}

【讨论】:

    猜你喜欢
    • 2019-02-02
    • 1970-01-01
    • 1970-01-01
    • 2023-04-04
    • 1970-01-01
    • 2020-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多