Julia conditional assignment based on | (or) in multidimensional array - arrays

I have a 3D array, and based on the values in one "row" using the or operator "|", I would like to substitute values in a different row. I've attempted to build a mwe, here goes:
# create new array
Pop = fill(Int8(3), 1, 3, 4)
# change values in one row of array
Pop[:,:,2] = [5,6,7]
# change values in second row of array
Pop[:,:,4] = [9,10,11]
# attempt conditional substitution, if element-wise value of Pop[:,:,2] equals either 6 or 7, then substitute the corresponding element wise (across row in dimension 3) for 8
Pop[:,:,2] == 6|7 Pop[:,:,3] .= 8
# to produce this end result
Pop[:,:,3] = [3,8,8]
Pop
I'm missing something in the syntax for the substitution. Thx. J

This works, but requires a temporary variable.
julia> temp = Pop[:,:,3]
1×3 Array{Int8,2}:
3 3 3
julia> temp[(Pop[:,:,2] .== 6) .| (Pop[:,:,2] .== 7)] .= 8
2-element view(::Array{Int8,1}, [2, 3]) with eltype Int8:
8
8
julia> Pop[:,:,3] = temp
1×3 Array{Int8,2}:
3 8 8

I think it's clearer to separate the stages:
julia> Pop = fill(Int8(3), 1, 3, 4);
julia> Pop[:,:,2] .= [5 6 7];
julia> ind = findall(x -> x==6 || x==7, Pop[:,:,2])
2-element Vector{CartesianIndex{2}}:
CartesianIndex(1, 2)
CartesianIndex(1, 3)
julia> Pop[ind, 3] .= 8;
julia> Pop[:, :, 3]
1×3 Matrix{Int8}:
3 8 8
You could also write ind == findall(in((6,7)), #view Pop[:,:,2]). You can write ind_bool = (Pop[:,:,2] .== 6) .| (Pop[:,:,2] .== 7) but I'm not sure Pop[ind_bool, 3] .= 8 will work on all versions of Julia.
Another way is just to write an explicit loop. We could loop separately over for i in axes(Pop,1), j in axes(Pop,2) but we can also use these multi-dimensional CartesianIndex things again:
julia> Pop = fill(Int8(3), 1, 3, 4);
julia> Pop[:,:,2] .= [5 6 7];
julia> for i in CartesianIndices(#view Pop[:,:,2])
x = Pop[i,2]
if x==6 | x==7
Pop[i,3] = 8
end
end
julia> Pop[:, :, 3]
1×3 Matrix{Int8}:
3 3 8

Thanks to help from #mcabbott and #Nathan Boyer, I've got a solution that works, even managed to add more conditions and it seems to work. I'll post the mwe solution.
# create new array
Pop = fill(Int8(3), 1, 3, 4)
# change some values
Pop[:,:,2] = [5,6,7]
Pop[:,:,4] = [9,10,9]
# what does it look like
Pop
# code to check condition values
# create temp array to be altered if conditions met
temp = Pop[:,:,3]
# see which indexes meet conditions, in this case
# array "Pop[:,:,2]" contains either 6 or 7 ... and ...
# array "Pop[:,:,4]" contains values >= 10
temp[(Pop[:,:,2] .== 6) .|
(Pop[:,:,2] .== 7) .&
(Pop[:,:,4] .>= 10)] .= 8
# make changes to original array based on temp
Pop[:,:,3] = temp
# check if it did the job correctly
Pop

Related

Julia, use findall to reset a third row in a 3d array

I'm attempting to use findall to get an index of which elements of one 1d array are greater than those of a second 1d array, and then use those indexes to set corresponding values of a third 1d array to 0.
MWE:
# create 3d array
a, b = [3;2;2], [4;3;2];
c = transpose(cat(a,b, dims = 2));
d, e = [1;2;3], [2;3;4];
f = transpose(cat(d,e, dims = 2));
g = cat(c, f, dims = 3);
g
2×3×2 Array{Int64,3}:
[:, :, 1] =
3 2 2
4 3 2
[:, :, 2] =
1 2 3
2 3 4
findall.(g[end,:,1] >= g[end-1,:,1])
and use indexes to reset elements of g[end,:,2] such that I end up with
g
2×3×2 Array{Int64,3}:
[:, :, 1] =
3 2 2
4 3 2
[:, :, 2] =
1 2 3
0 0 4
Thx. J
The code below gives the answer you request. You just have the . in the wrong spot. You want to compare the > operation element by element, and then apply findall to the entire resulting array (not element by element).
julia> g[end, findall(g[end,:,1] .> g[end-1,:,1]), 2] .= 0
2-element view(::Array{Int64,3}, 2, [1, 2], 2) with eltype Int64:
0
0
julia> g
2×3×2 Array{Int64,3}:
[:, :, 1] =
3 2 2
4 3 2
[:, :, 2] =
1 2 3
0 0 4
However, I wouldn't try to compile all your data into one big array like that. It would be easier to use three separate 1D array variables than three dimensions in one variable. Again using your variables above:
julia> e[b .> a] .= 0
2-element view(::Array{Int64,1}, [1, 2]) with eltype Int64:
0
0
julia> e
3-element Array{Int64,1}:
0
0
4

Julia, #view to prevent production of a new array

My old friend the 3d array called Pop, I want to remove columns (d2), across all of d3 when the value in the end of d1 == 1, so I have this code:
Pop[end, :, 1] .!=1
I thought adding #view in front of this would write the changes back to Pop, outwith producing an additional copy in memory. This code works fine
#view(Pop[ :, Pop[end, :, 1] .!=1, :])
but it does not alter the original 3d array called Pop. I could do
Pop = #view(Pop[ :, Pop[end, :, 1] .!=1, :])
but I believe this will create another copy in memory which I'm trying to avoid. What simple syntax have I missed? Thx. J
#view does not modify the size of the original array, it provides a "view" into it, (e.g., omitting some columns in your case). I don't think there is anything wrong with
Pop = view(Pop, :, Pop[end, :, 1] .≠ 1, :)
since now Pop is a view into your old, full Pop, but it behaves like an array, so you can modify its entries, e.g., you could then do things like
julia> using Random # using a fixed seed for reproducibility
julia> Random.seed!(0) ;
julia> Pop = rand(1:5, (2,4,2)) # original Pop
2×4×2 Array{Int64,3}:
[:, :, 1] =
4 3 5 5
1 1 3 5
[:, :, 2] =
2 2 3 1
2 5 1 1
julia> Pop[end,:,1] .≠ 1 # columns to keep
4-element BitArray{1}:
0
0
1
1
julia> Pop = view(Pop, :, Pop[end, :, 1] .≠ 1, :) # make it a view
2×2×2 view(::Array{Int64,3}, :, [3, 4], :) with eltype Int64:
[:, :, 1] =
5 5
3 5
[:, :, 2] =
3 1
1 1
julia> Pop[end,:,1] .= 1 ; # use your new view to manipulate data
julia> Pop # view of the modified Pop
2×2×2 view(::Array{Int64,3}, :, [3, 4], :) with eltype Int64:
[:, :, 1] =
5 5
1 1
[:, :, 2] =
3 1
1 1
julia> Pop.parent # original full Pop (now Pop.parent) has been modified
2×4×2 Array{Int64,3}:
[:, :, 1] =
4 3 5 5
1 1 1 1
[:, :, 2] =
2 2 3 1
2 5 1 1

View on Julia array using sliding window

What is the most efficient way to create a view on array using, for example, sliding window=2
Let's say we have:
x = collect(1:1:6)
# 1 2 3 4 5 6
And I want to create a view like this:
# 1 2
# 2 3
# 3 4
# 4 5
# 5 6
So far I found only this option, but not sure if it's an optimal one:
y = Array{Float32, 2}(undef, nslides, window)
#inbounds for i in 1:window
y[:, i] = #view x[i:end-(window-i)]
end
One solution with a package (well, with my package) is this:
julia> using Tullio
julia> x = 1:6; window = 2;
julia> #tullio y[r,c] := x[r+c-1] (c in 1:window)
5×2 Matrix{Int64}:
1 2
2 3
3 4
4 5
5 6
The one liner is:
view.(Ref(x), (:).(1:length(x)-1,2:length(x)))
Testing:
julia> x=collect(1:6);
julia> view.(Ref(x), (:).(1:length(x)-1,2:length(x)))
5-element Array{SubArray{Int64,1,Array{Int64,1},Tuple{UnitRange{Int64}},true},1}:
[1, 2]
[2, 3]
[3, 4]
[4, 5]
[5, 6]
Explanation:
creation of views is vectorized by the dot operator .
we do not want to vectorize on elements of x so use Ref(x) instead
(:) is just a shorter form for UnitRange and again we use the dot operator . to vectorize
I used 2 as the Window size but of course you can write view.(Ref(x), (:).(1:length(x)-(window-1),window:length(x)))
EDIT:
If you want rather a library function this would work for you:
julia> using ImageFiltering
julia> mapwindow(collect, x, 0:1,border=Inner())
5-element OffsetArray(::Array{Array{Int64,1},1}, 1:5) with eltype Array{Int64,1} with indices 1:5:
[1, 2]
[2, 3]
[3, 4]
[4, 5]
[5, 6]
Of course you could put them the function that you want to run on the sliding window rather than just collect.

How to remove duplicates from a numpy array with multiple dimensions

Lets say I have the following array:
board = np.random.randint(1, 9, size=(2, 5))
How do I remove duplicates from each element in the array
e.g.
[[6 1 2 8 4]
[8 3 2 3 6]]
So here there are two 3s, and I want one of those to be deleted, how can I perform such an action?
Given your example, it seems that you don't want repetition relatively to rows. You may be interested in numpy.random.choice and try something like this:
import numpy as np
nb_lines = 2
nb_columns = 5
min_value = 1
max_value = 9
range_value = max_value-min_value
# The number of columns should be <= than the integer range to have a solution
assert(range_value+1 >= nb_columns)
board = min_value + np.array([
np.random.choice(np.arange(range_value+1), nb_columns, replace=False)
for l in range(nb_lines)
])
print(board)
Output:
% python3 script.py
[[7 4 6 3 1]
[2 8 6 4 3]]

What is the mathematical explanation of adding 2d Array and 1d Array?

I can't seem to replicate this numpy arithmetic. I'm using Julia, but want to know the mathematical explanation for this code. It seems to break what I know about Linear Algebra.
X = np.arange(-5, 5, 0.2).reshape(-1, 1)
X.shape ## (50, 1)
test = np.sum(X**2, 1).reshape(-1, 1) + np.sum(X**2, 1)
test.shape ## (50, 50)
In Julia, I would write
X = reshape(collect(range(-5, stop=5, length=N)), :, 1);
size(X) ## (50, 1)
test = sum(X.^2, dims=2) + vec(sum(X.^2, dims=2));
size(test) ## (50, 1)
I'm trying to think how a 50x50 matrix would be the result of adding two vectors? I know numpy uses a lot of broadcasting under the hood, but it doesn't seem clear to me what this is doing.
What is the mathematical notation or Julia equivalent for what numpy is doing here?
Your are doing a lot of stuff that really obscures your point, which, I believe, concerns how to add arrays of different shapes.
Python:
In [21]: x = np.random.rand(5, 1)
In [22]: x.shape
Out[22]: (5, 1)
In [23]: y = np.random.rand(1, 4)
In [24]: y.shape
Out[24]: (1, 4)
In [25]: (x + y).shape
Out[25]: (5, 4)
Julia:
julia> x = rand(5);
julia> y = rand(1, 4);
julia> x + y
ERROR: DimensionMismatch("dimensions must match")
julia> x .+ y
5×4 Array{Float64,2}:
1.95779 1.31897 1.23345 1.32423
1.78126 1.14244 1.05692 1.14771
1.08306 0.444243 0.35872 0.449509
1.69756 1.05874 0.97322 1.06401
1.18661 0.547789 0.462265 0.553054
julia> size(x .+ y)
(5, 4)
As you can tell, Python broadcasts arrays by default, while Julia requires that you specifically ask for it, by using the dot operator, ..
It is exactly because it does not make sense to add two arrays of different shapes, that Julia does not broadcast by default. Similarly, with multiplication, * and .* differ:
julia> A = [1 2; 3 4]
2×2 Array{Int64,2}:
1 2
3 4
julia> B = [4 5; 6 7]
2×2 Array{Int64,2}:
4 5
6 7
julia> A * B
2×2 Array{Int64,2}:
16 19
36 43
julia> A .* B
2×2 Array{Int64,2}:
4 10
18 28
The ordinary * is matrix multiplication, while the latter is elementwise array multiplication.
Another example:
julia> A = [1 2 3; 4 5 6]
2×3 Array{Int64,2}:
1 2 3
4 5 6
julia> b = [7, 8]
2-element Array{Int64,1}:
7
8
julia> A * b
ERROR: DimensionMismatch("matrix A has dimensions (2,3), vector B has length 2")
julia> A .* b
2×3 Array{Int64,2}:
7 14 21
32 40 48

Resources