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
    $\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 at 0:34
  • $\begingroup$ @BobHanlon, information added. $\endgroup$ – Sid Jun 25 at 7:38
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}
| improve this answer | |
$\endgroup$
  • $\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 at 7:34
  • $\begingroup$ @Sid, please see the update. $\endgroup$ – kglr Jun 25 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 at 8:56
  • $\begingroup$ @Sid, if efficiency is not a concern; Tuples + Select is shorter than alternatives. $\endgroup$ – kglr Jun 25 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.

| improve this answer | |
$\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}}

| improve this answer | |
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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