biobabble作者群有一個小夥伴提出來的問題。
話說有一個矩陣,我們讓它某個值缺失,算出來的距離和原來的竟然是一樣的:> dat = matrix(1:10, 2)> dist(dat)122.236068> dat[1,1] = NA> dist(dat)122.236068這有點反直覺,實際上你再搞個缺失值,它算出來還是一樣的:
> dat[1,3] = NA> dist(dat) 12 2.236068但以我的經驗,我通常不敢輕易說人家有bug,我們得確認一下。
於是我讀了R的原始碼中歐式距離的代碼:
staticdoubleR_euclidean(double *x, int nr, int nc, int i1, int i2){double dev, dist;int count, j; count= 0; dist = 0;for(j = 0 ; j < nc ; j++) {if(both_non_NA(x[i1], x[i2])) { dev = (x[i1] - x[i2]);if(!ISNAN(dev)) { dist += dev * dev; count++; } } i1 += nr; i2 += nr; }if(count == 0) return NA_REAL;if(count != nc) dist /= ((double)count/nc);returnsqrt(dist);}把NA幹掉?
首先從代碼上看,缺失值會被去掉,這就是count計數看有多少有效值的作用。如果我們簡單粗爆地把NA值去掉,我們會算出來什麼值呢?
完整數據的情況下,距離是2.236068dat[1,1] = NA的情況下,距離是2dat[1,c(1,3)] = NA的情況下,距離是1.732051從這裡我們可以看到,缺失值越多,距離是越小的,這個從歐式距離的公式上也反映出來,所有的量都是正值,少了就小了。所以如果我們簡單地去NA值,那麼對距離的估算會低估。
怎麼補救?
這就是倒數第二句幹的事情:
if(count != nc) dist /= ((double)count/nc);如果有缺少,用已知的點的均值來估計。也就是用mean((qi-pi)^2)去代替缺失點。
不難驗證一下,前面第二種情況時距離是2,那麼被一下缺失值:
> sqrt(2^2 / (4/5))[1] 2.236068出來的就是第一種情況時的值了。
如果你看不明白的話,我再拆一下給你看:
2^2 是總和2^2/4 是均值2^2 + 2^2/4 就是補了缺失值的總和2^2 / (4/5)等同於上面的值sqrt(2^2 / (4/5))算出來就是距離,請對照公式來看一下。為什麼能夠完美估計原來的值?
這也是一開頭讓大家疑惑的地方,為什麼做為估計出來的值能和真實的值一樣?
這其實是個巧合,真的是巧合。你如果是一般的隨機數拿來試試,肯定無法一模一樣的值。
當然對於一個determinant的計算,巧合也是算出來的,必定是巧合,原因必須存在於數值之中。
如果你對dat算相關係數的話,就明白了。我們對原始值算一下:
> cor(dat[1,], dat[2,])[1] 1能夠完美估算出距離,其實等同於能夠完美復原缺失點,而這種情況只在於數據的相關係數等於一的情況下,數據的相關性越好,也就能夠復原得越好,這很好理解。
歐式距離對於outlier是很敏感的,你可以試試不同的值去替換掉原始值,再看看缺失值的情況下,計算出來的距離和不缺失的情況下,差距有多少。
寫到這裡,我想到了歐式距離可以拿來檢測outlier啊。比如我們每次讓一個點缺失,比如說我們有100個點,就可以計算出100個距離,這些距離各不相等(正常情況下數據相關系統不會剛剛好是1),它們和真實值的差值也可以算出來,這100個差值裡面,差值較大的,相對應的缺失點,就是outlier。