在一个数据帧中选择部分匹配另一个数据帧中的行的行

时间:2014-04-17 23:23:48

标签: r merge match apply

我希望在一个数据框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)

##########

2 个答案:

答案 0 :(得分:3)

与重新排序相比,我不确定这在时间方面会如何做,但您只需添加合并选项即可更改排序。

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)

答案 1 :(得分:3)

以下是针对此类问题的通用解决方案:

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)

第1步:将data.frame转换为data.table,前三列作为键。

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

步骤2:使用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)

希望这会有所帮助!!