Kutu BarLegend terdistorsi di MatrixPlot

Sep 04 2020

Seperti yang ditunjukkan di banyak utas di sini ( ini , ini , ...), Ticksadalah opsi yang valid dari BarLegend. Tapi tampaknya tidak berhasil dengan baik dalam hal sederhana ini MatrixPlot. Beberapa tanda centang hilang begitu saja dan 0,5 jelas tidak berada di tempat yang tepat (warna). Perhatikan bahwa saya ingin plot default dengan kutu khusus. Apa yang saya lewatkan di sini atau solusi lainnya?

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]

Memperbarui:

kasus yang lebih umum dengan rentang data asimetris

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]

Jawaban

9 kglr Sep 04 2020 at 07:04

Pembaruan: Fungsi Graphics`ArrayPlotDump`Private`HybridRankingAndNaturalScalemelakukan "penskalaan berdasarkan campuran nilai relatif dan peringkat untuk setiap elemen matriks" yang misterius . Kami membangun fungsi penskalaan ulang sebagian ( reScale) menggunakan mpReScaledan menggunakannya untuk menentukan nilai opsi untuk "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] &;

Contoh:

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]]

Sebuah samping: Kita dapat menggunakan opsi lain yang tidak terdokumentasi untuk menentukan label centang

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

untuk mendapatkan gambaran pada plot kedua diatas.

Ubah datadan ticksmenjadi

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

mendapatkan

Menggunakan contoh kedua di 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};

kita mendapatkan

Dengan

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};

kita mendapatkan

Jawaban asli:

Ini tampaknya terkait dengan cara khusus penskalaan dilakukan MatrixPlotseperti yang disebutkan dalam MatrixPlot >> Detail dan Opsi

Perbaikan termudah dalam kasus OP adalah mengubah spesifikasi tick menjadi 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]

Sebagai alternatif (lebih umum) penyelesaian, gunakan fungsi warna default dengan argumen skala ulang dan opsi 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]

Atau, tentukan fungsi warna di 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 telah menyelesaikan masalah, berikut ini hanya beberapa analisis tambahan dan kemungkinan penyelesaian lainnya.

Pertama-tama, menurut saya ini bukan bug, karena secara default MatrixPlottahu cara mengatur yang tepat Ticksuntuk BarLegend:

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

Melihat ke dalam plot, kami menemukan bahwa:

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]
 *)

Seperti yang bisa kita lihat, tanda centang BarLegendditetapkan oleh Ticksopsi yang tidak terdokumentasi , kami memplot tanda centang:

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

ListLinePlot[autotick]

Tidak sulit untuk menyadari bahwa itu adalah garis yang terpecah belah {0.5, 0.}. Kami selanjutnya dapat memverifikasi ini dengan LinearModelFit:

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

Jadi, meskipun kami tidak menyadarinya Graphics`ArrayPlotDump`Private`HybridRankingAndNaturalScale, kami masih dapat mengubah skala kutu dengan cara berikut:

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])