VinodV
June 28, 2024, 8:23am
1
I have a piece of code in python
def average_by(lst, fn = lambda x: x):
return sum(map(fn, lst), 0.0) / len(lst)
average_by([{ 'n': 4 }, { 'n': 2 }, { 'n': 8 }, { 'n': 6 }], lambda x: x['n'])
# 5.0
What would be the equivalent julia code?
function average_by(lst; fn = x -> x)
return sum(map(fn, lst)) / length(lst)
end
a = Dict("1" => 2,"2" => 3)
average_by(a,fn = x -> x["n"])
Error : ERROR: map is not defined on dictionaries
nsajko
June 28, 2024, 8:41am
2
Give a reproducible example.
Qfl3x
June 28, 2024, 8:42am
3
It works though; Make sure lst
is a Vector (List) of dictionaries, and not a dictionary itself (Same as the Python code):
julia> lst = [Dict("n" => 4), Dict("n" => 2), Dict("n" => 8), Dict("n" => 6)]
4-element Vector{Dict{String, Int64}}:
Dict("n" => 4)
Dict("n" => 2)
Dict("n" => 8)
Dict("n" => 6)
julia> function average_by(lst; fn = x -> x)
return sum(map(fn, lst)) / length(lst)
end
average_by (generic function with 1 method)
julia> average_by(lst; fn = x -> x["n"])
5.0
2 Likes
You could also try mean(f, itr)
(available with using Statistics
).
For example, mean(x -> x^2, [1,2,3])
.
1 Like
VinodV
June 28, 2024, 9:46am
5
ericphanson:
mean(x → x^2, [1,2,3])
julia> a = Dict("1" => 2,"2" => 3)
Dict{String, Int64} with 2 entries:
"1" => 2
"2" => 3
julia> mean(x -> x["n"], a)
ERROR: MethodError: no method matching getindex(::Pair{String, Int64}, ::String)
julia> st = [Dict("n" => 4), Dict("n" => 2), Dict("n" => 8), Dict("n" => 6)]
4-element Vector{Dict{String, Int64}}:
Dict("n" => 4)
Dict("n" => 2)
Dict("n" => 8)
Dict("n" => 6)
julia> mean(x -> x["n"], st)
5.0
are those the results you expected, or is there something you want to be able to do but are having trouble?
VinodV
June 28, 2024, 9:53am
7
I should have defined argument as list of dictionaries. Whatever you told is one of the solutions.
1 Like
VinodV
June 28, 2024, 10:49am
9
[quote="Qfl3x, post:3, topic:116341"]
`lst = [Dict("n" => 4), Dict("n" => 2), Dict("n" => 8), Dict("n" => 6)]`
[/quote]
julia> a = [Dict("n" => 4), Dict("n" => 2), Dict("n" => 8), Dict("n" => 6)]
4-element Vector{Dict{String, Int64}}:
Dict("n" => 4)
Dict("n" => 2)
Dict("n" => 8)
Dict("n" => 6)
julia> average_by(values(a))
ERROR: MethodError: no method matching +(::Dict{String, Int64}, ::Dict{String, Int64})
julia> a = Dict("1" => 2,"2" => 3)
Dict{String, Int64} with 2 entries:
"1" => 2
"2" => 3
julia> average_by(values(a))
2.5
Your code already works, but I would use Iterators.map
to handle arbitrary iterables.
Another issue is what should happen if your iterator is empty. The default implementation errors, which is probably the most reasonable thing to do. But you may want to return NaN
instead.
DNF
June 28, 2024, 6:17pm
12
This will first map over the vector, creating an unnecessary intermediate collection (particularly unnecessary for the default map function). It’s better to use the sum
function with the mapping argument, like this:
sum(fn, lst) / length(lst)
As for the function signature, two points
fn
isn’t a kwarg in the Python code, so the semicolon should be a comma.
There’s actually a function in Base that is equal to x -> x
, called identity
. It doesn’t make any difference here, but it’s common to use it as a ‘default function’.
3 Likes