无法获取:一个输入文件中缺少但存在于其他文件中的列应在输出文件中返回 NA

Unable to obtain: Columns missing in one input file but present in other files should return NA in output file

提问人:Alexia k Boston 提问时间:7/17/2023 最后编辑:Alexia k Boston 更新时间:7/18/2023 访问量:103

问:

我有各种txt文件,其中每个文件都包含多个列,但并非所有列都存在于所有文件中。 例如

Txt 文件1 的列

D/T  City_Name  Temp  Pres  Wind_Hor  Wind_Ver  S_Moist  Temp1  Pres1  Wind1  S_Moist1
20200101  Mi  27  1019  287  278  78  1  2  2  1
20200101  Mi  28  1019  289  277  78  2  2  1  3

txt 文件的列2

D/T  City_Name  Temp  Wind_Hor  Rainf  S_Moist   Temp1  Wind1  S_Moist1
20200501  Ch  29  88  74  78  2  2  2  3
20200501  Ch  26  83  74  76  2  3  3  3

Txt 文件的列3

D/T  City_Name  Pres  Wind_Vert  Rainf  S_Moist  Pres1  Wind1  Rainf1  S_Moist1    
20180701  Ny  1017  247  78  78  4  2  2  2
20180702  Ny  1017  249  79  79  2  3  3  3

我在 R 中运行了一个代码来处理这些文件,并为每个 txt 文件获取 1 个输出 csv。

我想要的输出是在所有输出文件中都有所有 7 列,如果输入中没有特定列,那么它应该在输出中具有 NA 值。

Txt File1 的所需输出列

D_T  City_Name  Temp  Pres  Wind_Hor  Wind_Ver  Rainf  S_Moist
202001  Mi  26  1018  288  277  NA  77
202002  Mi  27  1018  288  278  NA  77

Txt 文件2 的所需输出列

D_T  City_Name  Temp  Pres  Wind_Hor  Wind_Ver  Rainf  S_Moist
202005  Ch  28  NA  287  NA  77  77
202006  Ch  27  NA  284  NA  79  78

Txt 文件的期望输出列3

D_T  City_Name  Temp  Pres  Wind_Hor  Wind_Vert  Rainf  S_Moist   
201807  Mi  NA  1016  NA  276  77  79
201808  Mi  NA  1016  NA  276  77  81

我的代码如下:

library(data.table)
library(tidyr)
library(dplyr)
library(lubridate)

setwd("D:/Test3")

dirlist <- list.files(full.names = FALSE, no.. = TRUE) 

#Read the files
read_the_files <- function(filelist){
  lapply(filelist, function(file) {
    fread(file, skip = "D/T\tXY [XY]")             
  })
}

#Adjusting with Columns
adj_col_names <- function(dt_list){
  lapply(dt_list, FUN = function(x){
    #replace [] with () as use of [] may lead to issues
    colnames(x) <- gsub("\\[", "(", colnames(x))
    colnames(x) <- gsub("\\]", ")", colnames(x))

    #Applying quality codes given in the data and range given in paper
    if ("Temp1" %in% colnames(x)) {
      x[Temp1 != 5, Temp := NA]
      x[Temp < 0 | Temp > 40, Temp := NA]
    }

    if ("Pres1" %in% colnames(x)) {
      x[Pres1 != 5, Pres := NA]
      x[Pres < 1000 | Pres > 1300, Pres := NA]
    }

for (idx in seq_along(dirlist)){
  filelist <- list.files(path = dirlist[idx], full.names = TRUE, recursive = TRUE, pattern = 
                       ".txt$")
  dt_ <- read_the_files(filelist)
  dt.tidied <- adj_col_names(dt_)

  #bind, filling missing columns to NA  
  merged <- rbindlist(dt.tidied, fill = TRUE, use.names = TRUE)

  #Choosing the columns in the output    
  sel_col <- c('D/T', 'City_Name', 'Temp', 'Pres', 'Wind_Hor', 'Wind_Ver', 'Rainf', 'S_Moist')

  #calculating monthly average
  avg_mn <- merged %>%      
    select(any_of(sel_col)) %>%
    as_tibble() %>%
    group_by(D_T = lubridate::floor_date(`D/T`, "1 month")) %>%         
    summarise(across(where(is.numeric), ~ if(mean(is.na(.x)) > 0.1) NA else mean(.x, na.rm = TRUE)))     #summarise only numeric columns
    write.csv(paste0(dirlist[idx],"_mn.csv"))

由于各种依赖关系,我无法更改我的代码。我只能修改。谁能帮我修改它以获得所需的输出?

根据 jkatam 的回答测试了代码:

dfs1 <- list.files(path = 'D:/Test3', pattern = "*txt", recursive = TRUE)
var <- c('D/T', 'City_Name', 'Temp', 'Pres', 'Wind_Hor', 'Wind_Ver', 'Rainf', 'S_Moist')
lapply(dfs1, \(x) {
  dfn <- get(x, envir = .GlobalEnv)
  dfn[[var[which(is.na(match(var,names(dfn))))]]] <- NA
  dfn <- dfn %>% select(all_of(var))
  return(assign(x,dfn,envir = .GlobalEnv))
})

我收到错误:

Error in get(x, envir = .GlobalEnv) :
object 'Mi/Mi_2020-01_0100.txt' not found
r data.table tidyr rbind

评论

0赞 Onyambu 7/17/2023
您能否先将所有数据集读取到一个列表中。结婚rbindlist(lapply(your_file_list, read.table))
0赞 Alexia k Boston 7/17/2023
我无法使用上述命令读取数据集。由于文件结构不同(要跳过的初始行数不同),我创建了一个函数来读取文件。环境将dt_和dt_tidied显示为大列表,但在上述命令中使用它们会返回错误:“FUN(X[[i]], ...) 中的错误:'file'必须是字符串或连接'
1赞 Onyambu 7/17/2023
如果您已经读取了文件并且它们位于列表(所有文件)中,请直接再次使用 No need of for 循环。或者干脆rbindlistdplyr::bind_rows(your_list)
0赞 r2evans 7/17/2023
您将需要添加 和 (如果列顺序不同) 。rbindlistfill=TRUEuse.names=TRUE
0赞 Alexia k Boston 7/17/2023
@r2evans这已经存在于我的代码中,但它没有给我所需的输出。

答:

0赞 jkatam 7/17/2023 #1

请检查下面的代码,我假设您有不同的数据帧,例如等,您想检查预期的列是否存在,如果不存在,则使用 NA 派生该列并输出数据帧,稍后可以将其导出为 csvdf1df2

dfs1 <- c('df1','df2')

var <- c('City_Name',  'Temp',  'Pres' , 'Wind_Hor' , 'Wind_Ver' , 'Rainf' , 'S_Moist')

lapply(dfs1, \(x) {
  dfn <- get(x, envir = .GlobalEnv)
  dfn[[var[which(is.na(match(var,names(dfn))))]]] <- NA
  dfn <- dfn %>% select(all_of(var))
  return(assign(x,dfn,envir = .GlobalEnv))
})

评论

0赞 Alexia k Boston 7/18/2023
谢谢,但我没有数据帧。相反,我有文件。您能否告诉我如何使用文件而不是数据帧执行您提到的过程。
0赞 Alexia k Boston 7/18/2023
我使用文件列表(在帖子中提到)尝试了您的代码,但它给了我一个错误。此外,我试图将它放在adj_col_names上方,但我不确定它是否是这些代码行的正确位置。请帮忙。
0赞 r2evans 7/17/2023 #2

使用您的输入文件,我做了几件事:

  • 将它们命名为 、 和file1.txtfile2.txtfile3.txt
  • 将所有空格更改为单个制表符,并确保一行中没有尾随制表符 (file3)
  • 添加到 file2,因为它有 9 个列名和 10 个数据列S_Rain1

然后我运行了这段代码:

files <- list.files(pattern = "txt$", full.names = TRUE)
alldat <- lapply(setNames(nm = files), fread)
rbindlist(alldat, idcol = "path", fill = TRUE, use.names = TRUE) %>%
  setnames("D/T", "D_T") %>%
  .[, fwrite(.SD, sub("txt$", "tab", path[1]), sep = "\t", na = "NA"),
    .SDcols = c("D_T", "City_Name", "Temp", "Pres", "Wind_Hor", "Wind_Ver", "Rainf", "S_Moist"),
    by = .(path)]

生成的文件:

  • file1.csv:

    "D_T"   "City_Name" "Temp"  "Pres"  "Wind_Hor"  "Wind_Ver"  "Rainf" "S_Moist"
    20200101    "Mi"    27  1019    287 278 NA  78
    20200101    "Mi"    28  1019    289 277 NA  78
    
  • file2.csv

    "D_T"   "City_Name" "Temp"  "Pres"  "Wind_Hor"  "Wind_Ver"  "Rainf" "S_Moist"
    20200501    "Ch"    29  NA  88  NA  74  78
    20200501    "Ch"    26  NA  83  NA  74  76
    
  • file3.csv

    "D_T"   "City_Name" "Temp"  "Pres"  "Wind_Hor"  "Wind_Ver"  "Rainf" "S_Moist"
    20180701    "Ny"    NA  1017    NA  NA  78  78
    20180702    "Ny"    NA  1017    NA  NA  79  79
    

添加引号是因为我们提供了添加文字文本的引号。可以通过向表达式中添加引号来禁止引用。na=""NAquote=FALSEfwrite(..)

注意:由于 StackOverflow 代码渲染,上面的内容渲染带有空格;它们确实是结果文件中的选项卡。

评论

0赞 Alexia k Boston 7/17/2023
我尝试了代码,按照我的代码修改了它。我修改正确了吗?因为它给了我这个错误: FUN(X[[i]], ...) 中的错误: input= 必须是包含文件名的单个字符串、包含至少一个空格的系统命令、以“http[s]://”、“ftp[s]://”或“file://”开头的 URL,或者,输入数据本身包含至少一个 \n 或 \r 另外,您能否在代码中添加注释
0赞 r2evans 7/17/2023
对你来说看起来合理吗?是否所有文件都存在,格式是否正确?是否有不应读入批处理的额外文件名?filesfiles
0赞 Alexia k Boston 7/18/2023
文件对我来说看起来很合理,唯一的区别是因为我正在迭代处理多个文件,所以我在此步骤中使用了一个循环(请在我的代码中引用“filelist”)。是的,所有文件都存在。虽然文件格式不正确,但它已经在我的代码中得到处理。由于文件名很大,我保留了文件夹的名称作为文件名。如果您能将您的代码与我的代码合并并为其提供注释以便于理解,那将会很有帮助