decision - algoritmo chaid
¿Cómo manipulo/accedo a los elementos de una instancia de la clase "dist" utilizando el núcleo R? (10)
Desafortunadamente, no hay formas estándar de hacer esto. Aquí hay dos funciones que convierten el índice 1D en las coordenadas de la matriz 2D. No son bonitas, pero funcionan, y al menos puedes usar el código para hacer algo más agradable si lo necesitas. Lo estoy publicando solo porque las ecuaciones no son obvias.
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)
}
Un pequeño arnés de prueba para demostrar que funciona:
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]
Una clase básica / común en R se llama "dist"
y es una representación relativamente eficiente de una matriz de distancia simétrica. Sin embargo, a diferencia de un objeto "matrix"
, no parece haber soporte para manipular una instancia "dist"
por pares de índices utilizando el operador "["
.
Por ejemplo, el siguiente código no devuelve nada, NULL
o un error:
# 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]
Mientras tanto, los siguientes comandos funcionan, en cierto sentido, pero no hacen que sea más fácil acceder / manipular valores de pares de índices particulares:
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
Una solución, que quiero evitar, es convertir primero el objeto "dist"
en una "matrix"
, manipular esa matriz y luego volver a convertirla en "dist"
. Es decir, esto no es una pregunta sobre cómo convertir una instancia "dist"
en una "matrix"
, o alguna otra clase donde ya se han definido herramientas comunes de indexación de matrices; ya que esto ha sido respondido de varias maneras en una pregunta SO diferente
¿Hay herramientas en el paquete de stats
(o tal vez en algún otro paquete central de R) elementos de indexación / acceso dedicados de una instancia de "dist"
?
Esta respuesta es realmente solo un seguimiento extendido de la respuesta anterior de Christian A. Está justificado porque algunos lectores de la pregunta (yo incluido) pueden consultar el objeto dist como si fuera simétrico (no solo (7,13) como abajo, sino también (13,7). No tengo privilegios de edición y la respuesta anterior fue correcta siempre que el usuario tratara el objeto dist como un objeto dist y no como una matriz dispersa, por eso tengo una respuesta separada en lugar de una edición. Vota a Christian A por hacer el trabajo pesado si esta respuesta es útil La respuesta original con mis ediciones pegadas en:
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
Pero...
distdex(13,7,20) # =156
testd[156] #the wrong answer
La función del cristiano A solo funciona si i <j. Para i = j y i> j devuelve la respuesta incorrecta. Modificando la función de índice de desviación para devolver 0 cuando i == j y para transponer i y j cuando i> j resuelve el problema, de modo que:
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
La conversión a una matriz también estaba fuera de discusión para mí, porque la matriz resultante sería de 35K por 35K, así que la dejé como un vector (resultado de dist) y escribí una función para encontrar el lugar en el vector donde debería estar la distancia :
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)
}
Donde proporciona X e Y, las filas originales de los elementos en la matriz a partir de la cual calculó dist, y n es el número total de elementos en esa matriz. El resultado es la posición en el vector dist donde la distancia será. Espero que tenga sentido.
No parece haber herramientas en el paquete de stats
para esto. Gracias a @flodel por una implementación alternativa en un paquete no central.
Busqué en la definición de la clase "dist"
en la fuente principal R, que es S3 de la vieja escuela sin herramientas en el archivo fuente dist.R
, como lo que pregunto en esta pregunta.
La documentación de la función dist()
señala, de manera útil, que (y cito):
El triángulo inferior de la matriz de distancia almacenada por columnas en un vector, por ejemplo, do
. Si n
es el número de observaciones, es decir, n <- attr(do, "Size")
, entonces para i <j ≤ n, la diferencia entre (fila) i
y j
es:
do[n*(i-1) - i*(i-1)/2 + ji]
La longitud del vector es n*(n-1)/2
, es decir, de orden n^2
.
(cita final)
Aproveché esto en el siguiente código de ejemplo para un "dist"
acceso "dist"
. Tenga en cuenta que este ejemplo solo puede devolver un valor a la vez.
################################################################################
# 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)]]
}
################################################################################
Y esto parece funcionar bien, como se esperaba. Sin embargo, estoy teniendo problemas para que la función de reemplazo funcione.
################################################################################
# Define the replacement function
################################################################################
"[.dist<-" <- function(x, i, j, value){
x[[get.dist.index(x, i, j)]] <- value
return(x)
}
################################################################################
Una prueba de funcionamiento de este nuevo operador de asignación
dist1["5", "3"] <- 7000
Devoluciones:
"R> Error en dist1["5", "3"] <- 7000
: número incorrecto de subíndices en la matriz"
Según lo preguntado, creo que @flodel respondió mejor a la pregunta, pero todavía pensaba que esta "respuesta" también podría ser útil.
También encontré algunos buenos ejemplos S4 de acceso a corchetes y definiciones de reemplazo en el paquete Matrix , que podrían ser adaptados de este ejemplo actual con bastante facilidad.
No tengo una respuesta directa a su pregunta, pero si está utilizando la distancia euclidiana, eche un vistazo a la función rdist
del paquete de fields
. Su implementación (en Fortran) es más rápida que dist
, y la salida es de matrix
de clase. Como mínimo, muestra que algunos desarrolladores han optado por alejarse de esta clase dist
, tal vez por la razón exacta que mencionas. Si le preocupa que usar una matrix
completa para almacenar una matriz simétrica es un uso ineficiente de la memoria, puede convertirla en una matriz triangular.
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
Parece que los objetos dist se tratan de la misma manera que los objetos vectoriales simples. Por lo que puedo ver es un vector con atributos. Así que para sacar los valores:
x = as.vector(distobject)
¿Ver? Dist para una fórmula para extraer la distancia entre un par específico de objetos usando sus índices.
Podrías hacer esto:
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)
}
Lo que te permitiría hacer esto:
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
Sin embargo, se ignoran todos los cambios que realice en la diagonal o en el triángulo superior. Eso puede o no ser lo correcto a hacer. Si no es así, deberá agregar algún tipo de control de cordura o manejo adecuado para esos casos. Y probablemente otros.
Puede acceder a los atributos de cualquier objeto con str ()
para un objeto "dist" de algunos de mis datos (dist1), se ve así:
> 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)
puede ver que para este conjunto de datos en particular, el atributo "Etiquetas" es una cadena de caracteres de longitud = 96 con números del 1 al 96 como caracteres.
Podrías cambiar directamente esa cadena de caracteres haciendo:
> attr(dist1,"Labels") <- your.labels
"your.labels" debería ser una identificación. o factor vectorial, probablemente en los datos originales con el objeto "dist" fue creado.
Puede encontrar esto útil [desde ?? dist]:
El triángulo inferior de la matriz de distancia almacenada por columnas en un vector, diga ''hacer''. Si ''n'' es el número de observaciones, es decir, ''n <- attr (do, "Tamaño")'', entonces para i <j <= n, la diferencia entre (fila) i y j es ''do [n * (i-1) - i * (i-1) / 2 + ji] ''. La longitud del vector es n * (n-1) / 2, es decir, de orden n ^ 2.
as.matrix(d)
convertirá el objeto dist
d
en una matriz, mientras que as.dist(m)
convertirá la matriz m
nuevamente en un objeto dist
. Tenga en cuenta que este último no comprueba realmente que m
es una matriz de distancia válida; sólo extrae la parte triangular inferior.