Does Mathematica have advanced indexing?
Update: This is now available as the BoolEval package.
I recommend using
L UnitStep[15 - T]
for good performance.
To answer your question about boolean indexing:
When you write L[T > 15] = 0
in MATLAB, T > 15
evaluates to a boolean matrix of 0s and 1s, which can be used as a special selector index in assignments, as you showed (I am writing this for those Mathematica users who don't know this feature of MATLAB).
This feature is not available directly in Mathematica.
However, it is important to know that Mathematica is usually most effective when using it a functional way, i.e. using operations that do not have side effects. Code using heavy assignments is typically not the best/fastest way to approach a problem.
There is a function, Pick
, which is similar (but not identical) to this MATLAB feature. Pick[a, b]
will return those elements of a
for which the corresponding element of b
is True
. Pick[a, b, 1]
allows using 1
instead of True
. a
and b
can be arbitrary dimensional tensors or even ragged lists.
That said, there is no direct equivalent of L[T > 15] = 0
. However, anything that can be accomplished with this syntax in MATLAB should be doable using arithmetic operations between tensors (as in my example). This will buy you the performance advantage of vectorization, but it'll still be a bit slower than direct assignment.
Update:
This is a set of functions to help working in this paradigm:
greatereq[a_, b_] := UnitStep[a - b]
lesseq[a_, b_] := UnitStep[b - a]
greater[a_, b_] := 1 - lesseq[a, b]
less[a_, b_] := 1 - greatereq[a, b]
unequal[a_, b_] := Unitize[a - b]
equal[a_, b_] := 1 - unequal[a, b]
equal[a_, b_, c__] := equal[a, b] equal[b, c]
less[a_, b_, c__] := less[a, b] less[b, c]
greater[a_, b_, c__] := greater[a, b] greater[b, c]
lesseq[a_, b_, c__] := lesseq[a, b] lesseq[b, c]
greatereq[a_, b_, c__] := greatereq[a, b] greatereq[b, c]
unequal[a__] := Times @@ (unequal @@@ Subsets[{a}, {2}])
ClearAll[booleval]
booleval[condition_] :=
Unevaluated[condition] /. {arr_?ArrayQ :> arr, Less -> less, Greater -> greater,
LessEqual -> lesseq, Equal -> equal, Unequal -> unequal,
GreaterEqual -> greatereq, Or -> Composition[Unitize, Plus],
And -> Times, Not -> (1 - # &), True -> 1, False -> 0}
ClearAll[pick]
pick[array_, condition_] :=
Pick[array,
booleval[condition],
1]
booleval
will take a set of (in)equalities over arrays and will evaluate them to obtain an array of Boolean values (0 or 1) while making use of vectorization and keeping packed arrays packed. pick
will extract the part of an array corresponding to 1s. This will work similarly to MATLAB.
Example:
a = Range[10];
pick[a, a > 5 || a^2 <= 10]
(* ==> {1, 2, 3, 6, 7, 8, 9, 10} *)
The advantage is the convenience of using standard notation for inequalities instead of a cryptic UnitStep
.
The OP's example would be written as L booleval[T <= 15]
.
Using a little Mathematica pattern matching, I think you can get similar performance as @Szabolcs's answer while having nice Matlab-style syntax:
replaceWhere[cond_, selectTrue_, selectFalse_] :=
With[{evaluatedCondition = evaluateTensorCondition[cond]},
selectTrue*evaluatedCondition + selectFalse*(1 - evaluatedCondition)]
replaceWhere[cond_, selectTrue_] := replaceWhere[cond, selectTrue, 0]
evaluateTensorCondition[a_ > b_] := UnitStep[a - b]
evaluateTensorCondition[a_ < b_] := UnitStep[b - a]
evaluateTensorCondition[a_ && b_] :=
evaluateTensorCondition[a]*evaluateTensorCondition[b]
evaluateTensorCondition[a_ || b_] :=
UnitStep[evaluateTensorCondition[a]+evaluateTensorCondition[b]-1]
(implementing more comparison and logical operators should be straightforward)
Usage:
replaceWhere[T > 15, L, 0]
returns a new list, where every index that is greater than 15 in T
is set to 0 , the other elements are the same as L
. And it should have similar performance as @Szabolcs's answer, because all the conditions are just transformed into vectorizable tensor operations. (I haven't timed it, though.)
Note that this works for arbitrarily complex expressions, and the last two parameters can be tensors or scalars:
replaceWhere[(T > 5 && T < 15) || Sin[L]<Cos[T], L, L*2]
If you evaluate this expression symbolically:
Clear[T, L]
replaceWhere[(T > 5 && T < 15) || Sin[L] < Cos[T], L, 0]
you get one long tensor expression composed of UnitStep
's
Out = L UnitStep[-1 + UnitStep[15 - T] UnitStep[-5 + T] + UnitStep[Cos[T] - Sin[L]]]
So "under the hood" Mathematica will do the same operations as in @Szabolcs's answer. You just get a more readable syntax.
If you don't want to do all those fancy syntax manipulations, you can always use MapThread
to combine higher dimensional arrays in any way you'd like. For your question, this would be:
MapThread[If[#1 > 15, 0, #2] &, {T, L}, 2]
This takes the 2d-Tensors (last argument 2
) T
and L
(second argument {T, L}
), passes the corresponding elements in the two tensors to a function (first argument If[#1 > 15, 0, #2] &
) and returns a new tensor containing the return value of that function. This is slower than @Szabolic's answer, but it is also a lot more flexible.
L=ReplacePart[L, Position[T, i_/;i>15]->0]
Go with @Szabolcs answer whenever you can