Counting leading zeros in a vector - r

Let's say we have a few vectors containing some numeric data.
For example:
vect1<- c(0, 0, 0, 60, 40, 80, 0)
vect2<- c(100, 0, 0, 80, 40)
vect3<- c(0,0,1,2,3)
I would like to count the number of "leading zeros" in this vector... so In this case I would like to have a value of 3 returned for vect1, a value of 0 returned for vect2, and a value of 2 returned for vect3. How would I go about implementing this in R?
Thanks in advance!

You can use a simple for loop with a counter here.
counter <- 0
for (i in vect){
if (i == 0){
counter<-counter+1
} else {
break()
}
print(counter)
You could easily wrap this in a function for reuse and just replace the print with return.

There are several ways to do this. One option is to get the cumulative sum where the vector is not 0, then, convert it to a logical and get the sum
f1 <- function(x) sum(cumsum(x != 0) == 0)
-testing
> f1(vect1)
[1] 3
> f1(vect2)
[1] 0
> f1(vect3)
[1] 2
Or may also use rle
f2 <- function(x) with(rle(x == 0), lengths[1][values[1]][1])

Related

Addition involving numeric(0) values

Suppose x is a real number, or a vector. i is valued-False. Then x[i] will return numeric(0). I would like to treat this as a real number 0, or integer 0, which are both fit for addition.
numeric(0) added to any real number will return numeric(0), whereas I wish to obtain the real number being added as the result. What can I do to convert the numeric (0) value? Thanks in advance!
It is only when we do the +, it is a problem. This can be avoided if we use sum
sum(numeric(0), 5)
#[1] 5
sum(numeric(0), 5, 10)
#[1] 15
Or if we need to use +, an easy option is to concatenate with 0, select the first element. If the element is numeric(0), that gets replaced by 0, for other cases, the first element remain intact
c(numeric(0), 0)[1]
#[1] 0
Using a small example
lst1 <- list(1, 3, numeric(0), 4, numeric(0))
out <- 0
for(i in seq_along(lst1)) {
out <- out + c(lst1[[i]], 0)[1]
}
out
#[1] 8
You can use max/min with 0 to get 0 as output when input is numeric(0).
x <- 1:10
max(x[FALSE], 0)
#[1] 0
min(x[FALSE], 0)
#[1] 0

Cut elements from the beginning and end of an R vector

For time series analysis I handle data that often contains leading and trailing zero elements. In this example, there are 3 zeros at the beginning an 2 at the end. I want to get rid of these elements, and filter for the contents in the middle (that also may contain zeros)
vec <- c(0, 0, 0, 1, 2, 0, 3, 4, 0, 0)
I did this by looping from the beginning and end, and masking out the unwanted elements.
mask <- rep(TRUE, length(vec))
# from begin
i <- 1
while(vec[i] == 0 && i <= length(vec)) {
mask[i] <- FALSE
i <- i+1
}
# from end
i <- length(vec)
while(i >= 1 && vec[i] == 0) {
mask[i] <- FALSE
i <- i-1
}
cleanvec <- vec[mask]
cleanvec
[1] 1 2 0 3 4
This works, but I wonder if there is a more efficient way to do this, avoiding the loops.
vec[ min(which(vec != 0)) : max(which(vec != 0)) ]
Basically the which(vec != 0) part gives the positions of the numbers that are different from 0, and then you take the min and max of them.
We could use the range and Reduce to get the sequence
vec[Reduce(`:`, range(which(vec != 0)))]
#[1] 1 2 0 3 4
Take the cumsum forward and backward of abs(vec) and keep only elements > 0. if it were known that all elements of vec were non-negative, as in the question, then we could optionally omit abs.
vec[cumsum(abs(vec)) > 0 & rev(cumsum(rev(abs(vec)))) > 0]
## [1] 1 2 0 3 4

how to use function & ifelse() to sum up the total of negative values?

I wrote a simple function to hopefully calculate the total of negative values but failed. Ideally, when I passed a vector into the function, it should give the total count of negative values. Can anyone help, please?
My code:
arg <- c(rnorm(50, 0))
neg <- 0
count.negative.fun <- function(x) {
ifelse(x <= 0, neg = neg +1,)
return(neg)
}
When I called:
count.negative.fun(arg)
It gives me this error message: "Error in ifelse(x <= 0, neg = neg + 1, ) :
unused argument (neg = neg + 1)"
When using ifelse and defining a function, one might do
count.negative.fun <- function(x) sum(ifelse(x <= 0, 1, 0))
count.negative.fun(arg)
# [1] 26
See ?ifelse. It returns 1 for those cases when an element of x is nonpositive and 0 otherwise. Then we may sum the result.
However, you may also simply write
sum(arg < 0)
# [1] 26
Another possible way is by using length function itself. As:
length(arg[arg<0])
#[1] 26

Count negatives if at least six consecutive negative values in a column

I have a data.frame column with hourly values and want to count all negative values, if they are in a sequence of at least six consecutive negatives.
Example:
df <- data.frame(Values=c(-2, 2, 3, 4,-1,-1,-2,-3,
-1,-1,-1, 5, 4, 2,-4,-2,
-3,-4,-1, 3, 4, 4,-3,-1,
-2,-2,-3,-4))
The expected result would be 13, since the middle sequence of consecutive negatives contains only five negatives and is thus not counted.
You could use the base function rle() along with sign(). The sign() function converts negatives and positives to -1 and 1, respectively. This makes a nice vector to pass to rle() to get the run-lengths. Then we can subset the run-lengths with the desired condition and take the sum.
with(rle(sign(df$Values)), sum(lengths[lengths >= 6 & values < 0]))
# [1] 13
Try:
library(cgwtools)
res <- seqle(which(df<0))
sum(res$lengths[res$lengths>=6])
[1] 13
you can always define your own function and call it.
NegativeValues <- function(x) {
count <- 0
innercount <- 0
for (i in c(x, 0)) {
if (i < 0) {
innercount <- innercount + 1
}
else {
if (innercount >= 6)
count <- count + innercount
innercount <- 0
}
}
return(count)
}
NegativeValues(df$Values)

swap n percent of ones to zeros in R

I have a vector A which contains zeros and ones. I would like to randomly change n percent of the ones to zero. Is this the best way to do it in R (10% change):
for (i in 1:length(A))
{
if(A[i] > 0)
{
if(runif(1) <= 0.1)
{
A[i] = 0
}
}
}
Thanks.
You can do this without using the for loops and if statements:
##Generate some data
R> A = sample(0:1, 100, replace=TRUE)
##Generate n U(0,1) random numbers
##If any of the U's are less then 0.1
##Set the corresponding value in A to 0
R> A[runif(length(A)) < 0.1] = 0
The other point to note, is that you don't have to do anything special for values of A that actually equal 0, as the probability of change a 1 to a 0 is still 0.1.
As Hadley points out, your code doesn't randomly change 10% of 1's to 0. If that is really your intention, then:
##Select the rows in A equal to 1
R> rows_with_1 = (1:length(A))[A==1]
##Randomly select a % of these rows and set equal to zero
##Warning: there will likely be some rounding here
R> A[sample(rows_with_1, length(rows_with_1)*0.1)] = 0
If this is your A:
A <- round(rnorm(100, 0.5, 0.1))
This should do it:
n <- 10
A[sample(A[A==1], length(A[A==1])*n/100)] <- 0
where n is the percentage of your 1s that you want to change to 0s.
You can vectorize that:
A <- round(runif(20), 0)
A[sample(which(A == 1), 0.1 * length(A == 1))] <- 0
HTH

Resources