Related
I want to calculate the Euclidean distance between a tuple and each tuple within a Vector in Julia using the map function, like below (but I get two values instead of three):
julia> tups = [
(1, 3),
(11, 2),
(0, 1)
];
julia> map((x, y) -> √(sum((x.-y).^2)), tups, (3, 3))
2-element Vector{Float64}:
2.0
8.06225774829855
How can I make it work correctly?
Julia has the Distances package especially for these types of calculations. The 'Julian way' encourages interoperability between packages to allow benefitting from future development of the ecosystem. For example, new metric definitions, or specialized hardware code to compute distances.
For the problem in the post, the code would look:
julia> using Distances
julia> tups = [
(1, 3),
(11, 2),
(0, 1)
];
julia> euclidean.(tups,Ref((3,3)))
3-element Vector{Float64}:
2.0
8.06225774829855
3.605551275463989
Notice the use of broadcasting instead of map with dot syntax euclidean.. The Ref((3,3)) causes broadcasting to consider (3,3) as a single element to broadcast and not break it to a pair of Ints.
The code you've written is pretty equal to this:
[
func((1, 3), 3),
func((11, 2), 3)
]
The map function iterates over the given collections iter times equal to the lowest length:
julia> length((3, 3)), length(tups)
(2, 3)
So it iterates two times, not three. To make that work, you can repeat the (3, 3), three times or even omit the (3, 3) argument:
julia> map((x, y) -> √(sum((x.-y).^2)), tups, ((3, 3), (3, 3), (3, 3)))
3-element Vector{Float64}:
2.0
8.06225774829855
3.605551275463989
# OR
julia> map((x, y) -> √(sum((x.-y).^2)), tups, ((3, 3) for _∈1:3))
3-element Vector{Float64}:
2.0
8.06225774829855
3.605551275463989
# Or omit the last argument
julia> map(arg -> √((3 - arg[1])^2 + (3 - arg[2])^2), tups)
3-element Vector{Float64}:
2.0
8.06225774829855
3.605551275463989
I am stuck in a problem. I want to update my cartesian index.
I have a matrix (x) 200x6 that is binary. 1 if assigned, 0 otherwise. I want to find the cartesian index of when x is 1 in the first 3 columns and in the last 3 elements.
I have the following code:
index_right = findall( x -> x == 1, sol.x_assignment[:,1:3])
index_left = findall( x -> x == 1, sol.x_assignment[:,4:6])
index_left
However index_right is correct, index_left is wrong as it returns index between 1,2,3 instead of 4,5,6
CartesianIndex(2, 1)
CartesianIndex(3, 1)
CartesianIndex(10, 2)
CartesianIndex(11, 1)
Expected output:
CartesianIndex(2, 4)
CartesianIndex(3, 4)
CartesianIndex(10, 5)
CartesianIndex(11, 4)
How can I update index_left to add +3 in the second index for all?
One solution could be
index_left = findall( x -> x == 1, sol.x_assignment[:,4:6])
index_left = map(x -> x + CartesianIndex(0, 3), index_left)
I think you can also use ==(1) in place of x -> x + 1, looks a bit nicer :)
index_left = findall(==(1), sol.x_assignment[:,4:6])
and the inplace version of map should work too
map!(x -> x + CartesianIndex(0, 3), index_left, index_left).
An alternative could be first finding all the indices with 1 and then filtering afterwards, so smth like
full_index = findall(==(1), sol.x_assignment)
and then
left_index = filter(x -> x[2] <= 3, full_index)
right_index = filter(x -> x[2] > 3, full_index)
Assuming your x is:
using Random;Random.seed!(0);
x = rand(Bool, (5,6))
The first set can be found as:
findall(isone, #view x[:, 1:3])
For the second set you need to shift the results hence you want:
findall(isone, #view x[:, 4:6]) .+ Ref( CartesianIndex(0,3))
If you are searching for different value eg. 2 use ==(2) rather than a lambda as this is faster.
Similarly #view allows to avoid unnecessary allocations.
x = [1, 2, 3, 4]
y = [1, 2]
If I want to be able to operate on the two vectors with a default value filling in, what are the strategies?
E.g. would like to do the following and implicitly fill in with 0 or missing
x + y # would like [2, 4, 3, 4]
Ideally would like to do this in a generic way so that I could do arbitrary operations with the two.
Disregarding whether Julia has something built-in to do this, remember that Julia is fast. This means that you can write code to support this kind of need.
extend!(x, y::Vector, default=0) = extend!(x, length(y), default)
extend!(x, n::Int, default=0) = begin
while length(x) < n
push!(x, default)
end
x
end
Then when you have code such as you describe, you can symmetrically extend x and y:
x = [1, 2, 3, 4]
y = [1, 2]
extend!(x, y)
extend!(y, x)
x + y
==> [2, 4, 3, 4]
Note that this mutates y. In many cases, the desired length would come from outside the code and would be applied to both x and y. I can also imagine that 0 is a bad default in general (even though it is completely appropriate in your context of addition.
A comment below makes the worthy point that you should consider using append! instead of looping over push!. In fact, it is best to measure differences like that if you care about very small differences. I went ahead and tested:
julia> using BenchmarkTools
julia> extend1(x, n) = begin
while length(x) < n
push!(x, 0)
end
x
end
julia> #btime begin
x = rand(10)
sum(x)
end
59.815 ns (1 allocation: 160 bytes)
5.037723569560573
julia> #btime begin
x = rand(10)
extend1(x, 1000)
sum(x)
end
7.281 μs (8 allocations: 20.33 KiB)
6.079832879992913
julia> x = rand(10)
julia> #btime begin
x = rand(10)
append!(x, zeros(990))
sum(x)
end
1.290 μs (3 allocations: 15.91 KiB)
3.688526541987817
julia>
Pushing primitives in a loop is damned fast, allocating a vector of zeros so we can use append! is very slightly faster.
But the real lesson here is seen in the fact that the loop version takes microseconds to append nearly 1000 values (reallocating the array several times). Appending 10 values one by one takes just over 150ns (and append! is slightly faster). This is blindingly fast. Literally doing nothing in R or Python can take longer than this.
This difference would matter in some situations and would be undetectable in many others. If it matters, measure. If it doesn't, do the simplest thing that comes to mind because Julia has your back (performance-wise).
FURTHER UPDATE
Taking a hint from another of Colin's comments, here are results where we use append! but we don't allocate a list. Instead, we use a generator ... that is, a data structure that invents data when asked for it with an interface much like a list. The results are much better than what I showed above.
julia> #btime begin
x = rand(10)
append!(x, (0 for i in 1:990))
sum(x)
end
565.814 ns (2 allocations: 8.03 KiB)
Note the round brackets around the 0 for i in 1:990.
In the end, Colin was right. Using append! is much faster if we can avoid related overheads. Surprisingly, the base function Iterators.repeated(0, 990) is much slower.
But, no matter what, all of these options are pretty blazingly fast and all of them would probably be so fast that none of these subtle differences would matter.
Julia is fun!
Note that if you want to fill with missing or some other type different from the element type in your original vector, then you will need to change the type of your vectors to allow those new elements. The function below will handle any case.
function fillvectors(x, y, fillvalue=missing)
xl = length(x)
yl = length(y)
if xl < yl
x::Vector{Union{eltype(x), typeof(fillvalue)}} = x
for i in xl+1:yl
push!(x, fillvalue)
end
end
if yl < xl
y::Vector{Union{eltype(y), typeof(fillvalue)}} = y
for i in yl+1:xl
push!(y, fillvalue)
end
end
return x, y
end
x = [1, 2, 3, 4]
y = [1, 2]
julia> (x, y) = fillvectors(x, y)
([1, 2, 3, 4], Union{Missing, Int64}[1, 2, missing, missing])
julia> y
4-element Vector{Union{Missing, Int64}}:
1
2
missing
missing
julia> (x, y) = fillvectors(x, y, 0)
([1, 2, 3, 4], [1, 2, 0, 0])
julia> y
4-element Vector{Int64}:
1
2
0
0
julia> (x, y) = fillvectors(x, y, 1.001)
([1, 2, 3, 4], Union{Float64, Int64}[1, 2, 1.001, 1.001])
julia> y
4-element Vector{Union{Float64, Int64}}:
1
2
1.001
1.001
I have a two-element vector whose elements can only be 0 or 1. For the sake of this example, suppose x = [0, 1]. Suppose also there are four objects y00, y01, y10, y11. My goal is to update the corresponding y (y01 in this example) according to the current value of x.
I am aware I can do this using a series of if statements:
if x == [0, 0]
y00 += 1
elseif x == [0, 1]
y01 += 1
elseif x == [1, 0]
y10 += 1
elseif x == [1, 1]
y11 += 1
end
However, I understand this can be done more succinctly using Julia's metaprogramming, although I'm unfamiliar with its usage and can't figure out how.
I want to be able to express something like y{x[1]}{x[2]} += 1 (which is obviously wrong); basically, be able to refer and modify the correct y according to the current value of x.
So far, I've been able to call the actual value of the correct y (but I can't summon the y object itself) with something like
eval(Symbol(string("y", x[1], x[2])))
I'm sorry if I did not use the appropriate lingo, but I hope I made myself clear.
There's a much more elegant way using StaticArrays. You can define a common type for your y values, which will behave like a matrix (which I assume the ys represent?), and defines a lot of things for you:
julia> mutable struct Thing2 <: FieldMatrix{2, 2, Float64}
y00::Float64
y01::Float64
y10::Float64
y11::Float64
end
julia> M = rand(Thing2)
2×2 Thing2 with indices SOneTo(2)×SOneTo(2):
0.695919 0.624941
0.404213 0.0317816
julia> M.y00 += 1
1.6959194941562996
julia> M[1, 2] += 1
1.6249412302897646
julia> M * [2, 3]
2-element SArray{Tuple{2},Float64,1,2} with indices SOneTo(2):
10.266662679181893
0.9037708026795666
(Side note: Julia indices begin at 1, so it might be more idiomatic to use one-based indices for y as well. Alternatively, can create array types with custom indexing, but that's more work, again.)
How about using x as linear indices into an array Y?
x = reshape(1:4, 2, 2)
Y = zeros(4);
Y[ x[1,2] ] += 1
Any time you find yourself naming variables with sequential numbers it's a HUGE RED FLAG that you should just use an array instead. No need to make it so complicated with a custom static array or linear indexing — you can just make y a plain old 2x2 array. The straight-forward transformation is:
y = zeros(2,2)
if x == [0, 0]
y[1,1] += 1
elseif x == [0, 1]
y[1,2] += 1
elseif x == [1, 0]
y[2,1] += 1
elseif x == [1, 1]
y[2,2] += 1
end
Now you can start seeing a pattern here and simplify this by using x as an index directly into y:
y[(x .+ 1)...] += 1
I'm doing two things there: I'm adding one to all the elements of x and then I'm splatting those elements into the indexing expression so they're treated as a two-dimensional lookup. From here, you could make this more Julian by just using one-based indices from the get-go and potentially making x a Tuple or CartesianIndex for improved performance.
Using Prolog:
Write a predicate dispnth to display the nth element of a list. You may assume that the input list always has n or more elements.
For Example:
?- dispnth([1, [2, 3], 4, 5], 2, X). should return X = [2, 3]
I have this so far:
dispnth([X|_], 0, X).
dispnth([_|Xs], N, X) :-
dispnth(N1, X, Xs),
N is N1 + 1.
First let's give the predicate a more descriptive name, say list_nth_element/3. Next you might like to consider an auxiliary predicate list_nth_element_/4 with an additional argument, that holds the current position. From your given example I assume that you start counting at 1, so that's going to be the start value for the fourth argument. Then the predicates might look something like this:
list_nth_element(L,N,E) :-
list_nth_element_(L,N,E,1).
list_nth_element_([X|Xs],N,X,N). % if the 2nd and 4th elements are equal X is the nth element
list_nth_element_([_X|Xs],N,E,P0) :- % if the 2nd and 4th arguments
dif(P0,N), % differ
P1 is P0+1, % increment current position
list_nth_element_(Xs,N,E,P1). % and recurse
So essentially the fourth argument is used as a position indicator that is being incremented until you reached the desired position. However, there is no need to have this additional argument in the actual predicates interface, so it is "hidden" in the auxiliary predicate's interface.
Querying this predicate yields your desired result:
?- list_nth_element([1, [2, 3], 4, 5], 2, X).
X = [2,3] ? ;
no
You can also ask things like Which element is at what position?
?- list_nth_element([1, [2, 3], 4, 5], N, X).
N = X = 1 ? ;
N = 2,
X = [2,3] ? ;
N = 3,
X = 4 ? ;
N = 4,
X = 5 ? ;
no