DimensionMismatch or MethodError when broadcasting a function over 2 arrays - julia

I tried to create a function that (kind of) behaves like vlookup() from excel, with the following arguments
function foo(val::Float64, table::AbstractArray, col::Int64)
return table[findfirst(x->val<=x, table[:,1]), 2]
end
Let's say I have a table array that looks something like
arr = 4x2 Matrix{Any}:
0.26 | "string1"
0.60 | "string2"
0.73 | "string3"
1.00 | "string4"
When I run this function with just one Float64 as argument, it works:
julia> foo(rand(), arr, 2)
"string3"
but when applied over an array of Float64 using dot broadcasting, I get the following errors
julia> foo.(rand(3), arr, 2)
DimensionMismatch("arrays could not be broadcast to a common size; got a dimension with lengths 3 and 4")
julia> foo.(rand(4), arr, 2)
MethodError: no method matching foo(::Float64, ::Float64, ::Int64)
How can I fix this issue?

You can use Ref to treat values as scalars when broadcasting:
julia> arr = [rand(4);; ["a", "b", "c", "d"]]
4×2 Matrix{Any}:
0.866715 "a"
0.0901788 "b"
0.385088 "c"
0.404498 "d"
julia> foo.(rand(3), Ref(arr), Ref(2))
3-element Vector{String}:
"a"
"a"
"a"

Related

How to append to an empty list in Julia?

I want to create an empty lsit and gardually fill that out with tuples. I've tried the following and each returns an error. My question is: how to append or add and element to an empty array?
My try:
A = []
A.append((2,5)) # return Error type Array has no field append
append(A, (2,5)) # ERROR: UndefVarError: append not defined
B = Vector{Tuple{String, String}}
# same error occues
You do not actually want to append, you want to push elements into your vector. To do that use the function push! (the trailing ! indicates that the function modifies one of its input arguments. It's a naming convention only, the ! doesn't do anything).
I would also recommend creating a typed vector instead of A = [], which is a Vector{Any} with poor performance.
julia> A = Tuple{Int, Int}[]
Tuple{Int64, Int64}[]
julia> push!(A, (2,3))
1-element Vector{Tuple{Int64, Int64}}:
(2, 3)
julia> push!(A, (11,3))
2-element Vector{Tuple{Int64, Int64}}:
(2, 3)
(11, 3)
For the vector of string tuples, do this:
julia> B = Tuple{String, String}[]
Tuple{String, String}[]
julia> push!(B, ("hi", "bye"))
1-element Vector{Tuple{String, String}}:
("hi", "bye")
This line in your code is wrong, btw:
B = Vector{Tuple{String, String}}
It does not create a vector, but a type variable. To create an instance you can write e.g. one of these:
B = Tuple{String, String}[]
B = Vector{Tuple{String,String}}() # <- parens necessary to construct an instance
It can also be convenient to use the NTuple notation:
julia> NTuple{2, String} === Tuple{String, String}
true
julia> NTuple{3, String} === Tuple{String, String, String}
true

how to perform reduce(hcat, <Vector{Vector}>) by TensorCast.jl?

Is it possible to do the following via TensorCast.jl as well?
#This is an object of type Vector{Vector{Float64}}
julia> a = [rand(5) for i=1:5];
julia> reduce(hcat, a)
5×5 Matrix{Float64}:
0.0678613 0.266194 0.183221 0.485462 0.873282
0.735101 0.925276 0.956102 0.333281 0.885147
0.323555 0.74204 0.135538 0.26123 0.261068
0.81847 0.917006 0.0118195 0.295497 0.712431
0.420139 0.0197552 0.0617039 0.157477 0.500931
I tried something super silly, I guess (😰):
julia> #reduce b := hcat(a)
ERROR: MethodError: no method matching guesstarget(::Nothing, ::Vector{Any}, ::Vector{Any})
Closest candidates are:
guesstarget(::Expr, ::Any, ::Any) at C:\Users\Shayan\.julia\packages\TensorCast\mQB8h\src\macro.jl:1330
Just do:
#cast b[j,i] := a[i][j]
Example:
julia> a = [rand(3) for _ in 1:2]
2-element Vector{Vector{Float64}}:
[0.20012490537057803, 0.0365551498875093, 0.02494737196890595]
[0.6563493855249903, 0.181706254856571, 0.29210798163726615]
julia> #cast b[j,i] := a[i][j]
3×2 lazystack(::Vector{Vector{Float64}}) with eltype Float64:
0.200125 0.656349
0.0365551 0.181706
0.0249474 0.292108
julia> (#cast b[j,i] := a[i][j]) == reduce(hcat, a)
true
Exaplanation
The variables i and j are used by #cast to define the matrix layout - we have a Vector of Vectors and hence on the right side you can see a[i][j], := defines a new Matrix and b[j,i] says where the corresponding i and j elements should go. Note that you do not declare i and j variables - this is handled by the macro.
Finally, note that if you want the result to be materialized to an actual matrix (rather than a lazystack) you can next do collect(b)

Filtering a dictionary in julia

I want to filter a dictionary using filter() function but I am having trouble with it. What I wish to accomplish is, to return the key for some condition of the value. However I am getting a method error
using Agents: AbstractAgent
# Define types
mutable struct Casualty <: AbstractAgent
id::Int
ts::Int
rescued::Bool
function Casualty(id,ts; rescued = false)
new(id,ts,rescued)
end
end
mutable struct Rescuer <: AbstractAgent
id::Int
actions::Int
dist::Float64
function Rescuer(id; action = rand(1:3) , dist = rand(1)[1])
new(id,action,dist)
end
end
cas1 = Casualty(1,2)
cas2 = Casualty(2,3)
resc1 = Rescuer(3)
agents = Dict(1=> cas1, 2 => cas2, 3 => resc1)
Now to filter
filter((k,v) -> v isa Casualty, agents)
# ERROR: MethodError: no method matching (::var"#22#23")(::Pair{Int64, AbstractAgent})
# what I truly wish to achieve is return the key for some condition of the value
filter((k,v) -> k ? v isa Casualty : "pass", agents)
# ofcourse I am not sure how to "pass" using this format
Any idea how I can achieve this. Thanks
For dictionaries filter gets a key-value pair, so do either (destructuring Pair):
julia> dict = Dict(1=>"a", 2=>"b", 3=>"c")
Dict{Int64, String} with 3 entries:
2 => "b"
3 => "c"
1 => "a"
julia> filter(((k,v),) -> k == 1 || v == "c", dict)
Dict{Int64, String} with 2 entries:
3 => "c"
1 => "a"
or for example (getting Pair as a whole):
julia> filter(p -> first(p) == 1 || last(p) == "c", dict)
Dict{Int64, String} with 2 entries:
3 => "c"
1 => "a"
julia> filter(p -> p[1] == 1 || p[2] == "c", dict)
Dict{Int64, String} with 2 entries:
3 => "c"
1 => "a"
EDIT
Explanation why additional parentheses are needed:
julia> f = (x, y) -> (x, y)
#1 (generic function with 1 method)
julia> g = ((x, y),) -> (x, y)
#3 (generic function with 1 method)
julia> methods(f)
# 1 method for anonymous function "#1":
[1] (::var"#1#2")(x, y) in Main at REPL[1]:1
julia> methods(g)
# 1 method for anonymous function "#3":
[1] (::var"#3#4")(::Any) in Main at REPL[2]:1
julia> f(1, 2)
(1, 2)
julia> f((1, 2))
ERROR: MethodError: no method matching (::var"#1#2")(::Tuple{Int64, Int64})
Closest candidates are:
(::var"#1#2")(::Any, ::Any) at REPL[1]:1
julia> g(1, 2)
ERROR: MethodError: no method matching (::var"#3#4")(::Int64, ::Int64)
Closest candidates are:
(::var"#3#4")(::Any) at REPL[2]:1
julia> g((1, 2))
(1, 2)
As you can see f takes 2 positional argument, while g takes one positional argument that gets destructured (i.e. the assumption is that argument passed to g is iterable and has at least 2 elements).
See also https://docs.julialang.org/en/v1/manual/functions/#Argument-destructuring.
Now comes the tricky part:
julia> h1((x, y)) = (x, y)
h1 (generic function with 1 method)
julia> methods(h1)
# 1 method for generic function "h1":
[1] h1(::Any) in Main at REPL[1]:1
julia> h2 = ((x, y)) -> (x, y)
#1 (generic function with 1 method)
julia> methods(h2)
# 1 method for anonymous function "#1":
[1] (::var"#1#2")(x, y) in Main at REPL[3]:1
In this example h1 is a named function. In this case it is enough to just wrap arguments in extra parentheses to get destructuring behavior. For anonymous functions, because of how Julia parser works an extra , is needed - if you omit it the extra parentheses are ignored.
Now let us check filter docstring:
filter(f, d::AbstractDict)
Return a copy of d, removing elements for which f is false.
The function f is passed key=>value pairs.
As you can see from this docstring f is passed a single argument that is Pair. That is why you need to use either destructuring or define a single argument function and extract its elements inside the function.
The right syntax is:
filter(((k,v),) -> v isa Casualty, agents)
which prints
julia> filter(((k,v),) -> v isa Casualty, agents)
Dict{Int64, AbstractAgent} with 2 entries:
2 => Casualty(2, 3, false)
1 => Casualty(1, 2, false)
About the problem of only getting involved keys... I have no idea beside:
julia> filter(((k,v),) -> v isa Casualty, agents) |> keys
which prints
julia> filter(((k,v),) -> v isa Casualty, agents) |> keys
KeySet for a Dict{Int64, AbstractAgent} with 2 entries. Keys:
2
1

Dictionary element-wise operations in Julia

I would like to broadcast an operation to all values of a dictionary. For an array, I know I can broadcast an element-wise operation using:
julia> b1 = [1, 2, 3]
julia> b1./2
3-element Array{Float64,1}:
0.5
1.0
1.5
What is an efficient way of broadcasting the same operation to all values of a dictionary? Say, for the dictionary
a1 = Dict("A"=>1, "B"=>2)
An iteration protocol is defined for both keys and values of dictionaries, so you can just do, eg:
julia> d = Dict("a"=>1, "b"=>2)
Dict{String,Int64} with 2 entries:
"b" => 2
"a" => 1
julia> values(d).^2
2-element Array{Int64,1}:
4
1
If you want to alter the dictionary in-place, use map!, eg:
julia> map!(x->x^2, values(d))
Base.ValueIterator for a Dict{String,Int64} with 2 entries. Values:
4
1
julia> d
Dict{String,Int64} with 2 entries:
"b" => 4
"a" => 1
However, your function must output a type that can be converted back to the dictionary value type. In my example, I'm squaring Int which yields Int. However, in the question you are dividing by 2, which obviously yields Float64. If the float cannot be converted back to an integer, then you'll get an error.
Note, you can broadcast over keys also, e.g.:
julia> f(x) = "hello mr $(x)"
f (generic function with 1 method)
julia> f.(keys(d))
2-element Array{String,1}:
"hello mr b"
"hello mr a"
but this can not be done in-place, i.e. you can't use map! on keys.
Importantly, note that you should not instantiate the collection. Indeed, this would be inefficient. So avoid constructs like: collect(values(d)) ./ 2.

How to initialize a dictionary in Julia?

When I tried to do:
d = {1:2, 3:10, 6:300, 2:1, 4:5}
I get the error:
syntax: { } vector syntax is discontinued
How to initialize a dictionary in Julia?
The {} syntax has been deprecated in julia for a while now. The way to construct a dict now is:
Given a single iterable argument, constructs a Dict whose key-value pairs are taken from 2-tuples (key,value) generated by the argument.
julia> Dict([("A", 1), ("B", 2)])
Dict{String,Int64} with 2 entries:
"B" => 2
"A" => 1
Alternatively, a sequence of pair arguments may be passed.
julia> Dict("A"=>1, "B"=>2)
Dict{String,Int64} with 2 entries:
"B" => 2
"A" => 1
(as quoted from the documentation, which can be obtained by pressing ? in the terminal to access the "help" mode, and then type Dict)

Resources