11
$\begingroup$

How to select all elements above the main diagonal of matrix? I need to create a list of them.

$\endgroup$
14
$\begingroup$

The Diagonal command has a second argument that allows listing the elements of the jth superdiagonal. So we can map over all the superdiagonals:

n = 4;
mat = Array[a, {n, n}];
Flatten[Diagonal[mat, #] & /@ Range[n-1]]

which gives a list of all the elements above the diagonal:

{a[1, 2], a[2, 3], a[3, 4], a[1, 3], a[2, 4], a[1, 4]}
$\endgroup$
  • 1
    $\begingroup$ Niiiiice, I learned something new $\endgroup$ – Rojo Jan 30 '14 at 1:49
11
$\begingroup$

Let's use Span with Part:

mat=RandomInteger[{0, 100}, {5, 5}];

Flatten[mat[[#, # + 1 ;;]] & /@ Range[5]]
$\endgroup$
  • 1
    $\begingroup$ Nice, clean, clever, and faster than the naive solution. +1 $\endgroup$ – ciao Jan 30 '14 at 5:32
7
$\begingroup$

I believe that kale's approach is the best, but it can be improved with Join which is considerably faster than Flatten on packed lists, and it is a bit cleaner when written with Array:

Join @@ Array[mat[[#, # + 1 ;;]] &, n - 1]

To make this into a fast function it seems that one needs a Hold attribute (pass-by-reference):

SetAttributes[aboveDiag, HoldFirst];
aboveDiag[a_] := Join @@ Array[a[[#, # + 1 ;;]] &, Length[a] - 1]

Compare timings (done in v7) with bill s's code and R.M's upperElements function. (I left MatrixQ out of upperElements in these tests to eliminate overhead and level the field.)

n = 5000;
mat = RandomInteger[999, {n, n}];

Flatten[Diagonal[mat, #] & /@ Range[n - 1]] // Timing // First

upperElements[mat]                          // Timing // First

aboveDiag[mat]                              // Timing // First
0.3276

0.1872

0.02308

Not quite as superior on non-packed data, but still a good bit faster than the others.

n = 2000;
mat = Developer`FromPackedArray @ RandomInteger[999, {n, n}];

Flatten[Diagonal[mat, #] & /@ Range[n - 1]] // Timing // First

upperElements[mat]                          // Timing // First

aboveDiag[mat]                              // Timing // First
0.1404

0.0998

0.02308

Inspired by rasher's use of MapIndexed, here is another one that tests the fastest on unpacked data on my system (using last mat above):

Join @@ MapIndexed[Drop[#, #2[[1]]] &, mat] // Timing // First
0.01996

It is much slower than aboveDiag on packed data.

$\endgroup$
  • 1
    $\begingroup$ Join is not only faster but better than flatten (with no second arg) if the matrix can have list elements $\endgroup$ – Rojo Jan 30 '14 at 6:47
  • $\begingroup$ @Rojo Good point. $\endgroup$ – Mr.Wizard Jan 30 '14 at 7:05
  • 2
    $\begingroup$ Nice tuning, as usual! For non-huge arrays, I use (filter matching this particular problem in this case): FilterRules[ArrayRules[yourArrayHere], {a_, b_} /; b > a] Just seems... neat, if not a hot-rod. $\endgroup$ – ciao Jan 30 '14 at 7:53
  • 1
    $\begingroup$ @rasher You should post that ArrayRules method; it does have flair. $\endgroup$ – Mr.Wizard Jan 30 '14 at 7:54
  • $\begingroup$ I don't know but I've ran this and upperElemets and aboveDiag have comparable timings for PackedArray. Also in packed case my Pick is two times faster, unless I'm doing something wrong. $\endgroup$ – Kuba Jan 30 '14 at 8:35
3
$\begingroup$

Update:

Statistics`Library`UpperTriangularMatrixToVector is faster than all posted methods on packed data. Using the set up from @Mr.Wizard's answer:

n = 4000;
mat = RandomInteger[999, {n, n}];

(res0 = Statistics`Library`UpperTriangularMatrixToVector@mat) //   Timing // First

0.027995

(res1 = Join @@ MapIndexed[Drop[#, #2[[1]]] &, mat] )// Timing // First

0.158976

(res2 = aboveDiag[mat] )// Timing // First

0.250961

res0 == res1 == res2

True

On unpacked data, it is slower than Join@@MapIndexed[...] but faster than aboveDiag:

n = 2000;
mat = Developer`FromPackedArray @ RandomInteger[999, {n, n}];

(res0 = Statistics`Library`UpperTriangularMatrixToVector@mat) // Timing // First

0.096986

(res1 = Join @@ MapIndexed[Drop[#, #2[[1]]] &, mat] ) // Timing // First

0.029996

(res2 = aboveDiag[mat] ) // Timing // First

0.384942

res0 == res1 == res2

True

Previous version:

n = 4; mat = Array[a, {n, n}];

Statistics`Library`UpperTriangularMatrixToVector @ mat

{a[1, 2], a[1, 3], a[1, 4], a[2, 3], a[2, 4], a[3, 4]}

Also

mat[[##]] & @@@ Subsets[Range[n], {2}]
Extract[mat, Subsets[Range[n], {2}]]

{a[1, 2], a[1, 3], a[1, 4], a[2, 3], a[2, 4], a[3, 4]}

$\endgroup$
2
$\begingroup$
n = 4;
mat = Array[a, {n, n}];

Flatten@Table[If[j > i, mat[[i, j]], {}], {i, n}, {j, n}]

Mathematica graphics

$\endgroup$
  • $\begingroup$ I liked the replacing by {} for future flattening. +1 $\endgroup$ – Rojo Jan 30 '14 at 2:09
2
$\begingroup$

Here's an option for square matrices using undocumented properties of SparseArray, which clocks in slightly faster for me than Mr.Wizard's solution:

upperElements[mat_?MatrixQ] := With[{n = Length@mat}, 
    SparseArray[UpperTriangularize[mat, 1]]["NonzeroValues"] ~PadRight~ (n (n - 1)/2)];

This constructs a sparse strictly upper triangular matrix, extracts the non-zero values and then pads it to a length of $n(n-1)/2$, where $n$ is the dimension of the matrix (to account for any upper element that might have been zero).

For non-square matrices of dimension $m\times n$, you might try using a closed form expression for the number of upper triangular entries (see here, for instance).

$\endgroup$
  • $\begingroup$ How come I didn't think of padright and worried about deleting extra zeros. +1 $\endgroup$ – Rojo Jan 30 '14 at 7:01
  • $\begingroup$ On my system (v7) this is about six to seven times slower than aboveDiag with n = 5000; mat = RandomInteger[999, {n, n}]; (and leaving out MatrixQ for a fair test). What data are you testing with, and what timings do you get for my data in v9? $\endgroup$ – Mr.Wizard Jan 30 '14 at 7:04
  • $\begingroup$ @Mr.Wizard I don't have the timings right now, but it was very close to yours, but a wee bit faster. I see from the comments under yours that Kuba got similar results. I don't have v7 installed, so I can't confirm your findings. $\endgroup$ – rm -rf Jan 30 '14 at 16:55
2
$\begingroup$

Variation of Nasser answer:

n = 4;
m = Array[a, {n, n}];
Join @@ Table[m[[ i, j]], {i, n}, {j, i + 1, n}]
{a[1, 2], a[1, 3], a[1, 4], a[2, 3], a[2, 4], a[3, 4]}

just another way:

Join @@ Pick[
  m,
  UpperTriangularize[ConstantArray[1, {n, n}], 1],
  1]
$\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.