10
$\begingroup$

Bug introduced in 10.0 and fixed in 11.0


I am using ArrayPlot to show some arrays with values from 0 to 1. I want 0 to be black and 1 to be white, so I am using:

ColorFunction -> Function[a, RGBColor[a, a, a]]

(I realize I could use GrayLevel, but I eventually want to play with the components of RGBcolor).

The problem is that, in the context of ArrayPlot, this color function seems to misbehave with zero arrays. It plots the array with cells that are empty (opacity 0) instead of black. Even one value in the array greater than zero, however small, and all the cells plot fine.

Note that I am using ColorFunctionScaling -> False.

I finally discovered a work-around by using the explicit alpha channel of RGBColor, but I think that should not be necessary.

Code example

{ArrayPlot[{{0}}, 
   ColorFunction -> Function[a, RGBColor[a, a, a]], ColorFunctionScaling -> False], 
 ArrayPlot[{{0}}, 
   ColorFunction -> Function[a, RGBColor[a, a, a, 1]], ColorFunctionScaling -> False]}

The first produces an empty square (transparent), the second a black one. Both should be black, no?

Other information

  • The same happens with MatrixPlot.
  • No problem with other plot functions, like DensityPlot, or ListDensityPlot (even with InterpolationOrder -> 0)
  • I'm using Mathematica 10.4.0.0
$\endgroup$
  • 2
    $\begingroup$ This does look like some kind of bug, e.g. if your color function is Function[a,RGBColor[1,a,a]] it returns a red square. It seems to just not like a black square. If you look at the FullForm it uses a SparseArray with no elements to represent your data, even though you didn't ask it to, but only in the case when all the data is BLACK with no alpha channel. ColorRules->{0->Black,1->White} produces the same result. Another workaround is to do RGBColor[a+$MachineEpsilon,a,a]. $\endgroup$ – N.J.Evans May 26 '16 at 22:09
  • 2
    $\begingroup$ I believe this is the intended function of ArrayPlot. From the Details and Options section of the ArrayPlot documentation: "If array contains 0s and 1s, the 1s will appear as black squares and the 0s as white squares." $\endgroup$ – Rashid May 26 '16 at 22:12
  • 1
    $\begingroup$ Since ArrayPlot returns Graphics[Raster[data],opts], it might be easier to work directly with Raster,which supports RGB entries. $\endgroup$ – Rashid May 26 '16 at 23:12
  • 2
    $\begingroup$ @Rashid That statement is about the default behavior; it doesn't apply if you're specifying ColorRules or ColorFunction. $\endgroup$ – Brett Champion May 26 '16 at 23:33
  • 2
    $\begingroup$ @J.M. It works properly using version 9.0.1. My answer is based on version 10.4.1, Windows 10 x64. $\endgroup$ – Karsten 7. May 27 '16 at 3:24
8
$\begingroup$

Workarounds until this bug has been fixed:

1) ArrayPlot workaround

Using

arrayPlot[data_List] := Block[{list = Reverse@data, Reverse},
  Reverse[x_] := x;
  ArrayPlot[list, ColorFunction -> Function[a, RGBColor[a, a, a]], 
   ColorFunctionScaling -> False]
  ]

instead of ArrayPlot.

2) Fixing Reverse

Unprotect@Reverse;
Reverse[x_SparseArray] := SparseArray[Reverse[Normal@x]]
Protect@Reverse;

Tracking down the problem

I think this is a bug due to the creation of an improper SparseArray for the Raster of the ArrayPlot.

ap = ArrayPlot[{{0}}, ColorFunction -> Function[a, RGBColor[a, a, a]], 
      ColorFunctionScaling -> False];

{sa} = Cases[ap, _SparseArray, Infinity]

sa

The normal expression

Normal[sa]

{{0.}}

looks OK, but

InputForm[sa]

SparseArray[Automatic, {1, 1}, 0., {1, {{0, 0}, {}}, Pattern}]

looks suspicious to me, because of Pattern at the end.

If this SparseArray is replaced with its Normal form, the expected output is produced.

ap /. _SparseArray -> Normal[sa]

A replacement with SparseArray[{{0.}}] also results in the correct output.

ap /. _SparseArray -> SparseArray[{{0.}}]

SparseArray[{{0.}}]

newSA

% // InputForm

SparseArray[Automatic, {1, 1}, 0., {1, {{0, 0}, {}}, {}}]


The flawed SparseArray is created by the evaluation of

SparseArray[{{0.}}] // Reverse 

inside of

Graphics`ArrayPlotDump`Private`ArrayPlotInternal[False, {{0}}, 
 ColorFunction -> Function[a, RGBColor[a, a, a]], ColorFunctionScaling -> False]

which is called by ArrayPlot[{{0}}, ColorFunction -> Function[a, RGBColor[a, a, a]], ColorFunctionScaling -> False].


SparsArrays with all elements being 0 having other dimensions are effected by the same bug:

SparseArray[{{0., 0, 0}}] // Reverse // InputForm

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

SparseArray[{{0., 0}, {0., 0}}] // Reverse // InputForm

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


The reason why using ColorFunction -> Function[a, RGBColor[a, a, a, 1]] is not effected by this bug is that

Graphics`ArrayPlotDump`Private`ConvertToColorTensor[{{0.}}, -∞, ∞, 
 Function[a, RGBColor[a, a, a, 1]], "Automatic", False, None, Automatic, False]

evaluates to the List

{{{0., 0., 0., 1.}}}

while

Graphics`ArrayPlotDump`Private`ConvertToColorTensor[{{0.}}, -∞, ∞, 
 Function[a, RGBColor[a, a, a]], "Automatic", False, None, Automatic, False]

returns a SparseArray

SparseArray_PNG

$\endgroup$
  • $\begingroup$ This problem had cropped up not long ago in the context of MatrixPlot; ArrayPlot had also been mentioned as having the same problem. At that time we were convinced it was a likely bug, but couldn't find its origin. I think your analysis goes quite a bit further than I could at that time. Thanks! $\endgroup$ – MarcoB May 27 '16 at 6:20

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.