So I am doing this practice problem that wants me to write a function that create a vector with an existing vector.
The example is if vector a<-c(4, 0, 1, -2, 3)
, the output vector should be 4 5 -1 2 1
.
The rule is: if the existing vector a
has length = n
, the new vectorb
should be set as length = n
, and each element is calculated in such way that b[i] = a[i-1] a[i] a[i 1]
. If any of these a[i-1] a[i] a[i 1]
element does not exist, it should set to be equal to 0
.
z<-c(4, 0, 1, -2, 3)
solution <- function(n, a) {
b<-c()
for(i in 1:n){
if(is.na(a[i-1])){
a[i-1]<-0
}else if(is.na(a[i])){
a[i]<-0
}else if(is.na(a[i 1])){
a[i 1]<-0
}
b[i]<-a[i-1] a[i] a[i 1]
}
return(b)
}
solution(5,z)
With this I got this: Error in if (is.na(a[i - 1])) { : argument is of length zero
Then I change it to
z<-c(4, 0, 1, -2, 3)
solution <- function(n, a) {
b<-c()
for(i in 1:n){
if(is.null(a[i-1])){
a[i-1]<-0
}else if(is.na(a[i])){
a[i]<-0
}else if(is.na(a[i 1])){
a[i 1]<-0
}
b[i]<-a[i-1] a[i] a[i 1]
}
return(b)
}
solution(5,z)
But this time I got:
> solution(5,z)
[1] NA 5 -1 2 1
>
The first element of the new vector becomes NA
.
What do I get wrong?
BTW I also tried to set n = 12
> solution(12,z)
[1] NA 5 -1 2 1 3 0 0 0 0 0 0
>
The result is okay after passing 5.
CodePudding user response:
This applies along a vector that is used to index a
sapply(seq(length(a)), function(i) sum(a[(i-1):(i 1)], na.rm=TRUE))
If you want that as a function
solution <- function(x) {
sapply(seq(length(x)), function(i) sum(x[(i-1):(i 1)],
na.rm=TRUE))
}
CodePudding user response:
Because you are working with an exercise, I will try to explain each step and arrive at a simple solution (that might not necessarily be the best). We see that everything is working except for the first element, so we know that the issue is at i = 1
. What we can do is go through the loop for this value of i
and see what happens.
i = 1
is.null(a[i - 1])
#> FALSE
Even though we think that this should be true (as there is no element before i = 1
), the condition is still coming out to be FALSE
. We can see what's happening directly:
a[i - 1]
#> numeric(0)
numeric(0)
means an empty numeric vector (which is different from NULL
). This happens when you subset a vector by index 0. Note that you can identify numeric(0)
(and any empty vector) by checking whether its length is equal to 0:
length(a[i - 1]) == 0
#> TRUE
However, a different problem happens. Even after the condition is found to be TRUE
, notice that setting the 0th element of a vector to a value doesn't do anything:
x = c(1, 2, 3)
x[0] = 10
x
#> [1] 1 2 3
And unfortunately there is another problem! You used if
and else if
statements, so whenever one of these branches is reached, the rest will not be executed. This means that if there is an issue with more than just one of a[i - 1]
, a[i]
, and a[i 1]
, only the first issue found will be fixed.
I want to steer us in a different direction that will resolve all of these problems. First we need to address the issue with the start and ending of the vector. This can be done by just appending 0 to the beginning and end of the vector:
a = c(0, a, 0)
Next, we need to to set any NA
within a
to be 0:
a[is.na(a)] = 0
Now we are ready to solve the problem using a loop as you did, but this time it's very simple:
for (i in 1:n) {
b[i] = a[i - 1] a[i] a[i 1]
}
The last thing - instead of having n
be a parameter of the function, we can do better by just using length(a)
. But the best is to simply use seq_along(a)
, as I will show below. Putting it all together:
solution = function(a) {
a = c(0, a, 0)
a[is.na(a)] = 0
b = c()
for (i in seq_along(a)) {
b[i] = a[i - 1] a[i] a[i 1]
}
b
}
As you continue learning R you will see that there are many nice ways to solve the problem, such as the solution by @rg255.