如何防止具有重复索引/键的行被附加到数据框中?

10

我有一些数据,其中两个变量(“ManufactererId”和“ProductId”)的组合构成唯一键/标识符。数据看起来像这样:

my.data <- data.frame(ManufactererId = c(1, 1, 2, 2),
                      ProductId = c(1, 2, 1, 7),
                      Price = c(12.99, 149.00, 0.99, 3.99))
my.data
#   ManufactererId ProductId  Price
# 1              1         1  12.99
# 2              1         2 149.00
# 3              2         1   0.99
# 4              2         7   3.99

我希望确保我不能意外地添加另一行,其中ManufactererId-ProductId的组合与表中已存在的相同(就像数据库表上的唯一约束)。

也就是说,如果我尝试向我的数据框中添加一个ManufactererId = 2且ProductId = 7的行:

my.data <- rbind(my.data, data.frame(ManufactererId = 2, ProductId = 7, Price = 120.00))

...它应该失败并显示错误。如何实现这一点?

还是我应该使用不同的数据类型?

4个回答

7

1) zoo 这取决于您想进行哪些操作是否方便,但是zoo对象具有唯一的索引。我们可以通过将两个Id列粘在一起构建文本索引。

library(zoo)
z <- with(my.data, zoo(Price, paste(ManufactererId, ProductId)))

z <- c(z, zoo(90, "1 1")) # Error, not appended
z <- c(z, zoo(90, "1 3")) # OK

请注意,zoo对象的数据部分可以是向量(如上所示)或矩阵(如果您的数据不仅仅包含Price)。 2) SQLite 可以使用许多数据库来完成,但我们将在此处使用SQLite。首先,在SQLite数据库中创建一个带有唯一索引的表,然后插入行。
library(RSQLite)

con <- dbConnect(SQLite())
dbWriteTable(con, "my", my.data, row.names = FALSE)
dbGetQuery(con, "create unique index ix on my(ManufactererId, ProductId)")

dbGetQuery(con, sprintf("insert into my values(%d, %d, %d)", 1, 1, 99)) # error
dbGetQuery(con, sprintf("insert into my values(%d, %d, %d)", 1, 13, 90)) # OK

添加了第二种方法。 - G. Grothendieck

7
你可以像这样做:其中keys是你的唯一键。
append_save <- function(DF, to_be_appended, keys=c("ManufactererId", "ProductId")){
  if(ncol(DF) != ncol(to_be_appended) || !all(names(DF) %in% names(to_be_appended))){
    stop("must have the same columns")
  }
  if(nrow(merge(DF, to_be_appended, by=keys))==0){
    rbind(DF, to_be_appended)
  } else {
    stop("Trying to append douplicated indices")
  }
}

测试一下:

to_be_appended = data.frame(ManufactererId=2,ProductId=17,Price=3.99)
append_save(my.data, to_be_appended) # works
to_be_appended_err = data.frame(ManufactererId=2,ProductId=7,Price=3.99)
append_save(my.data, to_be_appended_err) # error

如果您仅基于键列添加数据,可以使用以下方式使用data.table

append_save <- function(DF, to_be_appended, keys=c("ManufactererId", "ProductId")){
  if(!all(keys %in% names(to_be_appended))){
    stop("key-columns must be present")
  }
  if(nrow(data.table::merge(DF, to_be_appended, on=keys))==0){
    data.table::setDF(data.table::rbindlist(list(DF, to_be_appended), fill = TRUE))[]
  } else {
    stop("Trying to append douplicated indices")
  }
}

1

使用基本的R语言方法之一是使用environment作为类似字典或哈希映射的对象。 my.dict <- new.env()

首先,编写一些辅助函数

make_key <- function(ManufactererId, ProductId)
  paste(ManufactererId, ProductId)

set_value <- function(key, value, dict){
         ## checking here assures desired behavior 
         if(any(key %in% names(dict)))
            stop("This key has been used")
         assign(key, value,  envir=dict)
}

然后,您可以生成如下的密钥:
keys <- make_key(my.data[[1]], my.data[[2]])

要设置值,您需要更加小心

# don't just do this as the first element is used by assign
# set_value(keys, my.data[[3]], dict=my.dict)

mapply(set_value, keys, my.data[[3]], MoreArgs = list(dict=my.dict))
ls.str(my.dict) # better than str for environments
# 1 1 :  num 13
# 1 2 :  num 149
# 2 1 :  num 0.99
# 2 7 :  num 3.99

set_value("1 1", 4, my.dict)
# Error in set_value("1 1", 4, my.dict) : This key has been used

0

排除重复项的rbind新数据的简单方法:

library(data.table)
my.data = data.table(ManufactererId = c(1, 1, 2, 2),
                     ProductId = c(1, 2, 1, 7),
                     Price = c(12.99, 149.00, 0.99, 3.99),
                     key = c("ManufactererId","ProductId"))
x = my.data # my data will be called 'x'
y = data.table(ManufactererId = 2, ProductId = 7, Price = 120.00)
rbind(x, y[!x, on=key(x)])
#   ManufactererId ProductId  Price
#1:              1         1  12.99
#2:              1         2 149.00
#3:              2         1   0.99
#4:              2         7   3.99

虽然您不需要设置键,只需直接向on参数提供字符向量即可。但我认为使用键来反映我们对数据结构的业务期望是值得的。


如果您想在这种情况下引发错误,可以使用以下方法:

unique.rbind = function(x, y, by=key(x)) {
    if (nrow(x[y, nomatch=0L, on=by])) stop("duplicates in 'y'")
    rbind(x, y)
}
unique.rbind(x, y)
# Error in unique.rbind(x, y) : duplicates in 'y'

如果出现错误,则不会插入 y 行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接