I am trying to create a heat map. I have the following code. Is there a simple way to create a heatmap, s.t. lst1 is the x-axis, lst2 is the y-axis and lst ist the intensity in the graph?
lst1 = []
lst2 = []
lst3 = []
for i in range(0,3.5,step = 0.5)
for j in range(0,4,step = 0.5)
println(i,j)
a = f(parameter,i,j)
push!(lst1,i)
push!(lst2,j)
push!(lst3,a)
print("($i , $j): $a %")
end
end
A somewhat shorter way of doing this, utilizing broadcasts, might be:
lst1 = 0:0.5:3.5
lst2 = 0:0.5:4
lst3 = f.(Ref(parameter), lst1, lst2')
lst1 and lst2 are constructed using the colon operator but are equivalent to the range call you showed.
lst3 is constructed using the Julia broadcast operator. Here, we wrap parameter in a Ref (think of it as a zero dimensional array or a pointer) to indicate that it should not be expanded during the broadcast. We pass lst1 as is, and its form mimics a column vector. We then pass the transpose of lst2 (obtained by the ' operator) which makes it have the form of a row vector.
These two differing dimensions cause the broadcast to create a Matrix with the first axis being lst1 and the second axis being lst2. For clarity you can examine the output of tuple.(lst1, lst2'), which will show you essentially the values passed into the function f.
In the end, lst3 will be a Matrix of elements of the type which f returns.
To actually plot this you should consider using the Plots.jl or Makie.jl packages.
Sorry I can't provide code examples right now, posting this on mobile. Will reformat later.
You may do it like this with Plots.jl
using Plots
f = (x, y) -> x^2 + y^2
x = range(-1, 1; length=10)
y = range(-2, 2; length=40)
z = fill(NaN, size(y, 1), size(x, 1))
for i in eachindex(x), j in eachindex(y)
z[j, i] = f(x[i], y[j])
end
heatmap(x, y, z)
Plots.heatmap waits for rectangular matrix of z-values, which I preallocated before the cycle. In this matrix, x-direction corresponds to the columns and y-direction to the rows.
z as matrix z when plotted
z11 z12 z13 z31 z32 z33
z21 z22 z23 z21 z22 z23
z31 z32 z33 z11 z12 z13
Take a look at
z = [1 2 3; 4 5 6; 7 8 9]
heatmap(z) # heatmap(1:size(z, 2), 1:size(z, 1), z)
Things to improve
replace fill with uninitialised matrix constructor
construct x-y pairs, e.g. by Iterators.product instead of nested loops
use broadcasting
Related
I have a function
eval_ = function(f, i) f(i)
for a list of functions, say
fns = list(function(x) x**2, function(y) -y)
and a vector of integers, say
is = 1:2
I would like to get eval_ evaluated at all combinations of fns and is.
I tried the following:
cross = expand.grid(fns, is)
names(cross) = c("f", "i")
results = sapply(1:nrow(cross), function(i) do.call(eval_, cross[i,]))
This throws an error:
Error in f(i) : could not find function "f"
I think that the underlying problem is, that cross is a data.frame and can not carry functions. Hence, it puts the function into a list and then carries a list (indeed, class(cross[1,][[1]]) yields "list". My ugly hack is to change the third line to:
results = sapply(
1:nrow(cross),
function(i) do.call(eval_, list(f = cross[i,1][[1]], i = cross[i,2]))
)
results
#[1] 1 -1 4 -2
This works, but it defeats the purpose of do.call and is very cumbersome.
Is there a nice solution for this kind of problem?
Note: I would like a solution that generalizes well to cases where the cross product is not only over two, but possibly an arbitrary amount of lists, e.g. functions that map R^n into R.
Edit:
For a more involved example, I think of the following:
fns = list(mean, sum, median)
is1 = c(1, 2, 4, 9), ..., isn = c(3,6,1,2) and my goal is to evaluate the functions on the cartesian product spanned by is1, ..., isn, e.g. on the n-dimensional vector c(4, ..., 6).
You can use mapply() for this:
eval_ <- function(f, i) f(i)
fns <- list(function(x) x**2, function(y) -y)
is <- 1:2
cross <- expand.grid(fns = fns, is = is)
cross$result <- mapply(eval_, cross$fn, cross$is)
print(cross)
#> fns is result
#> 1 function (x) , x^2 1 1
#> 2 function (y) , -y 1 -1
#> 3 function (x) , x^2 2 4
#> 4 function (y) , -y 2 -2
An attempt for my "more involved example" with n = 2.
Let X = expand.grid(c(1, 2, 4, 9), c(3,6,1,2)).
The following pattern generalizes to higher dimensions:
nfns = length(fns)
nn = nrow(X)
res = array(0, c(nfns, nn))
for(i in 1:nfns){
res[i,] = apply(X, MARGIN = 1, FUN = fns[[i]])
}
The shape of the margin of X (i.e. nrow(X)) must correspond to the shape of the slice res[i,] (i.e. nn). The function must map the complement of the margin of X (i.e. slices of the form X[i,]) to a scalar. Note that a function that is not scalar has components that are scalar, i.e. in a non-scalar case, we would loop over all components of the function.
I had a vector like this :
x= c(0.542949849, 0.242292905, 0.163459552, 0.069668097, 0.042969073, 0.035829825)
and I want to plot (x[i], x[i+1]). Using Excel I got this :
How can I get this graphic in R ? I tried this :
for(i in 1:5){
plot(x[i], x[i+1])
par(new = TRUE)
}
but it doesn't give the excepted result
Here are two solutions.
The first uses base R only.
x <- c(0.542949849, 0.242292905, 0.163459552, 0.069668097, 0.042969073, 0.035829825)
plot(range(x), range(x), type = "n")
for(i in seq_along(x)[-length(x)]){
points(x[i], x[i+1])
}
The second uses package tsDyn.
tsDyn::autopairs(x, type = "points")
Try this:
plot(embed(rev(x), 2))
or
plot(embed(x, 2)[, 2:1])
You can get what you want but you have to add a few intermediate steps.
You need to put in a qualifier to force the array to be numeric. This is the equivalent of forcing the array to be an array of float values. Otherwise all you get is integer values in your array.
You need to redefine the sub-components of x to 2 new vectors. Vector 'a' has an index of elements from 1 to 5 of the x array. It appears on the x-axis. Vector 'b' has an index of elements from 2 to 6 of the x array. It appears on the y-axis. The first elements in vectors a and b index position 1 are equivalent to x[i],x[i+1] where i is 1.
You need to bind the 2 vectors together and then plot the result.
x <- as.numeric(c(0.542949849, 0.242292905, 0.163459552, 0.069668097, 0.042969073, 0.035829825))
a <- x[1:5]
b <- x[2:6]
c <- cbind(a,b)
plot(c)
and the result graph is as follows
The following question came up in my course yesterday:
Suppose I have a matrix M = rand(3, 10) that comes out of a calculation, e.g. an ODE solver.
In Python, you can do
x, y, z = M
to extract the rows of M into the three variables, e.g. for plotting with matplotlib.
In Julia we could do
M = M' # transpose
x = M[:, 1]
y = M[:, 2]
z = M[:, 3]
Is there a nicer way to do this extraction?
It would be nice to be able to write at least (approaching Python)
x, y, z = columns(M)
or
x, y, z = rows(M)
One way would be
columns(M) = [ M[:,i] for i in 1:size(M, 2) ]
but this will make an expensive copy of all the data.
To avoid this would we need a new iterator type, ColumnIterator, that returns slices? Would this be useful for anything other than using this nice syntax?
columns(M) = [ slice(M,:,i) for i in 1:size(M, 2) ]
and
columns(M) = [ sub(M,:,i) for i in 1:size(M, 2) ]
They both return a view, but slice drops all dimensions indexed with
scalars.
A nice alternative that I have just found if M is a Vector of Vectors (instead of a matrix) is using zip:
julia> M = Vector{Int}[[1,2,3],[4,5,6]]
2-element Array{Array{Int64,1},1}:
[1,2,3]
[4,5,6]
julia> a, b, c = zip(M...)
Base.Zip2{Array{Int64,1},Array{Int64,1}}([1,2,3],[4,5,6])
julia> a, b, c
((1,4),(2,5),(3,6))
I want to multiply and then sum the unique pairs of a vector, excluding pairs made of the same element, such that for c(1:4):
(1*2) + (1*3) + (1*4) + (2*3) + (2*4) + (3*4) == 35
The following code works for the example above:
x <- c(1:4)
bar <- NULL
for( i in 1:length(x)) { bar <- c( bar, i * c((i+1) : length(x)))}
sum(bar[ 1 : (length(bar) - 2)])
However, my actual data is a vector of rational numbers, not integers, so the (i+1) portion of the loop will not work. Is there a way to look at the next element of the set after i, e.g. j, so that I could write i * c((j : length(x))?
I understand that for loops are usually not the most efficient approach, but I could not think of how to accomplish this via apply etc. Examples of that would be welcome, too. Thanks for your help.
An alternative to a loop would be to use combn and multiply the combinations using the FUN argument. Then sum the result:
sum(combn(x = 1:4, m = 2, FUN = function(x) x[1] * x[2]))
# [1] 35
Even better to use prod in FUN, as suggested by #bgoldst:
sum(combn(x = 1:4, m = 2, FUN = prod))
I want to create a function f in R which takes a list x and a vector y as its arguments and returns a list whose elements are products after multiplying each element of the vector by each element of the list.
x = list(x1 = runif(10), x2 = rnorm(10), x3 = 1:10, x4 = seq(1,.1,-.1))
y = c(2, 1, 3)
I want f(x,y) to return a list whose elements are x[[i]] * y[j] for each i = 1:length(x)
and j = 1:length(y)
This would work:
lapply(x, function(z) lapply(y, "*", z))
However you need to do a bit of work on moving things around with indexing as right now you're bordering on let me order up a function and you guys make it for me.
You could use mapply("*", x, y), but you need to modify "y" so that each multiplier is repeated length(x) times. In other words, the following should work:
mapply("*", x, rep(y, each = length(x)))
You may need to add names back in to the results.
If the length of each list item is the same (in your example, they all contain 10 values), the following is a variation on #Tyler's answer:
lapply(y, "*", do.call(rbind, x))
This will result in a list of matrices of the results.
This should also work - depend on what form of outcome you prefer
lapply(x , outer , y)