4

I'm trying to do a nonmetric MDS (R version 3.3.3) using the isoMDS function in the MASS package and I get this error:

Error in isoMDS(d): zero or negative distance between objects 1 and 2 

Here's an example of what I'm doing:

# LOAD LIBRARY
library(MASS)

# CREATE FAKE DATA
a <- c(1, 1, 1, 1)
b <- c(2, 2, 2, 2)
c <- c(3, 3, 4, 5)
d <- c(4, 4, 7, 9)
x <- data.frame(a, b, c, d)

x
  a b c d
1 1 2 3 4
2 1 2 3 4
3 1 2 4 7
4 1 2 5 9

# EUCLIDEAN DISTANCE BETWEEN ROWS 1, 2, 3 and 4
d <- dist(x)

d
         1        2        3
2 0.000000                  
3 3.162278 3.162278         
4 5.385165 5.385165 2.236068

# NMDS
fit <- isoMDS(d)

Error in isoMDS(d) : distance négative ou nulle entre les objets 1 et 2

I don't know if there's a way of getting around this issue or if I'm doing something wrong. I understand that objects 1 and 2 are identical and that that's probably why the distance is negative or equals to zero. I found out that my question was a "FAQ", but one of the only answers I found is this:

Short answer: you cannot compare distances including NAs, so there is no way to find a monotone mapping of distances. If the data really are identical for two rows, you can easily drop one of them whilst doing MDS, and then assign the position found for one to the other.

So, my next questions are: how do you drop rows whilst doing MDS, and is there any other way to perform a NMDS?

Any help would be greatly appreciated!

  • Thank you for your answer @Marco, but I'm not sure I understand. In my example, I wanted to compute the distances between rows 1, 2, 3 and 4 (not columns A, B, C and D). I just edited my question, to make it more clear! –  Jul 01 '17 at 23:08
  • What is the intuiton behind calculating distance between rows using euclidean or any other formula. ? How does it help in dimension reduction. – Naveen Gabriel Sep 18 '18 at 21:31

3 Answers3

0

The dist function computes the distances between the rows of a data matrix.
Your a, b, c, and d vectors are the columns of the x matrix, not the rows.
A simple solution is to transpose x:

library(MASS)

a <- c(1, 1, 1, 1)
b <- c(2, 2, 2, 2)
c <- c(3, 3, 4, 5)
d <- c(4, 4, 7, 9)
x <- data.frame(a, b, c, d)

# Calculate distance between the columns
d <- dist(t(x))

# NMDS
fit <- isoMDS(d)

# initial  value 0.000000 
# final  value 0.000000 
# converged

fit
# $points
#        [,1]       [,2]
# a -4.594429  0.4509513
# b -2.770312 -0.3638885
# c  1.098884 -0.3114594
# d  6.265857  0.2243966
# 
# $stress
# [1] 7.976932e-15

I hope it can help you.

Marco Sandri
  • 23,289
  • 7
  • 54
  • 58
0

As you noted, you have identical rows. You can omit identical rows when you first create the distance matrix

d <- dist(x[-1,])

Then continue as normal

fit <- isoMDS(d)
0

Alternatively, you could try the vegan::metaMDS function:

library(vegan)
#> This is vegan 2.5-3

x <- data.frame(a = c(1, 1, 1, 1), 
                b = c(2, 2, 2, 2), 
                c = c(3, 3, 4, 5), 
                d = c(4, 4, 7, 9))

# The warnings are expected for such a small dataset
fit <- vegan::metaMDS(comm = dist(x))
#> ... Procrustes: rmse 0.09543314  max resid 0.108719 
#> *** No convergence -- monoMDS stopping criteria:
#>     17: stress < smin
#>      3: scale factor of the gradient < sfgrmin
#> Warning in vegan::metaMDS(comm = dist(x)): stress is (nearly) zero: you may
#> have insufficient data

ordiplot(fit, type = "text")

enter image description here

Variables/columns "a" and "b" (1 and 2) get the same coordinates.


Similarly, using the smacof::mds function:

library(smacof)
fit2 <- smacof::mds(delta = dist(x), type = "ordinal")
fit2$conf
#>           D1           D2
#> 1  0.5742535  0.007220978 # 1 & 2 get the same coordinates
#> 2  0.5742535  0.007220978
#> 3 -0.2749314 -0.034928060
#> 4 -0.8735757  0.020486105
Valentin_Ștefan
  • 6,130
  • 2
  • 45
  • 68