BarLegend-Ticks in MatrixPlot verzerrt

Sep 04 2020

Wie in vielen Threads hier gezeigt ( dies , das , ...), Ticksist eine gültige Option von BarLegend. Aber so einfach scheint es nicht gut zu funktionieren MatrixPlot. Einige Zecken fehlen nur und 0,5 ist offensichtlich nicht an der richtigen Stelle (Farbe). Beachten Sie, dass ich das Standarddiagramm mit benutzerdefinierten Häkchen verwenden möchte. Was fehlt mir hier oder eine Problemumgehung?

data = Table[Sin[x] Cos[y], {x, 0, 2 Pi, 0.01}, {y, 0, 2 Pi, 0.01}];
MatrixPlot[data, 
 PlotLegends -> BarLegend[Automatic, "Ticks" -> {-0.5, 0, 0.5}], 
 LabelStyle -> Large]

Aktualisieren:

Ein allgemeinerer Fall mit asymmetrischem Datenbereich

data = Table[Sin[x] Cos[y] + 0.05 x y, {x, 0, 2 Pi, 0.01}, {y, 0, 2 Pi, 0.01}]; 
MatrixPlot[data, PlotLegends -> BarLegend[Automatic, "Ticks" -> {-0.2, 0.5, 1.1}], 
LabelStyle -> Large]

Antworten

9 kglr Sep 04 2020 at 07:04

Update: Die Funktion Graphics`ArrayPlotDump`Private`HybridRankingAndNaturalScaleführt die mysteriöse "Skalierung basierend auf einer Mischung aus relativem Wert und Rang für jedes Matrixelement" durch . Wir konstruieren eine stückweise Neuskalierungsfunktion ( reScale) mit mpReScaleund verwenden sie, um den Optionswert für Folgendes anzugeben "Ticks":

ClearAll[reScale,  cfMinMax]
reScale[{min_, max_}, {cfmin_, cfmax_}]  := 
  If[# < 0, Rescale[#, {min, 0}, {cfmin, 1/2}], Rescale[#, {0, max}, {1/2, cfmax}]] &;

cfMinMax = MinMax @ Graphics`ArrayPlotDump`Private`HybridRankingAndNaturalScale[
   Union @ SparseArray[#]["NonzeroValues"], 0., {0, 1}, .5] &;

Beispiele:

data = {{1, 2, 1}, {2, 0, 1}, {0, -5, -1}};
ticks = {-4, -2, 3/2, 2};

cfminmax = cfMinMax[data];
minmax = MinMax @ data;
 
Row[{MatrixPlot[data, ImageSize -> 400, 
   PlotLegends -> BarLegend[Automatic], LabelStyle -> 16, 
   PlotLabel -> "default"], 
  MatrixPlot[data, ImageSize -> 400, 
   PlotLegends -> BarLegend[Automatic, 
     "Ticks" -> (Transpose[{reScale[minmax, cfminmax] /@ #, #}] & @ ticks)], 
   LabelStyle -> 16, 
   PlotLabel -> Row[{"Ticks : ", ticks}]]}, Spacer[10]]

Nebenbei: Wir können eine andere undokumentierte Option verwenden, um Tick-Labels anzugeben

BarLegend[Automatic, "TickLabels" -> ticks, 
    "Ticks" -> (reScale[minmax, cfminmax] /@ ticks)]

um das Bild in der zweiten Handlung oben zu bekommen.

Ändern dataund tickszu

data = Table[Sin[x] Cos[y], {x, 0, 2 Pi, 0.01}, {y, 0, 2 Pi, 0.01}];
ticks = {-0.5, 0, 0.5};

bekommen

Verwenden Sie das zweite Beispiel in OP:

data = Table[Sin[x] Cos[y] + 0.05 x y, {x, 0, 2 Pi, 0.01}, {y, 0, 2 Pi, 0.01}];
ticks = {-0.2, 0.5, 1.1};

wir bekommen

Mit

data = 1. + Table[Sin[x] Cos[y] + 0.05 x y, {x, 0, 2 Pi, 0.01}, {y, 0, 2 Pi,  0.01}] ;
ticks = {0.1, 0.5, 1.1, 2};

wir bekommen

Ursprüngliche Antwort:

Dies scheint mit der speziellen Art und Weise der Skalierung zu tun zu haben, MatrixPlotwie in MatrixPlot >> Details und Optionen beschrieben

Im Fall von OP ist es am einfachsten, die Tick-Spezifikation zu ändern in Transpose[{Rescale[#, {-1, 1}, {0, 1}], #} &@{-0.5, 0, 0.5}]:

MatrixPlot[data, 
 PlotLegends -> 
  BarLegend[Automatic, 
   "Ticks" -> Transpose[{Rescale[#, {-1, 1}, {0, 1}], #} & @ {-0.5, 0, 0.5}]], 
 LabelStyle -> Large]

Verwenden Sie als alternative (allgemeinere) Problemumgehung die Standardfarbfunktion mit neu skaliertem Argument und der Option ColorFunctionScaling -> False:

defaultCF = "DefaultColorFunction" /.
    (Method /. Charting`ResolvePlotTheme[Automatic, MatrixPlot])
 Blend[System`PlotThemeDump`$ThemeDefaultMatrix, #1] &
MatrixPlot[data, 
 ColorFunction -> (defaultCF[Rescale[#, {-1, 1}]] &), 
 ColorFunctionScaling -> False, 
 PlotLegends -> BarLegend[Automatic, "Ticks" -> {-0.5, 0, 0.5}], 
 LabelStyle -> Large]

Alternativ können Sie die Farbfunktion angeben in BarLegend:

MatrixPlot[data, 
 PlotLegends -> BarLegend[{defaultCF[Rescale[#, {-1, 1}]] &, {-1, 1}}, 
   ColorFunctionScaling -> False, "Ticks" -> {-0.5, 0, 0.5}], 
 LabelStyle -> Large]

3 xzczd Sep 07 2020 at 08:38

kglr hat das Problem bereits gelöst. Hier sind nur einige zusätzliche Analysen und eine weitere mögliche Problemumgehung.

Erstens denke ich nicht, dass dies ein Fehler ist, da standardmäßig MatrixPlotweiß, wie man richtig einstellt Ticksfür BarLegend:

plot = MatrixPlot[data, ImageSize -> 400, PlotLegends -> Automatic]

Wenn plotwir uns das ansehen, stellen wir fest, dass:

plot[[2, 1]] // InputForm
(*
BarLegend[{Blend[System`PlotThemeDump`$ThemeDefaultMatrix, #1] & , 
  {0.2889327547713697, 1.}}, LabelStyle -> {}, 
 LegendLayout -> "Column", LegendMarkerSize -> 400, 
 Ticks -> {{0.39446607623278385, -0.5}, {0.5, 0.}, {0.3100389372190109, 
  -0.9}, {0.626985112913238, 0.5}, {0.753970225826476, 1.}, 
  {0.880955338739714, 1.5}, {0.9825434290703043, 1.9000000000000001}}, 
 "PinningPoint" -> 0.5, "SmoothRange" -> False, 
 Charting`TickSide -> Right, ColorFunctionScaling -> False]
 *)

Wie wir sehen können, werden die Ticks von BarLegenddurch die undokumentierte TicksOption gesetzt. Wir zeichnen die Ticks:

autotick = Cases[plot[[2, 1]], (Ticks -> a_) :> a][[1]] // Sort

ListLinePlot[autotick]

Es ist nicht schwer zu bemerken, dass es sich um eine stückweise Linie handelt, die sich aufteilt {0.5, 0.}. Wir können dies weiter überprüfen mit LinearModelFit:

LinearModelFit[autotick[[#]], {1, x}, x]["RSquared"] & /@ {;; 3, 3 ;;}
(* {1., 1.} *)

Selbst wenn wir uns dessen nicht bewusst sind Graphics`ArrayPlotDump`Private`HybridRankingAndNaturalScale, können wir die Zecken auf folgende Weise neu skalieren:

ticks = {-0.2, 0.5, 1.1}

Clear[rescale]
rescale[tick_List, rest__] := rescale[#, rest] & /@ tick
rescale[tick_?Positive, mysteryminmax_, {min_, max_}] := 
 Rescale[tick, {0, max}, {1/2, mysteryminmax[[2]]}]
rescale[tick_, mysteryminmax_, {min_, max_}] := 
 Rescale[tick, {min, 0}, {mysteryminmax[[1]], 1/2}]

plot /. (Ticks -> _) :> (Ticks -> {rescale[ticks, 
       Sequence @@ (autotick[[{1, -1}]]\[Transpose])], ticks}\[Transpose])