user1482923
user1482923

Reputation: 211

Apply function to each cell of matrix in R

I'm trying to perform a function to each cell of a data table in R, creating a second one based on the result of this loop.. For example, imagine I have Matrix A

    Ad1    Ad2    Ad3    Ad4
    AA      6       0     10
    AB      7      10     12
    AC      0       0     15

and I'm trying to create Matrix B

    Ad1    Ad2    Ad3    Ad4
    AA      1       0      1
    AB      1       0      1
    AC      0       0      1

in a way that each cell assumes the value 1 if that cell has a value > 0 AND the sum of the column minus that cell is also greater than 0.

For instance, AA~Ad2 is 6 and the sum of the column is 7 (6 + 7 + 0 - 6); then AA~Ad2 in matrix B assumes value 1.

Is there a way to perform this without performing a loop? I've managed to do this with a loop but it is taking too long:

A = read.table(text="Ad1    Ad2    Ad3    Ad4
AA     6      0     10
AA     7     10     12
AA     0     0     15", header=TRUE)

B = read.table(text="Ad1    Ad2    Ad3    Ad4
AA     0      0     0
AA     0     0     0
AA     0     0     0", header=TRUE)

for (i in 1:nrow(B)) {
    for (j in 2:ncol(B)) {
        if ((sum(A[,j], na.rm = T) - ifelse(is.na(A[i,j]), 0, A[i,j]))> 0 &
        ifelse(is.na(A[i,j]), 0, A[i,j]) > 0 ) 
        {B[i,j] <- 1}
    }
}

Upvotes: 5

Views: 2663

Answers (4)

989
989

Reputation: 12937

You could also do:

m <- as.matrix(A[,-1])
colsm <- matrix(colSums(m), ncol = ncol(m), nrow = nrow(m), byrow = T)
(colsm-m)>0 & m>0)*1

#    Ad2 Ad3 Ad4
#[1,]   1   0   1
#[2,]   1   0   1
#[3,]   0   0   1

Upvotes: 0

Keith Hughitt
Keith Hughitt

Reputation: 4960

Here is a solution using two logical expressions evaluated on the original matrix:

(A > 0 & (colSums(A) - A > 0)) * 1.0

The left side of the & checks that the values are greater than zero, while the right-side checks the requirements relating to the column sum.

By themselves, each of these produces a logical matrix of the same dimension as A. The & then allows you to combine logical matrices to generate a new one where the cells are TRUE only if the cells are TRUE in both of the input matrices.

Finally, the * 1.0 casts the logical matrix to numeric.

Upvotes: 1

lmo
lmo

Reputation: 38500

Here is an alternative base R method in 2 lines. The first pulls out the matrix which may reduce copying. The second line calculates the result, first checking that values are greater than 0, then checking that the total colSum is greater than each element. This part is accomplished in part through rep with the each argument.

# extract matrix from data.frame
myMat <- as.matrix(A[-1])
# calculate result and store in data.frame
A[-1] <- (myMat > 0) * ((rep(colSums(myMat), each=nrow(myMat))- myMat) > 0)
A
  Ad1 Ad2 Ad3 Ad4
1  AA   1   0   1
2  AA   1   0   1
3  AA   0   0   1

Upvotes: 1

akrun
akrun

Reputation: 887148

We can do this without a loop by creating two logical matrices -1) check whether the numeric column values are greater than 0 (A[-1] > 0), 2) check whether the difference of the column sums with the column values are also greater than 0. If both of them are TRUE (& condition), convert the logical matrix to binary (+) and assign it to the subset of the dataset (A[-1])

A[-1] <-  +(colSums(A[-1])[col(A[-1])]-A[-1]>0 & A[-1] > 0)
A
#  Ad1 Ad2 Ad3 Ad4
#1  AA   1   0   1
#2  AB   1   1   1
#3  AC   0   0   1

Upvotes: 7

Related Questions