Julia - equivalent of recursive sapply function in R - r

I had a function in R (onestep below) which took as an argument a vector v and returned a new vector v as output which was a function of the input vector. I then iterated this function niter times and kept the output vectors of each iteration (which are not all the same length and can occasionally also end up having length 0) in another function iterate as follows (minimal example) :
onestep = function (v) c(v,2*v)
iterate = function (v, niter) sapply(1:niter, function (iter) {v <<- onestep(v)
return(v) } )
Example :
v=c(1,2,3)
iterate(v,3)
[[1]]
[1] 1 2 3 2 4 6
[[2]]
[1] 1 2 3 2 4 6 2 4 6 4 8 12
[[3]]
[1] 1 2 3 2 4 6 2 4 6 4 8 12 2 4 6 4 8 12 4 8 12 8 16 24
I was wondering what would be a compact and idiomatic way to do such a recursive function that returns all the intermediate results in Julia? Any thoughts? (Apologies if this is trivial but I am new to Julia)

Not sure on the compact and idiomatic front, but this is how I'd do it
onestep(v) = [v 2*v]
function iterate(v, niter)
Results = Array(Array, niter)
Results[1] = onestep(v)
for idx = 2:niter
Results[idx] = onestep(Results[idx - 1])
end
Results
end
v = [1 2 3]
iterate(v, 3)

Here is another way that is a bit more concise and more truly recursive, as per your original question:
v = Array[[1, 2, 3]] ## create v as an array of one dimensional arrays
function iterate(v::Array{Array, 1}, niter::Int)
niter == 0 && return v[2:end]
push!(v, [v[end] ; 2v[end]])
niter -= 1
iterate(v, niter)
end
iterate(v, 3)

Related

How can I conditionally subset a data frame using a function in R?

i have a data frame that look like this :
a
b
1
1
2
2
3
3
4
4
5
5
i want to implement the following slicing:
if the window is 2 that means i have to take the first 2 elements of column a and the last 2 elements of columns b. Sum them and take the minimum of them (which i have done it). For example in the data frame above it must be
a
b
result
1
4
5
2
5
7
and will report 5.
But (and this is the problem) when the window equals the number of rows of the data frame then i want to take the first element of column a and the last element of column b and just sum them. In my data frame that would be 1+5 = 6.
My effort is this but i dont know how to insert this if else in the function
library(tidyverse)
a = seq(1,5,1)
b = seq(1,5,1)
w = tibble(a,b);w
w[1,1]+w[nrow(w),2]
im = function(mat,window){
if(window == nrow(mat))
mat[1,1] + mat[nrow(mat),2]
else
SA = mat%>%
dplyr::select(1)%>%
dplyr::slice_head(n=window)
SB = mat%>%
dplyr::dplyr::select(2)%>%
dplyr::slice_tail(n=window)
margin = min(SA+SB)
return(margin)
}
im(w,5)
to test it let's say that i have a vector or windows
vec = seq(1,5,1)
i want to run this function im that i have created for all the elements in the vector vec.
How can i do them in R ?
Any help
You can do it with a bunch of if/else:
f <- function(n, data){
if(n == 1){
data.frame(a = head(data[1], n),
b = head(data[2], n)) |>
transform(result = a + b)
}
else{
if(n == nrow(data)) n <- 1
data.frame(a = head(data[1], n),
b = tail(data[2], n)) |>
transform(result = a + b)
}
}
output
lapply(vec, f, data = w)
[[1]]
a b result
1 1 1 2
[[2]]
a b result
1 1 4 5
2 2 5 7
[[3]]
a b result
1 1 3 4
2 2 4 6
3 3 5 8
[[4]]
a b result
1 1 2 3
2 2 3 5
3 3 4 7
4 4 5 9
[[5]]
a b result
1 1 5 6
You can use ifelse function , it always simplify things for me more than if else instruction.
Try this it will works :
library(tidyverse)
a = seq(1,5,1)
b = seq(1,5,1)
w = tibble(a,b)
im = function(mat,window){
SA = unlist(#unlist to transform list type result of ifelse to vector
ifelse(
# here is the condition
window==nrow(mat),
# if condition is TRUE return first element
mat%>%
dplyr::select(1)%>%
dplyr::slice_head(n=1),
# if condition is FALSE return first window elements
mat%>%
dplyr::select(1)%>%
dplyr::slice_head(n=window))
)
SB = unlist(
ifelse(window==nrow(mat),
mat%>%
dplyr::select(2)%>%
dplyr::slice_tail(n=1),
mat%>%
dplyr::select(2)%>%
dplyr::slice_tail(n=window))
)
margin = min(SA+SB)
return(margin)
}
im(w,5)

How do I write a function that tests if a positive integer n can be written as sum of two squared integers, i. e. n = i^2 +j^2 with i; j 2 N>0

I've been cracking my head trying to solve this problem but so far I haven't been able to do it. How could I write this code in R?
Sadly I can't add a comment but I guess this wikipedia article can really help you:
https://en.wikipedia.org/wiki/Sum_of_two_squares_theorem
basically, you should decompose n to its primes and then check:
An integer greater than one can be written as a sum of two squares if and only if its prime decomposition contains no term p^k, where prime p = 3 mod 4 and k is odd.
Actually you can build a user function f like below
f <- function(n) {
p <- seq(floor(sqrt(n - 1)))
q <- sqrt(n - p**2)
idx <- q == floor(q)
data.frame(A = p[idx], B = q[idx])
}
such that
> f(9)
[1] A B
<0 rows> (or 0-length row.names)
> f(100)
A B
1 6 8
2 8 6
> f(500)
A B
1 4 22
2 10 20
3 20 10
4 22 4
> f(1000)
A B
1 10 30
2 18 26
3 26 18
4 30 10
where f(9) giving an empty data frame means that 9 cannot be decomposed as the sum of two squared integers.
Applying the theorem mentioned by Blop
library(gmp)
library(data.table)
is_sum_two_squared_ints <- function(x){
factor <- gmp::factorize(x)
factor_mod4 <- gmp::mod.bigz(factor, 4)
# converting from Big Integer to integer, assuming your number isn't too big
dt <- data.table(factor = as.integer(factor), factor_mod4 = as.integer(factor_mod4))
n_meets_criteria <-
dt[, .N, .(factor, factor_mod4)][factor_mod4 == 3 & N %% 2 == 1, .N]
return(n_meets_criteria == 0)
}
is_sum_two_squared_ints(25)
# [1] TRUE
is_sum_two_squared_ints(3)
# [1] FALSE
is_sum_two_squared_ints(220)
# [1] FALSE
is_sum_two_squared_ints(116)
# [1] TRUE

Writing a function in in R

I am doing an exercise to practice writing functions.
I'm trying to figure out the general code before writing the function that reproduces the output from the table function. So far, I have the following:
set.seed(111)
vec <- as.integer(runif(10, 5, 20))
x <- sort(unique(vec))
for (i in x) {
c <- length(x[i] == vec[i])
print(c)
}
But this gives me the following output:
[1] 1
[1] 1
[1] 1
[1] 1
[1] 1
[1] 1
[1] 1
[1] 1
[1] 1
I don't think I'm subsetting correctly in my loop. I've been watching videos, but I'm not quite sure where I'm going wrong. Would appreciate any insight!
Thanks!
We can sum the logical vector concatenate it to count
count <- c()
for(number in x) count <- c(count, sum(vec == number))
count
#[1] 3 1 4 1 5 4 3 2 7
In the OP's for loop, it is looping over the 'x' values and not on the sequence of 'x'
If we do
for(number in x) count <- c(count, length(vec[vec == number]))
it should work as well
You can try sapply + setNames to achieve the same result like table, i.e.,
count <- sapply(x, function(k) setNames(sum(k==vec),k))
or
count <- sapply(x, function(k) setNames(length(na.omit(match(vec,k))),k))
such that
> count
1 2 3 4 5 6 7 8 9
3 1 4 1 5 4 3 2 7
Here is a solution without using unique and with one pass through the vector (if only R was fast with for loops!):
count = list()
for (i in vec) {
val = as.character(i)
if (is.null(count[[val]]))
count[[val]] = 1
else
count[[val]] = count[[val]] + 1
}
unlist(count)

for loop with decimals and store results in a vector

I'm working on a for loop in R, and I had to store the results in a vector. I know that this is a quite common answer, and my problem is not there, but let's proceed with order.
I got those data:
# here the data
alpha <- c(1,2,3,4,5,6)
beta <- c(0.1,0.5,0.3,0.4,0.5,0.6)
data <- data.frame(alpha, beta)
And I make a simple function that select the data above a certain threshold:
# here the function
funny <- function(x,k)
{x[x[,2]>=k,]}
# here an example of the function
funny(data,0.5)
alpha beta
2 2 0.5
5 5 0.5
6 6 0.6
But what I want is the number of the rows that go over the threshold, so:
# here the result wanted
nrow(funny(data,0.5))
[1] 3
So I got a question: how many rows go over the threshold at the variation of k, the parameter of the function? And I would like to have the result in a vector. I created a for loop, looking at
For loop in R with increments
Saving results from for loop as a vector in r
And I created this: first of all let's see if everything is all right:
# here the sequence
s <-seq(0.1,0.6, by = 0.1)
# here the I loop
for(i in s) {print(nrow(funny(data,i)))}
[1] 6
[1] 5
[1] 4
[1] 4
[1] 3
[1] 1
But clearly this is not stored in a vector. The problem is here. I tried:
# already written sequence
s <-seq(0.1,0.6, by = 0.1)
# here the empty vector
vec <- vector("numeric")
# here the II problematic loop
for(i in s) {vec[i]<-(nrow(funny(data,i)))}
vec
And here the result I do not want, I expected something like [1] 6 5 4 4 3 1
[1] 0 0 0 0 0 0
Furthermore infos:
I tried something like this:
# sequence * 10
s <-seq(1,6, by = 1)
# here the vector
vec <- vector("numeric")
# and the III loop, that it works now.
for(i in s) {vec[i]<-(nrow(funny(data,i/10)))}
vec
[1] 6 5 5 4 3 1
But I do not like this, because I do not understand why the III works and why the II loop no.
What I am missing?
We can try with sapply which will return a vector
sapply(s, function(x) nrow(funny(data, x)))
#[1] 6 5 4 4 3 1
As far as why your loop II is not working. If you do,
for(i in s) {
print(i)
}
You'll get
[1] 0.1
[1] 0.2
[1] 0.3
[1] 0.4
[1] 0.5
[1] 0.6
So when you are trying to store in your loop II vec[i] <-, you are actually doing vec[0.1] in first case which is not correct.
To correct your loop, try
for(i in seq_along(s)) {vec[i]<-(nrow(funny(data,s[i])))}
vec
#[1] 6 5 4 4 3 1
Where seq_along(s) would return #[1] 1 2 3 4 5 6.

Sweep equivalent in Julia

From R documentation:
sweep: Return an array obtained from an input array by sweeping out a summary
statistic.
For example, here is how I divide each row by its row sum:
> rs = rowSums(attitude)
> ratios = sweep(attitude, 1, rs, FUN="/")
> head(ratios)
rating complaints privileges learning raises critical advance
1 0.1191136 0.1412742 0.08310249 0.1080332 0.1689751 0.2548476 0.12465374
2 0.1518072 0.1542169 0.12289157 0.1301205 0.1518072 0.1759036 0.11325301
3 0.1454918 0.1434426 0.13934426 0.1413934 0.1557377 0.1762295 0.09836066
4 0.1568123 0.1619537 0.11568123 0.1208226 0.1388175 0.2159383 0.08997429
5 0.1680498 0.1618257 0.11618257 0.1369295 0.1473029 0.1721992 0.09751037
6 0.1310976 0.1676829 0.14939024 0.1341463 0.1646341 0.1493902 0.10365854
> rowSums(ratios) # check that ratios sum up to 1
[1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
My attempt in Julia:
x = rand(3, 4)
x[1, 1] = 10
x[2, 1] = 20
x[3, 1] = 30
rowsum = sum(x, 2)
rowsum_mat = repmat(rowsum, 1, size(x, 2))
x = x ./ rowsum_mat
This works but is clunky. Is there a more elegant and efficient way of doing this?
No need to use repmat — all of Julia's .-operators do "broadcasting" by default. This means it matches the dimensions of the two arguments and then expands any dimensions that have length 1 (the singleton dimensions) to match the other array. Since reductions keep the same dimensionality of the source array, they can be used directly with any dot-operator.
In your case, you can just use:
x ./ sum(x, 2)
since:
julia> x ./ rowsum_mat == x ./ rowsum
true

Resources