6
$\begingroup$

For a generally non-square matrix M, I want the upper triangle that include the diagonal entries. Specifically, this is implemented via UpperTriangularize:

MatrixForm[UpperTriangularize[{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}]]

yields the matrix

$$\begin{pmatrix} 1&2&3\\0&5&6\\0&0&9 \end{pmatrix}.$$

Note, as per the documentation UpperTriangularize can also apply to non-diagonal matrices. Given the dimensions of the matrix, I want to return elements of the matrix that correspond to non-zero values of the resulting UpperTriangularize matrix.

This requires extending this solution to non-diagonal matrices and with the diagonal matrices. Any suggestions?

The desired output is a list of matrix elements that are non-zero. I.e. for a 3 by 3 matrix this should be the output:

{{1,1},{1,2},{1,3},...,{2,2},{2,3},...,{3,3}}

This should be generalised to the dimensions of the matrix. For this example, I'm currently using

Flatten[Append[{Subsets[Range[3], {2}]}, Table[{i, i}, {i, 1, 3}]], 1]

Wondering if there is a better approach.

$\endgroup$
2
  • 2
    $\begingroup$ What is the desired form of the output? Ragged matrix including empty lists, ragged matrix without empty lists, or a vector of the non-zero elements? $\endgroup$
    – Bob Hanlon
    Jun 25, 2020 at 0:34
  • $\begingroup$ @BobHanlon, information added. $\endgroup$
    – Sid
    Jun 25, 2020 at 7:38

3 Answers 3

5
$\begingroup$

Update: To get the position indices of the upper triangular part:

mat = Partition[Range[9], 3];

Tuples

Select[Apply @ LessEqual][Tuples[Range /@ Dimensions[#]]] & @ mat
{{1, 1}, {1, 2}, {1, 3}, {2, 2}, {2, 3}, {3, 3}}

SparseArray

Sort@SparseArray[{i_, j_} /; i <= j -> 1, Dimensions@#]["NonzeroPositions"] & @ mat

{{1, 1}, {1, 2}, {1, 3}, {2, 2}, {2, 3}, {3, 3}}

SparseArray[UpperTriangularize @ ConstantArray[1, Dimensions @ #]] 
    ["NonzeroPositions"]& @ mat 
{{1, 1}, {1, 2}, {1, 3}, {2, 2}, {2, 3}, {3, 3}}

Table

 Join @@ Table[{i, j}, {i, First@Dimensions[#]}, {j, i, Last@Dimensions[#]}] & @ mat
{{1, 1}, {1, 2}, {1, 3}, {2, 2}, {2, 3}, {3, 3}}

Position

Position[UpperTriangularize[ConstantArray[1, Dimensions @ #]], 1, 
  Heads -> False] & @ mat
{{1, 1}, {1, 2}, {1, 3}, {2, 2}, {2, 3}, {3, 3}}

MapIndexed

Join @@ MapIndexed[If[# == 0, Nothing, #2] &, 
   UpperTriangularize @ ConstantArray[1, Dimensions@#], {2}] & @ mat
{{1, 1}, {1, 2}, {1, 3}, {2, 2}, {2, 3}, {3, 3}}

Original answer:

mat = Partition[Range[25], 5];

Row[MatrixForm /@ {mat, UpperTriangularize @ mat}, Spacer[10]]

enter image description here

MapIndexed[#[[#2[[1]] ;;]] &] @ mat
 {{1, 2, 3, 4, 5}, {7, 8, 9, 10}, {13, 14, 15}, {19, 20}, {25}}

If you want to get a single list:

MapIndexed[## & @@ #[[#2[[1]] ;;]] &] @ mat
{1, 2, 3, 4, 5, 7, 8, 9, 10, 13, 14, 15, 19, 20, 25}

Few additional alternatives:

MapIndexed[Drop[#, #2[[1]] - 1] &] @ mat
 {{1, 2, 3, 4, 5}, {7, 8, 9, 10}, {13, 14, 15}, {19, 20}, {25}}
MapIndexed[Take[#, #2[[1]] - 1 - Length @ #] &] @ mat
 {{1, 2, 3, 4, 5}, {7, 8, 9, 10}, {13, 14, 15}, {19, 20}, {25}}
Pick[#, UpperTriangularize@ ConstantArray[1, Dimensions@#], 1] & @ mat
 {{1, 2, 3, 4, 5}, {7, 8, 9, 10}, {13, 14, 15}, {19, 20}, {25}}

If you need a single list, wrap the functions above with Apply[Join] or Flatten:

Join @@ MapIndexed[Drop[#, #2[[1]] - 1] &]@mat
 {1, 2, 3, 4, 5, 7, 8, 9, 10, 13, 14, 15, 19, 20, 25}

If the upper triangular part does not contain zeros (as in mat) you can also use SparseArray and extract "NonzeroValues":

SparseArray[UpperTriangularize @ #]["NonzeroValues"] & @ mat 
 {1, 2, 3, 4, 5, 7, 8, 9, 10, 13, 14, 15, 19, 20, 25}
$\endgroup$
4
  • $\begingroup$ How to get the matrix elements corresponding to the non-zero entries? I.e. {{1,1}, {1,2}, .... {2,2},{2,3}, ... , {3,3}...} $\endgroup$
    – Sid
    Jun 25, 2020 at 7:34
  • $\begingroup$ @Sid, please see the update. $\endgroup$
    – kglr
    Jun 25, 2020 at 8:03
  • $\begingroup$ This is great. Is there a preferred route out of the SparseArray, Table, Position, and MapIndexed methods. My matrices are quite small so efficiency shouldn't be a bottleneck. $\endgroup$
    – Sid
    Jun 25, 2020 at 8:56
  • $\begingroup$ @Sid, if efficiency is not a concern; Tuples + Select is shorter than alternatives. $\endgroup$
    – kglr
    Jun 25, 2020 at 9:46
4
$\begingroup$

(This answer was written before it became clear that the expected output is a list of positions. This answer is about how to retrieve the elements.)

Here's an implementation that support the second argument of UpperTriangularize:

upperTriangularElements[m_] := upperTriangularElements[m, 0]
upperTriangularElements[m_, k_] := Module[{nr, nc},
  {nr, nc} = Dimensions[m];
  Fold[
   #~Join~Diagonal[m, #2] &,
   {},
   Range[k, nc - 1]
   ]
  ]

m = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
m // MatrixForm

Matrix

upperTriangularElements[m]

{1, 5, 9, 2, 6, 3}

upperTriangularElements[m, 1]

{2, 6, 3}

This function also works for non-square matrices.

$\endgroup$
1
$\begingroup$
(ArrayRules@UpperTriangularize[Partition[Range[9],3]])[[All,1]] // Most

{{1, 1}, {1, 2}, {1, 3}, {2, 2}, {2, 3}, {3, 3}}

(ArrayRules@UpperTriangularize[Partition[Range[12],4]])[[All,1]] // Most

{{1, 1}, {1, 2}, {1, 3}, {1, 4}, {2, 2}, {2, 3}, {2, 4}, {3, 3}, {3, 4}}

$\endgroup$

Your Answer

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

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