5
$\begingroup$

1. Introduction

I am using an array of dimension 3 (might become more) to store some values. I would like to implement a function that takes as argument the array and a couple of numbers smaller than the array dimension and returns a standard matrix (dim=2) which is obtained by reduction or "projection" (I don't really know how to call it) of the array along all the "other" dimensions. First a quick example with an array of dimension/depth 3

data=Table[10 (i - 1) + 3 (j - 1) + k, {i, 2}, {j, 3}, {k, 4}]
--> data = {{{1, 2, 3, 4}, {4, 5, 6, 7}, {7, 8, 9, 10}},
           {{11, 12, 13, 14}, {14, 15, 16, 17}, {17, 18, 19, 20}}}

My desired function would do, for example, the following

Myfunction[data,{1,2}] = {{10,22,34},{50,62,74}}

After this brief introduction, let me explain where I've arrived. First let's generalize a little bit, as it is my goal, eventually.

2. Definitions

Let $M \in \mathbb{R}^{\Pi d_i}$ be an array of dimension $N \in \mathbb{N}$.

Let $d_i \in \mathbb{N}, \quad i=1,\dots,N\quad$ be the sublengths of each of the dimensions.

What I will call in the following the projection of the array along the dimension $q$ is the following surjective function : $$ \textrm{For } q\leq N \in \mathbb{N}_0,\quad P(M,q) : \mathbb{R}^{\Pi d_i} \rightarrow\mathbb{R}^{\frac{\Pi d_i}{d_q}} : M(n_1,\dots,n_N) \mapsto \sum_{j=1}^{n_q}M(n_1,\dots,n_{q-1},j,n_{q+1},\dots,n_N) $$

3. Goal

The goal is to write a function that takes any multidimensional array as well as a list of all the dimensions that will be "kept" in the final array. For the sake of any representation, the obtained array should be of dimension $\leq$ 2 but I would like to stay as general as I (we) could.

The dimensions ordering should simply be the one used to implement the array, and the code should be consistent in order to keep the dimensions in the same order at any time.

4. Implementation

ProjectedColumns[len_,cols_]:= Complement[Range[len],cols]

ProjectedColumns is a function that returns a list of the dimensions to project, where cols is the list of dimensions to be kept and len is the dimension of the array.

CompoundProjection[data_, cols_] :=
Module[{len, vect},
    len = Depth[data] - 1;
    vect = ProjectedColumns[len, cols];
    Do[
        SingleProjection[data, vect[[i]]],
        {i, Length[vect]}
    ]
]

CompoundProjection is a function that will project all the dimensions sequentially in order to arrive to the final result (The projection is commutative).

SingleProjection[data_, dimnumber_] := ???

Now what I need and I don't manage to get is the function that will actually perfom a one-dimensional projection. In my mind, I would need to have a number of Do loops which equals the dimension of the array.

In that case, I simply parse through the array and do the summation over the one I'm interested in. Is it possible to set up such a structure with all the commands that Mathematica offers and that I probably don't know ?

5. In summary

  1. Has anyone followed this nonsense ?
  2. Is there a Mathematica command that makes what I want directly ?

(If Yes then No to the previous questions, then I'm happy)

  1. Is there a way to improve the correctness or elegance of what I did ?
  2. Is there a way to intricate a dynamic number of Do loops ?
  3. Or, in general, is there another way to achieve the goal described in 3.
$\endgroup$
2
  • 1
    $\begingroup$ Maybe you can start from this : Map[Total , data, {2}], which will do what you seek with Myfunction $\endgroup$ Commented Jul 27, 2012 at 18:07
  • 1
    $\begingroup$ Seems that @b.gatessucks is right. But I would like to see a few more examples for the {1,2} parameter in your first code snippet $\endgroup$ Commented Jul 27, 2012 at 18:10

3 Answers 3

11
$\begingroup$

Did you know about the second argument of Total, which lets you sum up element at a certain level, which in practice means along a certain dimensions?

For example, if you want to keep levels 1 and 2, and sum up along level 3, you can use

Total[data, {3}]

(* ==> {{10, 22, 34}, {50, 62, 74}} *)

Or sum up along dimension 1:

Total[data, {1}]

(* ==> {{12, 14, 16, 18}, {18, 20, 22, 24}, {24, 26, 28, 30}} *) 

This is the same as removeDimensions[data, {3, 1}].

$\endgroup$
3
  • $\begingroup$ Reading this I though initially that you could actually get all the desired functionality out of just Total and complement, however you run into trouble with it if you want to remove for example dimension 1 and 4 in a 6 dimensional structure, and then you need to reorder the arguments, or call Total continually while recalculating the dimensions on each pass. $\endgroup$
    – jVincent
    Commented Jul 27, 2012 at 18:27
  • $\begingroup$ @jVincent Yes, for that it's necessary to call Total two times. $\endgroup$
    – Szabolcs
    Commented Jul 27, 2012 at 19:03
  • 1
    $\begingroup$ Total seems to be a good tool indeed. I've come up with a way of using it recursively. You don't need to relaculate anything in the process. You just apply the reduction beginning by the deepest dimensions and then it works fine. $\endgroup$
    – Pschoofs
    Commented Jul 30, 2012 at 9:08
5
$\begingroup$

I may be misunderstanding what you need, however consider this. If you have an structure of nested lists such as your data, then summing across the deepest list is easily accomplished using Map[Total,data,{-2}]. So, as long as you want to remove dimensions from "the back" you are good to go. And if we need to remove for example the second dimension, then you can just transpose it to the back, and then remove it:

 Map[Total,Transpose[data,{1,3,2}],{-2}]

Summing out more then one dimension would then just be pushing them all to the back, and applying Total at a higher level and summing all the way down. Which can be done as:

 transposeOrder[data_, dimensions_] := 
    Join[dimensions, Complement[Range[Depth[data] - 1], dimensions]]

 removeDimensions[data_, dimensions_] /; (Length[dimensions] < Depth[data] - 1) := 
    Map[Total[#, Infinity] &, 
    Transpose[data, transposeOrder[data, dimensions]],
    {-1 + Length[dimensions] - (Depth[data] - 1)}]

And for the case where you aren't removing any dimensions, I suppose it should just fall back to reordering them:

 sumOutDimensions[data_, def_] /; Length[def] == Depth[data] - 1 := Transpose[data, def]

Now these functions output the results with dimensions in the order given by the input, if you want to retain the order of the data structure, you could just sort the arguments to it.

 removeDimensions[data, {1, 2}]
{{10, 22, 34}, {50, 62, 74}}
$\endgroup$
2
$\begingroup$

Here's a version using Flatten to push all the "unwanted" dimensions down to the innermost level before applying the desired function.

keepDimensions[data_, dims_List, func_:Total]:=
Map[func,Flatten[data,Thread@{dims}~Join~{Complement[Range@ArrayDepth@data,dims]}],{-2}]

Examples :

data=Table[10 (i-1)+3 (j-1)+k,{i,2},{j,3},{k,4}]
{{{1,2,3,4},{4,5,6,7},{7,8,9,10}},{{11,12,13,14},{14,15,16,17},{17,18,19,20}}}
keepDimensions[data, {1,2}]
{{10,22,34},{50,62,74}}

Any combination of dimensions can be kept

keepDimensions[data, {1,3}]
{{12,15,18,21},{42,45,48,51}}

Kept dimensions can be re-ordered

keepDimensions[data, {3,1}]
{{12,42},{15,45},{18,48},{21,51}}

An optional third argument can be supplied to do something other than summation:

keepDimensions[data, {1,3}, Max]
{{7,8,9,10},{17,18,19,20}}
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.