Rの基本/共通クラスは"dist"
と呼ばれ、対称距離行列の比較的効率的な表現です。ただし、"matrix"
オブジェクトとは異なり、"dist"
演算子を使用してインデックスペアで"["
インスタンスを操作することはサポートされていないようです。
たとえば、次のコードは何も返さない、NULL
、またはエラーを返します。
# First, create an example dist object from a matrix
mat1 <- matrix(1:100, 10, 10)
rownames(mat1) <- 1:10
colnames(mat1) <- 1:10
dist1 <- as.dist(mat1)
# Now try to access index features, or index values
names(dist1)
rownames(dist1)
row.names(dist1)
colnames(dist1)
col.names(dist1)
dist1[1, 2]
一方、次のコマンドは、ある意味では機能しますが、特定のインデックスペア値へのアクセス/操作を容易にするものではありません。
dist1[1] # R thinks of it as a vector, not a matrix?
attributes(dist1)
attributes(dist1)$Diag <- FALSE
mat2 <- as(dist1, "matrix")
mat2[1, 2] <- 0
回避したい回避策は、最初に"dist"
オブジェクトを"matrix"
に変換し、その行列を操作してから、"dist"
に戻すことです。つまり、これは 変換方法に関する質問"dist"
インスタンスを"matrix"
、または一般的な行列インデックス作成ツールがすでに定義されている他のクラスに変換する方法ではありません。 ;これはいくつかの方法で答えられているので 異なるSO質問
stats
パッケージ(またはおそらく他のコアRパッケージ)に、"dist"
のインスタンスの専用のインデックス作成/アクセス要素にツールがありますか?
私はあなたの質問に正解はありませんが、ユークリッド距離を使用している場合は、rdist
パッケージのfields
関数を見てください。その実装(Fortranで)はdist
よりも高速で、出力はクラスmatrix
です。少なくとも、あなたが言及している正確な理由のために、一部の開発者がこのdist
クラスから離れることを選択したことを示しています。対称行列を格納するために完全なmatrix
を使用することがメモリの非効率的な使用であることが懸念される場合は、それを三角行列に変換できます。
library("fields")
points <- matrix(runif(1000*100), nrow=1000, ncol=100)
system.time(dist1 <- dist(points))
# user system elapsed
# 7.277 0.000 7.338
system.time(dist2 <- rdist(points))
# user system elapsed
# 2.756 0.060 2.851
class(dist2)
# [1] "matrix"
dim(dist2)
# [1] 1000 1000
dist2[1:3, 1:3]
# [,1] [,2] [,3]
# [1,] 0.0000000001 3.9529674733 3.8051198575
# [2,] 3.9529674733 0.0000000001 3.6552146293
# [3,] 3.8051198575 3.6552146293 0.0000000001
残念ながら、これを行うための標準的な方法はありません。これは、1Dインデックス間で2D行列座標に変換する2つの関数です。それらはきれいではありませんが、機能します。少なくとも、必要に応じてコードを使用して、より良いものを作成できます。方程式がはっきりしないという理由だけで投稿します。
distdex<-function(i,j,n) #given row, column, and n, return index
n*(i-1) - i*(i-1)/2 + j-i
rowcol<-function(ix,n) { #given index, return row and column
nr=ceiling(n-(1+sqrt(1+4*(n^2-n-2*ix)))/2)
nc=n-(2*n-nr+1)*nr/2+ix+nr
cbind(nr,nc)
}
それが機能することを示すための小さなテストハーネス:
dist(rnorm(20))->testd
as.matrix(testd)[7,13] #row<col
distdex(7,13,20) # =105
testd[105] #same as above
testd[c(42,119)]
rowcol(c(42,119),20) # = (3,8) and (8,15)
as.matrix(testd)[3,8]
as.matrix(testd)[8,15]
as.matrix(d)
はdist
オブジェクトd
をマトリックスに変換し、as.dist(m)
はマトリックスm
をdist
オブジェクトに戻します。後者は、m
が有効な距離行列であることを実際にはチェックしないことに注意してください。下三角部分を抽出するだけです。
Str()を使用して、任意のオブジェクトの属性にアクセスできます。
一部のデータ(dist1)の「dist」オブジェクトの場合、次のようになります。
> str(dist1)
Class 'dist' atomic [1:4560] 7.3 7.43 7.97 7.74 7.55 ...
..- attr(*, "Size")= int 96
..- attr(*, "Labels")= chr [1:96] "1" "2" "3" "4" ...
..- attr(*, "Diag")= logi FALSE
..- attr(*, "Upper")= logi FALSE
..- attr(*, "method")= chr "euclidean"
..- attr(*, "call")= language dist(x = dist1)
この特定のデータセットの場合、「Labels」属性は長さ= 96の文字列であり、1から96までの数字が文字であることがわかります。
次のようにして、その文字列を直接変更できます。
> attr(dist1,"Labels") <- your.labels
「your.labels」はIDである必要があります。または因子ベクトル、おそらく「dist」オブジェクトからの元のデータで作成されました。
あなたはこれが[?? distから]役に立つと思うかもしれません:
ベクトルの列によって格納された距離行列の下三角、たとえば「do」。 'n'が観測値の数、つまり 'n <-attr(do、 "Size")'の場合、i <j <= nの場合、(行)iとjの非類似度は 'do [n * (i-1)-i *(i-1)/ 2 + ji] '。ベクトルの長さはn *(n-1)/ 2、つまり次数n ^ 2です。
この応答は、実際には、クリスチャンAの以前の応答を拡張したものにすぎません。質問の一部の読者(私自身を含む)がdistオブジェクトを対称であるかのようにクエリする可能性があるため(以下の(7,13)だけでなく(13,7)も保証されます)、編集権限がなく、ユーザーがdistオブジェクトをスパース行列ではなくdistオブジェクトとして扱っている限り、以前の回答は正しかったので、編集ではなく個別の応答があります。この回答が役立つ場合は、クリスチャンAに投票してください。 。私の編集を貼り付けた元の回答:
distdex<-function(i,j,n) #given row, column, and n, return index
n*(i-1) - i*(i-1)/2 + j-i
rowcol<-function(ix,n) { #given index, return row and column
nr=ceiling(n-(1+sqrt(1+4*(n^2-n-2*ix)))/2)
nc=n-(2*n-nr+1)*nr/2+ix+nr
cbind(nr,nc)
}
#A little test harness to show it works:
dist(rnorm(20))->testd
as.matrix(testd)[7,13] #row<col
distdex(7,13,20) # =105
testd[105] #same as above
だが...
distdex(13,7,20) # =156
testd[156] #the wrong answer
クリスチャンAの関数は、i <jの場合にのみ機能します。 i = jおよびi> jの場合、間違った答えが返されます。 distdex関数を変更して、i == jの場合は0を返し、i> jの場合はiとjを転置すると、問題が解決します。
distdex2<-function(i,j,n){ #given row, column, and n, return index
if(i==j){0
}else if(i > j){
n*(j-1) - j*(j-1)/2 + i-j
}else{
n*(i-1) - i*(i-1)/2 + j-i
}
}
as.matrix(testd)[7,13] #row<col
distdex2(7,13,20) # =105
testd[105] #same as above
distdex2(13,7,20) # =105
testd[105] #the same answer
Distオブジェクトは、単純なベクターオブジェクトとほとんど同じように扱われるようです。私が見る限り、属性を持つベクトルを見ることができます。したがって、値を取得するには:
x = as.vector(distobject)
見る?インデックスを使用してオブジェクトの特定のペア間の距離を抽出する式のdist。
結果の行列は35Kx 35Kになるため、行列への変換も問題ではありませんでした。それをベクトル(distの結果)として残し、ベクトル内で距離が必要な場所を見つける関数を作成しました。 :
distXY <- function(X,Y,n){
A=min(X,Y)
B=max(X,Y)
d=eval(parse(text=
paste0("(A-1)*n -",paste0((1:(A-1)),collapse="-"),"+ B-A")))
return(d)
}
XとYを指定すると、distを計算した行列内の要素の元の行であり、nはその行列内の要素の総数です。結果は、距離が存在するdistベクトル内の位置です。私はそれが理にかなっていることを願っています。
あなたはこれを行うことができます:
d <- function(distance, selection){
eval(parse(text = paste("as.matrix(distance)[",
selection, "]")))
}
`d<-` <- function(distance, selection, value){
eval(parse(text = paste("as.matrix(distance)[",
selection, "] <- value")))
as.dist(distance)
}
これにより、これを行うことができます。
mat <- matrix(1:12, nrow=4)
mat.d <- dist(mat)
mat.d
1 2 3
2 1.7
3 3.5 1.7
4 5.2 3.5 1.7
d(mat.d, "3, 2")
[1] 1.7
d(mat.d, "3, 2") <- 200
mat.d
1 2 3
2 1.7
3 3.5 200.0
4 5.2 3.5 1.7
ただし、対角線または上三角に加えた変更はすべて無視されます。それは正しいことかもしれないし、そうでないかもしれない。そうでない場合は、何らかのサニティチェックまたは適切な処理を追加する必要があります。そしておそらく他の人。
このためのstats
パッケージにはツールがないようです。非コアパッケージでの代替実装については@flodelに感謝します。
コアRソースの_"dist"
_クラスの定義を掘り下げました。これは、この質問で質問しているような_dist.R
_ソースファイルにツールがない古い学校のS3です。
dist()
関数のドキュメントは、便利なことに、次のことを指摘しています(そして私は引用します)。
ベクトルの列によって格納された距離行列の下三角、たとえばdo
。 n
が観測値の数、つまりn <- attr(do, "Size")
の場合、i <j≤nの場合、(行)i
とj
の間の非類似度は:
do[n*(i-1) - i*(i-1)/2 + j-i]
ベクトルの長さはn*(n-1)/2
、つまり次数_n^2
_です。
(引用終了)
次のdefine-yourself _"dist"
_アクセサーのサンプルコードでこれを利用しました。この例では、一度に1つの値しか返すことができないことに注意してください。
_################################################################################
# Define dist accessor
################################################################################
setOldClass("dist")
getDistIndex <- function(x, i, j){
n <- attr(x, "Size")
if( class(i) == "character"){ i <- which(i[1] == attr(x, "Labels")) }
if( class(j) == "character"){ j <- which(j[1] == attr(x, "Labels")) }
# switch indices (symmetric) if i is bigger than j
if( i > j ){
i0 <- i
i <- j
j <- i0
}
# for i < j <= n
return( n*(i-1) - i*(i-1)/2 + j-i )
}
# Define the accessor
"[.dist" <- function(x, i, j, ...){
x[[getDistIndex(x, i, j)]]
}
################################################################################
_
そして、これは予想通りうまくいくようです。しかし、交換機能を動作させるのに問題があります。
_################################################################################
# Define the replacement function
################################################################################
"[.dist<-" <- function(x, i, j, value){
x[[get.dist.index(x, i, j)]] <- value
return(x)
}
################################################################################
_
この新しい代入演算子のテスト実行
_dist1["5", "3"] <- 7000
_
戻り値:
「R> _dist1["5", "3"] <- 7000
_のエラー:行列の添え字の数が正しくありません」
尋ねられたように、@ flodelは質問にもっとよく答えたと思いますが、それでもこの「答え」も役立つかもしれないと思いました。
また、 Matrix package に角括弧アクセサーと置換定義のNice S4の例がいくつか見つかりました。これは、この現在の例から非常に簡単に適合させることができます。
これは、distオブジェクトの値を名前で取得するための実用的なソリューションです。アイテム9を値のベクトルとして取得したいですか?
as.matrix(mat1)[grepl("9", labels(mat1))]