Is argmax working properly for an array of Strings? - julia

I'm using the function Argmax to get the string with max size in an array of Strings, and when the string contains a repetition of a single character, the result get weird.
For example:
x = ["ABC", "AAAA"]
argmax(x) # 1
# The return of argmax is 1, is that correct ?
x = ["ABC", "AAAABBBBCCCCDDDD"]
argmax(x) # = 1
x = ["ABC", "AAAABBBBCCCCDDDD", "ABCD"]
argmax(x) # = 3

The strings are compared lexicographically here, not by their length.
For this reason "ABC" is considered to be greater than "AAAA", and so the maximum element in the array ["ABC", "AAAA"] is indeed at index 1.
If your goal is to compare the strings by length, you could apply the length function to each of the strings in the array and then use argmax. For example:
julia> x = ["ABC", "AAAABBBBCCCCDDDD", "ABCD"]
julia> argmax(length.(x))
2

Related

How to count minimum on multiple variables in Julia

I want to plot correct y-axis limits. So, require to count the maximum y and minimum y.
y1=[2 3 4]
y2=[7 5 6]
...
m = minimum(y1)
m = minimum(m, minimum(y2))
error message
ERROR: MethodError: objects of type Int64 are not callable
Maybe you forgot to use an operator such as *, ^, %, / etc. ?
Stacktrace:
[1] mapreduce_first(f::Int64, op::Function, x::Int64)
# Base ./reduce.jl:419
[2] mapreduce(f::Int64, op::Function, a::Int64)
# Base ./reduce.jl:446
[3] minimum(f::Int64, a::Int64; kw::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
# Base ./reduce.jl:725
[4] minimum(f::Int64, a::Int64)
# Base ./reduce.jl:725
[5] top-level scope
# REPL[107]:1
Previous code is just a simplified code, in my case, it require parse and get from a loop the pseudo code like:
x_data, y_data, names, y_min, y_max = [], [], [], 100, 0
for filename in *.csv
df = parse_csv(filename) # df is a dataframe
push!(names, filename)
d = df.value
y_min = minimum(y_min, d)
....
end
# plot all file by the y_min, y_max
i=1
for d in y_data;
lineplot(x_data, d, ylims=(y_min,y_max), name=names[i])
i += 1
end
Here are two solutions:
First. This is simple, just take the minimums of the minimums, etc.
julia> min(minimum(y1), minimum(y2))
2
julia> max(maximum(y1), maximum(y2))
7
Second solution. This iterates over each pair of values from y1 and y2, takes the minimum/maximum of each pair, and then finds the minimum of those again.
julia> minimum(minimum, zip(y1, y2))
2
julia> maximum(maximum, zip(y1, y2))
7
Here's a third one:
julia> min(y1..., y2...)
2
julia> max(y1..., y2...)
7
Elegant, but splatting of vectors is often inefficient in terms of performance.
The problem is that you don't know the difference between the min function and the minimum function (or you're unaware of the min function):
minimum(itr; [init])
Returns the smallest element in a collection.
So it gets a collection (E.g., Array) and returns the minimum of it.
min(x, y, ...)
Return the minimum of the arguments.
This one gets indefinite arguments and returns the minimum of them! It can't apply min on the x if the x is a container by itself!
julia> min(2, 3)
2
julia> min([2, 3])
ERROR: MethodError: no method matching min(::Vector{Int64})
On the other hand, for the minimum function:
julia> minimum(2, 3)
ERROR: MethodError: objects of type Int64 are not callable
julia> minimum([2, 3])
2
So I wanted to explain these to you to understand your code's meaning better.
We have this minimum(m, minimum(y2)) expression in your code block. This is literally the same as minimum(2, 5). So you're not passing containers to the function, leading to an error! For this, you should choose min instead:
julia> m = min(m, minimum(y2))
2
Or we can wrap m and minimum(y2) in a container and use the minimum function to achieve the overall min:
julia> m = minimum([m, minimum(y2)])
2
If you follow the explanation, you can absolutely understand the following:
julia> min(m, minimum(y2)) == minimum([m, minimum(y2)]) == min(m, min(y2...))
true

How to delete an element from a list in Julia?

v = range(1e10, -1e10, step=-1e8) # velocities [cm/s]
deleteat!(v, findall(x->x==0,v))
I want to delete the value 0 from v. Following this tutorial, I tried deleteat! but I get the error
MethodError: no method matching deleteat!(::StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}, Int64}, ::Vector{Int64})
What am I missing here?
Notice the type that is returned by the function range.
typeof(range(1e10, -1e10, step=-1e8))
The above yields to
StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}, Int64}
Calling the help function for the function deleteat!.
? deleteat!()
deleteat!(a::Vector, inds)
Remove the items at the indices given by inds, and return the > modified a. Subsequent items are shifted to fill the resulting gap.
inds can be either an iterator or a collection of sorted and > unique integer indices, or a boolean vector of the same length as a with true indicating entries to delete.
We can convert the returned type of range using collect. Try the following code.
v = collect(range(1e10, -1e10, step=-1e8))
deleteat!(v,findall(x->x==0,v))
Notice that we can shorten x->x==0 to iszero which yields to
v = collect(range(1e10, -1e10, step=-1e8))
deleteat!(v,findall(iszero,v))
Use filter! or filter:
julia> filter!(!=(0), [1,0,2,0,4])
3-element Vector{Int64}:
1
2
4
In case of a range you can collect it or use:
julia> filter(!=(0), range(2, -2, step=-1))
4-element Vector{Int64}:
2
1
-1
-2
However for big ranges you might just not want to materialize them to save the memory footprint. In that case you could use:
(x for x in range(2, -2, step=-1) if x !== 0)
To see what is being generated you need to collect it:
julia> collect(x for x in range(2, -2, step=-1) if x !== 0)
4-element Vector{Int64}:
2
1
-1
-2

Check for equivalence of string and symbol

I have a vector of symbols a_sym of length N_sym and a vector of strings a_str of length N_str
a_sym contains symbols at each index, such as :H₅B₂O₆⁻
a_str contains strings at each index, such as H₅B₂O₆⁻
I would like to check for equivalence of a_sym and a_str to see which index the equivalence occurs in each vector.
I have tried to implement a loop to check these two vectors:
E = zeros(Int64,N_sym)
for i in 1:N_str
for ii in 1:N_sym
if a_sym[ii] == a_str[i]
E[ii] = i
end
end
end
Where E is my attempt to map equivalent indices, but my loop never detects the strings to be equivalent. How could this be remedied? (and perhaps simplified?)
For example:
a_sym = [:H₃BO₃ ,:H₄BO₄⁻ ,:Li⁺ ,:H₅B₂O₆⁻, :H₄B₃O₇⁻]
where N_sym would be 5, and:
a_str = ["O⁻","H₃BO₃","H₄BO₄⁻","H₅B₂O₆⁻","H₄B₃O₇⁻","H₃B₃O₆"]
where N_str would be 6. I require the loop to check both vectors and map the indices when there is equivalnce, for instance the index of H₃BO₃ in a_sym would be 1, and its index in a_str would be 2.
I expect a vector E = [2, 3, 0, 4, 5] which is filled with the indices of a_str, and 0 if a_str does not contain a match for a_sym
Symbols and strings are never equivalent:
julia> :a == "a"
false
So you have to convert either to the other first. I would write your function as follows using the builtin findfirst:
julia> E = [findfirst(==(String(b)), a_str) for b in a_sym]
5-element Array{Union{Nothing, Int64},1}:
2
3
nothing
4
5
(Although, as Przemislaw notes, converting the strings to symbols would likely be more efficient.)
nothing is what findfirst returns if it does not find anything. You can convert this to a default by broadcasting something:
julia> something.(E, 0)
5-element Array{Int64,1}:
2
3
0
4
5

How to convert a multidimensional array to/from vector of vector of ... vector in julia

Is there a method in julia to convert a multidimensional array to a vector of vector and so on, and vice versa? It is OK to define a method for a fix number of dimensions. But how about a method for arbitrary dims?
julia> s = (1,2,3)
julia> a = reshape(1:prod(s), s)
1×2×3 Base.ReshapedArray{Int64,3,UnitRange{Int64},Tuple{}}:
[:, :, 1] =
1 2
[:, :, 2] =
3 4
[:, :, 3] =
5 6
julia> b = [[[a[i,j,k] for i=1:s[1]] for j=1:s[2]] for k=1:s[3]]
3-element Array{Array{Array{Int64,1},1},1}:
Array{Int64,1}[[1], [2]]
Array{Int64,1}[[3], [4]]
Array{Int64,1}[[5], [6]]
julia> unstack(a) == b
ERROR: UndefVarError: unstack not defined
RecursiveArrayTools.jl can help with this kind of work.
recs = [rand(8) for i in 1:10]
A = VectorOfArray(recs)
A[i] # Returns the ith array in the vector of arrays
A[j,i] # Returns the jth component in the ith array
A[j1,...,jN,i] # Returns the (j1,...,jN) component of the ith array
So it acts like the matrix without ever building the matrix, which is a good way to save allocations if you tend to act on the columns (which are the separate arrays). It also has a fast conversion to a contiguous array via the indexing fallback (honestly, I tried to create a faster one but the fallback worked better than I could make it):
arr = convert(Array,A)
Converting back would require allocating of course
VA = VectorOfArray([A[:,i] for i in size(A,2)])

Julia : construct Dictionary with tuple values

Is there a possibility to construct dictionary with tuple values in Julia?
I tried
dict = Dict{Int64, (Int64, Int64)}()
dict = Dict{Int64, Tuple(Int64, Int64)}()
I also tried inserting tuple values but I was able to change them after so they were not tuples.
Any idea?
Edit:
parallel_check = Dict{Any, (Any, Any)}()
for i in 1:10
dict[i] = (i+41, i+41)
end
dict[1][2] = 1 # not able to change this way, setindex error!
dict[1] = (3, 5) # this is acceptable. why?
The syntax for tuple types (i.e. the types of tuples) changed from (Int64,Int64) in version 0.3 and earlier to Tuple{Int64,Int64} in 0.4. Note the curly braces, not parens around Int64,Int64. You can also discover this at the REPL by applying the typeof function to an example tuple:
julia> typeof((1,2))
Tuple{Int64,Int64}
So you can construct the dictionary you want like this:
julia> dict = Dict{Int64,Tuple{Int64,Int64}}()
Dict{Int64,Tuple{Int64,Int64}} with 0 entries
julia> dict[1] = (2,3)
(2,3)
julia> dict[2.0] = (3.0,4)
(3.0,4)
julia> dict
Dict{Int64,Tuple{Int64,Int64}} with 2 entries:
2 => (3,4)
1 => (2,3)
The other part of your question is unrelated, but I'll answer it here anyway: tuples are immutable – you cannot change one of the elements in a tuple. Dictionaries, on the other hand are mutable, so you can assign an entirely new tuple value to a slot in a dictionary. In other words, when you write dict[1] = (3,5) you are assigning into dict, which is ok, but when you write dict[1][2] = 1 you are assigning into the tuple at position 1 in dict which is not ok.

Resources