堪培拉距离 - 不一致的结果

6
我正在尝试理解我的坎贝拉距离计算的情况。我编写了自己简单的canberra.distance函数,但结果与dist函数不一致。我在函数中添加了选项na.rm = T,以便在分母为零时能够计算总和。从?dist中我了解到,它们使用类似的方法:分子和分母为零的术语被省略在总和之外,并被视为缺失值。
canberra.distance <- function(a, b){
  sum( (abs(a - b)) / (abs(a) + abs(b)), na.rm = T )
}

a <- c(0, 1, 0, 0, 1)
b <- c(1, 0, 1, 0, 1)
canberra.distance(a, b)
> 3 
# the result that I expected
dist(rbind(a, b), method = "canberra")
> 3.75 


a <- c(0, 1, 0, 0)
b <- c(1, 0, 1, 0)
canberra.distance(a, b)
> 3
# the result that I expected
dist(rbind(a, b), method = "canberra")
> 4   

a <- c(0, 1, 0)
b <- c(1, 0, 1)
canberra.distance(a, b)
> 3
dist(rbind(a, b), method = "canberra")
> 3
# now the results are the same

0-0 和 1-1 这两组数据似乎有问题。在第一种情况下(0-0),分子和分母都等于零,应该将这一对数据省略掉。在第二种情况下(1-1),分子为 0 但分母不为 0,因此这个项也为 0,总和应该保持不变。

我在这里错过了什么吗?

编辑: 为了与 R 定义相一致,函数 canberra.distance 可以修改如下:

canberra.distance <- function(a, b){
  sum( abs(a - b) / abs(a + b), na.rm = T )
}

然而,结果与之前相同。

2
我认为你可能在基本R中遇到了一个错误。我不确定,但是Wolfram Alpha同意你的看法。不幸的是,我找不到权威参考资料,但根据维基百科和沃尔夫拉姆的说法,你的实现似乎是正确的。 - Konrad Rudolph
事实上,dist的文档将Canberra距离定义为*sum(|x_i - y_i| / |x_i + y_i|)*(这与您和Wolfram的定义不同)。它还指出:“[t]这适用于非负值(例如计数):取分母的绝对值是1998年R修改的一项,以避免负距离。”-因此,R的定义已记录为不同。 - Konrad Rudolph
@KonradRudolph谢谢您的回复!我编辑了我的帖子。使用R定义的坎贝拉距离存在不一致性,因此我不认为这是问题所在。 - Adela
是的,我怀疑文档提到的“1998 R修改”不仅仅是去除负值。实际上,文档可能意味着“这个实现与其他定义有不确定的差异,因此产生不同的结果”。查看C源代码可能会澄清这一点。无论如何,至少文档记录得很差。 - Konrad Rudolph
1个回答

0

这可能会让你对差异有所了解。据我所见,这是实际运行计算距离的代码。

static double R_canberra(double *x, int nr, int nc, int i1, int i2)
{
    double dev, dist, sum, diff;
    int count, j;

    count = 0;
    dist = 0;
    for(j = 0 ; j < nc ; j++) {
    if(both_non_NA(x[i1], x[i2])) {
        sum = fabs(x[i1] + x[i2]);
        diff = fabs(x[i1] - x[i2]);
        if (sum > DBL_MIN || diff > DBL_MIN) {
        dev = diff/sum;
        if(!ISNAN(dev) ||
           (!R_FINITE(diff) && diff == sum &&
            /* use Inf = lim x -> oo */ (int) (dev = 1.))) {
            dist += dev;
            count++;
        }
        }
    }
    i1 += nr;
    i2 += nr;
    }
    if(count == 0) return NA_REAL;
    if(count != nc) dist /= ((double)count/nc);
    return dist;
}

我认为罪魁祸首就是这一行代码

if(!ISNAN(dev) ||
               (!R_FINITE(diff) && diff == sum &&
                /* use Inf = lim x -> oo */ (int) (dev = 1.))) 

这个处理特殊情况的函数可能没有文档记录。


谢谢你的回答。不幸的是,我并不是很擅长C++,所以我不确定这些特殊情况发生了什么。你能否请解释一下? - Adela
该行代码包含一个特殊情况,即如果两个值相同 diff == sum ,则将分子加一,因为它设置了 dev=1。但是,为什么代码首先通过 diff > DBL_MIN 是有点不清楚的。也许浮点转换/精度存在问题? - ekstroem
将分子加1可以解释1-1对和距离值4的情况。但我不明白这如何涉及0-0对的情况。有什么想法吗? - Adela
对于0-0对,它们的和为零,差也为零,因此diff == sum部分仍然会触发。 - ekstroem

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