49

I have an array:

a <- c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)

and would like to implement the following function:

w<-function(a){
  if (a>0){
    a/sum(a)
  }
  else 1
}

This function would like to check whether there is any value in a larger than 0 and if yes then divide each element by the sum of the total.

Otherwise it should just record 1.

I get the following warning message:

 Warning message:
 In if (a > 0) { :
 the condition has length > 1 and only the first element will be used

How can I correct the function?

zx8754
  • 52,746
  • 12
  • 114
  • 209
user1723765
  • 6,179
  • 18
  • 57
  • 85

7 Answers7

66

maybe you want ifelse:

a <- c(1,1,1,1,0,0,0,0,2,2)
ifelse(a>0,a/sum(a),1)

 [1] 0.125 0.125 0.125 0.125 1.000 1.000 1.000 1.000
 [9] 0.250 0.250
user1317221_G
  • 15,087
  • 3
  • 52
  • 78
  • 5
    Warning: ifelse only uses as many elements from your solution, as there are elements in the condition! It does NOT warn about this. For example `ifelse(TRUE, 1:5, 5:1)` returns `1` as answer and not `1:5`. ifelse(c(T, T), 1:5, 5:1)` returns `c(1, 2)`, etc. Use with caution. – Willem Dec 05 '18 at 14:09
56

if statement is not vectorized. For vectorized if statements you should use ifelse. In your case it is sufficient to write

w <- function(a){
if (any(a>0)){
  a/sum(a)
}
  else 1
}

or a short vectorised version

ifelse(a > 0, a/sum(a), 1)

It depends on which do you want to use, because first function gives output vector of length 1 (in else part) and ifelse gives output vector of length equal to length of a.

jem77bfp
  • 1,270
  • 11
  • 13
27

Just adding a point to the whole discussion as to why this warning comes up (It wasn't clear to me before). The reason one gets this is as mentioned before is because 'a' in this case is a vector and the inequality 'a>0' produces another vector of TRUE and FALSE (where 'a' is >0 or not).

If you would like to instead test if any value of 'a>0', you can use functions - 'any' or 'all'

Best

Atesh
  • 441
  • 4
  • 4
20

Here's an easy way without ifelse:

(a/sum(a))^(a>0)

An example:

a <- c(0, 1, 0, 0, 1, 1, 0, 1)

(a/sum(a))^(a>0)

[1] 1.00 0.25 1.00 1.00 0.25 0.25 1.00 0.25
Sven Hohenstein
  • 80,497
  • 17
  • 145
  • 168
  • 6
    This beats `ifelse` by about a factor of 7 (on a 100000 element array). – Matthew Lundberg Jan 05 '13 at 14:24
  • 1
    Could you recommend a succinct explanation/documentation of this `^` operator (e.g., what it's called and how it works). Preferably something more straightforward than the [Arithmetic Operators](https://stat.ethz.ch/R-manual/R-devel/library/base/html/Arithmetic.html) documentation... – theforestecologist Nov 13 '17 at 02:35
  • 5
    @theforestecologist The operator `^` is used for exponentiation. The command `x ^ y` means: `x` raised to the power of `y`. The code `2 ^ 3` will compute `2 * 2 * 2`. – Sven Hohenstein Nov 13 '17 at 20:19
3

The way I cam across this question was when I tried doing something similar where I was defining a function and it was being called with the array like others pointed out

You could do something like this however for this scenarios its less elegant compared to Sven's method.

sapply(a, function(x) afunc(x))

afunc<-function(a){
  if (a>0){
    a/sum(a)
  }
  else 1
}
rlhull6
  • 113
  • 1
  • 5
3

Use lapply function after creating your function normally.

lapply(x="your input", fun="insert your function name")

lapply gives a list so use unlist function to take them out of the function

unlist(lapply(a,w))
camille
  • 16,432
  • 18
  • 38
  • 60
1

I would say the most efficient way is the answer by user1317221_G. However, if you want to go back to the basics, then looping over the length of your vector (since the if function doesnt work over the length of the vector) using the for function would be useful.

w <- c() ##creates empty vector named 'w'
for(i in 1:length(a)){
  if (a[i]>0){
    w[i] <- a[i]/sum(a)
  }
  else 
    w[i] <- 1
}