web-dev-qa-db-ja.com

コアRを使用して「dist」クラスのインスタンスの要素を操作/アクセスするにはどうすればよいですか?

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"のインスタンスの専用のインデックス作成/アクセス要素にツールがありますか?

20
Paul McMurdie

私はあなたの質問に正解はありませんが、ユークリッド距離を使用している場合は、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
6
flodel

残念ながら、これを行うための標準的な方法はありません。これは、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]
8
Christian A

as.matrix(d)distオブジェクトdをマトリックスに変換し、as.dist(m)はマトリックスmdistオブジェクトに戻します。後者は、mが有効な距離行列であることを実際にはチェックしないことに注意してください。下三角部分を抽出するだけです。

4
Hong Ooi

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」オブジェクトからの元のデータで作成されました。

3
Ian Acuña

あなたはこれが[?? 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です。

1
ryszard

この応答は、実際には、クリスチャン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
1
csfowler

Distオブジェクトは、単純なベクターオブジェクトとほとんど同じように扱われるようです。私が見る限り、属性を持つベクトルを見ることができます。したがって、値を取得するには:

x = as.vector(distobject)

見る?インデックスを使用してオブジェクトの特定のペア間の距離を抽出する式のdist。

0
Ian Engelbrecht

結果の行列は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ベクトル内の位置です。私はそれが理にかなっていることを願っています。

0
NandaDorea

あなたはこれを行うことができます:

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

ただし、対角線または上三角に加えた変更はすべて無視されます。それは正しいことかもしれないし、そうでないかもしれない。そうでない場合は、何らかのサニティチェックまたは適切な処理を追加する必要があります。そしておそらく他の人。

0
Tyler

このためのstatsパッケージにはツールがないようです。非コアパッケージでの代替実装については@flodelに感謝します。

コアRソースの_"dist"_クラスの定義を掘り下げました。これは、この質問で質問しているような_dist.R_ソースファイルにツールがない古い学校のS3です。

dist()関数のドキュメントは、便利なことに、次のことを指摘しています(そして私は引用します)。

ベクトルの列によって格納された距離行列の下三角、たとえばdonが観測値の数、つまりn <- attr(do, "Size")の場合、i <j≤nの場合、(行)ijの間の非類似度は:

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の例がいくつか見つかりました。これは、この現在の例から非常に簡単に適合させることができます。

0
Paul McMurdie

これは、distオブジェクトの値を名前で取得するための実用的なソリューションです。アイテム9を値のベクトルとして取得したいですか?

as.matrix(mat1)[grepl("9", labels(mat1))]
0
NWaters

disto パッケージは、距離行列をR(メモリ内およびコア外)でラップするクラスを提供し、[のような便利な演算子よりもはるかに多くのものを提供します。 ビネット ここを確認してください。

PS:私はパッケージの作者です。

0
talegari