What's wrong with the logic of this function in R?

Viewed 74

I'm tyring to build a function in R which calculates the percentage change between rows based on any arbitrary index, this is, between any given row and the preceding one or any given row and n preceding ones.

perc_change <- function(x,n) {
    y <- c()
    z <- c()
        for (i in 1:length(x)) {
        z[i] <- (x[i]/(x[i-n])-1)*100
    }
    y <- c(rep(NA,n),z[(n+1):length(z)])
    y
}

When n is one the function works properly:

    x <- c(2,3.5,4,6)
    perc_change(x,1)

[1]       NA 75.00000 14.28571 50.00000

But when I change to 2 or other n, I receive this error:

Error in z[i] <- (x[i]/(x[i - n]) - 1) * 100 : 
  replacement has length zero

I just can't find why and where the logic of my function is wrong so I appreciate any comment or suggestion.

2 Answers

In the loop, when n is greater than 1, the i starting at 1 can result in negative or zero index (i.e. when n =2, 1 - 2). To avoid, that an if/else condition can be added

perc_change <- function(x,n) {
      y <- c()
      z <- c()
          for (i in 1:length(x)) {
          if(i > n) {
          z[i] <- (x[i]/(x[i-n])-1)*100
         } else z[i] <- NA
     }
      y <- c(rep(NA,n),z[(n+1):length(z)])
      y
  }


perc_change(x,1)
#[1]       NA 75.00000 14.28571 50.00000
perc_change(x, 2)
#[1]        NA        NA 100.00000  71.42857
perc_change(x, 3)
#[1]  NA  NA  NA 200

The following function lags the input vector and then computes the percent change with vectorized operations, no need for for loops. The lag function is a copy&paste of the last code lines of dplyr::lag.

perc_change <- function(x, n = 1) {
  lag <- function(x, n = 1){
    if(n == 0)
      return(x)
    xlen <- length(x)
    n <- pmin(n, xlen)
    out <- c(rep(NA, n), x[seq_len(xlen - n)])
    attributes(out) <- attributes(x)
    out
  }
  y <- lag(x, n)
  (x/y - 1)*100
}

x <- c(2, 3.5, 4, 6)
perc_change(x,1)
#[1]       NA 75.00000 14.28571 50.00000
perc_change(x, 2)
#[1]        NA        NA 100.00000  71.42857
Related