Returned array is unexpected - julia

The following lines
s = [1 2 5 7 3 3]
index=findall(x -> (x < 7 & x > 3), s)
[idx[2] for idx in index]
returns
0-element Array{Int64,1}
when there is a 5 in s. What is going wrong here?

& is bit-wise AND operator and the operator precedence kicks in here. The logical AND operator in Julia is &&.
You can use parenthesis to make your expression correct for your purpose, i.e. (x > 7) & (x > 3), though I would not recommend this one.
You should instead use logical AND operator &&, or perhaps better directly write what you would write on a paper i.e. 3 < x < 7. All of these methods work.
s = [1 2 5 7 3 3]
index=findall(x -> 3 < x < 7, s)
[idx[2] for idx in index]

& operates on bits and the logical and is &&.
For what you want to do just use filter:
julia> filter(x -> 7 > x > 3, s)
1-element Array{Int64,1}:
5

Related

How can I slice the high-order multidimeonal array (or tensor) on the specific axis in Julia?

I am using Julia1.6
Here, X is a D-order multidimeonal array.
How can I slice from i to j on the d-th axis of X ?
Here is an exapmle in case of D=6 and d=4.
X = rand(3,5,6,6,5,6)
Y = X[:,:,:,i:j,:,:]
i and j are given values which are smaller than 6 in the above example.
You can use the built-in function selectdim
help?> selectdim
search: selectdim
selectdim(A, d::Integer, i)
Return a view of all the data of A where the index for dimension d equals i.
Equivalent to view(A,:,:,...,i,:,:,...) where i is in position d.
Examples
≡≡≡≡≡≡≡≡≡≡
julia> A = [1 2 3 4; 5 6 7 8]
2×4 Matrix{Int64}:
1 2 3 4
5 6 7 8
julia> selectdim(A, 2, 3)
2-element view(::Matrix{Int64}, :, 3) with eltype Int64:
3
7
Which would be used something like:
julia> a = rand(10,10,10,10);
julia> selectedaxis = 5
5
julia> indices = 1:2
1:2
julia> selectdim(a,selectedaxis,indices)
Notice that in the documentation example, i is an integer, but you can use ranges of the form i:j as well.
If you need to just slice on a single axis, use the built in selectdim(A, dim, index), e.g., selectdim(X, 4, i:j).
If you need to slice more than one axis at a time, you can build the array that indexes the array by first creating an array of all Colons and then filling in the specified dimensions with the specified indices.
function selectdims(A, dims, indices)
indexer = repeat(Any[:], ndims(A))
for (dim, index) in zip(dims, indices)
indexer[dim] = index
end
return A[indexer...]
end
idx = ntuple( l -> l==d ? (i:j) : (:), D)
Y = X[idx...]

Filtering using conditional evaluation (&&)

I am having trouble filtering a simple array-based on two conditions. For example, to filter out values between 3 and 5, I tried the following but I get an ERROR: TypeError: non-boolean (BitArray{1}) used in boolean context error.
arr = Array{Int64}([1,2,3,4,5,6])
arr[(arr .> 3) && (arr.< 5)]
Any idea how to solve it?
Also on a side note, I am wondering if there is a function opposite to isless. Something to find a value greater than a certain value.
Here are two ways to do it:
julia> arr = [1,2,3,4,5,6]
6-element Array{Int64,1}:
1
2
3
4
5
6
julia> arr[(arr .> 3) .& (arr.< 5)]
1-element Array{Int64,1}:
4
julia> filter(v -> 3 < v < 5, arr)
1-element Array{Int64,1}:
4
(I personally prefer filter).
To get the opposite of isless just reverse its arguments, or if needed define a new function:
isgreater(x, y) = isless(y, x)
I prefer a set comparison approach because it's quite intuitive:
julia> arr = Array{Int64}([1,2,3,4,5,6])
julia> intersect( arr[ arr .> 1 ], arr[ arr .< 4 ] )
2-element Array{Int64,1}:
2
3
Or list comprehension:
[ x for x in arr if 3 < x < 5]
# or
[ x for x in arr if 3 < x && x < 5]
Also to define an array literal with specific type Int64, there is a dedicated syntax to make it simpler:
arr = Int64[1,2,3,4,5,6]

Modify object whose name is based on contents of an array

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.

indices of unique elements of vector in Julia

How to get indexes of unique elements of a vector?
For instance if you have a vector v = [1,2,1,3,5,3], the unique elements are [1,2,3,5] (output of unique) and their indexes are ind = [1,2,4,5]. What function allows me to compute ind so that v[ind] = unique(v) ?
This is a solution for Julia 0.7:
findfirst.(isequal.(unique(x)), [x])
or similar working under Julia 0.6.3 and Julia 0.7:
findfirst.(map(a -> (y -> isequal(a, y)), unique(x)), [x])
and a shorter version (but it will not work under Julia 0.7):
findfirst.([x], unique(x))
It will probably not be the fastest.
If you need speed you can write something like (should work both under Julia 0.7 and 0.6.3):
function uniqueidx(x::AbstractArray{T}) where T
uniqueset = Set{T}()
ex = eachindex(x)
idxs = Vector{eltype(ex)}()
for i in ex
xi = x[i]
if !(xi in uniqueset)
push!(idxs, i)
push!(uniqueset, xi)
end
end
idxs
end
Another suggestion is
unique(i -> x[i], 1:length(x))
which is about as fast as the function in the accepted answer (in Julia 1.1), but a bit briefer.
If you don't care about finding the first index for each unique element, then you can use a combination of the unique and indexin functions:
julia> indexin(unique(v), v)
4-element Array{Int64,1}:
3
2
6
5
Which gets one index for each unique element of v in v. These are all in base and works in 0.6. This is about 2.5 times slower than #Bogumil's function, but it's a simple alternative.
A mix between mattswon and Bogumił Kamiński answers (thanks !):
uniqueidx(v) = unique(i -> v[i], eachindex(v))
eachindex allows to work with any kind of array, even views.
julia> v = [1,2,1,3,5,3];
julia> uniqueidx(v)
4-element Vector{Int64}:
1
2
4
5
julia> v2 = reshape(v, 2, 3)
2×3 Matrix{Int64}:
1 1 5
2 3 3
julia> subv2 = view(v2, 1:2, 1:2)
2×2 view(::Matrix{Int64}, 1:2, 1:2) with eltype Int64:
1 1
2 3
julia> uniqueidx(subv2)
3-element Vector{CartesianIndex{2}}:
CartesianIndex(1, 1)
CartesianIndex(2, 1)
CartesianIndex(2, 2)

Julia: find maximum along columns in array

Suppose we have an array defined like this:
a=[1 2; 3 4; 5 5; 7 9; 1 2];
In Matlab, we could find the maximum values by writing:
[x y] = max(a)
x =
7 9
In Julia, we could use:
a=[1 2; 3 4; 5 5; 7 9; 1 2]
findmax(a,1)
returning:
([7 9],
[4 9])
However, I am interested not only in finding [7 9] for the two columns, but also their relative position within each column, like [4, 4]. Of course, I can write a bit more of coding lines, but can I do it directly with findmax?
The second matrix returned by findmax is the linear index of the locations of the maxima over the entire array. You want the position within each column; to get that, you can convert the linear indices into subscripts with ind2sub. Then the first element of the subscript tuple is your row index.
julia> vals, inds = findmax(a, 1)
(
[7 9],
[4 9])
julia> map(x->ind2sub(a, x), inds)
1×2 Array{Tuple{Int64,Int64},2}:
(4,1) (4,2)
julia> map(x->ind2sub(a, x)[1], inds)
1×2 Array{Int64,2}:
4 4
This is mentioned in the comments but I figured I'd do a response that's easy to see. I have version 1.0.3, so I don't know what's the earliest version that allows this. But now you can just do
julia> findmax(a) #Returns 2D index of overall maximum value
(9, CartesianIndex(4, 2))
julia> findmax(a[:,1]) #Returns 1D index of max value in column 1
(7, 4)
julia> findmax(a[:,2]) #Returns 1D index of max value in column 2
(9, 4)
Hope this makes things easier.
I've adopted the following function:
indmaxC(x) = cat(1, [indmax(x[:,c]) for c in 1:size(x,2)]...)
The Good: it's convenient and small
The Bad: it's only valid for 2-D arrays
A safer version would be:
function indmaxC(x::AbstractArray)
assert(ndims(x)==2)
cat(1, [indmax(x[:,c]) for c in 1:size(x,2)]...)
end

Resources