2
\$\begingroup\$

I would like to understand how to get rid of all the loops I am using in this code.

I have a dataframe of 20 rows. I am simply counting the number of occurrences that the variables rs and occ appear together.

Here is the data:

 d = structure(list(householdid.x = c("101366", "101366", "102481", 
                             "102481", "103755", "103755", "103788", "103788", "103799", "103799", 
                             "10422", "10422", "10429", "10429", "10433", "10433", "10499", 
                             "10499", "105280", "105280"), nhouse = c(4L, 4L, 4L, 4L, 4L, 
                                                                      4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L), 
           idno = c(1013661, 1013662, 1024812, 1024811, 1037552, 1037551, 
                    1037881, 1037882, 1037991, 1037992, 104222, 104221, 104291, 
                    104292, 104331, 104332, 104992, 104991, 1052802, 1052801), 
           rs = c(0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 
                  0, 0, 1), occ = c(0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 
                                    0, 0, 0, 0, 0, 1, 0)), class = "data.frame", .Names = c("householdid.x", 
                                                                                            "nhouse", "idno", "rs", "occ"), row.names = c(NA, -20L))

Here are my loops - slow and cumbersome:

countcouples = list()
countcouples$both <- 0
countcouples$unique <- 0
countcouples$zero <- 0

for(i in 1:nrow(d)){
  if(d$rs[i] == 1 & d$occ[i] == 1)
  {countcouples$both <- countcouples$both+1}

  if((d$rs[i] != 1 & d$occ[i] == 1) | (d$rs[i] == 1 & d$occ[i] != 1))
  {countcouples$unique <- countcouples$unique+1}

  if(d$rs[i] == 0 & d$occ[i] == 0)
  {countcouples$zero <- countcouples$zero+1}

}

Here are the results:

  both unique zero
    2      4   14

Any advice?

\$\endgroup\$
  • 1
    \$\begingroup\$ Could you please update your title to reflect the purpose of the code instead. \$\endgroup\$ – ferada Jun 26 '15 at 11:15
  • \$\begingroup\$ @ferada, sure - do you have a suggestion ? \$\endgroup\$ – giacomo Jun 26 '15 at 11:17
1
\$\begingroup\$

Sure, use sum and compare all the lists directly:

countcouples = list(
    both = sum(d$rs == 1 & d$occ == 1),
    unique = sum((d$rs != 1 & d$occ == 1) | (d$rs == 1 & d$occ != 1)),
    zero = sum(d$rs == 0 & d$occ == 0)
)

d$rs == 1 gives you a boolean vector, sum treats TRUE as 1, so the combination allows for much more succinct code. It could also be further optimised by reusing the individual results.

I also took the liberty to create the list directly instead of assigning results one by one.

If you only have 0/1 as values, consider using d$rs & d$occ and so on directly as well.

\$\endgroup\$
  • \$\begingroup\$ Thank you, very interesting the sum solution, I did not think about it. \$\endgroup\$ – giacomo Jun 26 '15 at 11:26
3
\$\begingroup\$
sum(d$rs & d$occ)
#[1] 2

The goal is "...simply counting the number of occurrences that the variables rs and occ appear together." If you feel that your code is long and cumbersome, the vectorization process of R is uniquely capable of speeding up the task without for loops and nested conditionals.

As mentioned in the previous answer, R coerces certain classes onto vectors when specific functions are called on them. The two columns in question are both a series of 1's and 0's. They are numeric values. I would like them to be thought of as TRUE's and FALSE's to evaluate my logical condition. Thanks to the brevity of R code I do not have to literally ask R to do that. As soon as I use the & operator, R will immediately treat all 0's as FALSE and other numbers as TRUE logical values.

Finally, sum does a similar action but in the opposite direction. It coerces (forces the class) to numeric in order to calculate the sum. It treats TRUE as 1 and 0 as FALSE. In the case of this code, Many complicated relationships are being handled by the R computing engine and, thankfully, not by us.

If you would like the other two values for unique and zero use:

sum(xor(d$rs, d$occ))
[1] 4
sum(!d$rs & !d$occ)
[1] 14

The function xor tests what is called the "exclusive or". So the code read aloud would be "How many pairs have a one in rs or occ, but not in both." The operator ! negates what comes after it. That code would read, "How many pairs have a value that is not 1 in rs and not 1 in occ."

\$\endgroup\$
  • \$\begingroup\$ You're welcome. I'm happy to help. \$\endgroup\$ – Pierre Lafortune Jun 27 '15 at 18:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.