我正在寻找一种实现方法,用于确定一个(例如,test
)数据帧中所有记录与第二个(例如training
)数据帧中任何记录的Gower距离的最小值。结果是一个向量,test
中的每一行都有一个元素。
数据是带有无序分类属性的分类,可以生成,例如,如下所示:
set.seed(20130926L)
DIMS <- 12
CATS <- 2
create.data <- function(SPARSITY) {
sparse.data <- rbinom(CATS ** DIMS, 1, SPARSITY)
sparse.array <- array(sparse.data, dim=rep(CATS, DIMS))
sparse.table <- as.table(sparse.array)
sparse.df <- as.data.frame(sparse.table)
sparse.df <- subset(sparse.df, Freq > 0, select=-Freq)
sparse.df
}
data.train <- create.data(0.001)
data.test <- create.data(0.01)
head(data.train, 3)
## Var1 Var2 Var3 Var4 Var5 Var6 Var7 Var8 Var9 Var10 Var11 Var12
## 745 A A A B A B B B A B A A
## 1156 B B A A A A A B A A B A
## 1574 B A B A A B A A A B B A
summary(data.test)
## Var1 Var2 Var3 Var4 Var5 Var6 Var7 Var8 Var9 Var10
## A:24 A:31 A:23 A:20 A:30 A:27 A:22 A:20 A:26 A:23
## B:24 B:17 B:25 B:28 B:18 B:21 B:26 B:28 B:22 B:25
## Var11 Var12
## A:24 A:22
## B:24 B:26
对于data.test
中的所有行,我如何找到Gower距离最小的data.training
中的行(或者至少距离该特定行的距离)?下面的代码可以使用,但是对于20个属性或超过2个类别需要太多内存:
nrow(data.test)
## [1] 48
library(StatMatch, quietly=T, warn.conflicts=F)
apply(gower.dist(data.train, data.test), 2, min)
## [1] 0.3333 0.4167 0.2500 0.5000 0.3333 0.4167 0.2500 0.3333 0.2500 0.4167
## [11] 0.5000 0.3333 0.3333 0.3333 0.4167 0.4167 0.2500 0.4167 0.1667 0.3333
## [21] 0.4167 0.3333 0.4167 0.5000 0.3333 0.5000 0.5000 0.4167 0.3333 0.3333
## [31] 0.2500 0.4167 0.5000 0.4167 0.3333 0.5000 0.3333 0.4167 0.3333 0.3333
## [41] 0.5000 0.5833 0.5000 0.2500 0.3333 0.4167 0.3333 0.5000
函数cluster::daisy()
也返回距离矩阵。
类似:How to calculate Euclidean distance (and save only summaries) for large data frames。在那里,建议对data.train
的子集多次调用距离函数。我可以这样做,但计算时间仍然过高。
毕竟,Gower距离的定义允许更有效的算法,也许是一种递归的分治方法,它按属性操作属性并在子集上调用自身。回想一下,Gower's distance是属性方向距离的(加权)总和,定义为
以下是Gower (A, A)
之间距离的简单演示
并计算A
和B
的所有组合。一个属性上不同的行的距离为0.5,两个属性上不同的行的最大距离为1.0:
(ex.train <- expand.grid(Var1=LETTERS[1:2], Var2=LETTERS[1:2]))
## Var1 Var2
## 1 A A
## 2 B A
## 3 A B
## 4 B B
ex.test <- ex.train[1, ]
gower.dist(ex.train, ex.test)
## [,1]
## [1,] 0.0
## [2,] 0.5
## [3,] 0.5
## [4,] 1.0
如果同时分析train.data
和test.data
,则可能的实现可能如下所示:
v
test.data
的子集,其中第一列的值为v
train.data
的子集,其中第一列的值为v
train.data
的子集,其中第一列的值为<> v
是否真的没有实现,或者可能是描述这种算法的论文?
答案 0 :(得分:4)
我不熟悉高尔的距离,但根据你的描述,似乎对于无序的分类属性,高尔的距离相当于汉明距离除以向量的长度。换句话说,向量x
和y
之间的Gower距离只是mean(x!=y)
。在这种情况下,您可以通过避免计算整个距离矩阵来节省大量的计算时间,而是使用colSums
。这是一个示例,包含三个级别和10000个训练行:
> set.seed(123)
> train.rows<-10000
> test.rows<-100
> cols<-20
> levels<-c("a","b","c")
> train.set<-sample(levels,train.rows*cols,T)
> dim(train.set)<-c(train.rows,cols)
> test.set<-sample(levels,test.rows*cols,T)
> dim(test.set)<-c(test.rows,cols)
> system.time(gdist<-apply(gower.dist(train.set,test.set),2,min))
user system elapsed
13.396 0.324 13.745
> system.time(hdist<-apply(test.set,1,function(x) min(colSums(x!=t(train.set))/cols)))
user system elapsed
0.492 0.008 0.504
> identical(hdist,gdist)
[1] TRUE
如果数据不是离散的和无序的,那么Gower距离的公式是不同的,但我怀疑有一种类似的方法可以更有效地计算它,而无需通过gower.dist
计算整个距离矩阵。
更新:使用@ Frank的建议可以提高效率,并且可以预先生成t(train.set)
而不是在函数内:
require(microbenchmark)
ttrain.set<-t(train.set)
microbenchmark(
a=apply(test.set,1,function(x) min(colSums(x!=t(train.set))/cols)),
b=apply(test.set,1,function(x) min(colSums(x!=ttrain.set)/cols)))
## Unit: milliseconds
## expr min lq median uq max neval
## a 523.3781 533.2950 589.0048 620.4411 725.0183 100
## b 367.5428 371.6004 396.7590 408.9804 496.4001 100
答案 1 :(得分:0)
我将此作为评论的一部分,但除非我错过了问题,否则它确实是一个候选人:不应该只是:
ddat <- gower.dist(data.train, data.test)
which(ddat==min(ddat), arr.ind=TRUE)
# row col
#[1,] 3 19
? (它已经设计为自己进行“应用”操作。)
如果目标是将min dist放到'data.test'中的特定行,那么它将更快并占用更少的空间。我仍然没有弄清楚为什么这会导致记忆困难。目标是找到最小距离或找出每个data.test行的最小距离。