Tell me more ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I'm trying to implement a function that given a data frame returns the same data frame with four columns added. These new four columns are: for each row, I get the maximun element and its index and put them as two new columns. I do the same with the second maximum element. Don't care if they are repeated. This is my code for this task:

add_2max <- function(x)
{
  max1 = max(x, na.rm=TRUE)
  indmax1 = which.max(x)
  y=x[-c(indmax1)]
  max2 = max(y, na.rm=TRUE)
  indmax2 = which(x==max2)
  indmax2 = ifelse(max1==max2, indmax2[2], indmax2[1])
  x=c(x, max1, max2, indmax1, indmax2)
  return (x)
}


add_2max_df <- function(DF)
{
  NewDF=t(apply(DF, 1, add_2max))
  return(NewDF)
} 

I'm sure this code can be improved! What do you recommend in order to do that? Is it fast enough?

Many thanks in advance Regards!

share|improve this question
ifelse is notoriously slow, but since its working on a single row it shouldn't be an issue. As far as you last question... is it fast enough for you? – Justin Apr 11 '12 at 15:41
Thanks for your advice @Dason – Nestorghh Apr 11 '12 at 15:42
That's the line I'd like to avoid @Justin, but it seems not being an issue due to the fact that it's working on a single row as you've said. Thank you! – Nestorghh Apr 11 '12 at 15:48
Just goes to show we all could use a modified which.max(x,nth_biggest) function. – Carl Witthoft Apr 11 '12 at 16:39

migrated from stackoverflow.com Apr 11 '12 at 15:57

2 Answers

up vote 4 down vote accepted

Here is a more compact version:

add_2maxBrian <- function(x) {
    r <- order(x, decreasing=TRUE)
    c(x, x[r[1:2]], r[1:2])
}

With some sample data (different than Tommy's because I want the chance of ties):

set.seed(123)
DF <- as.data.frame(matrix(sample(1:20, 10000, replace=TRUE), 2500, 4))

It's not as fast a Tommy's solution, but still faster than the original.

library("rbenchmark")

benchmark(a1 <- t(apply(DF, 1, add_2max)),
          a2 <- t(apply(DF, 1, add_2maxFaster)),
          a3 <- t(apply(DF, 1, add_2maxBrian)),
          order = "relative")
#                                   test replications elapsed relative user.self
#2 a2 <- t(apply(DF, 1, add_2maxFaster))          100   6.537 1.000000     6.441
#3  a3 <- t(apply(DF, 1, add_2maxBrian))          100   8.259 1.263424     8.073
#1       a1 <- t(apply(DF, 1, add_2max))          100  12.168 1.861404    12.038
#  sys.self user.child sys.child
#2    0.067          0         0
#3    0.082          0         0
#1    0.089          0         0
identical(a1, a2) #TRUE
identical(a1, a3) #TRUE
share|improve this answer
Great @Brian Diggs. Pretty compact and simple. I like a lot. – Nestorghh Apr 11 '12 at 17:34

Here's a faster way:

add_2maxFaster <- function(x)
{
  imax1 <- which.max(x)
  imax2 <- which.max(x[-imax1])
  if (imax2 >= imax1) imax2 <- imax2 + 1L
  c(x, x[imax1], x[imax2], imax1, imax2) 
}

set.seed(42)
m <- matrix(runif(1e6), 1e4)

# Compare speed:
system.time( a1<-apply(m, 1, add_2max) )        # 0.38 secs
system.time( a2<-apply(m, 1, add_2maxFaster) )  # 0.15 secs

# ...And compare results
all.equal(a1,a2) # TRUE
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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