The not operator (!) isn't working with array broadcasting - julia

I am wondering why the not operator isn't working with array broadcasting/element-wise operations. For example, if I enter
x = Array{Any}([1,2,3,4,missing,6])
!ismissing.(x)
I get an error ERROR: MethodError: no method matching !(::BitArray{1})
but if I try ismissing.(x) it works fine,
ismissing.(x)
#out > 6-element BitArray{1}: 0 0 0 0 1 0
and typing without the broadcasting works as a whole as well (one output for the entire array)
!ismissing(x)
#out > True
So I am wondering if there is a possibility to get a similar result as using the "!" operator.

You need to also broadcast ! to each element:
julia> x = Array{Any}([1,2,3,4,missing,6]);
julia> .!(ismissing.(x)) # the . comes before operators (!, +, -, etc)
6-element BitVector:
1
1
1
1
0
1
For this specific case you can instead negate the function before broadcasting:
julia> (!ismissing).(x)
6-element BitVector:
1
1
1
1
0
1

Related

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

Creating subset using or statement

I have a data frame with 6 columns and thousands of rows containing share transactions. I want to identify rows with bad price data. The following function gives me a subset with the rows with good price data:
function in_price_range(df)
price_good = subset(df, :UnitPrice => X-> (trough_share_price .<= X .<= peak_share_price), skipmissing=true)
return price_good
end
For a subset for bad data I tried:
function out_price_range(df)
price_discrepancy = subset(df, :UnitPrice => X-> (X .< trough_share_price || X .> peak_share_price), skipmissing=true)
return price_discrepancy
end
However, that givers error TypeError: non-boolean (BitVector) used in boolean context
I tried .|| rather than || but that then gives error: syntax: "|" is not a unary operator
How do I fix the code?
In Julia, || is
help?> ||
search: ||
x || y
Short-circuiting boolean OR.
The short-circuiting part meaning, that if x is true, || will not even bother to evaluate y. In other words, this will make a branch in the code. For example:
julia> 5 < 7 || print("This is unreachable")
true
This is great if you want to write code that is efficient for a case like
if something_easy_to_evaluate || something_costly_to_evaluate
# Do something
end
In other words, this is control flow! Obviously, this cannot be broadcasted. For that, what you want is the regular or operator |, which you can broadcast with .|. So for example:
julia> a = rand(3) .< 0.5
3-element BitVector:
1
0
0
julia> b = rand(3) .< 0.5
3-element BitVector:
0
1
0
julia> a .|| b
ERROR: syntax: "|" is not a unary operator
Stacktrace:
[1] top-level scope
# none:1
julia> a .| b
3-element BitVector:
1
1
0
The same applies to && vs &; the former is only used for control-flow, the latter is normal bitwise and.

Julia throws undefined error when the variable is defined

I have this simple while loop that uses i = 1 as an index.
global i = 1
n = rows
while i <= n
if prod(isa.(collect((y)[i,:]),Number))==0
delete!(y,i)
x_axis = x_axis[1:end .!= i]
n -= 1
end
i += 1
end
but I'm getting this error:
UndefVarError: i not defined
top-level scope#Local: 23
I even made my i global as per the suggestion on some similar questions on SO but the error persists. I am running this on Pluto.jl so maybe it could be an environment issue.
Firstly, note that if you use Julia v1.5+ then you don't need to make i a global (example below with current stable version v1.5.4):
julia> i = 1
1
julia> while i < 5
println(i)
i += 1 # <----- this works in Julia v1.5+
end
1
2
3
4
However, it seems you are using Julia v1.4 of older, in which case I think Logan Kilpatrick gave you the answer: You need to make i a global from inside the while loop's scope. As Logan mentioned, try adding global where you increment i, like in this example from the while function's docs:
julia> i = 1 ;
julia> while i < 5
println(i)
global i += 1 # <------------ Try this!
end
1
2
3
4
Note also that you don't need to specify it's a global if your while loop is inside a function, as in
julia> function foo(istart)
i = istart
while i < 5
println(i)
i += 1 # <-- 'global' not needed inside a function!
end
end
foo (generic function with 1 method)
julia> foo(1)
1
2
3
4
You have hitted the "ambiguous soft scope case".
In short: the assignment of a local variable inside a (soft) local scope depends if the code is inside a "REPL context"
or not.
For "REPL context" I mean the REPL and all environments that behaves as the REPL in this case, for example
Jupyter:
julia> i = 0
julia> while i < 3
i += 1
#info i
end
[ Info: 1
[ Info: 2
[ Info: 3
Instead code from non interactive context like file, eval and Pluto acts like that:
julia> code = """
i = 0
while i < 3
i += 1
#info i
end
"""
julia> include_string(Main, code)
┌ Warning: Assignment to `i` in soft scope is ambiguous because a global variable by the same name exists: `i` will be treated as a new local. Disambiguate by using `local i` to suppress this warning or `global i` to assign to the existing global variable.
└ # string:3
ERROR: LoadError: UndefVarError: i not defined
All of this has been designed to ensure both the convenience of REPL usage and to avoid unwanted side effects of using julia on a large scale.
Full details here.
To fix the problem you may be use global as already suggested or enclose your code inside a function.
Pluto implicitly wraps a cell into a function, see https://github.com/fonsp/Pluto.jl/pull/720, therefore the global annotation or explicit wrapping into a function should not be required.
Putting the following into a Pluto cells works for me:
begin
i = 1
n = 100
while i<=n
if i % 2 == 0
n -= 1
end
i += 1
end
end
The implicit function wrapping is disabled when macros are used inside a cell (which prevents Pluto to gather reactivity information), therefore the following does not work in Pluto due to the Julia scoping rules:
begin
i = 1
n = 100
while i<=n
if i % 2 == 0
n -= 1
end
#show i += 1
end
end
Throws:
UndefVarError: i not defined
top-level scope#Local: 5[inlined]
top-level scope#none:0

Two consecutive identical calls to echo have different output in Vim function

Original post on Vi and Vim Beta, which has had one interesting answer, but not much attention so far. I am sorry for the crossposting and I will ask for the original to be closed/deleted.
Given the following function in the .vimrc file,
fu! MyFun(count)
echo a:count
echo a:count
if a:count > 0
normal ,
call MyFun(a:count - 1)
endif
endf
calling :call MyFun(3) generates the following output.
3
3
2
2
1
1
0
0
However, if I define the mapping nn , :<C-U>execute "call MyFun(" . v:count . ")"<CR>, then the call to :call MyFun(3) generates
3
0
2
0
1
0
0
0
I do understand that the mapping of , makes the MyFun function call itself twice (if a:count > 0), however I cannot understand how this can cause a different result of the two successive calls to echo a:count.
The problem is all about screen redraw (see :h echo-redraw) in Vim.
Changing echo to echom still produces the same (broken) screen output (3 0 2 0 1 0 0 0), but :mess reveals what is hidden: 3 3 0 0 2 2 0 0 1 1 0 0 0 0.

Find indices of non-zero elements from [1,2,0,0,4,0] in julia and create an Arrray with them

I know there is a function that does this, for example:
A = [1,2,0,0,4,0]
find(A)
3-element Array{Int64,1}:
1
2
5
I am trying to do it on my own way, however, I am stuck here
for i=1:endof(A)
if A[i] != 0
[]
end
end
Thanks in advance.
Here's one alternative:
function myfind(c)
a = similar(c, Int)
count = 1
#inbounds for i in eachindex(c)
a[count] = i
count += (c[i] != zero(eltype(c)))
end
return resize!(a, count-1)
end
It actually outperformed find for all the cases I tested, though for the very small example vector you posted, the difference was negligible. There is perhaps some performance advantage to avoiding the branch and dynamically growing the index array.
I have notice that the question is really confusion (because is poorly formulated, sorry about that). Therefore, there are two possible answers: one is [1,2,4]which is an array with the non-zero elements; the other is [1,2,5] which is an array of the indices of the non-zero elements.
Let´s begin with the first option
A = [1,2,0,0,4,0]
B = []
for i=1:endof(A)
if A[i] != 0
push!(B,i)
end
end
println(B)
The output is Any[1,2,5]
However, the type is not the one I wanted. Using typeof(B) it shows Array{Any,1} so I added this code:
B = Array{Int64}(B)
println(B)
typeof(B)
And the result is the desired
[1,2,5]
Array{Int64,1}
To improve its efficiency, following with the recommendations in the comments, I have specified the type of B with the eltype() function before the loop as follows:
A1 = [1,2,0,0,4,0] #The default type is Array{Int64,1}
B1 = eltype(A1)[] #Define the type of the 0 element array B with the type of A
#B1 = eltype(typeof(A))[] this is also valid
for i=1:endof(A1)
if A1[i] != 0
push!(B1::Array{Int64,1},i::Int64)
end
end
println(B1)
typeof(B1)
Then, the output is again the desired
[1,2,5]
Array{Int64,1}
The simplest way of doing this is using the function find(). However, since I´m a beginner, I wanted to do it in another way. However, there is another alternative provided by #DNF that outperform find() for the cases he has tested it (see below answers).
The second option, which creates an output matrix with the non-zero elements has been provided by other users (#Harrison Grodin and #P i) in this discussion.
Thanks all of you for the help!
You have a few options here.
Using the strategy you started with, you can use push! inside the for loop.
julia> B = Int[]
0-element Array{Int64,1}
julia> for element = A
if element != 0
push!(B, element)
end
end
julia> B
3-element Array{Int64,1}:
1
2
4
You can also opt to use short-circuit evaluation.
julia> for element = A
element != 0 && push!(B, element)
end
julia> for element = A
element == 0 || push!(B, element)
end
Both filter and list comprehensions are valid, as well!
julia> B = [element for element = A if element != 0]
3-element Array{Int64,1}:
1
2
4
julia> filter(n -> n != 0, A)
3-element Array{Int64,1}:
1
2
4
Edit: Thanks to the OP's comment, I have realized that the desired result is the indices of the nonzero elements, not the elements themselves. This can be achieved simply with the following. :)
julia> find(A)
3-element Array{Int64,1}:
1
2
5
Just because I don't see a simple solution posted here, this is approach I took to the same problem. I think it is the simplest and most elegant solution. Hope this closes the thread!
julia> A = [1,2,0,0,4,0];
julia> findall(!iszero,A)
3-element Vector{Int64}:
1
2
5

Resources