【问题标题】:Error while merging data frames using "data.table" package使用“data.table”包合并数据帧时出错
【发布时间】:2014-10-02 00:19:49
【问题描述】:

以下是我正在经历并坚持的一种情况的可重现示例(它是一个测试客户端我用来评估合并数据集用于我的论文研究)。

testData <- "https://github.com/abnova/test/blob/master/mergeTestData.zip?raw=true"

tmpFile <- tempfile()
tmpDir <- tempdir()

download.file(testData, tmpFile, method = 'curl',
              extra = '-L', quiet = TRUE)
testFiles <- unzip(tmpFile, exdir = tmpDir)

# To enable desired merge option, uncomment corresponding line

#MERGE_OPTION <- "lapply_merge"
#MERGE_OPTION <- "lapply_merge2"
#MERGE_OPTION <- "reduce_merge"
#MERGE_OPTION <- "reduce_merge2"
#MERGE_OPTION <- "reshape"
#MERGE_OPTION <- "plyr"
#MERGE_OPTION <- "dplyr"
MERGE_OPTION <- "data.table"
#MERGE_OPTION <- "data.table2"

loadData <- function (dataFile) {

  if (file.exists(dataFile)) {
    data <- readRDS(dataFile)
  }
  else { # error() undefined - replaced for stop() for now
    stop("Data file \'", dataFile, "\' not found! Run 'make' first.")
  }
  return (data)
}

loadDataSets <- function (dataDir) {

  dataSets <- list()

  dataFiles <- dir(dataDir, pattern='\\.rds$')
  dataSets <- lapply(seq_along(dataFiles),
                     function(i) {
                       nameSplit <- strsplit(dataFiles[i], "\\.")
                       dataset <- nameSplit[[1]][1]
                       assign(dataset,
                              loadData(file.path(dataDir, dataFiles[i])))
                       return (get(dataset))
                     })
  return (dataSets)
}

# load the datasets of transformed data
dataSets <- loadDataSets(tmpDir)

if (MERGE_OPTION == "lapply_merge") { # Option 1

  flossData <- data.frame(dataSets[[1]][1])

  # merge all loaded datasets by common column ("Project ID")
  silent <- lapply(seq(2, length(dataSets)),
                   function(i) {merge(flossData, dataSets[[1]][i],
                                      by = "Project ID",
                                      all = TRUE)})
}

if (MERGE_OPTION == "lapply_merge2") { # Option 1

  pids <- which(sapply(dataSets,
                       FUN=function(x) {'Project ID' %in% names(x)}))

  flossData <- dataSets[[pids[1]]]

  for (id in pids[2:length(pids)]) {
    flossData <- merge(flossData, dataSets[[id]],
                       by='Project ID', all = TRUE)
  }
}

if (MERGE_OPTION == "reduce_merge") { # Option 2

  flossData <- Reduce(function(...) 
    merge(..., by.x = "row.names", by.y = "Project ID", all = TRUE),
    dataSets)
}

# http://r.789695.n4.nabble.com/merge-multiple-data-frames-tt4331089.html#a4333772
if (MERGE_OPTION == "reduce_merge2") { # Option 2

    mergeAll <- function(..., by = "Project ID", all = TRUE) {
    dotArgs <- list(...)
    dotNames <- lapply(dotArgs, names)
    repNames <- Reduce(intersect, dotNames)
    repNames <- repNames[repNames != by]
    for(i in seq_along(dotArgs)){
      wn <- which( (names(dotArgs[[i]]) %in% repNames) &
                     (names(dotArgs[[i]]) != by))
      names(dotArgs[[i]])[wn] <- paste(names(dotArgs[[i]])[wn],
                                       names(dotArgs)[[i]], sep = ".")
    }
    Reduce(function(x, y) merge(x, y, by = by, all = all), dotArgs)
  }

  flossData <- mergeAll(dataSets)
}

if (MERGE_OPTION == "reshape") { # Option 3

  if (!suppressMessages(require(reshape))) install.packages('reshape')
  library(reshape)
  flossData <- reshape::merge_all(dataSets)
}

if (MERGE_OPTION == "plyr") { # Option 4

  if (!suppressMessages(require(plyr))) install.packages('plyr')
  library(plyr)
  flossData <- plyr::join_all(dataSets)
}

if (MERGE_OPTION == "dplyr") { # Option 5

  if (!suppressMessages(require(dplyr))) install.packages('dplyr')
  library(dplyr)

  flossData <- dataSets[[1]][1]
  flossData <- lapply(dataSets[[1]][-1],
                      function(x) {dplyr::left_join(x, flossData)})
}

if (MERGE_OPTION == "data.table") { # Option 6

  if (!suppressMessages(require(data.table))) 
    install.packages('data.table')
  library(data.table)

  flossData <- data.table(dataSets[[1]], key="Project ID")

  for (id in 2:length(dataSets)) {
    flossData <- merge(flossData, data.table(dataSets[[id]]),
                       by='Project ID', all.x = TRUE, all.y = FALSE)
  }
}

# http://stackoverflow.com/a/17458887/2872891
if (MERGE_OPTION == "data.table2") { # Option 6

  if (!suppressMessages(require(data.table))) 
    install.packages('data.table')
  library(data.table)

  DT <- data.table(dataSets[[1]], key="Project ID")
  flossData <- lapply(dataSets[[1]][-1], function(x) DT[.(x)])
}

# Additional Transformations (see TODO above)

# convert presence of Repo URL to integer
flossData[["Repo URL"]] <- as.integer(flossData[["Repo URL"]] != "")

# convert License Restrictiveness' factor levels to integers
#flossData[["License Restrictiveness"]] <- 
#  as.integer(flossData[["License Restrictiveness"]])

# convert User Community Size from character to integer
flossData[["User Community Size"]] <- 
  as.integer(flossData[["User Community Size"]])

# remove NAs
#flossData <- flossData[complete.cases(flossData[,3]),]
rowsNA <- apply(flossData, 1, function(x) {any(is.na(x))})
flossData <- flossData[!rowsNA,]

print(str(flossData))

错误信息如下:

Starting bmerge ...done in 0.001 secs
Starting bmerge ...done in 0.002 secs
Error in vecseq(f__, len__, if (allow.cartesian) NULL else as.integer(max(nrow(x),  : 

加入结果为 121229 行;超过 100000 = 最大(nrow(x),nrow(i))。检查 i 中的重复键值,每个 它一遍又一遍地加入 x 中的同一组。如果没问题的话 尝试包括 j 并删除 by (by-without-by) 以便 j 运行 每个组避免大分配。如果你确定你想 继续,使用 allow.cartesian=TRUE 重新运行。否则,请搜索 FAQ、Wiki、Stack Overflow 和 datatable-help 中的此错误消息 寻求建议。

当前问题与启用的 data.table 选项有关,但是,由于它是同一个包,我也希望能提供关于下一个选项的建议,它使用 alternative data.table mergingsyntax (尽管我觉得它太混乱了,但为了知识的完整性)。提前谢谢!

【问题讨论】:

  • 忘了说:我很好奇为什么错误信息列出了 121229 行,而我的 base 数据框(通过 all.x = TRUE, all.y = FALSE 指定)用于 left join 正好有 100000 行。
  • 从错误信息中你有什么不明白的地方?
  • @Arun:正如我在之前的评论中提到的,我不明白 left join 合并导致的数据框中的行数如何更高比 base(左)数据框中的行数。

标签: r merge dataframe data.table


【解决方案1】:

我会以这种方式处理这个问题:

首先,有一条错误消息。它说什么?

加入结果为 121229 行;超过 100000 = max(nrow(x),nrow(i))。检查 i 中的重复键值,每个键值都一遍又一遍地加入 x 中的同一组。如果没问题,请尝试包含 j 并删除 (by-without-by),以便 j 为每个组运行以避免大量分配。如果您确定要继续,请使用 allow.cartesian=TRUE 重新运行。否则,请在 FAQ、Wiki、Stack Overflow 和 datatable-help 中搜索此错误消息以获取建议。

太棒了!但是我有很多我正在使用的数据集,还有很多包和很多函数。我必须将范围缩小到产生此错误的数据集。

一一测试:

ans1 = merge(as.data.table(dataSets[[1]]), as.data.table(dataSets[[2]]), 
                all.x=TRUE, all.y=FALSE, by="Project ID")
## works fine.

ans2 = merge(as.data.table(dataSets[[1]]), as.data.table(dataSets[[3]]), 
                all.x=TRUE, all.y=FALSE, by="Project ID")
## same error

啊哈,同样的错误。

读取第二行错误信息:

所以,dataSets[[3]] 似乎发生了一些事情。它说要检查i 中的重复键值。让我们这样做:

dim(dataSets[[3]])
# [1] 81487     3
dim(unique(as.data.table(dataSets[[3]]), by="Project ID"))
# [1] 49999     3

因此,dataSets[[3]] 具有重复的“项目 ID”值,因此对于每个重复值,将返回来自 dataSets[[1]] 的所有匹配行 - 这是第二行的第二部分解释的内容:each of which join to the same group in x over and over again

试用allow.cartesian=TRUE:

我知道有重复的键,但仍希望继续。但是错误信息提到了我们可以如何继续,添加“allow.cartesian=TRUE”。

ans2 = merge(as.data.table(dataSets[[1]]), as.data.table(dataSets[[3]]), 
                all.x=TRUE, all.y=FALSE, by="Project ID", allow.cartesian=TRUE)

啊哈,现在可以正常使用了!那么allow.cartesian = TRUE 是做什么的呢?或者为什么添加它?错误消息说要在 stackoverflow 上搜索消息(以及其他内容)。

在 SO 上搜索allow.cartesian=TRUE

搜索让我找到了这个Why is allow.cartesian required at times when when joining data.tables with duplicate keys? 问题,该问题解释了目的,并且在评论下还包含来自@Roland 的另一个链接:Merging data.tables uses more than 10 GB RAM 指向最初的问题,这一切都开始了它.现在让我阅读这些帖子。


base::merge 会给出不同的结果吗?

现在,base::merge 是否返回不同的结果(有 100,000 行)?

dim(merge(dataSets[[1]], dataSets[[3]], all.x=TRUE, all.y=FALSE, by="Project ID"))
# [1] 121229      4

不是真的。它提供的维度与使用 data.table 时相同,但它不关心是否存在重复键,而 data.table 会警告您合并结果可能会爆炸,并允许您做出明智的决定。

【讨论】:

  • 这是一个很棒的解释!非常感谢。所以,我应该确保键列中没有重复值,在我的情况下是Project ID,对吗?如果您能就下一个选项(替代后缀)提出建议,那就太好了。很好的答案,支持和接受。
  • 顺便说一句,我注意到您使用as.data.table 而不是data.table,就像在我的原始代码中一样。有什么区别还是只是个人喜好?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-28
  • 2013-12-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多