I am trying to understand a function in R. could you please declare some part of it for me:
the function is:
subsignals <- lapply(c(peakind$freqindex, midindex+1), function(x){
upperind <- x
fsub <- f
notnullind <- ((fsub$freqindex >= lowerind
& fsub$freqindex < upperind)
|
(fsub$freqindex > (lindex - upperind + 2)
& fsub$freqindex <= (lindex - lowerind + 2)))
fsub[!notnullind,"coef"] <- 0
lowerind <<- upperind
Re(fft(fsub$coef, inverse=TRUE)/length(fsub$coef))
})
Could some one explain me:
1-What could be the content of notnullind and generally, what does this part of code do:
notnullind <- ((fsub$freqindex >= lowerind
& fsub$freqindex < upperind)
|
(fsub$freqindex > (lindex - upperind + 2)
& fsub$freqindex <= (lindex - lowerind + 2)))
2-What does fsub[!notnullind,"coef"] <- 0 mean?
3-What does<<- in lowerind <<- upperind mean?
further information:
peakind looks like this:
coef freqindex
9 2.714391+3.327237i 9
17 1.273340+4.023808i 17
25 -0.445424+5.674848i 25
33 -1.378107+3.182281i 33
41 -2.798383+2.340895i 41
49 -4.479888+1.095193i 49
and fsub :
coef freqindex
1 19.2352397+0.0000000i 1
2 -0.4799684+0.1651822i 2
3 1.5235726+0.0790459i 3
4 -0.1165587+0.1217513i 4
5 2.2376900+1.6763410i 5
6 1.1256711+0.4624819i 6
.....
102 -0.1165587-0.1217513i 102
103 1.5235726-0.0790459i 103
104 -0.4799684-0.1651822i 104
It seems that the code is iterating through fsub in chunks defined by the difference between adjacent entries in peakind. Presumably peakind contains interesting points in fsub. You can see this because most of the fsub comparison are between x (which comes from peakind), and lowerind, which is set to be the prior loops x/upperind value.
notnullind will be a logical vector (TRUE, FALSE) that is TRUE for the rows in fsub that are between this iterations peakind$freqindex and the prior ones as well as something else based on lindex that I can't tell you b/c that variable is undefined in your code.
That line is setting all the values in fsub$coef that don't meet the condition described above to zero
lowerind<<-upperind is a global assignment outside of the function being run through lapply. This allows the function run by lapply to keep track of the last upperind from a previous call to that same function in lapply loop. The assignment must be global as otherwise the value would be lost after every iteration in lapply.
Basically, the function is doing an fft for the data between in fsub between adjacent pairs of index values defined in peakind.
Note that 3. suggests your function isn't structured in the best possible manner. You should generally avoid global assignments unless you really can't. In this case, I would loop through the rows of cbind(head(peakind$freqindex, -1L), tail(peakind$freqindex, -1L)) which contains the range of indices you care about for each iteration.
Related
I am a new R user and have very limited programming experience, hence my question and poorly written code.
I was assigned a problem where I had to use a while loop to generate the numbers of the Fibonacci sequence that are less than 4,000,000 (the Fibonacci sequence is characterized by the fact that every number after the first two is the sum of the two preceding ones).
Next, I had to compute the sum of the even numbers in the sequence that was generated.
I was successful with my response, however, I don't think the code is written very well. What could I have done better?
> x <- 0
> y <- 1
> z <- 0
if (x == 0 & y == 1) {
cat(x)
cat(" ")
cat(y)
cat(" ")
while (x < 4000000 & y < 4000000) {
x <- x + y
cat(x)
cat(" ")
if (x %% 2 == 0) {
z <- x + z
}
y <- x + y
cat(y)
cat(" ")
if (y %% 2 == 0) {
z <- y + z
}
}
}
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040 1346269 2178309 3524578 5702887 9227465
cat(z)
4613732
First of all, cat comes with a sep argument. You can do cat(x, y, sep = " ") rather than using 3 lines for that.
Secondly, when you call while (x < 4000000 & y < 4000000) note that y will always be greater than x because it is the sum of the last x and y ... so it should suffice to check for y < 4000000 here.
For the while loop, you could also use a counter - might be more intuitive. Indexing in R isn't that fast though
fib <- c(0, 1)
i <- 2
while (fib[i] < 4000000) {
fib <- c(fib, fib[i-1] + fib[i])
i <- i + 1
}
sum(fib[fib %% 2 == 0])
If you don't necessarily need the while, you could also approach it via recursion
fib <- function(x, y) {
s <- x + y
c(s, if (s < 4000000) fib(y, s))
}
f <- fib(0, 1)
sum(f[f %% 2 == 0])
First, there's no need o explicitly print everything out.
Second, it's more idiomatic in R to make a vector of the Fibonacci numbers and then sum. If you don't know an explicit closed form for the Fibonacci numbers, or if you've been told not to use this, then use a loop to create the list of Fibonacci numbers.
So to construct the list of Fibonacci numbers (two at a time) you can do
x <- 0
y <- 1
fib <- c()
while (x < 4000000 & y < 4000000){
x <- x + y
y <- x + y
fib = c(fib, x, y)
}
This will give you a vector of Fibonacci numbers, containing all those less than 4000000 and a few more (the last element is 9227465).
Then run
sum(fib[fib %% 2 == 0 & fib < 4000000])
to get the result. This returns 4613732, like your code does. The subsetting operator [], when you put a logical condition inside it, will output just those numbers which satisfy the logical condition -- in this case, that they're even and less than 4000000.
I am using the closed form of the fibonacci sequence as found here
fib = function(n) round(((5 + sqrt(5)) / 10) * (( 1 + sqrt(5)) / 2) ** (1:n - 1))
numbers <- 2
while (max(fib(numbers)) < 4000000){ # try amount of numbers while the maximum of the sequence is less than 4000000
sequence <- fib(numbers) # here the sequence that satisfies the "4000000 condition will be saved"
numbers <- numbers + 1 # increase the amount of numbers
}
total_sum <- sum(sequence[sequence%%2==0]) # summing the even numbers
This is how I would do it. First, I defined a global variable i to include the first two elements of the Fibonacci series. Then at the end, I re-assigned the global variable to its initial value (i.e. 1). If I don't do that, then when I call the function fib(0,1) again, the output is incorrect as it calls the function with the last value of i. It's also important to do return() to ensure it doesn't return anything in the else clause. If you don't specify return(), the final output will be 1, instead of the Fibonacci series.
Please note the series only goes till the number 13 (z<14) obviously you can change that to whatever you want. May also be a good option to include this as the third argument of the function, something like fib(0,1,14). Try it out!
i <<- 1
fib <- function(x,y){
z <- x+y
if(z<14){
if (i==1){
i <<- i+1
c(x,y,z,fib(y,z))
}
else c(z, fib(y,z))
}
else {
i <<- 1
return()
}
}
a <- fib(0,1)
a
I'm trying to create a sequence that increases its step by one at each interval, but can't manage to do that.
I would like my result to look like this:
vec <- c(1,3,6,10,15,21,28,36,45,55)
So basically, starting from 1, each index is equal to the precedent plus a number that is increasing by 1 step by step, something like
[i]=[i-1]+seq(from=i, to=10, by=1)
but of course this is not the correct notation.
How would you set this up in order to get something like shown in vec? I imagine some loop is necessary in order to have an indices in a vector exponentially increasing.
This is called the Triangular Number Sequence and has the form:
Tn = 1 + 2 + 3 + 4 + 5 + ... + n
This can be created with the cumsum function in R:
cumsum(1:10)
# [1] 1 3 6 10 15 21 28 36 45 55
or something like this if you prefer recursion:
tri_num = function(n){
if(n <= 1){
return(1)
}else{
return(n+tri_num(n-1))
}
}
sapply(1:10, tri_num)
I have following vector of numbers in r
bay_no <- c(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20)
bay_cont <- c(45,25,25,0,19,61,2,134,5,27,0,54,102,97,5,6,65,47,85,0)
count <- 3
bay_to_serve <- sum(bay_cont)/count
In above bay_cont vector I want to find sum which will be close to bay_to_serve in above case bay_to_serve = 268
Now, from (45 till 2) sum is 177 and (45 till 134) sum is 311,so 311 is closest to 268 then it should return the index of i.e 8 from bay_no
We will get one vector from bay_no = 1-8
Again starting from bay_cont from 5 till the sum close to 268
Desired output is
bay_no 1-8,9-14 and then remaining bay_nos
How can we do it in r?
Dunno if there is a smart way to do but I'd think of nested loops.
Your inner loop may look like this (Please note that I have no access to R right now, so I can't test it.):
old_sum = bay_count[1]
for(i in 2:length(by_cont)) {
new_sum <- sum (bay_count[1:i])
if (abs(bay_to_serve - new_sum) < abs(bay_to_serve - old_sum)) {
output <- paste("bay_no", paste(1,i, sep="-"), sep=" ") break
}else{
old_sum <- new_sum
}
}
This way, whenever the sum of the first X entries is smaller than the previous sum, it will break the loop and create an output string. Just add another loop around the first loop and one or to more if statements to run from j:length(by_cont), whereby j is first set to 1 and will be set to i+1 within the inner loop.
You can try:
res <- NULL
i = 1
while(i < length(bay_cont)){
tmp <- which.min(abs(cumsum(bay_cont[i:length(bay_cont)]) - bay_to_serve))
res <- append(res,tmp)
i = tmp + i
}
cumsum(res)
[1] 8 14 19
If you want to break ties specifically you can use rank together with which.min like follows:
which.min(rank(abs(cumsum(bay_cont[i:length(bay_cont)]) - bay_to_serve), ties.method = "last"))
Then I would create a matrix instead of pasting it together:
cbind(c(1, cumsum(res)[-length(cumsum(res))]+1), cumsum(res))
[,1] [,2]
[1,] 1 8
[2,] 9 14
[3,] 15 19
Of course you can paste it together as well:
apply(cbind(c(1, cumsum(res)[-length(cumsum(res))]+1), cumsum(res)), 1, paste, collapse="-")
[1] "1-8" "9-14" "15-19"
My solution uses a dirty for loop but yields the required indizes...
Hope that fits to you?
bay_no <- c(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20)
bay_cont <- c(45,25,25,0,19,61,2,134,5,27,0,54,102,97,5,6,65,47,85,0)
count <- 3
bay_to_serve <- sum(bay_cont)/count
temp_sum=0
for (i in 1:(length(bay_cont)-1)) {
temp_sum=temp_sum+bay_cont[i]
if ( abs(bay_to_serve-temp_sum)<abs(bay_to_serve-(temp_sum +bay_cont[i+1]))) {
print(i)
temp_sum=0
}
}
I probably misunderstand the question, but it seems more easy to do this:
bay_no[ which.min(abs(cumsum(bay_cont) - bay_to_serve)) ]
To start at 5, omit elements 1:4 and add 4 to the which.min index
bay_no[ which.min(abs(cumsum(bay_cont[-(1:4)]) - bay_to_serve))+4 ]
I have a vector 'participant' in R.
> participant
[1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
I am using a function 'modify' to change the contents of this vector.
modify <- function(x){
for (i in participant){
if (x[i] > 12)
(x[i]=x[i]-12)
print (x[i])
}}
When I run the function as modify(participant), it runs OK, but the elements of the vector participant remain unchanged.
Any suggestion, for where am I going wrong ?
Don't use a loop.
participant <- participant - (participant > 12) * 12
If you insist on using your function, loop over the indices, let you function return the modified vector and assign it:
modify <- function(x){
for (i in seq_along(participant)){
if (x[i] > 12) x[i]=x[i]-12
}
return(x)
}
participant <- modify(participant)
Of course, the loop is more difficult to write and also much slower.
Your problem is the function return. Use this solution, so the function returns the modified vector x:
modify <- function(x){
for (i in participant){
if (x[i] > 12)
(x[i] = x[i] - 12)
print (x[i])}
return(x)
}
participant <- modify(participant)
Another solution is the ifelse function:
participant <- ifelse(participant > 12, participant - 12, participant)
I want to do a conditional cumsum. I originally thought I could use the Reduce function but I wasnt able to. To explain clearly :
a <- rep(1,5)
b <- rnorm(n=5,mean=0.5)
c <- rep(2,5)
d <- rep(0.5,5)
Reduce( '+', a, init=200 , accumulate=TRUE)
Results in
[1] 200 201 202 203 204 205
This is just like a simple cumsum. But what I actually want is a conditional cumsum:
The recursion is defined as :
x0 = 200
index = 0
function (x) {
index = index + 1
if( x is between 200 and 202 ) return x + a[index]
else if(x is between 202 and 204) return x + b[index]
else if(x is between 204 and 206) return x + c[index]
else return x + d[index]
}
The expected result could be something like this (of course it will never be the same because of the randomness.
[1] 200 201 202.3 203.8 204.1 205
For those who are interested, the answer can be found here :
Convert simple recursive relation to functional program
I can't seem to find a way to mark this question as closed since the moderators keep on deleting whatever I add without suggesting a proper way to close.
I don't think you need Reduce here. Here an example that explain a use case of Reduce:
Reduce(paste,list(1:3,letters[1:3],LETTERS[1:3]))
[1] "1 a A" "2 b B" "3 c C"
I think what you try to do xan be done using ifelse, it is vectorized. Here for example a conditional cumultive sum of a and b starting from init value.
a <- rep(1,5)
b <- rep(0.01,5)
init <- 200
x <- seq_along(a)
cumsum(c(init,ifelse( x %% 2 ,a[x], b[x])))
[1] 200.00 201.00 201.01 202.01 202.02 203.02
Of course if you have multiple condition:
Use multiple condition: x %% 2 & x^2 < 5
Use nested ifelse : ifelse( x %% 2 ,ifelse(x^2 <2, a[x], b[x]),1)