Простая матрица подобия для непрерывных недвоичных данных?

Учитывая матрицу

structure(list(X1 = c(1L, 2L, 3L, 4L, 2L, 5L), X2 = c(2L, 3L, 
4L, 5L, 3L, 6L), X3 = c(3L, 4L, 4L, 5L, 3L, 2L), X4 = c(2L, 4L, 
6L, 5L, 3L, 8L), X5 = c(1L, 3L, 2L, 4L, 6L, 4L)), .Names = c("X1", 
"X2", "X3", "X4", "X5"), class = "data.frame", row.names = c(NA, 
-6L))

Я хочу создать матрицу расстояний 5 x 5 с соотношением совпадений и общим количеством строк между всеми столбцами. Например, расстояние между X4 и X3 должно быть равно 0,5, учитывая, что оба столбца совпадают 3 из 6 раз.

Я пытался использовать dist(test, method="simple matching") из пакета "proxy", но этот метод работает только для двоичных данных.


person Werner    schedule 24.05.2012    source источник


Ответы (5)


Использование outer (опять же :-)

my.dist <- function(x) {
 n <- nrow(x)
 d <- outer(seq.int(ncol(x)), seq.int(ncol(x)),
            Vectorize(function(i,j)sum(x[[i]] == x[[j]]) / n))
 rownames(d) <- names(x)
 colnames(d) <- names(x)
 return(d)
}

my.dist(x)
#           X1        X2  X3  X4        X5
# X1 1.0000000 0.0000000 0.0 0.0 0.3333333
# X2 0.0000000 1.0000000 0.5 0.5 0.1666667
# X3 0.0000000 0.5000000 1.0 0.5 0.0000000
# X4 0.0000000 0.5000000 0.5 1.0 0.0000000
# X5 0.3333333 0.1666667 0.0 0.0 1.0000000
person flodel    schedule 24.05.2012

Вот пример (dt — ваша матрица):

library(reshape)
df = expand.grid(names(dt),names(dt))
df$val=apply(df,1,function(x) mean(dt[x[1]]==dt[x[2]]))
cast(df,Var2~Var1)
person blindjesse    schedule 24.05.2012
comment
Это прекрасно работает! Большое спасибо. Есть только одна ошибка: df2 = df в строке 3. - person Werner; 24.05.2012

Вот решение, которое быстрее, чем два других, хотя и немного уродливое. Я предполагаю, что лежачие полицейские возникают из-за того, что не используется mean(), поскольку он может быть медленным по сравнению с sum(), а также вычисляется только половина выходной матрицы, а затем вручную заполняется нижний треугольник. В настоящее время функция оставляет NA по диагонали, но вы можете легко установить их равными единице, чтобы полностью соответствовать другим ответам с помощью diag(out) <- 1

FUN <- function(m) {
  #compute all the combinations of columns pairs
  combos <- t(combn(ncol(m),2))
  #compute the similarity index based on the criteria defined
  sim <- apply(combos, 1, function(x) sum(m[, x[1]] - m[, x[2]] == 0) / nrow(m))
  combos <- cbind(combos, sim)
  #dimensions of output matrix
  out <- matrix(NA, ncol = ncol(m), nrow = ncol(m))

  for (i in 1:nrow(combos)){
    #upper tri
    out[combos[i, 1], combos[i, 2]] <- combos[i,3]
    #lower tri
    out[combos[i, 2], combos[i, 1]] <- combos[i,3]
  }
  return(out)
}

Я взял два других ответа, превратил их в функции и провел сравнительный анализ:

library(rbenchmark)
benchmark(chase(m), flodel(m), blindJessie(m), 
          replications = 1000,
          order = "elapsed", 
          columns = c("test", "elapsed", "relative"))
#-----
       test elapsed relative
1  chase(m)   1.217 1.000000
2 flodel(m)   1.306 1.073131
3 blindJessie(m)  17.691 14.548520
person Chase    schedule 24.05.2012
comment
Чейз, в вашем коде есть ошибка: вы не можете использовать combos после того, как сделаете transform(combos, ...), потому что ... будет оцениваться внутри combos. Я подозреваю, что у вас была еще одна копия combos в вашей глобальной среде, поэтому она работала на вас. Это должно быть легко исправить, если сделать копию комбо перед вызовом transform. - person flodel; 24.05.2012
comment
@flodel - хороший улов, спасибо. Внес соответствующие коррективы и переделал тайминги. использование матриц и cbind также ускорило работу функции. - person Chase; 24.05.2012
comment
Что ж, тогда вы можете запустить их снова, так как я также улучшил скорость своего ответа. На моей машине моя версия все еще немного медленнее вашей, но не настолько: коэффициент снизился до 1,07. - person flodel; 24.05.2012
comment
@flodel - отличная работа, я получаю эквивалентные тесты. Мне нравится ваш ответ, так как он более канонический. Я думаю, что вы можете получить немного больше производительности (легко), изменив бит outer(names(x), names(x) на outer(seq.int(ncol(x)), seq.int(ncol(x)), поскольку это примитив. Я также думаю, что names() потерпит неудачу, если у матрицы нет имен. Когда я сделал это изменение, вы протестировали его в пределах 1.02 моего хакерского задания... наверное, микрооптимизации хватит на одну ночь :). - person Chase; 25.05.2012
comment
Хороший вопрос, Чейз, я внес предложенное вами изменение. Спасибо! - person flodel; 25.05.2012

Я получил ответ следующим образом: 1-й я внес некоторые изменения в данные строки как:

X1 = c(1L, 2L, 3L, 4L, 2L, 5L)
X2 = c(2L, 3L, 4L, 5L, 3L, 6L)
X3 = c(3L, 4L, 4L, 5L, 3L, 2L)
X4 = c(2L, 4L, 6L, 5L, 3L, 8L)
X5 = c(1L, 3L, 2L, 4L, 6L, 4L)
matrix_cor=rbind(x1,x2,x3,x4,x5)
matrix_cor

   [,1] [,2] [,3] [,4] [,5] [,6]
X1    1    2    3    4    2    5
X2    2    3    4    5    3    6
X3    3    4    4    5    3    2
X4    2    4    6    5    3    8
X5    1    3    2    4    6    4

тогда:

dist(matrix_cor)

     X1       X2       X3       X4
X2 2.449490                           
X3 4.472136 4.242641                  
X4 5.000000 3.000000 6.403124         
X5 4.358899 4.358899 4.795832 6.633250
person Dilnessa G.    schedule 18.02.2017
comment
Привет. Спасибо за ответ: я отредактировал его, чтобы код был читаемым. В будущем, пожалуйста, отформатируйте свои ответы, чтобы облегчить чтение (stackoverflow.com/editing-help). - person lbusett; 18.02.2017

Спасибо всем за ваши предложения. Основываясь на ваших ответах, я разработал трехстрочное решение (тест — это название набора данных).

require(proxy)
ff <- function(x,y) sum(x == y) / NROW(x)
dist(t(test), ff, upper=TRUE)

Вот результат:

          X1        X2        X3        X4        X5
X1           0.0000000 0.0000000 0.0000000 0.3333333
X2 0.0000000           0.5000000 0.5000000 0.1666667
X3 0.0000000 0.5000000           0.5000000 0.0000000
X4 0.0000000 0.5000000 0.5000000           0.0000000
X5 0.3333333 0.1666667 0.0000000 0.0000000          
person Werner    schedule 25.05.2012
comment
Я не могу заставить это работать, ff не определен... даже когда я изменил его на f, это не удалось с Error in as.character(x) : cannot coerce type 'closure' to vector of type 'character' - person Chase; 25.05.2012
comment
Я думаю, это потому, что функция dist, которую я использую, принадлежит пакетному прокси. Я добавлю в код require(proxy). - person Werner; 25.05.2012