7

I would like to define a version of MatrixForm that prints entries equal to zero in gray. The definition

matrixform[X_] := MatrixForm[X /. entry_ /; entry == 0 -> Style[0, LightGray]]

produces an output that looks how I'd want, but it actually changes the entries, rather than just the way they are displayed, so one cannot just use the output of my matrixform via % in algebraic manipulations.

Ideally I'm looking for an answer that works for sparse arrays too, without the conversion to a normal array: at least, I'd like to be able to work with the output without having to convert it back to a sparse array.

Note that I don't want just all zeroes in my notebook to be formatted in gray (so I don't think I can use Format), and I don't want matrices to be formatted as above automatically either.


Edit. Here is an example of what I'm hoping to achieve:

A = SparseArray[{1,1}->x,{2,2}]
(* Out: SparseArray[..., Specified elements: 1, Dimensions: {2,2}] as usual *) 

% // matrixform
(* Out: MatrixForm of A with 0s shown in gray *)

2 %
(* Out: SparseArray[..., Specified elements: 1, Dimensions: {2,2}] as usual *)

% // matrixform
(* Out: MatrixForm of 2*A with 0s shown in gray *)
Jules Lamers
  • 1,074
  • 9
  • 19
  • PS. I don't really understand if there's a difference between the tags output-formatting and formatting, so I've used both; feel free to edit them. – Jules Lamers Feb 05 '18 at 13:57

2 Answers2

8

To get the % behavior you want, you need to create a wrapper, that is, a function that does not evaluate, but creates a nice format when it is rendered. The usual way to do this is to use MakeBoxes. Here is a version that should target only matrix entries, and also avoid evaluation leaks:

matrixForm /: MakeBoxes[matrixForm[x_List], StandardForm] := Replace[
    Replace[Hold[x], 0->Style[0, Red], {3}],
    Hold[z_] :> MakeBoxes[MatrixForm[z]]
]

Note that I used Red instead of LightGray to improve visualization for this answer. Now that matrixForm is a wrapper, you can add it to $OutputForms as suggested in (51898).

Unprotect[$OutputForms];
AppendTo[$OutputForms, matrixForm];
Protect[$OutputForms];

Example:

matrixForm[{{f[0], 0}, {1, 0}}]

enter image description here

And using %:

%

{{f[0], 0}, {1, 0}}

For SparseArray objects, you could add the following format, but note that it relies on being able to reconstruct a SparseArray from its FullForm, updated to work in M13 as well (note that being able to reconstruct a sparse array in this way may change in the future):

matrixForm /: MakeBoxes[matrixForm @ s_SparseArray, StandardForm] := With[
    {
    new = SparseArray[
        Automatic, 
        Dimensions[s], 
        Style[s["Background"], Red],
        {1, {s["RowPointers"], s["ColumnIndices"]}, s["NonzeroValues"]}]
    },
    MakeBoxes[
        MatrixForm @ new,
        StandardForm
    ]
]

Example:

matrixForm[IdentityMatrix[3, SparseArray]]

enter image description here

Check %:

%

SparseArray[Automatic, {3, 3}, 0, { 1, {{0, 1, 2, 3}, {{1}, {2}, {3}}}, {1, 1, 1}}]

Carl Woll
  • 130,679
  • 6
  • 243
  • 355
6
SeedRandom[0]

(m = RandomInteger[{0, 2}, {4, 4}]) // MatrixForm

enter image description here

Note that by using parentheses, the definition of m does not include the wrapper MatrixForm

matrixForm[x_List] := ((x /. 0 :> Style[0, LightGray]) // MatrixForm)

m // matrixForm

enter image description here

The wrappers, MatrixForm or matrixForm are only for display. If you want to use the matrix, use m. Any subsequent operation is done on m and the display is formatted using the wrapper.

(m2 = Transpose@m) // matrixForm

enter image description here

m == (m2 // Transpose)

(* True *)
Bob Hanlon
  • 157,611
  • 7
  • 77
  • 198
  • I think that does more or less what my matrixform does. The problem is that such definitions alter the matrix you're displaying: the line after m // matrixform, writing e.g. 2 % gives {{4,4,2,2*Style[0,LightGray]},...} – Jules Lamers Feb 05 '18 at 14:07
  • Ad edit: Output formatted with MatrixForm can be used for algebraic manipulations. For example, starting from some sparse array a, even though the output of a // MatrixForm is nicely formatted, one can actually use that output in something like 2 % to get a sparse array whose entries are twice those of a. I am looking for something similar here, perferably without having to use parentheses as in(...) // matrixform, if possible – Jules Lamers Feb 05 '18 at 14:46
  • @JulesLamers - Then just use 2 % // matrixform – Bob Hanlon Feb 05 '18 at 15:00
  • 3
    @JulesLamers To get the % behavior you want, see (51898). – Carl Woll Feb 05 '18 at 15:19
  • @CarlWoll Thanks! That looks very relevant. (And complicated...) I'll take a better look at it later – Jules Lamers Feb 05 '18 at 15:25
  • @JulesLamers All you have to do is Unprotect[$OutputForms]; AppendTo[$OutputForms, matrixForm]; Protect[$OutputForms]; – Carl Woll Feb 05 '18 at 15:34
  • @CarlWoll That'd be fantastic. But unfortunately it doesn't work: % still yields Style[0,LightGray]... – Jules Lamers Feb 05 '18 at 15:43
  • @JulesLamers I forgot that matrixForm needs to be a wrapper. See my answer for more details. – Carl Woll Feb 05 '18 at 16:02