       RE: List operations "in a given dimension"

• To: mathgroup at smc.vnet.net
• Subject: [mg33505] RE: [mg33441] List operations "in a given dimension"
• From: "Wolf, Hartmut" <Hartmut.Wolf at t-systems.com>
• Date: Sun, 24 Mar 2002 01:44:00 -0500 (EST)
• Sender: owner-wri-mathgroup at wolfram.com

```> -----Original Message-----
> From: Martin Johansson [mailto:martin.n.johansson at emw.ericsson.se]
To: mathgroup at smc.vnet.net
> Sent: Thursday, March 21, 2002 3:27 PM
> To: mathgroup at smc.vnet.net
> Subject: [mg33505] [mg33441] List operations "in a given dimension"
>
>
> Hi,
>
> coming from the matlab world, I am used to operating
> on subgroups of elements in arbitrary dimensions of
> multidimensional matrices. An example of such an
> operation would be the calculation of the maximum
> value over all elements in a particular dimension,
> i.e. apply max() on all elements in the desired
> dimension for all index combinations in the remaining
> dimensions, resulting in matrix with one less
> dimension than the original matrix.
>
> Is there a "neat" (built-in) way to do this in
> Mathematica?
>
> Numerical example (2D):
>
> aa = {{11,12,13},{21,22,23}};
>
> Operating on aa with Max[] "w.r.t. the second
> index" should yield
>
> {13,23}.
>
>
> Numerical example (3D):
>
> aaa =
>  {{{111, 112, 113, 114}, {121, 122, 123, 124}, {131, 132, 133, 134}},
>   {{211, 212, 213, 214}, {221, 222, 223, 224}, {231, 232, 233, 234}}}
>
> Again, operating on aa with Max[] "w.r.t. the second
> index" should yield
>
> {{131, 132, 133, 134}, {231, 232, 233, 234}}.
>
>
> A way of doing (which I think is correct) is as
> follows:
>
> MaxInDim[list_, maxdim_] :=
>  Module[
>  {dims = Length[Dimensions[list]],
>   dimList},
>   dimList = Insert[Range[dims - 1], dims, maxdim];
>   Apply[Max, Transpose[list, dimList], {dims - 1}]
> ]
>
> It seems to work, but if there is a "general" way of
> doing this I could maybe avoid having to construct
> similar versions for other common operations.
>
> Matlab->Mathematica syntax conversions would
> also be welcome.
>
> Ciao,
>
> Martin
>

Martin,

what do you mean by a "general way"? All you need to do, is to abstract Max
to an arbitrary function!

To test for correctness (and to find a solution), it is useful to select a
reasonable general test case:

In:= SeedRandom
In:=
m = Table[Random[Integer, {0, 1000}], {i, 3}, {j, 4}, {k, 2}, {l, 3}]

Mostly it is of advantage to define a simple, albeit not performant,
function to check the results.

So applying a function f to all elements of the second index, say, is done
by
In:=
Table[f @@ m[[i, All, k, l]], {i, 3}, {k, 2}, {l, 3}]

and substituting Max for f:

In:= % /. f -> Max
Out=
{{{703, 984, 710}, {913, 652, 934}},
{{981, 512, 777}, {853, 922, 831}},
{{773, 993, 802}, {901, 728, 772}}}

This is simple and clear.

We can automate this method, if we can Table with a variable number of
iterators. There has been a recent post on this theme. Using my answer from
that, we get at:

In:=
ApplyAtIndex0[f_Symbol | f_Function, m_,
(index_Integer)?Positive] /;
index <= (rank = TensorRank[m]) :=
With[{dims = Dimensions[m]},
Table @@
({Unevaluated[f @@ Table[
m[[Sequence@@(\[Iota][#1]&)/@Range[rank]]],
{\[Iota][index], dims[[index]]}]], ##1}&)@@
({\[Iota][#1], dims[[#1]]}&)/@ Delete[Range[rank],index]]

This is not so horrible as it seems to be: every \[Iota][i], for i =
1,..,rank is an iterator "variable". These are brought as indices to the
matrix m. The inner Table iterates the index in question, the outer Table
all other indices. The function is held until it comes to the outer Table.

This however is nothing to be content with. You already found by yourself an
(to my opinion) perfect solution (here in my words):

In:=
ApplyAtIndex[(f_Symbol | f_Function), m_, index_Integer?Positive] /;
index <= (rank = TensorRank[m]) :=
Module[{levels = Insert[Range[rank - 1], rank, index]},
Apply[f, Transpose[m, levels], {rank - 1}]]

In:=
x2 = ApplyAtIndex[Max, m, 2]
Out=
{{{703, 984, 710}, {913, 652, 934}},
{{981, 512, 777}, {853, 922, 831}},
{{773, 993, 802}, {901, 728, 772}}}

There is a similar ("dual") solution with MapThread:

In:=
ApplyAtIndex2[(f_Symbol | f_Function), m_, index_Integer?Positive] /;
index <= (rank = TensorRank[m]) :=
Module[{levels = Insert[Range[2, rank], 1, index]},
MapThread[f, Transpose[m, levels], rank - 1]]

See how nicely they correspond. Finally I found still another solution:

In:=
ApplyAtIndex3[(f_Symbol | f_Function), m_, index_Integer?Positive] /;
index <= (rank = TensorRank[m]) :=
Map[MapThread[f, #, rank - index] &, m, {index - 1}]

This is the shortest expression. In performance all functions (except for
the Table solution) should come close, I guess your solution to be fastest,
I didn't test however.

We show the equivalence of all solutions:

In:=
SameQ @@ Through[{ApplyAtIndex0, ApplyAtIndex, ApplyAtIndex2,
ApplyAtIndex3}[
f, m, #]] & /@ Range
Out=
{True, True, True, True}

It is an interesting question, whether such a function is needed, or better
say, whether you will need it any longer after having adapted to
"Mathematica style".

--
Hartmut Wolf

```

• Prev by Date: Re:Full text of the current warning message(s)
• Next by Date: Global optimization codes
• Previous by thread: Re: List operations "in a given dimension"
• Next by thread: ListPlot with multiple (related) series.