8

I have a square $n \times n$ matrix $m$, and need to apply a function $f$ to all elements on and above the diagonal.

This is of course easy to do using a nested table:

 Table[Table[f @ m[[i,j]],{i,1,n}],{j,i,n}]

Is there a more elegant functional equivalent to this line? Something that would be more declarative in style?

David G. Stork
  • 41,180
  • 3
  • 34
  • 96
verse
  • 1,287
  • 6
  • 18

10 Answers10

15

Example square matrix:

n = 4;

m = Range[n^2] ~Partition~ n;

m // MatrixForm

$\left( \begin{array}{cccc} 1 & 2 & 3 & 4 \\ 5 & 6 & 7 & 8 \\ 9 & 10 & 11 & 12 \\ 13 & 14 & 15 & 16 \\ \end{array} \right)$

Operation:

MapAt[f, m, {#, # ;;} & ~Array~ Length @ m]     // MatrixForm

$\left( \begin{array}{cccc} f(1) & f(2) & f(3) & f(4) \\ 5 & f(6) & f(7) & f(8) \\ 9 & 10 & f(11) & f(12) \\ 13 & 14 & 15 & f(16) \\ \end{array} \right)$

A hybrid method inspired by other answers:

MapAt[f, #, #2[[1]] ;;] & ~MapIndexed~ m
Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
7

I would use MapIndexed, e.g.

data = Partition[Range[9], 3];
MapIndexed[If[LessEqual @@ #2, f@#1, #1] &, data, {2}]
(* {{f[1], f[2], f[3]}, {4, f[5], f[6]}, {7, 8, f[9]}} *)
rcollyer
  • 33,976
  • 7
  • 92
  • 191
  • this solution traverses the entire matrix. is there any way to only traverse the upper triangular part? – verse Apr 10 '15 at 20:28
  • Yes, it traverse the entire matrix, which may not be a bad thing, but it only applies f above the diagonal. So, it still minimizes how many times you execute f. – rcollyer Apr 10 '15 at 20:32
6
mapAboveDiagonal1 = With[{dim = Dimensions[#2]}, 
 MapAt[#, #2, Join @@ Table[{i, j}, {i, dim[[1]]}, {j, i, dim[[2]]}]]] &

or

mapAboveDiagonal2 = MapAt[#, #2, 
  SparseArray[UpperTriangularize[
              ConstantArray[1, Dimensions[#2]]]]["NonzeroPositions"]]&;

mm = Array[m, {5, 5}];
Row[MatrixForm /@ {mm, mapAboveDiagonal1[f, mm]}]

enter image description here

kglr
  • 394,356
  • 18
  • 477
  • 896
4

Another option is to take advantage of SparseArray index selection:

f[x_] := x^2;
n = 5;
(data = RandomInteger[10, {n, n}]) // MatrixForm

Mathematica graphics

And now apply the function f[x] above to only the top triangle

SparseArray[{{i_, j_} /; i <= j :> f@data[[i, j]], 
             {i_, j_} /; i > j :> data[[i, j]]}, {n, n}]

Mathematica graphics

Jinxed
  • 3,753
  • 10
  • 24
Nasser
  • 143,286
  • 11
  • 154
  • 359
3

In case another way is needed:

matrix = Array[m, {5, 5}];

Fold[MapAt[f, #1, {#2, #2 ;;}] &, matrix, Range[5]]
(* {{f[m[1, 1]], f[m[1, 2]], f[m[1, 3]], f[m[1, 4]], 
  f[m[1, 5]]}, {m[2, 1], f[m[2, 2]], f[m[2, 3]], f[m[2, 4]], 
  f[m[2, 5]]}, {m[3, 1], m[3, 2], f[m[3, 3]], f[m[3, 4]], 
  f[m[3, 5]]}, {m[4, 1], m[4, 2], m[4, 3], f[m[4, 4]], 
  f[m[4, 5]]}, {m[5, 1], m[5, 2], m[5, 3], m[5, 4], f[m[5, 5]]}} *)
chuy
  • 11,205
  • 28
  • 48
  • You've got my +1 on this as it's nearly my own formulation. However Fold proves unnecessary and slightly less clean, IMHO. – Mr.Wizard Apr 11 '15 at 00:59
2

Thanks everyone for contributing interesting suggestions.

I thought I'd also attach my own solution:

f[m[#1, #2]]& @@@ Select[Tuples[Range @ n, 2], #[[1]] <= #[[2]] &]

For n=5 the output is as follows:

{f[m[1, 1]], f[m[1, 2]], f[m[1, 3]], f[m[1, 4]], f[m[1, 5]], f[m[2, 2]], f[m[2, 3]], f[m[2, 4]], f[m[2, 5]], f[m[3, 3]], f[m[3, 4]], f[m[3, 5]], f[m[4, 4]], f[m[4, 5]], f[m[5, 5]]}

verse
  • 1,287
  • 6
  • 18
2
MapThread[Compose, {Array[If[#1 <= #2, f, Identity] &, Dimensions@m], m}, 2]
alephalpha
  • 1,293
  • 8
  • 17
0

For the case you gave:

f@UpperTriangularize@m
Jinxed
  • 3,753
  • 10
  • 24
  • doesn't UpperTriangularize simply replace the elements below the diagonal with zeros? If so, $f$ would still be evaluated for those elements, increasing the run time. In my specific case $f$ takes a substantial amount of time to evaluate, and I must keep the total number of evaluations to a minimum – verse Apr 10 '15 at 20:24
  • @verse: You gave none of this information in your question. :| Can you provide an examplary matrix and the function f? On the other hand: Did you try my approach regarding timing? – Jinxed Apr 10 '15 at 20:31
0

Building upon @Jinxed's insights, this may be one of the shortest code snippets, though admittedly it isn't efficient code:

(f[#] - #) & @ UpperTriangularize@m + m
David G. Stork
  • 41,180
  • 3
  • 34
  • 96
0

Just wanted to join party but not near computer...will check edit when I get chance

ad[m_,f_]:= Module[{n =     Length[m[[1]]], mf = Flatten[m], nf}, 
nf = List /@ Flatten[NestList[n + Rest@# &, Range[n], n - 1]];
Partition[MapAt[f, mf, nf], n]]

Here m is square matrix and f function to be applied.

For example,

MatrixForm[#] -> MatrixForm[ad[#, f]] &@Partition[Range[25], 5]

enter image description here

ubpdqn
  • 60,617
  • 3
  • 59
  • 148