首页 > 解决方案 > (R) 比较多个 csv 文件并添加缺失值/观察值

问题描述

这是一篇重新编辑的帖子 - 原帖不够清晰,但我希望这篇文章得到足够的改进

我有大约 400 个 .csv 文件,所有这些文件都有相同数量的列(总共 7 个)。每天生成一个文件(因此它们是单独的文件,我希望暂时保持这种方式)。由于获取数据时出现问题,这些文件中的一堆(大约 30 个左右连续的文件)在其中一列中缺少数据:Programme_Duration. 这些数据很有可能存在于一个或多个其他“完整”文件中(我不会详细说明如何/为什么,但数据中有很多重复)。

以下是相关文件的示例(我不太确定如何共享数据,因为一些观察结果是非常长的字符串。希望这些图片就足够了):

“完整”文件的示例,带有观察结果Programme_Duration(注意:并非每一行都有观察结果)。

“不完整”文件的示例,其中缺少Programme_Duration.

在我进入我正在研究的方法之前,值得指出的是,该Programme_Synopsis_url列将在完整文件和不完整文件中匹配。因此,这可能是解决这个问题的关键。即创建一个脚本:

  1. 创建一个数据框,它结合了所有 370 个“完整”的 csv 文件(我们称之为 df_complete)。
  2. 读入第一个“不完整”文件(我们称之为incomplete_file)。
  3. df_complete标识列之间和列incomplete_file的任何匹配行Programme_Synopsis_url
  4. 如果匹配,则将相关行的内容从Programme_Durationfrom复制df_complete到 的对应行Programme_Durationincomplete_file
  5. 写出。
  6. 重复,即遍历所有 30 个“不完整”文件。

其中一些我可以做到(步骤 1、2、5 和 6!)但重要的中间部分让我难过。希望这次帖子更清楚。对此的任何帮助将不胜感激!

更新:已解决

如果有人遇到这篇文章并且处于类似情况,我想我会用我的解决方案更新它。完全披露,这不是一个整洁的解决方案。我几乎没有编程经验,我在下面所做的一切都是通过反复试验和来自不同来源的在线拼凑而成的。我特别感谢@kstew,他的回答帮助我解决了这个问题。

在我分享对我有用的代码之前,还有一个免责声明:这是一个非常独特和不寻常的问题。我认为我在原来的帖子中解释得不是特别好(主要是因为我对这个领域的无知)。事实上,我在执行此操作时还面临其他几个重要因素/挑战 - 例如,我必须保留“不完整”文件中行的原始顺序(我通过简单地添加一个名为 的新列来解决这个问题index,以便可以恢复行的原始顺序)。

同样,对于更有经验的程序员来说,这段代码可能看起来一团糟,但它对我有用。话虽如此,请随时编辑/整理!

这是代码,每个步骤都有解释:


### First, create data.frame from "complete" csv files ###
folder_complete <-"insert path here"
df_list_complete <- list.files(path=folder_complete, pattern="*.csv", full.names = TRUE)
df_complete = ldply(df_list_complete, read_csv)

### Then, read in and edit "incomplete" files one at a time using for loop ### 
### Note "incomplete" files are in a different director - this was set during the session ###
filenames <- dir(pattern = "*.csv")
for (i in 1:length(filenames)) {
    tmp <- read.csv(filenames[i], stringsAsFactors = FALSE)
    ### Merge / Identify matches between "complete" data.frame and "incomplete" 
    file ### 
    ### using "Programme Synopsis" as the unique column ###
    tmp_new <- merge(tmp, df_complete, by = "Programme_Synopsis")
    ### Delete any rows with NAs in specific columns - ###
    ### I did this because the previous step matched empty rows for these columns, and I didn't want these ###
    tmp_new <- distinct(tmp_new,Programme_Synopsis_url.x, .keep_all = TRUE)
    tmp_new <- distinct(tmp_new,Programme_Duration.y, .keep_all = TRUE)
    ### Delete Duplicate columns - merging created several duplicate columns (.y, .x) ###
    ### I only wanted to add the matching "Programme Duration" column from the "complete" data.frame to the "incomplete" file ###
    ### but wasn't sure how to do this. ###
    ### Instead, I had to retrospectively remove the duplicate columns ###
    tmp_new <- tmp_new[ -c(2:7) ]
    ### Rename columns ###
    tmp_new2 <- rename(tmp_new, c("Programme_Synopsis_url.y" = 
    "Programme_Synopsis_url", 
    "Programme_Duration.y" = "Programme_Duration",
    "Programme_Category.y" = "Programme_Category", 
    "Programme_Availability.y" = "Programme_Availability", 
    "Programme_Genre.y" = "Programme_Genre", 
    "Programme_Title.y" = "Programme_Title"))
    ### Merge (again!) using plyr Join function ###
    df <- join(tmp_new2, tmp, by = "Programme_Synopsis_url", type = "full")
    ### Delete any without an index ###
    ### (i.e. those that don't belong in this dataframe) ###
    df <- df[!is.na(df$index), ]
    ### Re-order by original index ###
    df <- df[order(df$index), ]
    ### Remove duplicated index columns ###
    df$index.x <- NULL
    df$index.y <- NULL
    ### Write out the new file ###
    write.csv(df, filenames[[i]], row.names = FALSE)

无论如何,希望此更新对其他人有所帮助。同样,如果您看到这一点并能想到一个更优雅的解决方案,我很想听听。

标签: rcsvmergemissing-data

解决方案


写作为答案,因为评论太长了。

data.table当我有大型数据集或必须读取大量文件时,我更喜欢使用它,它速度快且内存效率高。下面的代码将所有“完整”文件读入一个列表,然后将该列表合并为一个大文件data.table

代码

library(data.table)

# assuming you have all csv files in the same location
basedir <- choose.dir()
fnames <- dir(path = basedir, pattern = '.*csv', all.files = T, full.names = T, recursive = F)

# names for the columns you want to read, using alphabets as an example 
column_names <- LETTERS[1:7]

big_list <- lappply(fnames, function(fname){
  dat <- fread(file = fname, select = 1:7, col.names = column_names)

  # test for empty column, say, column B 
  if( dat[!is.na(B), .N] < nrow(dat)){
    dat$type <- 'imcomplete'
  }else{
    dat$type <- 'complete'
  }

})

# combine them all into one list
big_data <- rbindlist(l = big_list, use.names = T, fill = T)

# set column B as the key
setkey(big_data, 'B')

complete <- big_data[type == 'complete']
incomplete <- big_data[type == 'incomplete']

或者,您可以根本不阅读“不完整”的文件。

对于填写缺失的数据,您可以使用多种不同的方式,我不确定您要使用的逻辑。例如,您merge只能使用不同complete数据集的不完整部分。

此处描述了一种基于的方法:使用data.table相同的列(单行)填充另一个数据框中的缺失值。我不确定你想使用什么逻辑,所以我在这里做一些:key

# sample for complete
complete <- as.data.table(mtcars)

# sample for incomplete
incomplete <- as.data.table(mtcars[1:20, ])

# set some values to NA - examples of missing data
incomplete[runif(5, 1, .N), mpg := NA]

核实:

> incomplete[is.na(mpg), .N]
[1] 5

基于wt和合并qsec

# I used the on argument on two variables 
# because both wt and qsec are not unique for each observation
setDT(incomplete)[complete, mpg := i.mpg, on = .(wt, qsec)]

结果:

> incomplete[is.na(mpg), .N]
[1] 0

# Verifying that we have the right values filled in
> identical(complete[1:20, ], incomplete)
[1] TRUE

要写出结果,您可以使用fwrite(complete, 'complete.csv'). 如果需要,您可以省略该type列。


推荐阅读