Reputation: 211
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
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
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
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
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