【问题标题】:select rows in one data frame that partially match rows in another data frame选择一个数据帧中与另一个数据帧中的行部分匹配的行
【发布时间】:2014-04-17 23:23:48
【问题描述】:

我希望在一个数据帧data.1 中选择与第二个数据帧keep.these 中的行部分匹配的行,以获得desired.result。我在这里找到了几个基于一列匹配的问题,但我想匹配三列:STATECOUNTYCITY。到目前为止,我已经提出了三种解决方案,但似乎没有一个是理想的。

请注意,在我的真实数据中,每一行都包含STATECOUNTYCITY 的唯一组合。

当我使用merge 时,我必须重新使用order。函数match 似乎可以工作,但我不熟悉它,不知道我对这个函数的使用是否符合预期。下面的apply 解决方案显然太复杂了。

如果我不必重新排序结果,merge 方法将是理想的。对于大型数据集,重新排序可能会很耗时。如果有人可以确认这是一种合理的方法,match 方法似乎还可以。

有没有更好的解决方案,最好是在基础R 中?

data.1 <- read.table(text= "
     CITY     COUNTY   STATE        AA
       1          1         1        2
       2          1         1        4
       1          2         1        6
       2          2         1        8
       1          1         2       20
       2          1         2       40
       1          2         2       60
       2          2         2       80
       1          1         3      200
       2          1         3      400
       1          2         3      600
       2          2         3      800
       1          1         4     2000
       2          1         4     4000
       1          2         4     6000
       2          2         4     8000
       1          1         5    20000
       2          1         5    40000
       1          2         5    60000
       2          2         5    80000
", header=TRUE, na.strings=NA)

keep.these <- read.table(text= "
     CITY     COUNTY     STATE      BB
       1          1         2      -10
       2          1         2      -11
       1          2         2      -12
       2          2         2      -13
       1          1         4      -14
       2          1         4      -15
       1          2         4      -16
       2          2         4      -17
", header=TRUE, na.strings=NA)

desired.result <- read.table(text= "
     CITY     COUNTY    STATE       AA
       1          1         2       20
       2          1         2       40
       1          2         2       60
       2          2         2       80
       1          1         4     2000
       2          1         4     4000
       1          2         4     6000
       2          2         4     8000
", header=TRUE, na.strings=NA)

##########

# this works, but I need to reorder

new.data.a <- merge(keep.these[,1:3], data.1, by=c('CITY', 'COUNTY', 'STATE'))

new.data.a <- new.data.a[order(new.data.a$STATE, new.data.a$COUNTY, new.data.a$CITY),]

rownames(desired.result) <- NULL
rownames(new.data.a)     <- NULL

all.equal(desired.result, new.data.a)

##########

# this seems to work, but match is unfamiliar

new.data.2 <- data.1[match(data.1$CITY  , keep.these$CITY  , nomatch=0) & 
                     match(data.1$STATE , keep.these$STATE , nomatch=0) & 
                     match(data.1$COUNTY, keep.these$COUNTY, nomatch=0),]

rownames(desired.result) <- NULL
rownames(new.data.2)     <- NULL

all.equal(desired.result, new.data.2)

##########

# this works, but is too complex

data.1b      <- data.frame(my.group = apply(    data.1[,1:3], 1, paste, collapse = "."),     data.1)
keep.these.b <- data.frame(my.group = apply(keep.these[,1:3], 1, paste, collapse = "."), keep.these)

data.1b <- data.1b[apply(data.1b, 1, function(x) {x[1] %in% keep.these.b$my.group}),]
data.1b <- data.1b[,-1]

rownames(desired.result) <- NULL
rownames(data.1b)        <- NULL

all.equal(desired.result, data.1b)

##########

【问题讨论】:

    标签: r merge match apply


    【解决方案1】:

    对于这类问题,这是一个非常有效的通用解决方案:

    data.1.ID <- paste(data.1[,1],data.1[,2],data.1[,3])
    
    keep.these.ID <- paste(keep.these[,1],keep.these[,2],keep.these[,3])
    
    desired.result <- data.1[data.1.ID %in% keep.these.ID,]
    

    我只是为每条记录创建了一个唯一的 ID,然后搜索它。 注意:这将更改行名,您可能需要添加以下内容:

    row.names(desired.result) <- 1:nrow(desired.result)
    

    编辑:

    这是解决相同问题的另一种方法。

    如果您有一个非常大的数据集,比如数百万行,另一个非常有效的解决方案是使用包data.table。它的工作速度比merge 快​​近 50-100 倍,具体取决于您拥有多少数据。

    您所要做的就是:

    library(data.table)
    

    Step1:将data.frame转换为data.table,前三列为key。

    d1 <- data.table(data.1, key=names(data.1)[1:3])
    kt <- data.table(keep.these, key=names(keep.these)[1:3])
    

    Step2:使用data.table的二分查找进行合并:

    d1[kt]
    

    注意1:执行的简单性。 注意2:这将按键排序数据。为避免这种情况,请尝试以下操作:

    data.1$index <- 1:nrow(data.1)  # Add index to original data
    d1 <- data.table(data.1,key=names(data.1)[1:3]) # Step1 as above
    kt <- data.table(keep.these,key=names(keep.these)[1:3])  # Step1 as above
    d1[kt][order(index)]  # Step2 as above
    

    如果您想删除最后两列(indexBB),也很简单:

    d1[kt][order(index)][,-(5:6),with=F] #Remove index
    

    用大型数据集试试这个,并将时间与merge 进行比较。它通常快 50-100 倍。

    要了解有关data.table 的更多信息,请尝试:

    vignette("datatable-intro")
    vignette("datatable-faq")
    vignette("datatable-timings")
    

    或者看看它的实际效果:

    example(data.table)
    

    希望这会有所帮助!

    【讨论】:

    • 谢谢。非常好。这是我的apply 解决方案,删除了所有apply 函数!
    • +1,如果您坚持使用 data.tables,使用 setDT 通过引用将 data.frame 修改为 data.table 将是即时的(+ 无复制 - 对大数据有用) .
    【解决方案2】:

    与重新排序相比,我不确定这在时间方面的效果如何,但您可以简单地添加一个合并选项以不更改排序。

    new.data.a <- merge(keep.these[,1:3], data.1, by=c('CITY', 'COUNTY', 'STATE'), sort = FALSE)
    rownames(desired.result) <- NULL
    rownames(new.data.a)     <- NULL
    all.equal(desired.result, new.data.a)
    

    【讨论】:

    • 完美!我查看了merge 帮助页面,但没有注意到该选项。
    猜你喜欢
    • 2018-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-12
    • 2014-09-20
    • 1970-01-01
    • 1970-01-01
    • 2019-08-14
    相关资源
    最近更新 更多