Brandon S Brandon S - 1 month ago 6
R Question

Creative loop for filling in data frame values based on other data frame values in R

For those familiar with the Analytical Hierarchy Process, this should be a little more intuitive...

Use the following code to create the data.frame

criteria
:

c <- 5
cri_names <- c("Applicability", "Deployment", "Scalability", "Ease-of-Use", "TCO")
c1 <- data.frame(c = rep(0, c))
criteria <- data.frame(do.call("cbind", rep(c1, c)))
colnames(criteria) <- cri_names
rownames(criteria) <- cri_names

cvs <- data.frame(c1 = c(1, 5, 3, 1, 3),
c2 = c(1, 1, 1/3, 1/5, 1/5),
c3 = c(1, 1, 1, 1/3, 3),
c4 = c(1, 1, 1, 1, 5),
c5 = c(1, 1, 1, 1, 1))

for(v in c(1:ncol(cvs))) {
criteria[v, ] <- cvs[, v]
}

print(criteria)

# Applicability Deployment Scalability Ease-of-Use TCO
#Applicability 1 5 3.0000000 1.0000000 3.0
#Deployment 1 1 0.3333333 0.2000000 0.2
#Scalability 1 1 1.0000000 0.3333333 3.0
#Ease-of-Use 1 1 1.0000000 1.0000000 5.0
#TCO 1 1 1.0000000 1.0000000 1.0


What I now want to do is replace every
1
to the left of
criteria[x, x]
with the inverse of its opposite's value. For example:

criteria["Deployment", "Applicability"] <- 1/criteria["Applicability", "Deployment"]


The final result should look like this:

# Applicability Deployment Scalability Ease-of-Use TCO
#Applicability 1.0000000 5 3.0000000 1.0000000 3.0
#Deployment 0.2000000 1 0.3333333 0.2000000 0.2
#Scalability 0.3333333 3 1.0000000 0.3333333 3.0
#Ease-of-Use 1.0000000 5 3.0000000 1.0000000 5.0
#TCO 0.3333333 5 0.3333333 0.2000000 1.0


I'm pretty confident that this can be accomplished with nested for loops, but I can't quite grasp it and my time to work on this is running out.

Answer

I think this is what you're looking for:

l <- lower.tri(criteria)
criteria[l] <- 1/t(criteria)[l]

print(criteria)
##               Applicability Deployment Scalability Ease-of-Use TCO
## Applicability     1.0000000          5   3.0000000   1.0000000 3.0
## Deployment        0.2000000          1   0.3333333   0.2000000 0.2
## Scalability       0.3333333          3   1.0000000   0.3333333 3.0
## Ease-of-Use       1.0000000          5   3.0000000   1.0000000 5.0
## TCO               0.3333333          5   0.3333333   0.2000000 1.0

FYI, this won't work:

criteria[lower.tri(criteria)] <- 1/criteria[upper.tri(criteria)]

You want to columns of lower triangle to be filled with inverse of rows of upper triangle. However, R reads matrix columnwise so criteria[upper.tri(criteria)] will return the following instead:

 [1] 5.0000000 3.0000000 0.3333333 1.0000000 0.2000000 0.3333333 3.0000000 0.2000000 3.0000000 5.0000000

This can be solved by taking the transpose and getting the lower triangle, which is equivalent to taking the rows of upper triangle.