# MatrixPlot with Tooltips

MatrixPlot is very nice to have a first look of data, I would like to have a MatrixPlot with tooltips for each value. I tried adding Tooltip to all the values but it didn't work. My matrix is not very big (~20x25), so each square is visible.

EDIT: This is the version I did using the answer of Heike:

matPlot[matWithTooltips_, opts : OptionsPattern[]] :=
With[{dim = Dimensions[matWithTooltips],
mat = matWithTooltips /. Tooltip[a_, ___] :> a},
DynamicModule[{pt = {0, 0}, ij, xy, label, direction},
direction[mxy_] := 1 - 2 Boole[Thread[mxy > dim/2]];
LocatorPane[Dynamic[pt],
Dynamic[
xy = Floor[pt];
ij = {dim[[1]], 1} + Cross[xy];
label = If[And @@ Thread[1 <= ij <= dim],
{{FaceForm[], EdgeForm[{Thick, LightGray}], Rectangle[xy]},
Text[
Framed[Replace[
Extract[matWithTooltips, ij], {Tooltip[a_, b_] :> b,
Tooltip[a_] :> a}], Background -> White],
direction[xy] + xy,
-1.2 direction[xy]]},
{}];
MatrixPlot[mat, Epilog -> label, opts]],
AutoAction -> True,
Appearance -> None]]]


The main improvement is that it can receive a matrix with its own tooltips in the data.

Thanks to all for the help.

-

This is just an elaboration of faleichik's answer. To create a MatrixPlot with tooltip labelling and highlighting of the selected square similar to for example BarChart or BubbleChart you could do something like

matPlot[mat_, opts : OptionsPattern[]] :=
With[{dim = Dimensions[mat]},
DynamicModule[{pt = {0, 0}, ij, xy, label, direction},
direction = 1 - 2 Boole[Thread[# > dim/2]] &;
LocatorPane[Dynamic[pt],
Dynamic[xy = Floor[pt];
ij = {dim[[1]], 1} + Cross[xy];
label = If[Nand @@ Thread[1 <= ij <= dim], {},
(* else *)
{{FaceForm[], EdgeForm[{Thick, LightGray}], Rectangle[xy]},
Text[Framed[mat ~Extract~ ij, Background -> White],
direction[xy] + xy, -1.2 direction[xy]]}];
MatrixPlot[mat, Epilog -> label, opts]],
AutoAction -> True,
Appearance -> None]
]]


Screenshot

mat = RandomReal[1, {30, 40}];
matPlot[mat, ColorFunction -> "DeepSeaColors"]


-
Very nice! Looking to the code: is there any particular reason to use With[] wrapping here? Why not include dim = Dimensions[mat] directly to DynamicModule? Am I missing something? –  faleichik Mar 2 '12 at 14:37
@faleichik No particular reason other than that With is usually more efficient for localizing constants than DynamicModule (although I don't think it would make any noticeable difference in this case). –  Heike Mar 2 '12 at 14:53
I like this a lot, but something I don't like is that when I changed the tooltip for something more complex, it got cut by the borders, maybe it could be fix to change the position of the tooltip towards the center of the image. I will try it. Thanks! –  FJRA Mar 2 '12 at 15:51
I inserted a simple direction function instead of 1+xy and worked very nice! direction := 1 - 2 Boole[Thread[# > dim/2]] & and changed 1+xy -> direction[xy]+xy, {-1.2,-1.2} -> -1.2 direction[xy]. –  FJRA Mar 2 '12 at 17:48
Why do I even bother to answer Graphics questions? +1 :-) –  Mr.Wizard Mar 2 '12 at 19:16

My variant is without tooltips but is fast. You need to click on the desired cell to get the value. One more advantage is that you have the generic MatrixPlot, not a substitute.

A = Table[Sin[x y/10 + x], {x, 1, 50}, {y, 1, 50}] // N;
{n, m} = Dimensions@A;
DynamicModule[{pt = {1, 1}/2, trans, ij},
trans[{x_, y_}] := {Max[1, Min[n, Floor[n - y] + 1]],
Max[1, Min[m, Floor@x + 1]]};
Column@{
LocatorPane[Dynamic[pt], MatrixPlot[A, ImageSize -> 500]],
Dynamic[(ij = trans@pt) -> A[[Sequence @@ ij]]]
}
]


EDIT: more dynamic version without LocatorPane (no need to click) + using Clip as Mr.Wizard kindly adviced.

   A = Table[Sin[x y/10 + x], {x, 1, 50}, {y, 1, 50}] // N;
{n, m} = Dimensions@A;
DynamicModule[{pt = {1, 1}/2, trans, ij},
trans[{x_, y_}] := {Clip[Floor[n - y] + 1, {1, n}],
Clip[Floor@x + 1, {1, m}]};
Column@{MatrixPlot[A, ImageSize -> 500],
Dynamic[(ij = trans@MousePosition["Graphics", {0, 0}]) ->
A[[Sequence @@ ij]]]}
]


EDIT 2: using Appearance->None and Epilog, creating a stylish Tooltip in the right place, with the right alignment (you can probably do more to style it better if you want to spend the time).

A = Table[Sin[x y/10 + x], {x, 1, 50}, {y, 1, 50}] // N;
{n, m} = Dimensions@A;
DynamicModule[{pt = {1, 1}/2, trans, ij},
trans[{x_, y_}] := {Max[1, Min[n, Floor[n - y] + 1]],
Max[1, Min[m, Floor@x + 1]]};
gLoc[{x_, y_}] := {First@#, 1 + n - Last@#} &@Reverse@trans@{x, y};
Column@{LocatorPane[Dynamic[pt],
MatrixPlot[A, ImageSize -> 500,
Epilog ->
Dynamic@{EdgeForm[Red], FaceForm[None],
Rectangle[gLoc@pt, gLoc@pt - 1], EdgeForm[Black],
FaceForm[{Opacity[.7], RGBColor[1, 1, .6]}],
If[First@gLoc@pt < n - 12,
{Rectangle[gLoc@pt + {6, 2}, gLoc@pt + {-1, 0}],
Text[A[[Sequence @@ (trans@pt)]], gLoc@pt + {2, 1}]},
{Rectangle[gLoc@pt + {-7, 2}, gLoc@pt],
Text[A[[Sequence @@ (trans@pt)]], gLoc@pt + {-3, 1}]}]}],
Appearance -> None, AutoAction -> True],
Dynamic[(ij = trans@pt) -> A[[Sequence @@ ij]]]}]

-
Technically not tooltips, but quite possibly the better alternative. +1 –  Mr.Wizard Mar 2 '12 at 7:53
Noted. You may care to know there is a built-in function for the Max[ Min [ ] ] thing: Clip[. . ., {1, m}] –  Mr.Wizard Mar 2 '12 at 8:21
I like this approach. You could set AutoAction -> True in DynamicModule if you want the locator position to be continuously updated. Also, to mimic Tooltip, you could play around with Appearance in LocatorPane, or use Epilog to dynamically plot a label with the current value on top of the matrix plot. –  Heike Mar 2 '12 at 9:17
This is a very good alternative, since I can locate the "tooltip" above and use it as title comment. Thanks! –  FJRA Mar 2 '12 at 15:53

EDIT: y-axis corrected

You could do something like this:

a = RandomInteger[99, {7, 5}];

minmax = {Min@a, Max@a};
cf = ColorData["SunsetColors"];
ticks = Table[{i, # - i + 1}, {i, #}] & @ Length[a]

Graphics[
{cf[1 - Rescale[#2, minmax]],
Tooltip[Rectangle[# - 0.5], #2]} & @@@
Most @ ArrayRules @ Reverse[a\[Transpose], {2}],
Frame -> True,
FrameTicks -> {All, ticks}
]


-
+1. It is VERY hard to make something faster than you guys. Although this Tooltip aproach becomes very slow for large matrices. –  faleichik Mar 2 '12 at 7:22
@faleichik it is slow, but unless there is a specialized option for Raster I am overlooking I don't know of a better way. –  Mr.Wizard Mar 2 '12 at 7:28
I thought also about doing my own matrix plot, anyway I wanted to know if MatrixPlot could be adapted to have tooltips. Here would be necessary to add the MatrixPlot behavior for non numeric cases (like Indeterminate). Thanks! –  FJRA Mar 2 '12 at 15:58

One can also use the option CoordinatesToolOptions for MatrixPlot and make use of the Get Coordinates tool as follows:

 MatrixPlot[mat, ColorFunction -> "DeepSeaColors",
CoordinatesToolOptions ->
{"DisplayFunction" ->
Function[pt,
With[{rows = Dimensions[mat][[1]], columns = Dimensions[mat][[2]]},
indices = {Clip[Floor[rows - pt[[2]]] + 1, {1, rows}],
Clip[Floor@pt[[1]] + 1, {1, columns}]};
Row[{"mat[", Row[indices, ","], "]  =  ", Extract[mat, indices]},
Background -> White, ImageSize -> {150, 30},
Alignment -> Center]
]]}]


where I used faleichik's transformation.

Screenshot:

Update: Generalizing, one can

• Embed the needed coordinate transformations into DisplayFunction option for tooltips, and into CopiedValuesFunction option for copy/paste,
• Use these user-specified functions as values for the CoordinatesToolOptions option in MatrixPlot, and
• Let the Get Coordinates tool manage the dynamics needed for tooltips and copy/paste

as in:

  mtrxPlot2[mat_, opts : OptionsPattern[]] :=
With[{dims = Dimensions[mat],
indx = {Clip[Floor[#1[[1]] - #2[[2]]] + 1, {1, #1[[1]]}],
Clip[Floor[#2[[1]]] + 1, {1, #1[[2]]}]} &},
With[{copiedvalues = "CopiedValueFunction" -> Function[pt,
{indx[dims, pt], Extract[mat, indx[dims, pt]]}],
coordtooltips = "DisplayFunction" -> Function[pt,
Row[{"mat[[", Row[indx[dims, pt], ","], "]]  =  ",
Extract[mat, indx[dims, pt]]},
Background -> White, ImageSize -> {Automatic, 30},
ImageMargins -> {{5, 5}, {10, 10}}, Alignment -> Center]]},
MatrixPlot[mat, opts,
CoordinatesToolOptions -> {coordtooltips, copiedvalues}]]]

-
This is really cool and a best answer IMO. –  faleichik Mar 4 '12 at 16:47
@faleichik, thank you. I like your and Heikes LocatorPane approach better because it allows you to highlight the cells (which I do not know how to do with CoordinatesToolOptions) –  kguler Mar 5 '12 at 4:56
After trying it, I think it's very fast, but I had a problem, when using the output of this graphic into a GraphicsGrid, it looses the custom coordinates tooltips. Anyway this is the fastest one! –  FJRA Mar 6 '12 at 16:45

One way to do this:

is = 150; rm = RandomInteger[100, {5, 5}]; m =  Map[Tooltip[#, #] &, rm, {2}];
A = GraphicsGrid[m, ImageSize -> is {1, 1}]; B =  MatrixPlot[rm, FrameTicks -> None,
Mesh -> All, PlotRangePadding -> 0, ImageSize -> is {1, 1}]; Overlay[{A, B}, All, 1]


It'll be a bit more elaborate to add frame ticks.

-
Good option, but tooltips only show up when you are in the center of the squares (if they are bigger than text numbers behind). I like that this is simple enough to be done without references once you know it :P. –  FJRA Mar 2 '12 at 15:55
@FJRA Yes, thanks. Overlay` is neat but often overlooked function - plus it preserves interactivity ;) –  Vitaliy Kaurov Mar 2 '12 at 22:33