Пользовательская цветовая функция / ColorData в ArrayPlot (и аналогичные функции)

#wolfram-mathematica

#wolfram-mathematica

Вопрос:

Это связано с вопросом Саймона об изменении цветовых данных по умолчанию в Mathematica. Хотя все решения касались проблемы изменения ColorData линейных графиков, я не совсем счел обсуждение полезным при изменении ColorFunction / ColorData в ContourPlot / ArrayPlot / Plot3D и т.д.

TLDR: Есть ли способ заставить mma использовать пользовательские цвета в ArrayPlot / ContourPlot / etc.


Рассмотрим следующий пример построения графика функции sin(x^2 y^3) , который я создал в MATLAB:

введите описание изображения здесь

Теперь делаем то же самое в mma, что и:

 xMax = 3; yMax = 3;
img = Transpose@
   Table[Sin[y ^3   x^2], {x, -xMax, xMax, 0.01}, {y, -yMax, yMax, 
     0.01}];
plot = ArrayPlot[img, ColorFunction -> ColorData["Rainbow"], 
   AspectRatio -> 1, 
   FrameTicks -> {FindDivisions[{0, (img // Dimensions // First) - 1},
       4], FindDivisions[{0, (img // Dimensions // Last) - 1}, 4], 
     None, None}, 
   DataReversed -> 
    True] /. (FrameTicks -> {x_, 
      y_}) :> (FrameTicks -> {x /. {a_?NumericQ, b_Integer} :> {a, 
         2 xMax (b/((img // Dimensions // First) - 1) - 1/2)}, 
      y /. {a_?NumericQ, b_Integer} :> {a, 
         2 yMax (b/((img // Dimensions // Last) - 1) - 1/2)}})
  

Я получаю следующий график:

введите описание изображения здесь

Я предпочитаю насыщенные, яркие цвета в MATLAB пастельным / тусклым цветам mma. Как мне заставить mma использовать эти цвета, если у меня есть значения RGB цветовой карты из MATLAB?

Вы можете загрузить значения RGB цветовой карты по умолчанию в MATLAB и импортировать ее в mma как

 cMap = Transpose@Import["path-to-colorMapJet.mat", {"HDF5", 
      "Datasets", "cMap"}];
  

cMap представляет собой 64x3 массив значений между 0 и 1 .

Просто чтобы дать вам некоторое представление, вот соответствующий текст из документации MathWorks по colormap

Цветовая карта представляет собой матрицу размером m на 3 с действительными числами в диапазоне от 0.0 до 1.0. Каждая строка представляет собой вектор RGB, который определяет один цвет. k-я строка цветовой карты определяет k-й цвет, где map(k,:) = [r(k) g(k) b(k)]) определяет интенсивность красного, зеленого и синего.

Здесь map=cMap , и m=64 .

Я попытался ткнуть в ColorDataFunction , и я вижу, что ColorData формат похож на colormap . Однако я не уверен, как заставить ArrayPlot использовать это (и, предположительно, это должно быть то же самое для других функций построения).


Кроме того, поскольку мое упражнение здесь направлено исключительно на достижение уровня комфорта в mma, аналогичного тому, что у меня есть в MATLAB, я был бы признателен за комментарии и предложения по улучшению моего кода. В частности, я не слишком доволен своим взломом способа «исправить» FrameTicks … наверняка должен быть более приятный / простой способ сделать это.

Комментарии:

1. Вы пробовали использовать ColorData [«LightTemperatureMap»] или ColorData [«TemperatureMap»] вместо «Rainbow»? Они создают гораздо более яркий график.

2. Есть выпускники-консультанты старше Йоды?

Ответ №1:

Замените свою ColorData["Rainbow"] на эту:

 Function[Blend[RGBColor @@@ cMap, Slot[1]]]
  

и вы получаете это:

введите описание изображения здесь


Что касается вашего второго вопроса, вы можете сделать это таким образом:

 xMax = 3; yMax = 3;
img = Transpose@
   Table[Sin[y^3   x^2], {x, -xMax, xMax, 0.01}, {y, -yMax, yMax, 
     0.01}];
plot = ArrayPlot[img, 
  ColorFunction -> Function[Blend[RGBColor @@@ cMap, Slot[1]]], 
  AspectRatio -> 1, FrameTicks -> Automatic, 
  DataRange -> {{-xMax, xMax}, {-yMax, yMax}}, DataReversed -> True]
  

введите описание изображения здесь

но почему вы не используете DensityPlot?

 DensityPlot[Sin[y^3   x^2], {x, -xMax, xMax}, {y, -yMax, yMax}, 
 ColorFunction -> Function[Blend[RGBColor @@@ cMap, Slot[1]]], 
 PlotPoints -> 300]
  

введите описание изображения здесь


Редактировать
Обратите внимание, что на втором графике обозначение y-диапазона отменено. Это потому, что он учитывает параметр DataReversed. ArrayPlot выводит строки массивов в том же порядке, в каком они отображаются при печати содержимого массива на экране. Таким образом, первая строка отображается сверху, а последняя строка — внизу. Высокие значения строк соответствуют низким значениям y и наоборот. DataReversed-> True исправляет это явление, но в данном случае оно также «исправляет» значения y. Обходной путь заключается в заполнении массива, начиная с высоких значений y, вплоть до более низких. В этом случае вам не нужен DataReversed:

 xMax = 3; yMax = 3;
img = Transpose@
   Table[Sin[y^3   x^2], {x, -xMax, xMax, 0.01}, {y, 
     yMax, -yMax, -0.01}];
plot = ArrayPlot[img, 
  ColorFunction -> Function[Blend[RGBColor @@@ cMap, Slot[1]]], 
  AspectRatio -> 1, FrameTicks -> Automatic, 
  DataRange -> {{-xMax, xMax}, {-yMax, yMax}}]
  

введите описание изображения здесь

Комментарии:

1. @Sjoerd: Это здорово! DataRange избавляет меня от множества ненужных замен. Вы правы, я тоже мог бы сделать это с помощью DensityPlot . Я просто использую значение по умолчанию ArrayPlot , потому что все мои выходные данные из MATLAB (которые я затем импортирую в mma) являются массивами. Это был просто пример, который имел аккуратную функциональную форму.

2. @Sjoerd: Почему DataRange вертикальная ось переворачивается? Если вы посмотрите на последние два графика в своем ответе, метки yaxis на ArrayPlot рисунке перевернуты.

3. @Sjoerd: Спасибо. Я также могу использовать Reverse@ для массивов, где я не контролирую порядок заполнения.

4. @Mr.Wizard Нет, это то же самое. Предупреждение для всех, кто собирается использовать эту версию: действительно, вы должны позаботиться о том, чтобы круглые скобки были там, потому что -> имеют более высокий приоритет, чем amp;

5. @yoda Вы правильно догадались. Попробуйте задать вопрос типа «Я знаю, как это сделать в Matlab, но как мне сделать это в Mathematica?». Модератор изменит это в «Я знаю, как это сделать в другой системе, но как …». Я предполагаю, что это для предотвращения флейм-войн, но я чувствую, что сравнение методов работы в различных системах CA не обязательно должно заканчиваться этим.

Ответ №2:

(Я надеюсь, что это не слишком позднее дополнение.)

Как выясняется, даже не нужно хранить весь набор из шестидесяти четырех RGBColor[] директив для целей использования с Blend[] подсказкой, что это, безусловно, так, предоставляется ListPlot[] s столбцов cMap :

 {rr, gg, bb} = Transpose[Rationalize[cMap]];
GraphicsGrid[{MapThread[
   ListPlot[#1, DataRange -> {0, 1}, Frame -> True, 
     GridLines -> {{1/9, 23/63, 13/21, 55/63}, None}, 
     PlotLabel -> #2] amp;, {{rr, gg, bb}, {"Red", "Green", "Blue"}}]}]
  

LisPlot [] ы компонентов RGB цветовой карты jet от MATLAB

и мы видим, что неявно функции, представляющие эти компоненты, являются кусочно-линейными. Поскольку Blend[] обязательно выполняется линейная интерполяция между цветами, если мы сможем найти те цвета, которые соответствуют «углам» в кусочно-линейных графиках, мы можем исключить все остальные цвета между этими углами (поскольку Blend[] интерполяция будет выполняться за нас), и, таким образом, потенциально придется переносить только, скажем, семь вместо шестидесяти четырех цветов.

Прочитав приведенный выше код, вы заметите, что я уже нашел для вас эти точки перехода (подсказка: проверьте настройку для GridLines ). Дополнительные подсказки о том, какими могут быть эти цвета, содержатся в документации для colormap() :

jet варьируется от синего до красного и проходит через цвета голубой, желтый и оранжевый.

Может ли это быть? Давайте проверим:

 cols = RGBColor @@@ Rationalize[cMap];
Position[cols, #][[1, 1]] amp; /@ {Blue, Cyan, Yellow, 
  Orange // Rationalize, Red}
{8, 24, 40, 48, 56}
  

Это просто указывает позиции цветов в массиве cols , но мы можем изменить масштаб, чтобы соответствовать диапазону аргументов, ожидаемому от цветовой карты:

 (# - 1)/(Length[cols] - 1) amp; /@ %
{1/9, 23/63, 13/21, 47/63, 55/63}
  

и именно там находятся точки останова кусочно-линейных функций, соответствующих компонентам RGB цветовой карты. Это пять цветов; чтобы обеспечить плавную интерполяцию, мы также добавляем в этот список первый и последний цвета,

 cols[[{1, Length[cols]}]]
{RGBColor[0, 0, 9/16], RGBColor[1/2, 0, 0]}
  

сократите исходный cols список в общей сложности до семи. Поскольку 7/64 составляет примерно 11%, это довольно большая экономия.

Таким образом, цветовая функция, которую мы ищем, является

 jet[u_?NumericQ] := Blend[
        {{0, RGBColor[0, 0, 9/16]}, {1/9, Blue}, {23/63, Cyan}, {13/21, Yellow},
         {47/63, Orange}, {55/63, Red}, {1, RGBColor[1/2, 0, 0]}}, 
                          u] /; 0 <= u <= 1
  

Для проверки мы проводим два сравнения jet[] . Вот график градиента, сравнивающий ColorFunction s jet и Blend[cols, #]amp; :

 GraphicsGrid[{{
   Graphics[Raster[{Range[100]/100}, ColorFunction -> (Blend[cols, #] amp;)], 
    AspectRatio -> .2, ImagePadding -> None, PlotLabel -> "Full", 
    PlotRangePadding -> None], 
   Graphics[Raster[{Range[100]/100}, ColorFunction -> jet], 
    AspectRatio -> .2, ImagePadding -> None, 
    PlotLabel -> "Compressed", PlotRangePadding -> None]}}]
  

сравнение цветового градиента струйной и явной 64-цветовой смеси

и вот механическая проверка того, что 64 цвета в cols хорошо воспроизведены:

 Rationalize[Table[jet[k/63], {k, 0, 63}]] === cols
True
  

Теперь вы можете использовать jet[] как ColorFunction для любой функции построения графиков, которая ее поддерживает. Наслаждайтесь!