Как в maple построить несколько графиков на одном
Важное значение имеет возможность построения на одном рисунке графиков нескольких функций. В простейшем случае для построения таких графиков достаточно перечислить нужные функции и установить для них общие интервалы изменения.
Обычно графики разных функций автоматически строятся разными цветами. Но это не всегда удовлетворяет пользователя — например, при распечатке графиков монохромным принтером некоторые кривые могут выглядеть слишком блеклыми или даже не пропечататься вообще. Используя списки параметров color (цвет линий) и style (стиль линий), можно добиться выразительного выделения кривых — когда линии графиков выделяются стилем. В то же время при задании кривых разным цветом они при черно-белой печати могут перестать различаться.
Рисунок 3 — График функции sin(x)/x и ее полиномиальной аппроксимации
На рис. 3 показан еще один пример такого рода. Здесь построен график функции sin(x)/x и график ее полиномиальной аппроксимации. Она выполняется настолько просто, что соответствующие функции записаны прямо в списке параметров функции plot.
В данном случае сама функция построена сплошной линией, а график полинома точками — ромбами. Хорошо видно, что при малых х аппроксимация дает высокую точность, но затем с ростом х ее погрешность резко возрастает.
Показанный на рис. 3 график полинома, построенный ромбиками, не означает, что полином представлен отдельными точками. В данном случае просто выбран стиль линии в виде точек. Однако часто возникает необходимость построения графиков функций, которые представлены просто совокупностями точек, т. е. по существу таблично. Такая совокупность может быть создана искусственно, либо просто задаваться списком координат х и значений функции.
В данном случае переменная Р имеет вид списка, в котором попарно перечислены координаты точек функции sin(x). В этом нетрудно убедиться, заменив знак «:» после выражения, задающего Р, на знак «;». Далее по списку Р построен график точек в виде кружков, которые отображают отдельные значения функции sin(x).
На рис. 4 показано построение графиков функций по точкам при явном задании функции списком координат ее отдельных точек. В первом примере эти точки соединяются отрезками прямых, так что получается кусочно-линейный график. Видно также, что указание типа точек после указания стиля линии игнорируется.
Во втором примере (рис. 4) показано построение только точек заданной функциональной зависимости. Они представлены маленькими кружками.

Рисунок 4 — Построение графика функции, явно заданной отдельными точками
Основные объекты и команды Maple. §2.1 Способы задания функций. Замена переменных Maple как работать на нескольких страницах одновременно
Математический пакет Maple предоставляет возможность пользователям составлять собственные программы, процедуры и библиотеки. Для этого в пакете существует довольно широкий набор команд и конструкций аналогичный алгоритмическим языкам программирования высокого уровня.
10.1. Условный оператор
Условный оператор в Maple начинается с зарезервированного слова if и обязательно должен заканчиваться, словом fi и имеет следующую структуру:
if условие then выражение 1 else выражение 2 fi ;
Данная конструкция дает возможность зависимости от значения логического условия выполнять выражение 1 (в случае если условие истинно) или выражение 2 (в случае если условие ложно). В качестве выражений 1 или 2 могут выступать любые последовательности команд из пакета Maple. Условный оператор может быть записан в сокращенном виде:
if условие then выражение 1 fi ;
[> restart;
[>if x>4 then print (‘x>4’); else x:=x^2;
print (2*x); fi;
Для реализации сложных условий необходимо использовать полный вариант условного оператора, который имеет следующую структуру.
if условие 1 then выражение 1 elif условие2 then выражение2 … elif условие n then выражение n else выражение n +1 fi ;
Как следует из структуры данного оператора вложенность условий может быть практически неограниченной и реализуется при помощи служебного слова elif . В качестве выражений можно использовать любые последовательности команд Maple.
[> restart;
[>if x
10. 2 . Операторы цикла
В математическом пакете Maple для реализации циклического вычислительного процесса используются четыре вида операторов цикла. Телом всех операторов цикла является последовательность команд, заключенных между служебными словами do и od . Оператор цикла перечисляемого типа, который содержится практически во всех алгоритмических языках имеет, следующую структуру:
for имя переменной цикла from начальное значение переменной цикла by шаг приращения значения переменной цикла to конечное значение переменной цикла
[>for i from 0 by 4 to 8 do i od;
Оператор цикла типа «пока» в Maple имеет вид
while условие do выражение od ;
В данном случае тело цикла (выражение) выполняется до тех пор, пока значение логического условия истинно и прекращается, если условие — ложно.
[> restart;
[>while n
Следующий оператор цикла является симбиозом двух предыдущих и имеет следующую структуру:
for имя переменной цикла from начальное значение переменной цикла by значение приращение шага while условие do выражения od ;
В данном операторе цикла выражения выполняются до тех пор, пока логическое выражение условия является истинным, а переменная цикла изменяется от своего начального значения с заданным шагом.
[> restart;
[> for y from 0 by 2 while y
Четвертый оператор цикла предназначен для работы с аналитическими выражениями и представляется следующей структурой:
for имя переменной цикла in выражение 1 do выражение 2 od ;
Здесь тело цикла выражение 2 выполняется, в случае если символьная переменная заданная своим именем последовательно принимает значение каждого из операндов алгебраического выражения 1. Отметим, что работа данной конструкции зависит от внутреннего представления выражения 1. Так в случае если выражение 1 является суммой, то имя переменной цикла принимает поочередно значение каждого слагаемого, если произведение – то каждого сомножителя.
[> restart;
[> a:=5*x^2+x+6/x;
[> b:=simplify(%);
[> for m in a do m; od;
[> for m in b do m; od;
10.3. Процедуры-функции
Процедуры-функции в Maple можно задавать двумя способами. Для задания процедур-функций первый способ использует символ ( ) и задается следующей структурой:
имя функции:=(список формальных параметров) выражение;
где имя функции задается набором символов латинского алфавита, список формальных параметров вводится через запятую. Выражение – команда Maple, реализующая тело процедуры-функции.
[> f1:=(x1,x2)->simplify(x1^2+x2^2);
[> f 1 (cos(x),sin(x));
Второй способ задания процедур-функций использует команду unapply и имеет следующую структуру:
имя функции:= unapply (выражение или операция, список переменных);
Этот способ задания процедур-функций полезен при определении новой функции через известную или, когда вычисленное выражение предполагает использовать как функцию.
[> f3:=unapply(diff(z(r)^2,r)-2,z);
[ > f3(sin);
[ > combine(%);
10.4. Процедуры
Любая процедура в Maple начинается с заголовка, состоящего из имени процедуры, за которым следует знак присваивания и служебное слово proc , далее в круглых скобках через запятую указываются формальные параметры. Процедура обязательно заканчивается служебным словом end . Все выражения и команды заключенными между служебными словами proc и end составляют тело процедуры.
имя процедуры:= proc (список формальных параметров); команды (или выражения); end ;
Если процедура загружена, то ее вызов осуществляется по имени. Возвращаемым значением по умолчанию является значение последнего выполненного оператора (команды) из тела процедуры, при этом тип результата работы процедуры зависит от типа возвращаемого значения.
[> f:=proc(x,y);x^2+y^2;simplify(%);end:
[ > f(sin(x),cos(x));
При написании процедур в Maple можно использовать ряд команд и служебных слов, кроме указанного выше обязательного минимального набора, которые позволяют описывать переменные, управлять выходом из процедуры, сообщать об ошибках.
При описании формальных параметров процедуры можно явно задавать их тип через двоеточие. При таком описании Maple автоматически проверяет тип фактического параметра и выдает сообщение об ошибки в случае его несовпадения с типом формального параметра.
После заголовка процедуры может следовать описательная часть процедуры, отделяющаяся от него пробелом. При описании локальных переменных, используемых только внутри данной процедуры можно использовать описатель, который задается служебным словом local , после которого через пробел необходимо указать имена локальных переменных. Использование глобальных переменных в процедуре можно задавать служебным словом global , который должен размещаться в описательной части процедуры.
Для выхода из процедуры в любом месте ее тела и присваивания результату ее работы по выполнению нужной команды можно использовать команду RETURN ( val ), где val – возвращаемое значение, которое может иметь различный тип при выходе из разных мест процедуры.
Для аварийного выхода из процедуры в случае возникновения ошибки и сообщения о случившемся можно использовать команду ERROR (‘ string ’) , здесь string – сообщение, которое выводится на экран монитора в аварийной ситуации. Таким образом, общий вид структуры процедуры можно изобразить следующим образом:
имя процедуры:= proc (список параметров процедуры) local список локальных переменных, приведенных через запятую; global список глобальных переменных, приведенных через запятую; RETURN ( val ); ERROR (‘ error in body of procedure ’);… end ;
[ > examp(-1);
[> examp(0);
[ > examp(2);
11. СПОСОБЫ ВВОДА И ВЫВОДА ИНФОРМАЦИИ
В СРЕДЕ MAPLE
Для сохранения имен (индентификаторов) переменных и их значений во внешнюю память в виде файла с именем name . txt необходимо ввести команду:
save список имен переменных, перечисленных через запятую, “имя файла с расширением txt ”;
Если в качестве расширения указан символ m , то файл будет записан во внутреннем Maple-формате, при всех других расширениях в текстовом формате. Для вывода на экран сохраненной в файле информации используется команда
read “ имя файла ”;
[> restart;
[> examp:=proc(x) local y,w; global z; if x
[ > examp(-1);
[> examp(0);
Error, (in examp) Variablex = 0
[ > examp(2);
[ > read «nnn.txt»;
Для записи всего содержимого экрана в файл можно использовать следующие две команды.
writeto (“имя файла”)
в результате выполнения этой команды вся информация, содержащаяся на экране, будет сохранена в файле с указанным именем. Причем, если указанный файл существовал во внешней памяти, то хранящаяся информация будет заменена на новую.
appendto (“имя файла”)
дает возможность добавить информацию, отображаемую на экране, после данной команды в конец существующего файла.
[ > f:=12;
[> f1:=factor (y^2-3*y); save f,f1, «n1.txt»;
[> appendto («n1.txt»);
[> solve(x^2-3*x+2=0,x);
В результате выполнения команды save f , f 1, « n 1. txt «; будет создан текстовый файл n 1. txt , который будет содержать следующую информацию:
f:= 12;
f1:= y*(y-3);
а в результате выполнения команды appendto (« n 1. txt «); содержимое файла примет вид:
f:= 12;
f1:= y*(y-3);
[ > solve ( x ^2-3* x +2=0, x );
В пакете Maple предусмотрен ряд команд вывода информации на экран. Наиболее простыми из них являются команды
print (список Maple
lprint (список Maple -выражений, перечисляемых через запятую);
причем, если переменной ничего не присвоено, то на печать выводиться ее имя, в противном случае выводится ее значение.
[> x:=y^2: print (x, «primer 1», y, factor(x-5*y));
[> x:=y^2: lprint (x, «primer 2», y, factor(x-5*y));
y^2, primer 2, y, y*(y-5)
Из приведенных примеров следует, что команда print выводит выражения через запятую в естественном математическом виде, а команда lprint выводит информацию в стиле строки вывода и выражения отделяются друг от друга запятой и пробелами.
Пакет Maple можно использовать для анализа и графической интерпретации числовой информации, находящейся в текстовом файле, полученной как при помощи самого пакета, так и других программных приложений. Как правило, в текстовом файле числа записаны по строкам. Для считывания числовой информации из текстового файла можно использовать команду:
readdata (“имя файла”, тип переменной( integer / float – последний тип устанавливается по умолчанию),счетчик чисел);
Перед использованием данной команды необходимо ее активизировать при помощи команды:
readlib(readdata):
[> restart;
[> readlib(readdata):
[> ff:=readdata(«aa.txt»,integer,8);
[ > x:=ff;
[ > y:=x;
[ > y1:=ff;
[ > f:=readline(«aa.txt»);
Двойная индексация в переменной ff связана с тем, что числа представляются в виде двумерного массива, при этом число строк массива соответствует числу считанных строк, а количество столбцов определяется последним параметром команды readdata . Как следует из приведенного примера команда readline выводит числовые данные в виде переменной типа string .
12. ИСПОЛЬЗОВАНИЕ МАТЕМАТИЧЕСКОГО ПАКЕТА MAPLE ДЛЯ НАУЧНЫХ ИССЛЕДОВАНИЙ
В данном разделе рассмотрим пример исследования средствами Maple решения прикладных инженерных задач. Приведенные примеры показывают возможности пакета Maple при решении инженерных задач, связанных с исследованием режимов работы оборудования, в зависимости от конструктивных и технологических параметров, комплексов и проиллюстрировать возможности программного и командного режимов работы пользователя в среде Maple. Далее приведены фрагменты исследований, сопровождаемые краткими пояснениями.
12.1. Исследование влияния изменяемых параметров плоской помольной камеры мельницы противоточного действия на скорость энергоносителя
12 .1.1. Постановка задачи
Струйные мельницы являются разновидностью ударных измельчителей и состоят из разгонного аппарата (одного или нескольких), в котором струя газа-энергоносителя сообщает, скорость частицам обрабатываемого материала, и камеры, в которой происходит взаимодействие потоков материала между собой и(или) со специальными отбойными поверхностями. В качестве энергоносителя в струйных мельницах чаще всего применяется воздух, реже – инертный газ, водяной пар, продукты сгорания.
Струйный помол дает возможность сочетания помола и разделения со смешением, сушкой и другими технологическими процессами. А работа в замкнутом цикле обеспечивает минимальное выделение пыли в окружающую среду.
Любой струйный аппарат включает в себя эжектор, представляющий собой узел, в котором происходит смешение и обмен энергией двух потоков (основного и эжектируемого) и помольную камеру, в которой взаимодействуют смешанные потоки. Ускоренные энергоносителем в разгонных трубках эжекторов частицы попадают в помольную камеру, а затем в зону встречи струй (рис. 12.1.).
Струя, выходящая из разгонной трубки, не сразу заполняет все поперечное сечение помолной камеры, струя в месте входа в нее отрывается от стенок и дальше движется в виде свободной струи, отделенной от остально среды поверхностью раздела. Поверхность раздела неустойчива, на ней возникают вихри, в результате чего струя перемешивается с окружающей средой.
При истечении струи из разгонной трубки скорости потока в ее выходном сечении 1-1 во всех точках сечения равны между собой. На протяжении длины– начальном участке, осевая скорость постоянна по величине и равна скорости на срезе разгонной трубки V 0 . В области треугольника АВС (рис. 12.1.) во всех точках струи скорости энергоносителя равны между собой и также равны V 0 — эта область образует так называемое ядро струи. Далее осевая скорость постепенно уменьшается и на основном участке длинной l осн осевая скорость V ОС V 0 .

Рис. 12.1. Схема струи в помольной камере
Известно, что скорость энергоносителя от среза разгонной трубки до плоскости соударения струй изменяется по закону
где V z – скорость энергоносителя с помольной камере на расстоянии z от среза разгонной трубки, м/с;
V 0 – скорость энергоносителя на срезе разгонной трубки, м/с;
z 0 – расстояние от среза разгонной трубки до плоскости встречи струй, м.
При определении изменения кинетической энергии конечного объема сплошной среды, необходимо знать работу сил межкомпонентного взаимодействия частиц измельченного материала и энергоносителя. Эта работа зависит от вектора силы динамического воздействия энергоносителя на частицу, которая вычисляется следующим образом
где R – вектор силы динамического воздействия воздуха на частицу, Н;
F m – площадь сечения частицы, м 2 ;
где m – масса частицы измельчаемого материала, кг.
где — плотность частиц измельчаемого материала, кг/м.
Выражение (12.7) примет вид
Полученное уравнение может быть использовано для определения изменения скорости частиц, измельчаемого материала в помольной камере на участве от среза разгонных трубок до области взаимодействия встречных потоков.
Система дифференциальных уравнений, описывающих процесс изменения скорости частиц и энергоносителя в помольной камере от среза разгонной трубки до области соударения встречных потоков
Расстояние l стр – между срезом разгонной трубки и серединной плоскость в помольной камере выбрано из условия
где d тр = 18 диаметр разгонной трубки, мм.
РЕШЕНИЕ МАТЕМАТИЧЕСКИХ ЗАДАЧ
В MAPLE
ЧАСТЬ I
ФЕДЕРАЛЬНОЕ АГЕНТСТВО ПО ОБРАЗОВАНИЮ
Государственное образовательное учреждение высшего
«Нижегородский государственный университет им. »
МАТЕМАТИЧЕСКИХ ЗАДАЧ В MAPLE
факультета для студентов, обучающихся по
направлению подготовки 010100 — «Математика».
Решение задач в MAPLE. Часть I. Составители: , : Учебно-методическая разработка. – Н. Новгород: Издательство Нижегородского госуниверситета, 2007. – 35 с.
доцент кафедры ЧиФА факультета ВМК,
доцент кафедры ПиУОС Физического факультета,
Данная разработка представляет собой практическое руководство по изучению возможностей пакета аналитических вычислений Maple . Последовательное изучение тем и выполнение заданий позволит шаг за шагом освоить основные приемы работы в математической системе Maple .
Учебно-методическая разработка предназначена для студентов 2 и 3 курсов механико-математического факультета .
университет им. , 2007
Системы компьютерной алгебры – это новые технологии в научных исследованиях и образовании. В последние годы получили широкое распространение такие системы общего назначения, как Maple, Mathematica.
Система Maple включена в интегрированную систему Scientific WorkPlace и применяется во многих ведущих университетах мира как в научных исследованиях, так и в учебном процессе. Ядро Maple входит в другие распространенные пакеты, такие как MathCad, MathLab.
Данная разработка позволит начинающему войти в технологию использования системы Maple, получить первые навыки, после чего он сможет уже самостоятельно разобраться в более тонких вопросах использования Maple. Хотелось бы отметить, что эта разработка ни в коей мере не является описанием системы Maple. Она предназначена в первую очередь для обучения студентов-математиков решению основных математических задач при помощи Maple.
1. НАЧАЛЬНЫЕ СВЕДЕНИЯ. ТИПЫ ДАННЫХ
Диалог с системой протекает в стиле «вопрос-ответ». Команда начинается с символа > и заканчивается либо точкой запятой (; ), либо двоеточием (: ). Для выполнения команды необходимо нажать клавишу Enter . Если в конце команды стоит точка с запятой, то на экран будет выведен результат действия команды или сообщение об ошибке. Двоеточие в конце команды означает, что команда будет выполнена, но ее результат на экран не будет выводиться. Символ # используется для ввода текстовых комментариев. Также для ввода текста используется клавиша с символом T на панели инструментов. Для возвращения к вводу команд следует нажать клавишу с символом >. Для вызова результата действия предыдущих команд используются символы %, %% или %%%, соответственно. Команда restart отменяет результат действия всех предыдущих команд.
Переменные в Maple характеризуются именем и типом. Имя переменной в Maple может состоять из букв, цифр и некоторых специальных символов, но обязательно должно начинаться с буквы. Ограничений на длину имени нет. Кроме того, Maple различает строчные и прописные буквы. Для присваивания переменной конкретного значения применяется оператор := . Переменные могут использоваться в математических выражениях и функциях без предварительного определения.
Рассмотрим особенности записи в Maple данных числового, строкового и множественного типов.
Выражение принадлежит к целому типу (integer ), если оно состоит из последовательности цифр, не разделенных никакими знаками. Выражения вида a/b, где a, b – целые числа принадлежат к дробному типу (fraction ). К числам с плавающей точкой (float ) относятся выражения вида a. b, a. и. b. Также числа типа float можно записать в показательной форме a*10^b. Комплексные числа (complex ) в Maple записываются в алгебраической форме: a+I*b, где a, b – вещественные числа.
Строковое выражение типа string — это любая конечная последовательность символов, с обеих сторон заключенная в верхние двойные кавычки. Последовательность символов, взятая в обратные кавычки, считается символом (symbol ).
Множество (set ) в Maple задается перечислением в фигурных скобках элементов множества. Например,
https://pandia.ru/text/78/155/images/image003_72.gif» width=»197″ height=»26″>
Для создания массива используется команда array(i1..j1, i2..j2. M), которая возвращает массив с элементами из списка M.
Обращение к элементам множества, списка, массива происходит с указанием индексов элемента в квадратных скобках.
https://pandia.ru/text/78/155/images/image006_53.gif» width=»16 height=19″ height=»19″>
Массив также можно задать командой вида V:=array(1..2,1..2,1..2,); , переопределив затем значения V с помощью оператора присваивания.
В Maple можно записать буквы греческого алфавита в полиграфическом виде. Для этого в командной строке набирается название греческой буквы.
1. Задайте множество A, состоящее из целых чисел от 3 до 20, и множество B, состоящее из квадратов этих чисел. Найдите объединение, пересечение, разность множеств A и B. Найдите мощности всех полученных множеств.
2. Задайте произвольный список и четырехмерный массив.
2. АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ, ФУНКЦИИ. ПРЕОБРАЗОВАНИЯ АРИФМЕТИЧЕСКИХ
ВЫРАЖЕНИЙ И РЕШЕНИЕ УРАВНЕНИЙ
2.1. Вычисления в Maple
Для записи математических выражений в Maple используются операторы сложения (+), вычитания (-), умножения (*), деления (/), возведения в степень (^), оператор присваивания (:=). Порядок выполнения математических операций является стандартным.
Основные константы в Maple обозначаются следующим образом: Pi — число π, I — мнимая единица i, exp(1) — основание натуральных логарифмов e, infinity – бесконечность, true — истина, false – ложь. Используются следующие знаки сравнения: , >=, , = .
Система Maple одинаково успешно справляется как с символьными вычислениями, так и с численными. По умолчанию расчеты проводятся символьно.
>1/2+123/100+ sqrt (3);
Часть выражения, в которой встречается число, записанное с плавающей запятой (float), будет вычислена приближенно.
>2+ sqrt (2.0)- Pi ;
Все вычисления по умолчанию проводятся с десятью значащими цифрами. Количество значащих цифр можно изменить, применив команду > Digits : = n .
Для того, чтобы получить значение выражения в численном виде используется функция
https://pandia.ru/text/78/155/images/image012_43.gif» width=»414″ height=»19″>
2.2. Задание функций
В Maple встроено большое количество функций. Перечислим обозначения для основных элементарных функций..gif» width=»83 height=57″ height=»57″>
Рассмотрим несколько способов определения новых функций:
1) присваивание переменной некоторого выражения
имя переменной:=выражение;
имя переменной(список параметров):=выражение;
> f (t ):= cos (t )^2+1;
> f (0);
При таком способе задания функции для того, чтобы вычислить значение функции в некоторой точке нужно определить с помощью оператора присваивания значения переменных (параметров), либо использовать оператор подстановки subs.
https://pandia.ru/text/78/155/images/image018_28.gif» width=»106″ height=»33″>
https://pandia.ru/text/78/155/images/image021_25.gif» width=»100″ height=»33″>
>x := Pi : y :=10: f ;
Команда value (выражение) используется для вычисления значения выражения.
Следует обратить внимание на то, что после присвоения переменной x конкретного значения x:=a, переменная x перестанет быть неопределенной. Вернуть x статус непределенной переменной можно командой > x := evaln (x ); или снять присваивание командой > x :=’ x ’ ; либо отменить все присваивания командой restart .
2) Определение функции с помощью функционального оператора
имя функции:=список параметров -> выражение;
Обращение к функции, заданной таким способом, происходит стандартным образом: имя функции(a, b, …), где a, b, … — конкретные значения переменных.
> f 1:=(x , y , z ) -> x ^(y ^ z );
https://pandia.ru/text/78/155/images/image024_25.gif» width=»25″ height=»26 src width=»189″ height=»107″> используется команда
https://pandia.ru/text/78/155/images/image028_21.gif» width=»248″ height=»77″>
> f1:=convert(f, piecewise);
> f 2(-1/2); f 2(-1);
https://pandia.ru/text/78/155/images/image032_20.gif» width=»196″ height=»27″>.gif» width=»73″ height=»49″>. Упростите полученные выражения.
3. Найдите значение выражения . Для выполнения преобразований комплексного числа применяется функция evalc .
4. Запишите функцию без знака модуля.
5. Задайте и найдите f(-10)+3f(-1)+f(3).
6. Задайте функцию в виде функционального оператора и найдите ее значение при x=-1, y=.
2.3. Преобразование математических выражений
Maple обладает широкими возможностями для аналитических преобразований математических формул. К ним относятся такие операции, как приведение подобных, разложение на множители, раскрытие скобок, приведение рациональной дроби к нормальному виду и многие другие.
В Maple можно преобразовывать как все выражение в целом, так и отдельные его части.
Для выделения левой (правой) части в математическом выражении вида A=B используются команды
lhs (выражение);
rhs (выражение);
Для выделения числителя (знаменателя) используются команды
numer (выражение);
denom (выражение);
>denom (rhs (F ));
Для выделения некоторой части выражения, списка или множества служит команда
op (i ,выражение) , где i – число, определяющее позицию в выражении.
> x + y + z ; >op (2,%);
Gif» width=»12″ height=»12 src 54″ width=»266″ loading=lazy>
> isolate (R , sin (x ));
1) Приведение подобных членов в выражении по переменной осуществляется командой
https://pandia.ru/text/78/155/images/image047_14.gif» width=»439″ height=»28″>
2) Разложить на множители выражение можно с помощью команды
https://pandia.ru/text/78/155/images/image050_16.gif» width=»186″ height=»56″>
>alias(w=RootOf(x^3+2*x+1)); factor(x^3+2*x+1,w);
https://pandia.ru/text/78/155/images/image055_15.gif» width=»504″ height=»26 src width=»303″ height=»57″>
> factor(x^2+x+1,complex);
Gif» width=»12″ height=»12 src 58″ width=»173″ loading=lazy>
4) Привести дробь к нормальному виду можно с помощью команды
https://pandia.ru/text/78/155/images/image063_16.gif» width=»60″ height=»54″>
>normal(sin(2*x+3+4/(x-1)+5/(x-2)), expanded);
5) Для преобразований выражений, содержащих радикалы, применяется команда
rationalized « для того, чтобы избавиться от иррациональностей в знаменателях, « expanded « для раскрытия всех скобок.
> (7+5* sqrt (2))^(1/3);
> radnormal ((7+5* sqrt (2))^(1/3));
> a := sqrt (3)/(3^(1/2)+6^(1/2));
rationalized «);
6) Упрощение выражений осуществляется командой
DIV_ADBLOCK515″>
>simplify((sqrt(2)+sqrt(3))*(sqrt(2)-sqrt(3)));
> f:=(1-cos(x)^2+sin(x)*cos(x))/(sin(x)*cos(x)+cos(x)^2); simplify(f, trig);
Также в опциях указываются предположения о значении аргумента. Для формальных символьных преобразований многозначных функций в опциях можно указать symbolic .
> simplify(g, assume=real);
> simplify(g, assume=positive);
>simplify(g, symbolic);
Команда simplify позволяет выполнить преобразования в выражении при заданных условиях (условия указываются в фигурных скобках).
В некоторых случаях бывает полезно предварительно преобразовать выражение при помощи команды
https://pandia.ru/text/78/155/images/image076_12.gif» width=»276″ height=»54″>
>simplify(B, trig);
>simplify (%);
7) Объединить части выражения по определенным правилам можно при помощи команды
https://pandia.ru/text/78/155/images/image079_12.gif» width=»94″ height=»25 src width=»12″ height=»12 src 22″ width=»125″ loading=lazy>
> solve(x^4-11*x^3+37*x^2-73*x+70);
https://pandia.ru/text/78/155/images/image086_12.gif» width=»349″ height=»22 src >
> _EnvExplicit:=true:
Максимальное количество решений, которое может быть найдено с помощью команды solve, определяется значением глобальной переменной _MaxSols , имеющей по умолчанию значение равное 100. Если придать глобальной перменной _EnvAllSolutions значение true , то в случае бесконечного множества решений, команда solve для некоторых уравнений сможет оформить ответ в виде выражения, содержащего переменные определенного типа. Например, для тригонометрических уравнений ответ будет содержать целочисленные параметры вида _Z
> _EnvAllSolutions:= true:
>solve(sin(2*x)=cos(x), x);
https://pandia.ru/text/78/155/images/image094_11.gif» width=»274″ height=»51 src width=»12″ height=»12 src width=»641″ height=»23″>
Для разрешения реккурентностей применяется команда
https://pandia.ru/text/78/155/images/image098_10.gif» width=»255″ height=»22 src width=»119″ height=»23 src width=»180″ height=»56″>.
2. Упростите выражение .
3. Упростите выражение .
4.Приведите подобные в выражении и вычислите его значение при a=-3, x=1.
5. Упростите выражение а) ; б) .
6. Избавьтесь от иррациональностей в знаменателе выражения .
7. Выразите , https://pandia.ru/text/78/155/images/image113_7.gif» width=»46″ height=»48 src width=»164″ height=»41″>;
б) https://pandia.ru/text/78/155/images/image120_7.gif» width=»88″ height=»47 src 27″ width=»153″ loading=lazy> на множители над полем вещественных чисел и над полем рациональных чисел. Найдите разложение в радикалах.
12. Разложите многочлен на множители над полем вещественных чисел и над полем комплексных чисел. Найдите разложение в радикалах.
13. Решите уравнение .
14. Решите систему уравнений .
15. Решите систему уравнений и упростите ответ.
16. Численно найти все решения уравнения .
17. Найти три численных решения уравнения .
18. Решите систему неравенств .
19. Решите неравенство .
3 . ПОСТРОЕНИЕ ГРАФИКОВ
Эта часть посвящена возможностям системы Maple V в визуализации самых разнообразных вычислений.
3.1. Двумерные графики
Для построения графиков функции f(x) от одной переменной (в интервале https://pandia.ru/text/78/155/images/image132_6.gif» width=»69″ height=»24″> по оси Оу ) используется команда
plot(f(x), x=a..b, y=c..d, options),
где options – опция или набор опций, задающих стиль построения графика. Если их не указывать, то будут использованы установки по умолчанию. Настройка изображения также может осуществляться с панели инструментов. Для этого следует щелкнуть левой кнопкой мыши по изображению. После этого становятся активными кнопки в нижнем ряду панели. Также можно узнать координаты точки на графике. Для этого необходимо подвести курсор к нужной точке графика и щелкнуть левой кнопкой мыши. Слева в нижнем ряду кнопок на панели появятся координаты. Настройка изображения также может осуществляться с помощью контекстного меню. Оно вызывается правой кнопкой мыши.
Основные параметры команды plot :
title=”text”, где text- заголовок рисунка (текст можно оставлять без кавычек, если он не содержит пробелов).
coords=polar – установка полярных координат (по умолчанию установлены декартовы).
axes – установка типа координатных осей: axes=NORMAL – обычные оси; axes=BOXED – график в рамке со шкалой; axes=FRAME – оси с центром в левом нижнем углу рисунка; axes=NONE – без осей.
scaling – установка масштаба рисунка: scaling=CONSTRAINED – одинаковый масштаб по осям; scaling=UNCONSTRAINED – график масштабируется по размерам окна.
style = LINE – вывод линиями, style = POINT вывод точками.
numpoints=n – число вычисляемых точек графика (по умолчанию n=50 ).
сolor – установка цвета линии: английское название цвета, например, yellow – желтый и т. д.
xtickmarks=nx и ytickmarks=ny – число меток по оси Оx и оси Оy , соответственно.
thickness=n, где n=1,2,3… — толщина линии (по умолчанию n=1 ).
linestyle=n – тип линии: непрерывная, пунктирная и т. д. (по умолчанию n=1 – непрерывная).
symbol=s — тип символа, которым помечают точки: BOX, CROSS, CIRCLE, POINT, DIAMOND .
font= – установка типа шрифта для вывода текста: f задает название шрифтов: TIMES, COURIER, HELVETICA, SYMBOL; style задает стиль шрифта: BOLD, ITALIC, UNDERLINE; size – размер шрифта в pt.
labels= – надписи по осям координат: tx – по оси Оx и ty – по оси Оy .
discont=true – указание для построения бесконечных разрывов, при этом на графике асимптоты не рисуются.
Пример. Построить график https://pandia.ru/text/78/155/images/image134_1.jpg» width=»292 height=292″ height=»292″>
Построение графика функции, заданной параметрически
С помощью команды plot можно строить также графики функций, заданных параметрически y=y(t), x=x(t) :
plot(, parameters) .
Пример. Построить график параметрической кривой , https://pandia.ru/text/78/155/images/image138_2.jpg» width=»231 height=164″ height=»164″>
Построение графика функции, заданной неявно
Для построения графика неявной функции F(x, y)=0 используется команда https://pandia.ru/text/78/155/images/image139_5.gif» width=»80″ height=»27″>.
>with(plots):implicitplot(x^2+y^2=1, x=-1..1, y=-1..1);
Gif» width=»12 height=12″ height=»12″> textplot(, options), где x0, y0 – координаты точки, с которой начинается вывод текста ’text’ .
Вывод нескольких графических объектов на один рисунок
Если на одном рисунке нужно совместить несколько графиков функций, то можно воспользоваться командой
Если необходимо нарисовать несколько графических объектов, полученных при помощи различных команд, то для этого результат действия команд присваивается некоторым переменным:
> p := plot (…): t := textplot (…):
При этом на экран вывод не производится. Для вывода графических изображений необходимо выполнить команду из пакета plots:
display(, options) .
Пример . Построить графики функций https://pandia.ru/text/78/155/images/image142_6.gif» width=»73″ height=»20 src width=»59″ height=»24 src width=»297 height=24″ height=»24″> , то для этого можно использовать команду inequal из пакета plots:
inequals( c1,…,fn(x, y)>cn>, x=x1…x2, y=y1..y2, options)
В фигурных скобках указывается система неравенств, задающих область, затем размеры координатных осей и параметры. С помощью параметров можно регулировать толщину линий границ, цвета открытых и закрытых границ, цвета внешней и внутренней областей:
.gif» width=»12″ height=»12 src width=»12″ height=»12 src width=»75″ height=»43″>.
3..gif» width=»72″ height=»20″>, в рамке.
4..gif» width=»83″ height=»23 src width=»75″ height=»20 src width=»321″ height=»198″>
График поверхности, заданной параметрически
Если требуется построить поверхность, заданную параметрически: x =x (u ,v ), y =y (u ,v ), z =z (u ,v ), то эти функции перечисляются в квадратных скобках в команде:
plot3d(, u=u1..u2, v=v1..v2) .
Пример . Построить тор.
> plot3d(, s=0..2*Pi, t=0..11*Pi/6, grid=, style=patch, axes=frame, scaling=constrained);
https://pandia.ru/text/78/155/images/image162_4.gif» width=»99″ height=»24″>, строится с помощью команды пакета plot s :
implicitplot3d(F(x, y,z)=c, x=x1..x2, y=y1..y2, z=z1..z2), где указывается уравнение поверхности и размеры рисунка по координатным осям.
График пространственных кривых
В пакете plot s имеется команда spacecurve для построения пространственной кривой, заданной параметрически: .
spacecurve ([ x (t ), y (t ), z (t )], t = t 1.. t 2) , где переменная t изменяется от t1 до t2 .
Построение нескольких трехмерных фигур на одном графике
Команда plot 3 d позволяет строить одновременно несколько фигур, пересекающихся в пространстве. Для этого достаточно вместо описания одной поверхности задать список описаний ряда поверхностей. При этом функция plot 3 d обладает уникальной возможностью – автоматически вычисляет точки пересечения фигур и показывает только видимые части поверхностей. Это создает изображения, выглядящие вполне естественно.
Пример. Выполнить построение двух поверхностей и в пределах https://pandia.ru/text/78/155/images/image168_4.gif» width=»39″ height=»19″>.
> plot 3 d ( , x =- Pi .. Pi , y =- Pi .. Pi , grid =, axes = FRAMED , color = x + y );
Maple позволяет выводить на экран движущиеся изображения с помощью команд animate (двумерные) и animate3d (трехмерные) из пакета plot s . Суть анимации при использовании данных функций заключается в построении серии кадров, причем каждый кадр связан со значением изменяемой во времени переменной t. Среди параметров команды animate3d есть
frames – число кадров анимации (по умолчанию frames=8 ).
Управлять движущимся изображением удобнее с помощью контекстного меню.
Задание 3 .2
1. Построить график поверхности .
2. Построить шар :
3..gif» width=»65″ height=»21 src width=»173 height=53″ height=»53″>.gif» width=»95″ height=»48 src width=»71″ height=»23 src width=»12″ height=»12 src width=»215″ height=»58 src >
https://pandia.ru/text/78/155/images/image182_4.gif» width=»262″ height=»54″>
В Maple имеется несколько способов представления функции.
Способ 1. Определение функции с помощью оператора присваивания (:= ): какому-то выражению присваивается имя, например:
Если задать конкретное значение переменной х , то получится значение функции f для этого х . Например, если продолжить предыдущий пример и вычислить значение f при , то следует записать:
После выполнения этих команд переменная х имеет заданное значение .
Чтобы насовсем не присваивать переменной конкретного значения, удобнее использовать команду подстановки subs( ,f), где в фигурных скобках указываются переменные хi и их новые значения аi (i =1,2,…), которые следует подставить в функцию f . Например:
Все вычисления в Maple по умолчанию производятся символьно, то есть результат будет содержать в явном виде иррациональные константы, такие как, и другие. Чтобы получить приближенное значение в виде числа с плавающей запятой, следует использовать команду evalf(expr,t), где expr – выражение, t – точность, выраженная в числах после запятой. Например, в продолжение предыдущего примера, вычислим полученное значение функции приближенно:
Здесь использован символ (% ) для вызова предыдущей команды.
Способ 2. Определение функции с помощью функционального оператора, который ставит в соответствие набору переменных (x1,x2,…) одно или несколько выражений (f1,f2,…) . Например, определение функции двух переменных с помощью функционального оператора выглядит следующим образом:
Обращение к этой функции осуществляется наиболее привычным в математике способом, когда в скобках вместо аргументов функции указываются конкретные значения переменных. В продолжение предыдущего примера вычисляется значение функции:
Способ 3. С помощью команды unapply(expr,x1,x2,…) , где expr – выражение, x1,x2,… – набор переменных, от которых оно зависит, можно преобразовать выражение expr в функциональный оператор. Например:
В Maple имеется возможность определения неэлементарных функций вида
> piecewise(cond_1,f1, cond_2, f2, …).
записывается следующим образом.
04. 01 Преобразование уравнений. Команды lhs и rhs
* Entering and Manipulating Equations: The lhs and rhs commands *
Напомним, что уравнению, точно так же как и выражению, можно присвоить имя. В следующей командной строке мы введём уравнение и присвоим ему имя » eq1 » :
Мы можем вывести на экран левую и правую части уравнения по-отдельности при помощи команд lhs и rhs :
Воспользуемся командами lhs и rhs для того, чтобы привести уравнение к стандартному виду, в котором все члены собраны слева, а справа остался только 0:
> eq2:=lhs(eq1)-rhs(eq1)=0;
04. 02 Нахождение точных корней. Команда solve
* Finding Exact Solutions: The solve command *
Рассмотрим вначале рациональные уравнения. Известно, что существуют алгоритмы определения точных корней рациональных корней вплоть до 4-го порядка включительно. В Maple-команду solve и заложены эти алгоритмы.
Воспользуемся командой solve для нахождения точных корней кубического уравнения :
Обратите внимание: в команде мы указываем, относительно какой переменной следует решать уравнение. Хотя в нашем конкретном случае это и не обязательно:
Maple нашел все 3 действительных корня и вывел их (в неупорядоченном виде ).
Иногда очень важно выбрать конкретный корень, чтобы потом использовать в дальнейших преобразованиях именно его. Для этого заранее следует присвоить имя результату исполнения команды solve . Назовём его X . Тогда конструкция X будет соответствовать первому корню из списка (подчеркнем: это не обязательно меньший корень! ), X — второму корню, и т.д. (Скобки — квадратные! ):
Посмотрите, однако, что будет выведено в результате выполнения похожей команды:
Ещё раз подчеркнём: практика показывает, что уравнению целесообразно присвоить имя. Традиционно в Maple такое имя начинается с букв eq :
(Не путать оператор присваивания » := » со знаком равенства » = » !)
Теперь решим уравнение при помощи команды solve . Множеству корней присвоим имя X :
Для убедительности проверим, нет ли среди найденных корней посторонних. Проверку выполним непосредственной подстановкой
Разумеется, частенько «точные» решения довольно громоздки, если не сказать иначе. Например, это касается уравнения :
Теперь Вам понятно, о чем речь? Для себя заметьте, что мнимая единица в Maple обозначается посредством прописной буквы I . Разумеется, в таких случаях не грех найти приближенные значения корней. Имея на руках точное решение, Вы и сами сообразите, как это сделать:
В подобных ситуациях хорошей альтернативой команде solve является fsolve , особенности которой будут рассмотрены в следующем параграфе.
Команда solve используется при отыскании точных решений не только рациональных уравнений. Ниже приведено несколько тому иллюстраций. Но для многих типов иррациональных, показательных, логарифмических, тригонометрических и даже рациональных уравнений точное решение искать бесполезно. На помощь призывается команда fsolve .
Иногда (а в тригонометрии — всегда ) Maple, по умолчанию , не выводит всё множество корней:
Но безвыходных ситуаций не бывает! Взяв за основу полученный результат, воспользуйтесь своими знаниями тригонометрических уравнений и запишите полное решение (как? ).
Упражнение 4.1
Решить уравнение Разберитесь, сколько различных корней имеет уравнение. Как Maple поступает при наличии равных корней?
Совет : разложите на множители левую часть уравнения.
> factor(x^3-11*x^2+7*x+147);
Корень х = 7 является двукратным, а потому у кубического уравнения только два различных корня. Разложение на множители левой части уравнения — тому подтверждение.
04. 03 Поиск приближенных корней. Команда fsolve
* Finding Approximate Solutions: The fsolve command *
Для приближенного решения уравнений используется Maple-команда fsolve . В случае рационального уравнения, fsolve выводит весь список действительных корней (см. Пример 01). Для трансцендентных уравнений эта команда, по умолчанию, выводит только один корень (см. Примеры 02 и 03).
При помощи fsolve найдём приближенные значения сразу всех четырёх действительных корней рационального уравнения :
Эти четыре корня и составляют исчерпывающее решение исходного рационального уравнения (хотя и приближенное ).
Используя команду fsolve , найти хотя бы один действительный корень уравнения :
Maple и вывел только один корень. На этот раз Maple не стал «живописать». Как теперь убедиться в том, что других действительных корней нет? Следующий пример даёт такой инструментарий.
Получить все действительные корни уравнения и убедиться в этом.
Шаг первый ( Основная идея ) : найдём графическое решение уравнения. Для этого построим график функции, стоящей в левой части уравнения. Абсциссы точек пересечения этого графика с осью Ох и будут искомыми корнями.
Т.к. мы умело подобрали диапазоны изменений абсцисс и ординат точек графика, то легко обнаружим 4 точки пересечения линии с осью Ох. Одна из них соответствует корню, найденному в Примере 02 (какая именно? ).
Второй корень очевиден: х = 0. А как поточнее найти остальные?
Шаг второй ( Уточнение ) : применим команду fsolve более «зряче». В Maple предусмотрена возможность указания промежутка, на котором отыскиваются корни. В частности, для определения отрицательного корня нашего уравнения, укажем, что поиски следует вести в «районе» [-1;-0.2]. Об этом красноречиво свидетельствует графическое решение.
Оставшиеся корни явно принадлежат промежуткам и . Расскажем об этом команде fsolve :
Ну а что произойдёт, если мы подсунем Maple «пустой участок»? Например, отрезок для нашего уравнения. Там графического решения явно нет:
Maple выдаёт название команды, само уравнение, имя аргумента и отрезок. Т.е. ничего нового. Мол: «Ищите корни сами, а я не нашел».
Шаг третий ( Дополнительный анализ ) : Как теперь удостовериться в том, что найдены все корни , а не только в видимой области графического решения? Для этого следует расширить интервал поисков:
Новых точек пересечения нет. В конце концов мы понимаем, что экспоненциальное слагаемое на границах промежутка вносит самый существенный вклад в величину функции из левой части уравнения. Значения функции в этой области стремятся к , а потому дополнительных корней нам не найти.
Попробуем в других местах: справа и слева от области найденных корней.
И здесь ни одного дополнительного корня! Поняв, что с влиянием показательной части уравнения всё ясно, делаем окончательные выводы.
Исчерпывающее решение уравнения состоит из четырёх корней: -.8251554597 , 0 , 1.545007279 , 4.567036837 .
Применим команду fsolve для приближенного решения трансцендентного уравнения .
Как и в предыдущем случае, найдём вначале качественное графическое решение. Для этого ещё нужно угадать, как разбросать по обеим частям уравнения его члены. Но графические возможности Maple настолько великолепны, что почти всегда можно собирать все члены уравнения с одной стороны.
Рассмотрим уравнение, равносильное данному: . Абсциссы точек пересечения графика функции, стоящей в левой части уравнения, с осью Ох и будут искомыми корнями.
График указывает область поисков корней: промежуток . Настаёт черёд команды fsolve :
Корень найден. Но, очевидно, он — не единственный. Расширьте область поисков и ещё раз примените команду fsolve для отыскания второго корня.
Упражнение 4.2
Найти все действительные корни уравнения , начав с графического решения.
Построим график левой части уравнения:
В результате находим корни уравнения в первом приближении: -2 ; -1.5 ; 0 . Применим теперь команду fsolve без указания диапазона поиска (оценим возможности Maple ):
С удовлетворением отмечаем, что Maple выводит все три корня (Не будем забывать, что решали рациональное уравнение.)
Упражнение 4.3
Найти все корни уравнения . Воспользуйтесь графическим решением. Проверьте каждый корень непосредственной подстановкой.
Приведём уравнение к стандартному (для этого раздела) виду:
Теперь построим график левой части уравнения:
По всей видимости, существует два корня. Один примерно равен -2, а другой, похоже, 2.
Применим команду fsolve , ограничив диапазон поиска:
Непосредственной подстановкой проверим корни:
Обратите внимание: в обоих случаях истинного равенства нет. С учётом ошибок при округлении, разумное расхождение вполне допустимо.
Убедитесь в отсутствии других корней. Ответ обоснуйте.
Упражнение 4.4
Графики функций и дважды пересекаются на отрезке [-5;5].
а). Постройте в одной системе координат графики обеих функций и при помощи мыши найдите координаты точек пересечения.
b). Составьте уравнение, корнями которого являются абсциссы точек пересечения графиков.
c). Используйте команду fsolve для решения этого уравнения.
d). Используйте результаты из пункта с) для оценки ординат точек пересечения графиков.
e). У Вас не создалось впечатление, что линии могут пересекаться и в третьей точке с координатами (1;9)? Используйте fsolve и графические возможности Maple, чтобы убедиться в противном.
Теперь построим графики функций:
Приближенные координаты точек пересечения: (-1.8, 6.6) и (2.75, 2) .
b) Составим уравнение:
с) Команда fsolve поможет найти соответствующие корни:
d) Используем команду subs для определения соответствующих ординат точек пересечения:
Общие точки графиков: (-1.800,6.763) и (2.773,2.311) .
e) Графически исследуем окрестность точки х = 1:
Команда fsolve на этот раз позволит доказать отсутствие корней вблизи точки х = 1:
04. 04 Решение уравнений в общем виде
* Solving Literal Equations *
Во многих случаях Maple находит решение уравнения в общем (символьном) виде. Речь идёт об уравнении (а не системы!), содержащем несколько переменных. Решение состоит в том, что какая-то из переменных выражается через остальные.
Пусть необходимо решить уравнение относительно переменной g . По привычке, используем команду solve . И она оправдывает наши надежды:
А так решение можно оформить в привычном виде:
Упражнение 4.4
Решить последнее уравнение относительно других переменных: T, k и v.
Упражнение 4.5
Решить уравнение относительно у. Присвоить последовательности корней имя S. Как связаны корни S и S ?
Тема 9. Двумерная графика в системе Maple Команда plot и ее параметры
Для построения графиков функции f=f(x) одной переменной (на сегменте a x b по оси Оx и на сегменте c y d по оси Оy) используется команда plot(f(x),x=a..b,y=c..d,parameters), где опция parameters задает параметры управления изображением.
Если их не указать, то будут использованы установки по умолчанию. Настройка изображения также может осуществляться непосредственно с панели инструментов.
Основные параметры команды plot:
1. title=”text”, где text — заголовок рисунка (текст можно оставлять без кавычек, если он содержит только латинские буквы без пробелов).
2. coords=polar – установка полярных координат (по умолчанию используются декартовые координаты).
3. axes – установка типа координатных осей: axes=NORMAL – обычные оси; axes=BOXED – график в рамке со шкалой; axes=FRAME – оси с центром в левом нижнем углу рисунка; axes=NONE – без осей.
4. scaling – установка масштаба рисунка: scaling=CONSTRAINED – одинаковый масштаб по осям; scaling=UNCONSTRAINED – график масштабируется по размерам окна.
5. style=LINE(POINT) – вывод линиями (или точками).
6. numpoints=n – число вычисляемых точек графика (по умолчанию n=49).
7. сolor – установка цвета линии (английское название цвета, например, black – черный и т.д.).
8. xtickmarks=nx и ytickmarks=ny – число меток по оси Оx и оси Оy, соответственно.
9. thickness=n, где n=1,2,3… — толщина линии (по умолчанию n=1).
10. linestyle=n – тип линии: непрерывная, пунктирная и т.д. (n=1 – непрерывная, установлено по умолчанию).
11. symbol=s – тип символа, которым помечают точки: BOX, CROSS, CIRCLE, POINT, DIAMOND.
12. font=[f,style,size] – установка типа шрифта для вывода текста: f задает название шрифтов: TIMES, COURIER, HELVETICA, SYMBOL; style задает стиль шрифта: BOLD, ITALIC, UNDERLINE; size – размер шрифта в pt (пункты).
13. labels=[tx,ty] – надписи по осям координат: tx – по оси Оx и ty – по оси Оy.
14. discont=true – указание для построения бесконечных разрывов.
С помощью команды plot можно строить не только обычные графики функций вида y=f(x), но также и графики функций, заданных параметрически (т.е. неявно) y=y(t),x=x(t), если записать для этого команду plot([y=y(t),x=x(t) t=a..b],parameters).
Примеры

1. Построить график жирной линией в интервале по x от -4π до 4π.


2. Построить график функции, имеющей разрывы: .

Обратим внимание, что на данном рисунке автоматически появились две вертикальные асимптоты x=1.

3. Построить график параметрической кривой , и окружить его рамкой.


4. Построить в полярных координатах график кардиоиды с соответствующим названием.

5. Построить два графика на одном рисунке: график функции
и касательную к нему в точке x=1, определяемую уравнением
.
Доклад: Графика в системе Maple V
Если задано построение графика точками то параметр symbol позволяет представить точки в виде различных символов, например, прямоугольника, креста, окружности или ромба.
Другой параметр color позволяет установить обширный набор цветов линий графиков:
aquamarine black blue navy coral cyan brown gold green gray grey khaki magenta maroon orange pink plum red sienna tan turquoise violet wheat white yellow
Различные цветовые оттенки получаются использованием RGB-комбинаций базовых цветов: red — красный, green — зеленый, blue — синий. Приведем перевод ряда других комбинированных цветов: black — черный, white — белый, khaki — цвет «хаки», gold — золотистый, orange — оранжевый, violet — фиолетовый, yellow — желтый и т.д. Перевод цветов некоторых оттенков на русский язык не всегда однозначен и потому не приводится. Средства управления стилем графиков дают возможность легко выделять различные кривые на одном рисунке, даже если для выделения не используются цвета.
13.2. Примеры построения основных типов 20-графиков
13.2.1. Построение графиков одной функции
При построении графика одной функции она записывается в явном виде на место шаблона f. Примеры построения графика одной функции представлены на рис. 13.1. Обратите внимание на то, что график функции sin(x)/x строится без
характерного провала в точке х=0, который наблюдается при построении графиков этой функции многими программами. Он связан с используемым в них правилом — функция задается равной нулю, если ее числитель равен нулю. Данная функция в этой точке дает устранимую неопределенность 0/0->1, что и учитывает графический процессор системы Maple V.

Рис. 13.1. Примеры построения графиков одной функции.
При построении графиков одной функции могут быть введены указатели масштабов и различные опции, например задания цвета кривой, толщины линии, которой строится график функции и другие параметры. К примеру, запись в списке параметров со1ог=0 (не документированная возможность) или запись color=black задают вывод кривых черным цветом, а запись thinkness=2 задает во втором примере рис. 13.1 построение графика линией, удвоенной в сравнении с обычной толщиной.
13.2.2. Управление масштабом графиков
Для управления масштабом графиков служат указатели масштабов. В ряде случаев их можно не применять и система автоматически задает приемлемые масштабы. Однако их явное применение позволяет задать масштаб «вручную». Иногда соответствующее задание масштаба случайно или целенаправленно ведет к отсечению части графика — например на рис. 13.2 в первом примере отсечена верхняя часть графика.
Правильный выбор масштаба повышает представительность графиков функций. Рекомендуется вначале пробовать строить такие графики с автоматическим масштабированием, а уже затем использовать указатели масштабов.
13.2.3. Графики функций в неограниченном масштабе
Изредка встречаются графики функций цх), которые надо построить при изменении значения х от нуля до бесконечности или даже от минус бесконечности до

Рис. 13.2. Построение графиков функции с явным указанием масштаба.
плюс бесконечности. Бесконечность в таких случаях задается в указателях масштаба как особая константа infinity. В этом случае масштаб автоматически меняется по ходу построения графика. Рис. 13.2 (второй пример) иллюстрирует сказанное. Пересчет значении координаты х, устремляющейся в бесконечность, выполняется с помощью функции для арктангенса.
13.2.4. Графики функций с разрывами
Некоторые функции, например tan(x), имеют при определенных значениях х разрывы, причем случается что значения функции в этом случае устремляются в бесконечность. Функция tan(x), к примеру, в точках разрывов устремляется к +°° и -°°. Построение графиков таких функций нередко дает плохо предсказуемые результаты. Графический процессор Maple V не всегда в состоянии определить оптимальный масштаб по оси ординат, а график функции выглядит весьма непредставительно — если не сказать безобразно (см. рис. 13.3 — первый пример).
Среди параметров функции plot есть специальный параметр discont. Если задать его значение равным true, то качество графиков существенно улучшается — см. рис. 13.3 — второй пример. Улучшение достигается разбивкой графика на несколько участков, в которых функция непрерывна, и более тщательным контролем за масштабом.
13.2.5. Построение графиков нескольких функций на одном рисунке
Важное значение имеет возможность построения на одном рисунке графиков нескольких функций. В простейшем случае (рис. 13.4 первый пример) для построения таких графиков достаточно перечислить нужные функции и установить для них общие масштабы.

Рис. 13.3. Построение графиков функции с разрывами.
Обычно графики разных функций автоматически строятся разными цветами. Но они не всегда удовлетворяют пользователя — например, при распечатке графиков монохромным принтером некоторые кривые могут выглядеть слишком блеклыми или даже не пропечататься вообще. Используя списки параметров color (цвет линии) и style (стиль линий) можно добиться выразительного выделения кривых — это показывает второй пример на рис. 13.4.

Рис. 13.4. Графики трех функции на одном рисунке.
На рис. 13.5 показан еще один пример такого рода. Здесь построен график функции sin(x)/x и график ее полиномиальной аппроксимации. Она выполняется настолько просто, что соответствующие функции записаны прямо в списке параметров функции plot.

Рис. 13.5. График функции sin(x)/x и ее полиномиальной аппроксимации.
В данном случае сама функция построена сплошной линией, а график полинома — крестиками. Хорошо видно, что при малых х аппроксимация дает высокую точность, но затем с ростом х погрешность ее резко возрастает.
Рис. 13.6 показывает построение нескольких любопытных функций, полученных с помощью комбинаций элементарных функций. Эти комбинации позволяют получать периодические функции, моделирующие сигналы стандартного вида в технических устройствах: в виде напряжения на выходе двухполупериодного выпрямителя, симметричных прямоугольных колебаний (меандр), пилообразных и треугольных импульсов, треугольных импульсов со скругленной вершиной.
Похожие публикации:
- Как в matlab хранятся приложения с gui
- Как ввести в математическое выражение латинские цифры в программе mathcad
- Как войти в чужой icq new
- Как вставить текст в mathcad
Rukovodstvo
статьи и идеи для разработчиков программного обеспечения и веб-разработчиков.
Matplotlib: построение графиков из нескольких линий в одном и в разных масштабах
Введение Matplotlib — одна из наиболее широко используемых библиотек визуализации данных в Python. От простых до сложных визуализаций — это библиотека для большинства. В этом уроке мы рассмотрим, как построить несколько линейных графиков в Matplotlib — на одних и тех же осях или фигуре. Если вы хотите узнать больше о построении линейных графиков в целом, а также об их настройке, обязательно прочтите наше руководство по построению линейных графиков с помощью Matplotlib [/ matplotlib-line-plot-tutorial-and-examples /]. Pl
Время чтения: 6 мин.
Вступление
Matplotlib — одна из наиболее широко используемых библиотек визуализации данных в Python. От простых до сложных визуализаций — это библиотека для большинства.
В этом уроке мы рассмотрим, как построить несколько линейных графиков в Matplotlib — на одних и тех же Axes или Figure .
Если вы хотите узнать больше о построении линейных графиков в целом, а также об их настройке, обязательно прочтите наше руководство по построению линейных графиков с помощью Matplotlib .
Постройте многострочные графики в Matplotlib
В зависимости от стиля, который вы используете, ООП или стиля MATLAB, вы будете использовать либо plt , либо ax для построения графика с тем же подходом.
Чтобы построить несколько линейных графиков в Matplotlib, вы просто несколько раз вызываете plot() , которая применяет изменения к одному и тому же объекту Figure
import matplotlib.pyplot as plt x = [1, 2, 3, 4, 5, 6] y = [2, 4, 6, 5, 6, 8] y2 = [5, 3, 7, 8, 9, 6] fig, ax = plt.subplots() ax.plot(x, y) ax.plot(x, y2) plt.show()
Без установки каких-либо флагов настройки будет применяться палитра по умолчанию, рисуя оба линейных графика на одном и том же Figure и настраивая цвет, чтобы различать их:
Теперь давайте сгенерируем несколько случайных последовательностей с помощью Numpy и немного настроим линейные графики, установив для каждого определенный цвет и пометив их:
import matplotlib.pyplot as plt import numpy as np line_1 = np.random.randint(low = 0, high = 50, size = 50) line_2 = np.random.randint(low = -15, high = 100, size = 50) fig, ax = plt.subplots() ax.plot(line_1, color = 'green', label = 'Line 1') ax.plot(line_2, color = 'red', label = 'Line 2') ax.legend(loc = 'upper left') plt.show()
Нам не нужно передавать значения оси X на линейный график, и в этом случае будут применяться 0..n n , где n — последний элемент данных, которые вы строите. В нашем случае у нас есть две последовательности данных — line_1 и line_2 , которые будут нанесены на одну и ту же ось X.
Во время построения мы присвоили им цвета, используя color , и метки для легенды, используя аргумент label Это приводит к:
Постройте многолинейные графики с разными масштабами
Иногда у вас может быть два набора данных, подходящих для линейных графиков, но их значения значительно отличаются, что затрудняет сравнение обеих линий. Например, если в line_1 была экспоненциально возрастающая последовательность чисел, а в line_2 — линейно возрастающая последовательность — несомненно и достаточно быстро, в line_1 были бы значения настолько большие, чем в line_2 , что последняя исчезает из поля зрения.
Давайте использовать Numpy, чтобы создать экспоненциально возрастающую последовательность чисел и построить ее рядом с другой линией на тех же Axes , линейно:
import matplotlib.pyplot as plt import numpy as np linear_sequence = np.linspace(0, 10, 10) # [0, 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 10] exponential_sequence = np.exp(linear_sequence) # [1.00e+00, 3.03e+00, 9.22e+00, 2.80e+01, 8.51e+01, 2.58e+02, 7.85e+02, 2.38e+03, 7.25e+03, 2.20e+04] fig, ax = plt.subplots() ax.plot(linear_sequence) ax.plot(exponential_sequence) plt.show()
Выполнение этого кода приводит к:
Экспоненциальный рост exponential_sequence выходит из-под контроля очень быстро, и похоже, что нет абсолютно никакой разницы в linear_sequence , поскольку она настолько мала по сравнению с экспоненциальным трендом другой последовательности.
Теперь давайте построим exponential_sequence в логарифмическом масштабе, что даст визуально прямую линию, поскольку масштаб Y будет экспоненциально увеличиваться. Если мы построим его в логарифмическом масштабе, а linear_sequence просто увеличится на ту же константу, у нас будет две перекрывающиеся линии, и мы сможем увидеть только одну, построенную после первой.
Давайте linear_sequence чтобы сделать его наблюдаемым, как только мы построим оба графика:
import matplotlib.pyplot as plt import numpy as np # Sequences linear_sequence = [1, 2, 3, 4, 5, 6, 7, 10, 15, 20] exponential_sequence = np.exp(np.linspace(0, 10, 10)) fig, ax = plt.subplots() # Plot linear sequence, and set tick labels to the same color ax.plot(linear_sequence, color='red') ax.tick_params(axis='y', labelcolor='red') # Generate a new Axes instance, on the twin-X axes (same position) ax2 = ax.twinx() # Plot exponential sequence, set scale to logarithmic and change tick color ax2.plot(exponential_sequence, color='green') ax2.set_yscale('log') ax2.tick_params(axis='y', labelcolor='green') plt.show()
На этот раз нам придется использовать интерфейс ООП, поскольку мы создаем новый экземпляр Axes One Axes имеет один масштаб, поэтому мы создаем новый, в том же положении, что и первый, и устанавливаем его масштаб на логарифмический, и строим экспоненциальную последовательность.
Мы также изменили цвета меток галочки, чтобы они соответствовали цвету самих линейных графиков, иначе было бы трудно отличить, какая линия на каком масштабе.
Постройте графики с несколькими линиями с несколькими осями Y
Наконец, мы можем применить тот же масштаб (линейный, логарифмический и т. Д.), Но иметь разные значения по оси Y каждого линейного графика. Это достигается за счет того, что несколько осей Y на разных Axes находятся в одном и том же положении.
Например, linear_sequence не будет превышать 20 по оси Y, в то время как exponential_sequence будет увеличиваться до 20000. Мы можем построить их оба линейно , просто нанеся их на разные Axes в одной и той же позиции, каждый из которых автоматически установите отметки оси Y, чтобы они соответствовали вводимым нами данным:
import matplotlib.pyplot as plt import numpy as np # Sequences linear_sequence = [1, 2, 3, 4, 5, 6, 7, 10, 15, 20] exponential_sequence = np.exp(np.linspace(0, 10, 10)) fig, ax = plt.subplots() # Plot linear sequence, and set tick labels to the same color ax.plot(linear_sequence, color='red') ax.tick_params(axis='y', labelcolor='red') # Generate a new Axes instance, on the twin-X axes (same position) ax2 = ax.twinx() # Plot exponential sequence, set scale to logarithmic and change tick color ax2.plot(exponential_sequence, color='green') ax2.tick_params(axis='y', labelcolor='green') plt.show()
Мы снова создали еще одну Axes в той же позиции, что и первая, поэтому мы можем рисовать в том же месте на Figure но разные Axes , что позволяет нам устанавливать значения для каждой оси Y индивидуально.
Без установки логарифмической шкалы Y на этот раз, оба графика будут построены линейно:
Заключение
В этом руководстве мы рассмотрели, как построить несколько линейных графиков на одной Figure или Axes в Matplotlib и Python. Мы рассмотрели, как построить график на одних и тех же Axes с одинаковым масштабом и осью Y, а также как построить график на одном Figure с разными и одинаковыми масштабами оси Y.
Если вас интересует визуализация данных и вы не знаете, с чего начать, обязательно ознакомьтесь с нашим комплектом книг по визуализации данных в Python :
Визуализация данных в Python
. Станьте опасными с визуализацией данных
✅ 30-дневная гарантия возврата денег без вопросов
✅ от начального до продвинутого
✅ Регулярно обновляется бесплатно (последнее обновление в апреле 2021 г.)
✅ Обновлено с бонусными ресурсами и руководствами . . .
Визуализация данных в Python с помощью Matplotlib и Pandas — это книга, предназначенная для абсолютных новичков в работе с Pandas и Matplotlib с базовыми знаниями Python и позволяющая им создать прочную основу для расширенной работы с этими библиотеками — от простых графиков до анимированных трехмерных графиков с интерактивными кнопки.
Он служит подробным руководством, которое научит вас всему, что вам нужно знать о Pandas и Matplotlib, в том числе о том, как создавать типы графиков, которые не встроены в саму библиотеку.
Книга «Визуализация данных в Python» , книга для начинающих и средних разработчиков Python, проведет вас через простые манипуляции с данными с помощью Pandas, охватит основные библиотеки построения графиков, такие как Matplotlib и Seaborn, и покажет, как использовать преимущества декларативных и экспериментальных библиотек, таких как Altair. В частности, на протяжении 11 глав эта книга охватывает 9 библиотек Python: Pandas, Matplotlib, Seaborn, Bokeh, Altair, Plotly, GGPlot, GeoPandas и VisPy.
Он служит уникальным практическим руководством по визуализации данных в виде множества инструментов, которые вы можете использовать в своей карьере.
Licensed under CC BY-NC-SA 4.0
Шпаргалка по визуализации данных в Python с помощью Plotly
Plotly — библиотека для визуализации данных, состоящая из нескольких частей:
- Front-End на JS
- Back-End на Python (за основу взята библиотека Seaborn)
- Back-End на R
Извиняюсь за замыленные gif’ки это происходит при конвертации из видео, записанного с экрана.
Jupyter Notebook со всеми примерами из статьи:
Так же на базе plotly и веб-сервера Flask существует специальная библиотека для создания дашбордов Dash.
- Plotly — бесплатная библиотека, которую вы можете использовать в коммерческих целях
- Plotly работает offline
- Plotly позволяет строить интерактивные визуализации
Для начала необходимо установить библиотеку, т.к. она не входит ни в стандартный пакет, ни в Anaconda. Для этого рекомендуется использовать pip:
pip install plotly
Если вы используете Jupyter Notebook, то можно использовать мэджик «!», поставив данный символ перед командой:
!pip install plotly
Перед началом работы необходимо импортировать модуль. В разных частях шпаргалки для разных задач нам понадобятся как основной модуль, так и один из его подмодулей, поэтому полный набор инструкций импорта у нас.
Так же нам понадобятся библиотеки Pandas и Numpy для работы с сырыми данными
Код импорта функций
import plotly
import plotly.graph_objs as go
import plotly.express as px
from plotly.subplots import make_subplots
import numpy as np
import pandas as pd
Линейный график
Начнём с простой задачи построения графика по точкам.
Используем функцию f(x)=x 2
Сперва поступим совсем просто и «в лоб»:
- Создадим график с помощью функции scatter из подмодуля plotly.express (внутрь передадим 2 списка точек: координаты X и Y)
- Тут же «покажем» его с помозью метода show()
Всё это делается с помощью JS в вашем браузере. А значит, при желании вы можете этим управлять уже после построения фигуры (но мы этого делать пожалуй не будем, т.к. JS != Python)
x = np.arange(0, 5, 0.1) def f(x): return x**2 px.scatter(x=x, y=f(x)).show()

Более читабельно и правильно записать тот же в код в следующем виде:
fig = px.scatter(x=x, y=f(x)) fig.show()
- Создаём фигуру
- Рисуем график
- Показываем фигуру
Но маловато гибкости, поэтому мы практически сразу переходим к более продвинутому уровню — сразу создадим фигуру и нанесём на неё объекты.
Так же сразу выведем фигуру для показа с помощью метода show().
В отличие от Matplotlib отдельные объекты осей не создаются, хотя мы с ними ещё столкнёмся, когда захотим построить несколько графиков вместе
fig = go.Figure() #Здесь будет код fig.show()

Как видим, пока пусто.
Чтобы добавить что на график нам понадобится метод фигуры add_trace.
fig.add_trace(ТУТ_ТО_ЧТО_ХОТИМ_ПЕРЕДАТЬ_ДЛЯ_ОТОБРАЖЕНИЯ_И_ГДЕ)
Но ЧТО мы хотим нарисовать? График по точкам. График мы уже рисовали с помощью Scatter в Экспрессе, у Объектов есть свой Scatter, давайте глянем что он делает:
go.Scatter(x=x, y=f(x))

А теперь объединим:
fig = go.Figure() fig.add_trace(go.Scatter(x=x, y=f(x))) fig.show()

Как видим, отличия не только в коде, но и в результате — получилась гладкая кривая.
Кроме того, такой способ позволит нам нанести на график столько кривых, сколько мы хотим:
fig = go.Figure() fig.add_trace(go.Scatter(x=x, y=f(x))) fig.add_trace(go.Scatter(x=x, y=x)) fig.show()
Погодите, что это такое? Справа появилась ещё и легенда!
Впрочем, логично, пока график был один, зачем нам легенда?
Но магия Plotly тут не заканчивается. Нажмите на любую из подписей в легенде и соответствующий график исчезнет, а надпись станет более бледной. Вернуть их позволит повторный клик.
Подписи графиков
Добавим атрибут name, в который передадим строку с именем графика, которое мы хотим отображать в легенде.
Plotly поддерживает LATEX в подписях (аналогично matplotlib через использование $$ с обеих сторон).
fig = go.Figure() fig.add_trace(go.Scatter(x=x, y=f(x), name='$$f(x)=x^2$$')) fig.add_trace(go.Scatter(x=x, y=x, name='$$g(x)=x$$')) fig.show()

К сожалению, это имеет свои ограничения, как можно заметить подсказка при наведении на график отображается в «сыром» виде, а не в LATEX.
Победить это можно, если использовать HTML разметку в подписях. В данном примере я буду использовать тег sup. Так же заметьте, что шрифт для LATEX и HTML отличается начертанием.
fig = go.Figure() fig.add_trace(go.Scatter(x=x, y=f(x), name='f(x)=x2')) fig.add_trace(go.Scatter(x=x, y=x, name='$$g(x)=x$$')) fig.show()

С увеличением длины подписи графика, легенда начала наезжать на график. Мне это не нравится, поэтому перенесём легенду вниз.
Для этого применим к фигуре метод update_layout, у которого нас интересует атрибут legend_orientation fig.update_layout(legend_orientation=»h»)
fig = go.Figure() fig.add_trace(go.Scatter(x=x, y=f(x), name='f(x)=x2')) fig.add_trace(go.Scatter(x=x, y=x, name='$$g(x)=x$$')) fig.update_layout(legend_orientation="h") fig.show()

Хорошо, но слишком большая часть рабочего пространства ноутбука не используется. Особенно это заметно сверху — большой отступ сверху до поля графика.
По умолчанию поля графика имеют отступ 20 пикселей. Мы можем задать свои значения отступам с помощью update_layout, у которого есть атрибут margin, принимающий словарь из отступов:
- l — отступ слева
- r — отступ справа
- t — отступ сверху
- b — отступ снизу
update_layout можно применять последовательно несколько раз, либо можно передать все аргументы в одну функцию (мы сделаем именно так)
fig = go.Figure() fig.add_trace(go.Scatter(x=x, y=f(x), name='f(x)=x2')) fig.add_trace(go.Scatter(x=x, y=x, name='$$g(x)=x$$')) fig.update_layout(legend_orientation="h", margin=dict(l=0, r=0, t=0, b=0)) fig.show()
Поскольку подписи в легенде короткие, мне не нравится, что они расположены слева. Я бы предпочёл выровнять их по центру.
Для этого можно использовать у update_layout атрибут legend, куда передать словарь с координатами для сдвига (сдвиг может быть и по вертикали, но мы используем только горизонталь).
Сдвиг задаётся в долях от ширины всей фигуры, но важно помнить, что сдвигается левый край легенды. Т.е. если мы укажем 0.5 (50% ширины), то надпись будет на самом деле чуть сдвинута вправо.
Т.к. реальная ширина зависит от особенностей вашего экрана, браузера, шрифтов и т.п., то этот параметр часто приходится подгонять. Лично у меня для этого примера неплохо работает 0.43.
Чтобы не шаманить с шириной, можно легенду относительно точки сдвига с помощью аргумента xanchor.
В итоге для легенды мы получим такой словарь:
legend=dict(x=.5, xanchor="center")
Код целиком
fig = go.Figure() fig.add_trace(go.Scatter(x=x, y=f(x), name='f(x)=x2')) fig.add_trace(go.Scatter(x=x, y=x, name='$$g(x)=x$$')) fig.update_layout(legend_orientation="h", legend=dict(x=.5, xanchor="center"), margin=dict(l=0, r=0, t=0, b=0)) fig.show()
Стоит сразу задать подписи к осям и графику в целом. Для этого нам вновь понадобится update_layout, у которого добавится 3 новых аргумента:
title="Plot Title", xaxis_title="x Axis Title", yaxis_title="y Axis Title",
Следует заметить, что сдвиги, которые мы задали ранее могут негавтивно сказаться на читаемости подписей (так заголовок графика вообще вытесняется из области видимости, поэтому я увеличу отступ сверху с 0 до 30 пикселей
fig = go.Figure() fig.add_trace(go.Scatter(x=x, y=f(x), name='f(x)=x2')) fig.add_trace(go.Scatter(x=x, y=x, name='$$g(x)=x$$')) fig.update_layout(legend_orientation="h", legend=dict(x=.5, xanchor="center"), title="Plot Title", xaxis_title="x Axis Title", yaxis_title="y Axis Title", margin=dict(l=0, r=0, t=30, b=0)) fig.show()

Вернёмся к самим графикам, и вспомним, что они состоят из точек. Выделим их с помощью атрибута mode у самих объектов Scatter.
Используем разные варианты выделения для демонстрации:
fig = go.Figure() fig.add_trace(go.Scatter(x=x, y=f(x), mode='lines+markers', name='f(x)=x2')) fig.add_trace(go.Scatter(x=x, y=x, mode='markers', name='$$g(x)=x$$')) fig.update_layout(legend_orientation="h", legend=dict(x=.5, xanchor="center"), margin=dict(l=0, r=0, t=0, b=0)) fig.show()

Теперь особенно заметно, что LATEX в функции g(x)=x отображается некорректно при наведении курсора мыши на точки.
Давайте скроем эту информацию.
Зададим для всех графиков с помощью метода update_traces поведение при наведении. Это регулирует атрибут hoverinfo, в который передаётся маска из имён атрибутов, например, «x+y» — это только информация о значениях аргумента и функции:
fig = go.Figure() fig.add_trace(go.Scatter(x=x, y=f(x), mode='lines+markers', name='f(x)=x2')) fig.add_trace(go.Scatter(x=x, y=x, mode='markers', name='$$g(x)=x$$')) fig.update_layout(legend_orientation="h", legend=dict(x=.5, xanchor="center"), margin=dict(l=0, r=0, t=0, b=0)) fig.update_traces(hoverinfo="x+y") fig.show()

Как-то недостаточно наглядно, не находите?
Давайте разрешим использовать информацию из всех аргументов и сами зададим шаблон подсказки.
- hoverinfo=«all»
- в hovertemplate передаём строку, используем HTML для форматирования, а имена переменных берём в фигурные скобки и выделяем %, например, %
fig = go.Figure() fig.add_trace(go.Scatter(x=x, y=f(x), mode='lines+markers', name='f(x)=x2')) fig.add_trace(go.Scatter(x=x, y=x, mode='markers', name='g(x)=x')) fig.update_layout(legend_orientation="h", legend=dict(x=.5, xanchor="center"), margin=dict(l=0, r=0, t=0, b=0)) fig.update_traces(hoverinfo="all", hovertemplate ) fig.show()

А что если мы хотим сравнить информацию на 2 кривых в точках, например, с одинаковых аргументом?
Т.к. это касается всей фигуры, нам нужен update_layout и его аргумент hovermode.
fig = go.Figure() fig.add_trace(go.Scatter(x=x, y=f(x), mode='lines+markers', name='f(x)=x2')) fig.add_trace(go.Scatter(x=x, y=x, mode='markers', name='g(x)=x')) fig.update_layout(legend_orientation="h", legend=dict(x=.5, xanchor="center"), hovermode="x", margin=dict(l=0, r=0, t=0, b=0)) fig.update_traces(hoverinfo="all", hovertemplate ) fig.show()

Кстати, маркерами можно управлять для конкретной кривой и явно.
Для этого используется аргумент marker, который принимает на вход словарь. Подробный пример.
А мы лишь ограничимся баловством:
fig = go.Figure() fig.add_trace(go.Scatter(x=x, y=f(x), mode='lines+markers', name='f(x)=x2')) fig.add_trace(go.Scatter(x=x, y=x, mode='markers',name='g(x)=x', marker=dict(color='LightSkyBlue', size=20, line=dict(color='MediumPurple', width=3)))) fig.update_layout(legend_orientation="h", legend=dict(x=.5, xanchor="center"), hovermode="x", margin=dict(l=0, r=0, t=0, b=0)) fig.update_traces(hoverinfo="all", hovertemplate ) fig.show()

Кажется теперь на графике плохо видно ту часть, где кривые пересекаются (вероятно наиболее интересную для нас).
Для этого у нас есть методы фигуры:
- update_yaxes — ось Y (вертикаль)
- update_xaxes — ось X (горизонталь)
fig = go.Figure() fig.update_yaxes(range=[-0.5, 1.5]) fig.update_xaxes(range=[-0.5, 1.5]) fig.add_trace(go.Scatter(x=x, y=f(x), mode='lines+markers', name='f(x)=x2')) fig.add_trace(go.Scatter(x=x, y=x, mode='markers',name='g(x)=x', marker=dict(color='LightSkyBlue', size=20, line=dict(color='MediumPurple', width=3)))) fig.update_layout(legend_orientation="h", legend=dict(x=.5, xanchor="center"), hovermode="x", margin=dict(l=0, r=0, t=0, b=0)) fig.update_traces(hoverinfo="all", hovertemplate ) fig.show()

Хорошо, но правильно было бы нанести осевые линии.
Для этого у тех же функций есть 3 атрибута:
- zeroline — выводить или нет осевую линию
- zerolinewidth — задаёт толщину осевой (в пикселях)
- zerolinecolor — задаёт цвет осевой (строка, можно указать название цвета, можно его код, как принято в HTML-разметке)
fig = go.Figure() fig.update_yaxes(range=[-0.5, 1.5], zeroline=True, zerolinewidth=2, zerolinecolor='LightPink') fig.update_xaxes(range=[-0.5, 1.5], zeroline=True, zerolinewidth=2, zerolinecolor='#008000') fig.add_trace(go.Scatter(x=x, y=f(x), mode='lines+markers', name='f(x)=x2')) fig.add_trace(go.Scatter(x=x, y=x, mode='markers',name='g(x)=x', marker=dict(color='LightSkyBlue', size=20, line=dict(color='MediumPurple', width=3)))) fig.update_layout(legend_orientation="h", legend=dict(x=.5, xanchor="center"), hovermode="x", margin=dict(l=0, r=0, t=0, b=0)) fig.update_traces(hoverinfo="all", hovertemplate ) fig.show()

Давайте добавим больше разных функций на наш график, но сделаем так, чтобы по умолчанию их не было видно.
Для этого у объекта Scatter есть специальный атрибут:
visible='legendonly'
Т.к. мы центрировали легенду относительно точки сдвига, то нам не пришлось менять величину сдвига с увеличением числа подписей.
def h(x): return np.sin(x) def k(x): return np.cos(x) def m(x): return np.tan(x) fig = go.Figure() fig.update_yaxes(range=[-0.5, 1.5], zeroline=True, zerolinewidth=2, zerolinecolor='LightPink') fig.update_xaxes(range=[-0.5, 1.5], zeroline=True, zerolinewidth=2, zerolinecolor='#008000') fig.add_trace(go.Scatter(visible='legendonly', x=x, y=h(x), name='h(x)=sin(x)')) fig.add_trace(go.Scatter(visible='legendonly', x=x, y=k(x), name='k(x)=cos(x)')) fig.add_trace(go.Scatter(visible='legendonly', x=x, y=m(x), name='m(x)=tg(x)')) fig.add_trace(go.Scatter(x=x, y=f(x), mode='lines+markers', name='f(x)=x2')) fig.add_trace(go.Scatter(x=x, y=x, mode='markers',name='g(x)=x', marker=dict(color='LightSkyBlue', size=20, line=dict(color='MediumPurple', width=3)))) fig.update_layout(legend_orientation="h", legend=dict(x=.5, xanchor="center"), hovermode="x", margin=dict(l=0, r=0, t=0, b=0)) fig.update_traces(hoverinfo="all", hovertemplate ) fig.show()

Наверное всё же не следует смешивать вместе тригонометрические и арифметические функции. Давайте отобразим их на разных, соседних графиках.
Для этого нам потребуется создать фигуру с несколькими осями.
Фигура с несколькими графиками создаётся с помощью подмодуля make_subplots.
Необходимо указать количество:
- row — строк
- col — столбцов
fig = make_subplots(rows=1, cols=2, specs=[[, ]])
fig = make_subplots(rows=1, cols=2) fig.update_yaxes(range=[-0.5, 1.5], zeroline=True, zerolinewidth=2, zerolinecolor='LightPink') fig.update_xaxes(range=[-0.5, 1.5], zeroline=True, zerolinewidth=2, zerolinecolor='#008000') fig.add_trace(go.Scatter(x=x, y=h(x), name='h(x)=sin(x)'), 1, 1) fig.add_trace(go.Scatter(x=x, y=k(x), name='k(x)=cos(x)'), 1, 1) fig.add_trace(go.Scatter(visible='legendonly', x=x, y=m(x), name='m(x)=tg(x)'), 1, 1) fig.add_trace(go.Scatter(x=x, y=f(x), mode='lines+markers', name='f(x)=x2'), 1, 2) fig.add_trace(go.Scatter(x=x, y=x, mode='markers',name='g(x)=x', marker=dict(color='LightSkyBlue', size=20, line=dict(color='MediumPurple', width=3))), 1, 2) fig.update_layout(legend_orientation="h", legend=dict(x=.5, xanchor="center"), hovermode="x", margin=dict(l=0, r=0, t=0, b=0)) fig.update_traces(hoverinfo="all", hovertemplate ) fig.show()

Заметили, наши изменения осей применились к обоим графикам?
Естественно, если у метода, изменяющего оси указать аргументы:
- row — координата строки
- col — координата столбца
fig = make_subplots(rows=1, cols=2) fig.update_yaxes(range=[-0.5, 1.5], zeroline=True, zerolinewidth=2, zerolinecolor='LightPink', col=2) fig.update_xaxes(range=[-0.5, 1.5], zeroline=True, zerolinewidth=2, zerolinecolor='#008000', col=2) fig.add_trace(go.Scatter(x=x, y=h(x), name='h(x)=sin(x)'), 1, 1) fig.add_trace(go.Scatter(x=x, y=k(x), name='k(x)=cos(x)'), 1, 1) fig.add_trace(go.Scatter(visible='legendonly', x=x, y=m(x), name='m(x)=tg(x)'), 1, 1) fig.add_trace(go.Scatter(x=x, y=f(x), mode='lines+markers', name='f(x)=x2'), 1, 2) fig.add_trace(go.Scatter(x=x, y=x, mode='markers',name='g(x)=x', marker=dict(color='LightSkyBlue', size=20, line=dict(color='MediumPurple', width=3))), 1, 2) fig.update_layout(legend_orientation="h", legend=dict(x=.5, xanchor="center"), hovermode="x", margin=dict(l=0, r=0, t=0, b=0)) fig.update_traces(hoverinfo="all", hovertemplate ) fig.show()

А вот если бездумно использовать title, xaxis_title и yaxis_title для update_layout, то может выйти казус — подписи применятся только к 1 графику:
Код, приводящий к казусу
fig = make_subplots(rows=1, cols=2) fig.update_yaxes(range=[-0.5, 1.5], zeroline=True, zerolinewidth=2, zerolinecolor='LightPink', col=2) fig.update_xaxes(range=[-0.5, 1.5], zeroline=True, zerolinewidth=2, zerolinecolor='#008000', col=2) fig.add_trace(go.Scatter(x=x, y=h(x), name='h(x)=sin(x)'), 1, 1) fig.add_trace(go.Scatter(x=x, y=k(x), name='k(x)=cos(x)'), 1, 1) fig.add_trace(go.Scatter(visible='legendonly', x=x, y=m(x), name='m(x)=tg(x)'), 1, 1) fig.add_trace(go.Scatter(x=x, y=f(x), mode='lines+markers', name='f(x)=x2'), 1, 2) fig.add_trace(go.Scatter(x=x, y=x, mode='markers',name='g(x)=x', marker=dict(color='LightSkyBlue', size=20, line=dict(color='MediumPurple', width=3))), 1, 2) fig.update_layout(legend_orientation="h", legend=dict(x=.5, xanchor="center"), hovermode="x", margin=dict(l=0, r=0, t=30, b=0)) fig.update_layout(title="Plot Title", xaxis_title="x Axis Title", yaxis_title="y Axis Title") fig.update_traces(hoverinfo="all", hovertemplate ) fig.show()

Поэтому заголовки графиков можно задать, при создании фигуры, передав в аргумент subplot_titles кортеж/список с названиями.
Подписи осей под графиками можно поменять с помощью методов фигуры:
- fig.update_xaxes
- fig.update_yaxes
Код, подписывающий все графики независимо
fig = make_subplots(rows=1, cols=2, subplot_titles=("Plot 1", "Plot 2")) fig.update_yaxes(range=[-0.5, 1.5], zeroline=True, zerolinewidth=2, zerolinecolor='LightPink', col=2) fig.update_xaxes(range=[-0.5, 1.5], zeroline=True, zerolinewidth=2, zerolinecolor='#008000', col=2) fig.add_trace(go.Scatter(x=x, y=h(x), name='h(x)=sin(x)'), 1, 1) fig.add_trace(go.Scatter(x=x, y=k(x), name='k(x)=cos(x)'), 1, 1) fig.add_trace(go.Scatter(visible='legendonly', x=x, y=m(x), name='m(x)=tg(x)'), 1, 1) fig.add_trace(go.Scatter(x=x, y=f(x), mode='lines+markers', name='f(x)=x2'), 1, 2) fig.add_trace(go.Scatter(x=x, y=x, mode='markers',name='g(x)=x', marker=dict(color='LightSkyBlue', size=20, line=dict(color='MediumPurple', width=3))), 1, 2) fig.update_layout(legend_orientation="h", legend=dict(x=.5, xanchor="center"), hovermode="x", margin=dict(l=0, r=0, t=40, b=0)) fig.update_layout(title="Plot Title") fig.update_xaxes(title='Ось X графика 1', col=1, row=1) fig.update_xaxes(title='Ось X графика 2', col=2, row=1) fig.update_yaxes(title='Ось Y графика 1', col=1, row=1) fig.update_yaxes(title='Ось Y графика 2', col=2, row=1) fig.update_traces(hoverinfo="all", hovertemplate ) fig.show()

И конечно, если необходимо сделать так, чтобы один график был больше, а другой меньше, то для этого используется аргументы
- column_widths — задаёт отношения ширины графиков (в одной строке)
- row_heights — задаёт отношения высот графиков (в одном столбце)
Рассмотрим на примере ширин. Сделаем левый график вдвое шире правого, т.е. зададим соотношение 2:1.
fig = make_subplots(rows=1, cols=2, column_widths=[2, 1]) fig.update_yaxes(range=[-0.5, 1.5], zeroline=True, zerolinewidth=2, zerolinecolor='LightPink', col=2) fig.update_xaxes(range=[-0.5, 1.5], zeroline=True, zerolinewidth=2, zerolinecolor='#008000', col=2) fig.add_trace(go.Scatter(x=x, y=h(x), name='h(x)=sin(x)'), 1, 1) fig.add_trace(go.Scatter(x=x, y=k(x), name='k(x)=cos(x)'), 1, 1) fig.add_trace(go.Scatter(visible='legendonly', x=x, y=m(x), name='m(x)=tg(x)'), 1, 1) fig.add_trace(go.Scatter(x=x, y=f(x), mode='lines+markers', name='f(x)=x2'), 1, 2) fig.add_trace(go.Scatter(x=x, y=x, mode='markers',name='g(x)=x', marker=dict(color='LightSkyBlue', size=20, line=dict(color='MediumPurple', width=3))), 1, 2) fig.update_layout(legend_orientation="h", legend=dict(x=.5, xanchor="center"), hovermode="x", margin=dict(l=0, r=0, t=0, b=0)) fig.update_traces(hoverinfo="all", hovertemplate ) fig.show()

А что если мы хотим выделить одному из графиков больше места, чем другим, например, 2 строки или наоборот, 2 столбца?
В matplotlib мы использовали бы несколько фигур, либо оси с заданными размерами, здесь у нас есть другой инструмент. Мы можем сказать каким-то осям объединиться вдоль колонок или вдоль строк.
Для этого нам потребуется написать спецификацию на фигуру (для начала очень простую).
Спецификация — это список (если точнее, то даже матрица из списков), каждый объект внутри которого — словарь, описывающий одни из осей.
Если каких-то осей нет (например, если их место занимают растянувшиеся соседи, то вместо словаря передаётся None.
Давайте сделаем матрицу 2х2 и объединим вместе левые графики, получив одни высокие вертикальные оси. Для этого первому графику передадим атрибут «rowspan» равный 2, а его нижнего соседа уничтожим (None):
fig = make_subplots(rows=2, cols=2, specs=[[, <>], [None, <>]]) fig.update_yaxes(range=[-0.5, 1.5], zeroline=True, zerolinewidth=2, zerolinecolor='LightPink', col=2) fig.update_xaxes(range=[-0.5, 1.5], zeroline=True, zerolinewidth=2, zerolinecolor='#008000', col=2) fig.add_trace(go.Scatter(x=x, y=h(x), name='h(x)=sin(x)'), 2, 2) fig.add_trace(go.Scatter(x=x, y=k(x), name='k(x)=cos(x)'), 2, 2) fig.add_trace(go.Scatter(x=x, y=m(x), name='m(x)=tg(x)'), 1, 1) fig.add_trace(go.Scatter(x=x, y=f(x), mode='lines+markers', name='f(x)=x2'), 1, 2) fig.add_trace(go.Scatter(x=x, y=x, mode='markers',name='g(x)=x', marker=dict(color='LightSkyBlue', size=20, line=dict(color='MediumPurple', width=3))), 1, 2) fig.update_layout(legend_orientation="h", legend=dict(x=.5, xanchor="center"), hovermode="x", margin=dict(l=0, r=0, t=0, b=0)) fig.update_traces(hoverinfo="all", hovertemplate ) fig.show()

Как видим, в вертикальный график идеально вписался тангенс, который отныне не невидим.
Для объединения используется:
- rowspan — по вертикали
- colspan — по горизонтали
Код, объединяющий ячейки в другом направлении
fig = make_subplots(rows=2, cols=2, specs=[[, None], [<>, <>]]) fig.update_yaxes(range=[-0.5, 1.5], zeroline=True, zerolinewidth=2, zerolinecolor='LightPink', col=2) fig.update_xaxes(range=[-0.5, 1.5], zeroline=True, zerolinewidth=2, zerolinecolor='#008000', col=2) fig.add_trace(go.Scatter(x=x, y=h(x), name='h(x)=sin(x)'), 2, 2) fig.add_trace(go.Scatter(x=x, y=k(x), name='k(x)=cos(x)'), 2, 2) fig.add_trace(go.Scatter(x=x, y=m(x), name='m(x)=tg(x)'), 1, 1) fig.add_trace(go.Scatter(x=x, y=f(x), mode='lines+markers', name='f(x)=x2'), 2, 1) fig.add_trace(go.Scatter(x=x, y=x, mode='markers',name='g(x)=x', marker=dict(color='LightSkyBlue', size=20, line=dict(color='MediumPurple', width=3))), 2, 1) fig.update_layout(legend_orientation="h", legend=dict(x=.5, xanchor="center"), hovermode="x", margin=dict(l=0, r=0, t=0, b=0)) fig.update_traces(hoverinfo="all", hovertemplate ) fig.show()

Последний вариант получился слишком узким по вертикали.
Высоту легко увеличить в помощью атрибута height у метода update_layout.
Размеры фигуры регулируются 2 атрибутами:
- width — ширина (в пикселях)
- height — высота (в пикселях)
fig = make_subplots(rows=2, cols=2, specs=[[, None], [<>, <>]]) fig.update_yaxes(range=[-0.5, 1.5], zeroline=True, zerolinewidth=2, zerolinecolor='LightPink', col=2) fig.update_xaxes(range=[-0.5, 1.5], zeroline=True, zerolinewidth=2, zerolinecolor='#008000', col=2) fig.add_trace(go.Scatter(x=x, y=h(x), name='h(x)=sin(x)'), 2, 2) fig.add_trace(go.Scatter(x=x, y=k(x), name='k(x)=cos(x)'), 2, 2) fig.add_trace(go.Scatter(x=x, y=m(x), name='m(x)=tg(x)'), 1, 1) fig.add_trace(go.Scatter(x=x, y=f(x), mode='lines+markers', name='f(x)=x2'), 2, 1) fig.add_trace(go.Scatter(x=x, y=x, mode='markers',name='g(x)=x', marker=dict(color='LightSkyBlue', size=20, line=dict(color='MediumPurple', width=3))), 2, 1) fig.update_layout(legend_orientation="h", legend=dict(x=.5, xanchor="center"), hovermode="x", margin=dict(l=0, r=0, t=0, b=0), height=1000, width=600) fig.update_traces(hoverinfo="all", hovertemplate ) fig.show()

Увеличиваем плотность информации
Тепловая карта
Вернёмся к 1 графику, но постараемся уместить на нём больше информации, используя цветовую кодировку (что-то вроде тепловой карты — чем выше значение некой величины, тем «теплее» цвет).
Для этого у объекта go.Scatter используем уже знакомый атрибут marker (напомним, он принимает словарь). Передаём следующие атрибуты в словарь:
- color — список значений по которым будут выбираться цвета. Элементов списка должно быть столько же, сколько и точек.
- colorbar — словарь, описывающий индикационную полосу цветов справа от графика. Принимает на вход словарь. Нас интересует пока только 1 значение словаря — title — заголовок полосы.
fig = go.Figure() fig.add_trace(go.Scatter(x=x, y=f(x), mode='lines+markers', name='f(x)=x2', marker=dict(color=h(x), colorbar=dict(title="h(x)=sin(x)")) )) fig.add_trace(go.Scatter(visible='legendonly', x=x, y=h(x), name='h(x)=sin(x)')) fig.add_trace(go.Scatter(visible='legendonly', x=x, y=k(x), name='k(x)=cos(x)')) fig.update_layout(legend_orientation="h", legend=dict(x=.5, xanchor="center"), margin=dict(l=0, r=0, t=0, b=0)) fig.update_traces(hoverinfo="all", hovertemplate ) fig.show()

В предыдущем примере цветовая шкала не очень похожа на тепловую карту.
На самом деле цвета на шкале можно изменить, для этого служит атрибут colorscale, в который передаётся имя палитры.
fig = go.Figure() fig.add_trace(go.Scatter(x=x, y=f(x), mode='lines+markers', name='f(x)=x2', marker=dict(color=h(x), colorbar=dict(title="h(x)=sin(x)"), colorscale='Inferno') )) fig.add_trace(go.Scatter(visible='legendonly', x=x, y=h(x), name='h(x)=sin(x)')) fig.add_trace(go.Scatter(visible='legendonly', x=x, y=k(x), name='k(x)=cos(x)')) fig.update_layout(legend_orientation="h", legend=dict(x=.5, xanchor="center"), margin=dict(l=0, r=0, t=0, b=0)) fig.update_traces(hoverinfo="all", hovertemplate ) fig.show()

Можно ли добавить больше информации? Конечно можно, но тут возникают хитрости.
Для ещё одного измерения можно использовать размер маркеров.
Важно. Размер — задаётся в пикселях, т.е. величина не отрицательная (в отличие от цвета), поэтому мы будем использовать модуль одной из функций.
так же, величины меньше 2 пикселей обычно плохо видны на экране, поэтому для размера мы добавим множитель.
Размеры задаётся атрибутом size того же словаря внутри marker. Этот атрибут принимает 1 значение (число), либо список (чисел).
fig = go.Figure() fig.add_trace(go.Scatter(x=x, y=f(x), mode='lines+markers', name='f(x)=x2', marker=dict(color=h(x), colorbar=dict(title="h(x)=sin(x)"), colorscale='Inferno', size=50*abs(h(x))) )) fig.add_trace(go.Scatter(visible='legendonly', x=x, y=h(x), name='h(x)=sin(x)')) fig.add_trace(go.Scatter(visible='legendonly', x=x, y=abs(h(x)), name='h_mod(x)=|sin(x)|')) fig.add_trace(go.Scatter(visible='legendonly', x=x, y=k(x), name='k(x)=cos(x)')) fig.update_layout(legend_orientation="h", legend=dict(x=.5, xanchor="center"), margin=dict(l=0, r=0, t=0, b=0)) fig.update_traces(hoverinfo="all", hovertemplate ) fig.show()

Анимация
Можно ли ещё уплотнить информацию на графике? Да, можно, если использовать «четвёртое измерение» — время. Это так же может быть полезно и само по себе для оживления вашего графика.
Вернёмся на пару шагов назад. Мы будем анимировать график построения параболы. Для этого нам понадобятся:
- Начальное состояние
- Кнопки (анимация не должна начинаться сама по себе, поэтому для начала мы создадим простую кнопку, её запускающую, а постепенно перейдём к временной шкале)
- Фреймы (или кадры) — промежуточные состояния
Это то, что будет на графике до начала анимации. В нашем случае это будет стартовая точка.
Уберём практически всё лишнее из предыдущих шагов
Шаг 1 — код полуфабрикат
fig = go.Figure() fig.add_trace(go.Scatter(x=[x[0]], y=[f(x)[0]], mode='lines+markers', name='f(x)=x2')) fig.update_layout(legend_orientation="h", legend=dict(x=.5, xanchor="center"), margin=dict(l=0, r=0, t=0, b=0)) fig.update_traces(hoverinfo="all", hovertemplate ) fig.show()
Код минимальной работоспособной кнопки выглядит так:
"updatemenus": []>]
updatemenus — это один из элементов слоя, т.е. layout фигуры, а значит, мы добавим кнопку с помощью метода update_layout.
Пока она не будет ничего делать, т.к. у нас нечего анимировать.
Шаг 2 — код всё ещё полуфабрикат
fig = go.Figure() fig.add_trace(go.Scatter(x=[x[0]], y=[f(x)[0]], mode='lines+markers', name='f(x)=x2')) fig.update_layout(legend_orientation="h", legend=dict(x=.5, xanchor="center"), updatemenus=[dict(type="buttons", buttons=[dict(label="Play", method="animate", args=[None])])], margin=dict(l=0, r=0, t=0, b=0)) fig.update_traces(hoverinfo="all", hovertemplate ) fig.show()
Это список «кадров» из которых состоит наша анимация.
Каждый фрейм должен содержать внутри себя целиком готовый график, который просто будет отображаться на нашей фигуре, как в декорациях.
Фрейм создаётся с помощью go.Frame()
График передаётся внутрь фрейма в аргумент data.
Таким образом, если мы хотим построить последовательность графиков (от 1 точки до целой фигуры), нам надо просто пройти в цикле:
frames=[] for i in range(1, len(x)): frames.append(go.Frame(data=[go.Scatter(x=x[:i], y=f(x[:i]))]))
После этого фреймы необходимо передать в фигуру. У каждой фигуры есть атрибут frames, который мы и будем использовать:
fig.frames = frames
Шаг 3 — Ёлочка гори, в смысле код, запускающий анимацию
fig = go.Figure() fig.add_trace(go.Scatter(x=[x[0]], y=[f(x)[0]], mode='lines+markers', name='f(x)=x2')) frames=[] for i in range(1, len(x)): frames.append(go.Frame(data=[go.Scatter(x=x[:i+1], y=f(x[:i+1]))])) fig.frames = frames fig.update_layout(legend_orientation="h", legend=dict(x=.5, xanchor="center"), updatemenus=[dict(type="buttons", buttons=[dict(label="Play", method="animate", args=[None])])], margin=dict(l=0, r=0, t=0, b=0)) fig.update_traces(hoverinfo="all", hovertemplate ) fig.show()
Другой способ задать начальное состояние, слой (с кнопками) и фреймы — сразу передать всё в объект go.Figure:
- data — атрибут для графика с начальным состоянием
- layout — описание «декораций» включая кнопки
- frames — фреймы (кадры) анимации
frames=[] for i in range(1, len(x)): frames.append(go.Frame(data=[go.Scatter(x=x[:i+1], y=f(x[:i+1]))])) fig = go.Figure(data=go.Scatter(x=[x[0]], y=[f(x[0])], mode='lines+markers', name='f(x)=x2'), frames=frames, layout=dict(legend_orientation="h", legend=dict(x=.5, xanchor="center"), updatemenus=[dict(type="buttons", buttons=[dict(label="Play", method="animate", args=[None])])], margin=dict(l=0, r=0, t=0, b=0))) fig.update_traces(hoverinfo="all", hovertemplate ) fig.show()
Естественно, если добавить на графики (как на начальный, так и те, что во фреймах) маркеры с указанием цвета, цветовой шкалы и размера, то анимация будет более сложного графика.
fig = go.Figure() fig.add_trace(go.Scatter(x=[x[0]], y=[f(x)[0]], mode='lines+markers', name='f(x)=x2', marker=dict(color=h(x[0]), colorbar=dict(title="h(x)=sin(x)"), colorscale='Inferno', size=50*abs(h(x[0]))) )) frames=[] for i in range(1, len(x)): frames.append(go.Frame(data=[go.Scatter(x=x[:i+1], y=f(x[:i+1]), marker=dict(color=h(x[:i+1]), size=50*abs(h(x[:i+1]))))])) fig.frames = frames fig.update_layout(legend_orientation="h", legend=dict(x=.5, xanchor="center"), updatemenus=[dict(type="buttons", buttons=[dict(label="Play", method="animate", args=[None])])], margin=dict(l=0, r=0, t=0, b=0)) fig.update_traces(hoverinfo="all", hovertemplate ) fig.show()
Заметим, что код простейшей кнопки, которая запускает воспроизведение видео выглядит так:
dict(label="Play", method="animate", args=[None])
dict(label="", method="animate", args=[None])
Если мы хотим добавить кнопку «пауза» (в отличие от стандартной паузы повторное нажатие не будет вызывать воспроизведение, для начала воспроизведения придётся нажат Play), код усложнится:
dict(label="❚❚", method="animate", args=[[None], , "mode": "immediate", "transition": >])
Правда, если добавить 2 такие кнопки, то вы заметите, что кнопка play, нажатая после паузы, в итоге начинает воспроизведение с начала. Это не совсем интуитивное поведение, поэтому ей следует добавить ещё 1 аргумент:
dict(label="", method="animate", args=[None, ])
Теперь полный набор из 2 наших кнопок будет выглядеть так:
buttons=[dict(label="►", method="animate", args=[None, ]), dict(label="❚❚", method="animate", args=[[None], , "mode": "immediate", "transition": >])])]
Код с 2 кнопками: запуска и остановки анимации
fig = go.Figure() fig.add_trace(go.Scatter(x=[x[0]], y=[f(x)[0]], mode='lines+markers', name='f(x)=x2', marker=dict(color=h(x[0]), colorbar=dict(title="h(x)=sin(x)"), colorscale='Inferno', size=50*abs(h(x[0]))) )) frames=[] for i in range(1, len(x)): frames.append(go.Frame(data=[go.Scatter(x=x[:i+1], y=f(x[:i+1]), marker=dict(color=h(x[:i+1]), size=50*abs(h(x[:i+1]))))])) fig.frames = frames fig.update_layout(legend_orientation="h", legend=dict(x=.5, xanchor="center"), updatemenus=[dict(type="buttons", buttons=[dict(label="►", method="animate", args=[None, ]), dict(label="❚❚", method="animate", args=[[None], , "mode": "immediate", "transition": >])])], margin=dict(l=0, r=0, t=0, b=0)) fig.update_traces(hoverinfo="all", hovertemplate ) fig.show()
Иногда полезно перенести кнопки в другоме место. Рассмотрим некоторые из атрибутов, которые с этим помогут:
- direction — направление расположения кнопок (по умолчанию сверху-вниз, но если указать «left», то будет слева-направо)
- x, y — положение (в долях от фигуры)
- xanchor, yanchor — как выравнивать кнопки. У нас была раньше проблема с выравниванием легенд, тут та же история. Если хотим выровнять по центру, то x=0.5 и xanchor=«center» помогут.
Код с кнопками внизу
fig = go.Figure() fig.add_trace(go.Scatter(x=[x[0]], y=[f(x)[0]], mode='lines+markers', name='f(x)=x2', marker=dict(color=h(x[0]), colorbar=dict(title="h(x)=sin(x)"), colorscale='Inferno', size=50*abs(h(x[0]))) )) frames=[] for i in range(1, len(x)): frames.append(go.Frame(data=[go.Scatter(x=x[:i+1], y=f(x[:i+1]), marker=dict(color=h(x[:i+1]), size=50*abs(h(x[:i+1]))))])) fig.frames = frames fig.update_layout(legend_orientation="h", legend=dict(x=.5, xanchor="center"), updatemenus=[dict(direction="left", x=0.5, xanchor="center", y=0, type="buttons", buttons=[dict(label="►", method="animate", args=[None, ]), dict(label="❚❚", method="animate", args=[[None], , "mode": "immediate", "transition": >])])], margin=dict(l=0, r=0, t=0, b=0)) fig.update_traces(hoverinfo="all", hovertemplate ) fig.show()
Слайдер
Слайдер по принципу работы похож на анимацию, но есть серьёзное отличие.
Слайдер — это элемент навигации, полоска по которой скользит ползунок, который управляет состоянием графиков на фигуре.
Т.е. если фреймы в анимации меняются один за другим, то в случае использования слайдера все графики одновременно есть. Но большая часть из них невидима. И при перемещении ползунка мы просто какие-то скрываем, а другие наоборот показываем (и перестраиваем оси, конечно).
1. Создаём список графиков. Важно, что один из графиков (например, 1й) должен быть видимым.
Для установления видимости/невидимости используется аргумент visible:
- Видимый график — go.Scatter(visible=True, x=[x[0]], y=[f(x)[0]], mode=’lines+markers’, name=’f(x)=x 2 ‘)
- Невидимый график — go.Scatter(visible=False, x=[x[0]], y=[f(x)[0]], mode=’lines+markers’, name=’f(x)=x 2 ‘)
2. Создаём список «шагов» слайдера.
Шаг имеет определённый синтаксис. По сути он описывает состояние (какие графики видимы, какие нет) и метод перехода к нему.
Шаг должен описывать состояние всех графиков.
Минимальный синтаксис 1 шага:
dict(
method = ‘restyle’,
args = #СПИСОК СОСТОЯНИЙ ВСЕХ ГРАФИКОВ
)
Состояние видимости/невидимости задаётся парой ‘visible’ и списка логических значений (какие графики ‘visible’, а какие нет). Поэтому для КАЖДОГО шага мы создадим список False, а потом поменяем нужное значение на True, чтобы показать какой-то конкретный график.
Шаги нужно собрать в список (т.е. это будет список словарей).
Наконец все шаги надо добавить в фигуру:
Минимально рабочий код (внимательно изучите его, т.к. там есть несколько тонких моментов):
num_steps = len(x) trace_list = [go.Scatter(visible=True, x=[x[0]], y=[f(x)[0]], mode='lines+markers', name='f(x)=x2')] for i in range(1, len(x)): trace_list.append(go.Scatter(visible=False, x=x[:i+1], y=f(x[:i+1]), mode='lines+markers', name='f(x)=x2')) fig = go.Figure(data=trace_list) steps = [] for i in range(num_steps): # Hide all traces step = dict( method = 'restyle', args = ['visible', [False] * len(fig.data)], ) # Enable trace we want to see step['args'][1][i] = True # Add step to step list steps.append(step) sliders = [dict( steps = steps, )] fig.layout.sliders = sliders fig.show()

Если мы хотим добавить не 1 график, а 2, то добавить их придётся парой везде:
- в первоначальное активное состояние
- все неактивные (скрытые)
- парами генерировать состояние шагов
- парами активировать видимые на шаге графики
num_steps = len(x) trace_list = [go.Scatter(visible=True, x=[x[0]], y=[h(x)[0]], mode='lines+markers', name='h(x)=sin(x)'), go.Scatter(visible=True, x=[x[0]], y=[k(x)[0]], mode='lines+markers', name='k(x)=cos(x)')] # Так выглядел процесс добавления этих функций раньше: #fig.add_trace(go.Scatter(x=x, y=h(x), name='h(x)=sin(x)'), 1, 1) #fig.add_trace(go.Scatter(x=x, y=k(x), name='k(x)=cos(x)'), 1, 1) for i in range(1, len(x)): trace_list.append(go.Scatter(visible=False, x=x[:i+1], y=h(x[:i+1]), mode='lines+markers', name='h(x)=sin(x)')) trace_list.append(go.Scatter(visible=False, x=x[:i+1], y=k(x[:i+1]), mode='lines+markers', name='k(x)=cos(x)')) fig = go.Figure(data=trace_list) steps = [] for i in range(num_steps): step = dict( method = 'restyle', args = ['visible', [False] * len(fig.data)], ) step['args'][1][2*i] = True step['args'][1][2*i+1] = True steps.append(step) sliders = [dict( steps = steps, )] fig.layout.sliders = sliders fig.show()

Просто добавим кнопки из предыдущей части. Они ничего не будут делать, т.к. мы ещё никакой анимации не добавили. Просто «прицелимся».
Код слайдера с нерабочими кнопками анимации
num_steps = len(x) trace_list = [go.Scatter(visible=True, x=[x[0]], y=[h(x)[0]], mode='lines+markers', name='h(x)=sin(x)'), go.Scatter(visible=True, x=[x[0]], y=[k(x)[0]], mode='lines+markers', name='k(x)=cos(x)')] for i in range(1, len(x)): trace_list.append(go.Scatter(visible=False, x=x[:i+1], y=h(x[:i+1]), mode='lines+markers', name='h(x)=sin(x)')) trace_list.append(go.Scatter(visible=False, x=x[:i+1], y=k(x[:i+1]), mode='lines+markers', name='k(x)=cos(x)')) fig = go.Figure(data=trace_list) steps = [] for i in range(num_steps): step = dict( method = 'restyle', args = ['visible', [False] * len(fig.data)], ) step['args'][1][2*i] = True step['args'][1][2*i+1] = True steps.append(step) sliders = [dict( steps = steps, )] fig.update_layout(updatemenus=[dict(direction="left", x=0.5, xanchor="center", y=0, type="buttons", buttons=[dict(label="►", method="animate", args=[None, ]), dict(label="❚❚", method="animate", args=[[None], , "mode": "immediate", "transition": >])])], ) fig.layout.sliders = sliders fig.show()
Если просто добавить фреймы с анимацией так, как мы это делали раньше, то кнопки анимации конечно заработают. Но только на 1 состоянии слайдера.
Код искалеченного слайдера
num_steps = len(x) trace_list = [go.Scatter(visible=True, x=[x[0]], y=[h(x)[0]], mode='lines+markers', name='h(x)=sin(x)'), go.Scatter(visible=True, x=[x[0]], y=[k(x)[0]], mode='lines+markers', name='k(x)=cos(x)')] for i in range(1, len(x)): trace_list.append(go.Scatter(visible=False, x=x[:i+1], y=h(x[:i+1]), mode='lines+markers', name='h(x)=sin(x)')) trace_list.append(go.Scatter(visible=False, x=x[:i+1], y=k(x[:i+1]), mode='lines+markers', name='k(x)=cos(x)')) frames=[] for i in range(1, len(x)): frames.append(go.Frame(data=[go.Scatter(visible=True, x=x[:i+1], y=h(x[:i+1]), mode='lines+markers', name='h(x)=sin(x)'), go.Scatter(visible=True, x=x[:i+1], y=k(x[:i+1]), mode='lines+markers', name='k(x)=cos(x)')])) fig = go.Figure(data=trace_list) steps = [] for i in range(num_steps): step = dict( method = 'restyle', args = ['visible', [False] * len(fig.data)], ) step['args'][1][2*i] = True step['args'][1][2*i+1] = True steps.append(step) sliders = [dict( steps = steps, )] fig.update_layout(updatemenus=[dict(direction="left", x=0.5, xanchor="center", y=0, type="buttons", buttons=[dict(label="►", method="animate", args=[None, ]), dict(label="❚❚", method="animate", args=[[None], , "mode": "immediate", "transition": >])])], ) fig.layout.sliders = sliders fig.frames = frames fig.show()
ОК, значит нам придётся сделать «финт ушами» и отказаться от невидимых графиков, а привязать слайдер непосредственно к фреймам анимации (Почему мы так не сделали сразу? Чтобы был альтернативный способ, подходящий непосредственно для слайдера без изысков)
- Закомментируем всё, что касается trace_list — списка наших «невидимых» графиков.
- Вынесем созданием 2 видимых графиков в первоначальное создание фигуры
- Добавим параметр name каждому фрейму
- Переделаем шаблон шага:
- Добавим label, совпадающий с именем соответствующего фрейма
- Сменим метод на «animate»
- Все аргументы заменим на 1 список из 1 единственной строки, совпадающей с именем фрейма
- Уберём «проявление» невидимых графиков на определённых позициях слайдера, т.к. теперь слайдер будет привязан к фреймам
Код относительно работающего слайдера с кнопками анимации
num_steps = len(x) fig = go.Figure(data=[go.Scatter(x=[x[0]], y=[h(x)[0]], mode='lines+markers', name='h(x)=sin(x)'), go.Scatter(x=[x[0]], y=[k(x)[0]], mode='lines+markers', name='k(x)=cos(x)')]) ''' trace_list = [go.Scatter(visible=True, x=[x[0]], y=[h(x)[0]], mode='lines+markers', name='h(x)=sin(x)'), go.Scatter(visible=True, x=[x[0]], y=[k(x)[0]], mode='lines+markers', name='k(x)=cos(x)')] for i in range(1, len(x)): trace_list.append(go.Scatter(visible=False, x=x[:i+1], y=h(x[:i+1]), mode='lines+markers', name='h(x)=sin(x)')) trace_list.append(go.Scatter(visible=False, x=x[:i+1], y=k(x[:i+1]), mode='lines+markers', name='k(x)=cos(x)')) ''' frames=[] for i in range(0, len(x)): frames.append(go.Frame(name=str(i), data=[go.Scatter(x=x[:i+1], y=h(x[:i+1]), mode='lines+markers', name='h(x)=sin(x)'), go.Scatter(x=x[:i+1], y=k(x[:i+1]), mode='lines+markers', name='k(x)=cos(x)')])) #fig = go.Figure(data=trace_list) steps = [] for i in range(num_steps): step = dict( #method = 'restyle', #args = ['visible', [False] * len(fig.data)], label = str(i), method = "animate", args = [[str(i)]] ) #step['args'][1][2*i] = True #step['args'][1][2*i+1] = True steps.append(step) sliders = [dict( steps = steps, )] fig.update_layout(updatemenus=[dict(direction="left", x=0.5, xanchor="center", y=0, type="buttons", buttons=[dict(label="►", method="animate", args=[None, ]), dict(label="❚❚", method="animate", args=[[None], , "mode": "immediate", "transition": >])])], ) fig.layout.sliders = sliders fig.frames = frames fig.show()
Единственный момент, который слегка раздражает — когда происходит «воспроизведение», то ползунок слайдера не ползёт.
Это легко исправить с помощью аргумента showactive для меню. Так же его выключение снимет состояние «нажато» с кнопок Play/Pause.
Код слайдера, который уже нормально анимирован
num_steps = len(x) fig = go.Figure(data=[go.Scatter(x=[x[0]], y=[h(x)[0]], mode='lines+markers', name='h(x)=sin(x)'), go.Scatter(x=[x[0]], y=[k(x)[0]], mode='lines+markers', name='k(x)=cos(x)')]) frames=[] for i in range(0, len(x)): frames.append(go.Frame(name=str(i), data=[go.Scatter(x=x[:i+1], y=h(x[:i+1]), mode='lines+markers', name='h(x)=sin(x)'), go.Scatter(x=x[:i+1], y=k(x[:i+1]), mode='lines+markers', name='k(x)=cos(x)')])) steps = [] for i in range(num_steps): step = dict( label = str(i), method = "animate", args = [[str(i)]] ) steps.append(step) sliders = [dict( steps = steps, )] fig.update_layout(updatemenus=[dict(direction="left", x=0.5, xanchor="center", y=0, showactive=False, type="buttons", buttons=[dict(label="►", method="animate", args=[None, ]), dict(label="❚❚", method="animate", args=[[None], , "mode": "immediate", "transition": >])])], ) fig.layout.sliders = sliders fig.frames = frames fig.show()
Конечно добавим данные тепловой карты и размер маркера для увеличения плотности информации
Заметьте, что colorbar мы добавили всего 1 раз, однако, в него пришлось внести некоторые правки — мы сдвинули его по вертикали слегка вниз, т.к. теперь в правой колонке у нас есть легенда.
num_steps = len(x) fig = go.Figure(data=[go.Scatter(x=[x[0]], y=[h(x)[0]], mode='lines+markers', name='h(x)=sin(x)', marker=dict(color=[f(x[0])], colorbar=dict(yanchor='top', y=0.8, title ), colorscale='Inferno', size=[50*abs(h(x[0]))])), go.Scatter(x=[x[0]], y=[k(x)[0]], mode='lines+markers', name='k(x)=cos(x)', marker=dict(color=[f(x[0])], colorscale='Inferno', size=[50*abs(k(x[0]))]))]) frames=[] for i in range(0, len(x)): frames.append(go.Frame(name=str(i), data=[go.Scatter(x=x[:i+1], y=h(x[:i+1]), mode='lines+markers', name='h(x)=sin(x)', marker=dict(color=f(x[:i+1]), colorscale='Inferno', size=50*abs(h(x[:i+1])))), go.Scatter(x=x[:i+1], y=k(x[:i+1]), mode='lines+markers', name='k(x)=cos(x)', marker=dict(color=f(x[:i+1]), colorscale='Inferno', size=50*abs(k(x[:i+1]))))])) steps = [] for i in range(num_steps): step = dict( label = str(i), method = "animate", args = [[str(i)]] ) steps.append(step) sliders = [dict( steps = steps, )] fig.update_layout(updatemenus=[dict(direction="left", x=0.5, xanchor="center", y=0, showactive=False, type="buttons", buttons=[dict(label="►", method="animate", args=[None, ]), dict(label="❚❚", method="animate", args=[[None], , "mode": "immediate", "transition": >])])], ) fig.layout.sliders = sliders fig.frames = frames fig.show()

Осталось немного облагородить панель слайдера.
Добавим подписи к графику и осям, увеличим и оформим подпись текущего значения слайдера (в других обстоятельствах он стал бы временной шкалой), сместим кнопки анимации влевой, а слайдер чуть сожмём, чтобы освободить им место.
- Аргумент currentvalue — задаёт форматирование подписи к текущему шагу, включая префикс, положение на слайде, шрифт
- Аргументы x, y, xanchor, yanchor, pad — задают положение и отступы для слайдера и их синтаксис аналогичен таковому у кнопок
num_steps = len(x) fig = go.Figure(data=[go.Scatter(x=[x[0]], y=[h(x)[0]], mode='lines+markers', name='h(x)=sin(x)', marker=dict(color=[f(x[0])], colorbar=dict(yanchor='top', y=0.8, title ), colorscale='Inferno', size=[50*abs(h(x[0]))])), go.Scatter(x=[x[0]], y=[k(x)[0]], mode='lines+markers', name='k(x)=cos(x)', marker=dict(color=[f(x[0])], colorscale='Inferno', size=[50*abs(k(x[0]))]))]) frames=[] for i in range(0, len(x)): frames.append(go.Frame(name=str(i), data=[go.Scatter(x=x[:i+1], y=h(x[:i+1]), mode='lines+markers', name='h(x)=sin(x)', marker=dict(color=f(x[:i+1]), colorscale='Inferno', size=50*abs(h(x[:i+1])))), go.Scatter(x=x[:i+1], y=k(x[:i+1]), mode='lines+markers', name='k(x)=cos(x)', marker=dict(color=f(x[:i+1]), colorscale='Inferno', size=50*abs(k(x[:i+1]))))])) steps = [] for i in range(num_steps): step = dict( label = str(i), method = "animate", args = [[str(i)]] ) steps.append(step) sliders = [dict( currentvalue = >, len = 0.9, x = 0.1, pad = , steps = steps, )] fig.update_layout(title="Строим синус и косинус по шагам", xaxis_title="Ось X", yaxis_title="Ось Y", updatemenus=[dict(direction="left", pad = , x = 0.1, xanchor = "right", y = 0, yanchor = "top", showactive=False, type="buttons", buttons=[dict(label="►", method="animate", args=[None, ]), dict(label="❚❚", method="animate", args=[[None], , "mode": "immediate", "transition": >])])], ) fig.layout.sliders = sliders fig.frames = frames fig.show()

Возникает вопрос зачем же мы делали вариант с невидимыми графиками, если они не пригодились?
На самом деле они нужны, в том числе для анимации. Дело в том, что если вы хотите на разных слайдах анимации показывать разное количество графиков, то вам надо в самом начале на этапе создания фигуры добавить столько графиков, сколько их может отображаться максимально. Они все должны быть невидимыми.
Я специально задам 2 переменные:
- graphs_invisible — содержит как невидимый корректный график, так и пустой объект графика вообще без указания видимости
- graphs_visible — содержит корректные видимые графики, которые надо показывать по очереди
graphs_invisible = [go.Scatter(visible = False, x=x, y=f(x), name='f(x)=x2'), go.Scatter(visible = False, x=x, y=x, name='g(x)=x'), go.Scatter(visible = False, x=x, y=h(x), name='h(x)=sin(x)'), go.Scatter(visible = False, x=x, y=k(x), name='k(x)=cos(x)')] graphs_visible = [go.Scatter(visible = True, x=x, y=f(x), name='f(x)=x2'), go.Scatter(visible = True, x=x, y=x, name='g(x)=x'), go.Scatter(visible = True, x=x, y=h(x), name='h(x)=sin(x)'), go.Scatter(visible = True, x=x, y=k(x), name='k(x)=cos(x)')] fig = go.Figure(data=graphs_invisible) frames=[] for i in range(len(graphs_visible)+1): frames.append(go.Frame(name=str(i), data=graphs_visible[:i]+graphs_invisible[i:])) steps = [] for i in range(len(graphs_visible)+1): step = dict( label = str(i), method = "animate", args = [[str(i)]] ) steps.append(step) sliders = [dict( currentvalue = >, len = 0.9, x = 0.1, pad = , steps = steps, )] fig.update_layout(title="Выводим графики по очереди", xaxis_title="Ось X", yaxis_title="Ось Y", updatemenus=[dict(direction="left", pad = , x = 0.1, xanchor = "right", y = 0, yanchor = "top", showactive=False, type="buttons", buttons=[dict(label="►", method="animate", args=[None, ]), dict(label="❚❚", method="animate", args=[[None], , "mode": "immediate", "transition": >])])], ) fig.layout.sliders = sliders fig.frames = frames fig.show()

Философский вопрос — зачем мы так мучаемся, если есть plotly.express?
Документация plotly по анимации plotly.com/python/animations начинается с феерического примера:
Код, создающий довольно сложную анимацию данных с помощью Plotly Express
import plotly.express as px df = px.data.gapminder() px.scatter(df, x="gdpPercap", y="lifeExp", animation_frame="year", animation_group="country", size="pop", color="continent", hover_name="country", log_x=True, size_max=55, range_x=[100,100000], range_y=[25,90])
Действительно, функции Экспресса принимают на вход датафреймы (да, обычные из Pandas), вам лишь нужно указать колонки по которым производится агрегация данных. И можно сразу строить и тепловые карты, и анимации очень небольшим количеством кода, как в этом примере:
Ответ и прост и сложен:
- Стандартные примеры Экспресса могут не удовлетворить ваших потребностей, нужно что-то чуть более сложное и хитрое, например, совместить график и гистограмму. Вручную вы получаете больше гибкости.
- Понять как сгруппировать и агрегировать данные в датафрейме для такой визуализации порой сложнее, чем просто построить набор картинок для фреймов анимации/слайдера.
Показ plotly графиков на сайте
Plotly — это не только библиотека для Python, но ещё и JS, это значит, что любые графики, которые вы строите в jupyter notebook, вы можете показывать и на сайте (если он на Python, либо если вы заранее выгрузите всё необходимое).
В рамках этого урока мы не будем рассматривать полный цикл разработки дашборда или веб приложения, просто рассмотрим небольшой пример:
import json #Здесь вы создаёте свой график в фигуре fig graphJSON = json.dumps(fig, cls=plotly.utils.PlotlyJSONEncoder)
В graphJSON окажется приличный JSON. Не будем вдаваться в его особенности, хотя легко заметить, что его структура соответствует нашему объекту фигуры, со всеми графиками, фреймами, подложками, кнопками и т.п.
Давайте добавим немного по краям и сложим этот код в файл:
with open('example.JSON', 'w') as file: file.write('var graphs = <>;'.format(graphJSON))
А теперь откроем страницу example.html, когда в одном каталоге с ней есть наш файл example.JSON (Для корректной работы необходимо подключение к интернет, т.к. некоторые стили и JS подтягиваются со сторонних сайтов).
Удивительно, не правда ли?
При этом, если посмотреть структуру, то она очень проста и содержит всего 4 важных объекта (их порядок на странице важен):
1. Подключение JS библиотеки Plotly (в примере это делается онлайн из CDN, но если вы делаете локальный продукт, например приватный дашборд для работы внутри организации, JS легко поместить на локальный сервер и поменять ссылки)
2. Блок, где будет выводиться график
Важно понимать, что Plotly по умолчанию не фиксирует размеры графика, поэтому вы можете применить к контейнеру свои стили, сверстав его такого размера и в том месте, где это необходимо вам. Plotly постарается занять всё свободное место по ширине.
Важно использовать действительно уникальный id — именно по нему plotly будет находить место на странице, куда надо встроить график.
3. JS переменная, содержащая наш JSON, описывающий график. Мы сформировали её и сложили в файл целиком. Вы можете выводить её непосредственно в коде страницы.
4. Вызов JS функции plotly, которая построит график.
- первым передаётся id контейнера, в котором выводить график
- вторым передаётся переменная, содержащая JSON Plotly объекта
А теперь испробуйте код на каком-нибудь своём графике!
Круговые диаграммы
Для полноты картины рассмотрим несколько других способов визуализации данных, кроме линейных графиков. Начнём с круговых диаграмм
Для нашего эксперимента «подбросим» 100 раз пару игральных кубиков (костей) и запишем суммы выпавших очков.
dices = pd.DataFrame(np.random.randint(low=1, high=7, size=(100, 2)), columns=('Кость 1', 'Кость 2')) dices['Сумма'] = dices['Кость 1'] + dices['Кость 2'] # Первые 5 бросков игральных костей display(dices.head()) sum_counts = dices['Сумма'].value_counts().sort_index() # количество выпавших сумм display(sum_counts)
Для того чтобы создать круговую диаграмму используем go.Pie, который добавляем так же, как мы добавляли график на созданную фигуру.
Используем 2 основных атрибута:
- values — размер сектора диаграммы, в нашем случае прямо пропорционален количеству той или иной суммы
- labels — подпись сектора, в нашем случае значение суммы. Если не передать подпись, то в качестве подписи будет взят индекс значения из списка values
fig = go.Figure() fig.add_trace(go.Pie(values=sum_counts, labels=sum_counts.index)) fig.show()

Сразу бросается в глаза то, что хотя мы передали массив, упорядоченный по индексам, но при построении он был пересортирован по значениям.
Это легко исправить с помощью аргумента sort = False
fig = go.Figure() fig.add_trace(go.Pie(values=sum_counts, labels=sum_counts.index, sort = False)) fig.show()

Так же при желании мы можем «выдвинуть» один или несколько секторов.
Для этого используем аргумент pull, который принимаем список чисел. Каждое число — доля, на которую надо выдвинуть сектор из круга:
- 0 — не выдвигать
- 1 — 100% радиуса круга
Обратите внимание, мы не используем метод idxmax Pandas, т.к. наш массив имеет индексы, соответствующие суммам. А определение какой сектор выдвигать на диаграмме происходит по индексу списка, к которому наш массив приводится.
fig = go.Figure() pull = [0]*len(sum_counts) pull[sum_counts.tolist().index(sum_counts.max())] = 0.2 fig.add_trace(go.Pie(values=sum_counts, labels=sum_counts.index, pull=pull)) fig.show()

Если вам не нравятся классические круговые диаграммы «пирожки», то легко превратить их в «пончики», вырезав сердцевину. Для этого используем аргумент hole, в который передаём число (долю радиуса, которую надо удалить):
- 0 — не вырезать ничего
- 1 — 100% вырезать, ничего не оставить
fig = go.Figure() pull = [0]*len(sum_counts) pull[sum_counts.tolist().index(sum_counts.max())] = 0.2 fig.add_trace(go.Pie(values=sum_counts, labels=sum_counts.index, pull=pull, hole=0.9)) fig.show()

Кстати, образовавшаяся «дырка от бублика» — идеальное место для подписи, которую можно сделать с помощью атрибута annotations слоя.
Не забываем, что аннотаций может быть много, поэтому annotations принимаем список словарей.
Текст аннотации поддерживает HTML разметку (чем мы воспользуемся, задав абсурдно длинный текст, не помещающийся в 1 строку)
fig = go.Figure() pull = [0]*len(sum_counts) pull[sum_counts.tolist().index(sum_counts.max())] = 0.2 fig.add_trace(go.Pie(values=sum_counts, labels=sum_counts.index, pull=pull, hole=0.9)) fig.update_layout( annotations=[dict(text='Суммы очков
при броске
2 игральных костей', x=0.5, y=0.5, font_size=20, showarrow=False)]) fig.show()

Естественно обычный способы оформления визуализаций, показанные для графиков, тут тоже работают:
fig = go.Figure() pull = [0]*len(sum_counts) pull[sum_counts.tolist().index(sum_counts.max())] = 0.2 fig.add_trace(go.Pie(values=sum_counts, labels=sum_counts.index, pull=pull, hole=0.9)) fig.update_layout( title="Пример кольцевой/круговой диаграммы", title_x = 0.5, margin=dict(l=0, r=0, t=30, b=0), legend_orientation="h", annotations=[dict(text='Суммы очков
при броске
2 игральных костей', x=0.5, y=0.5, font_size=20, showarrow=False)]) fig.show()

Что, если вы хотим детализовать картинку?
Sunburst или диаграмма «солнечные лучи»
Нам на помощь приходит диаграмма «солнечные лучи» — иерархическая диаграмма на основе круговой. По сути это набор кольцевых диаграмм, нанизанных друг на друга, причём сегменты следующего уровня находятся в пределах границ сегментов своего «родителя» на предыдущем.
Например, получить 8 очков с помощью 2 игральных костей можно несколькими способами:
- 2 + 6
- 3 + 5
- 4 + 4
- values — значения, задающие долю от круга на диаграмме
- branchvalues=«total» — такое значение указывает, что значения родительского элемента являются суммой значений потомков. Это необходимо для того, чтобы составить полный круг на каждом уровне.
- labels — список подписей, которые отображаются на диаграмме
- parents — список подписей родителей, для построения иерархии. Для элементов 0 уровня (без родителей) родителем указывается пустая строка.
# 1-й уровень, центр диаграммы labels = ["Всего событий: " + str(sum(sum_counts))] parents = [""] values = [sum(sum_counts)] # 2-й уровень, "лепестки" диаграммы second_level_dict = Σ = ' + str(x) for x in sum_counts.index> labels += map(lambda x: second_level_dict[x], sum_counts.index) parents += [labels[0]]*len(sum_counts) values += sum_counts.tolist() fig = go.Figure(go.Sunburst( labels = labels, parents = parents, values=values, branchvalues="total" )) #fig.update_layout(margin = dict(t=0, l=0, r=0, b=0)) fig.show()

А теперь добавим группировку по парам исходов игральных костей и вычисление для таких пар «родителей».
Конечно, если кости идентичны, то 6+2 и 2+6 — это идентичные исходы, как и пара 3+5 и 5+3, но в рамках следующего примера мы будем считать их разными, просто чтобы не добавлять лишнего кода.
Так же уменьшим отступы, т.к. подписи получаются уж очень мелкими.
# 1-й уровень, центр диаграммы labels = ["Всего событий: " + str(sum(sum_counts))] parents = [""] values = [sum(sum_counts)] # 2-й уровень, "промежуточный" second_level_dict = Σ = ' + str(x) for x in sum_counts.index> labels += map(lambda x: second_level_dict[x], sum_counts.index) parents += [labels[0]]*len(sum_counts) values += sum_counts.tolist() # Готовим DataFrame для 3 уровня (группируем ) third_level = dices.groupby(['Кость 1', 'Кость 2']).count().reset_index() third_level.rename(columns=, inplace=True) third_level['Сумма'] = third_level['Кость 1'] + third_level['Кость 2'] third_level['Label'] = third_level['Кость 1'].map(str) + ' + ' + third_level['Кость 2'].map(str) third_level['Parent'] = third_level['Сумма'].map(lambda x: second_level_dict[x]) # 3-й уровень, "лепестки" диаграммы values += third_level['Value'].tolist() parents += third_level['Parent'].tolist() labels += third_level['Label'].tolist() fig = go.Figure(go.Sunburst( labels = labels, parents = parents, values=values, branchvalues="total" )) fig.update_layout(margin = dict(t=0, l=0, r=0, b=0)) fig.show()

Гистограммы
Естественно не круговыми диаграммами едиными, иногда нужны и обычные столбчатые.
Простейшая гистограмма строится с помощью go.Histogram. В качестве единственного аргумента в x передаём список значений, которые участвуют в выборке (Plotly самостоятельно сгруппирует их в столбцы и вычислит высоту), в нашем случае это колонка с суммами.
fig = go.Figure(data=[go.Histogram(x=dices['Сумма'])]) fig.show()

Если по какой-то причине нужно построить не вертикальную, а горизонтальную гистограмму, то меняем x на y:
fig = go.Figure(data=[go.Histogram(y=dices['Сумма'])]) fig.show()

А что, если у нас 2 или 3 набора данных и мы хотим их сравнить? Сгенерируем ещё 1100 бросков пар кубиков и просто добавим на фигуру 2 гистограммы:
dices2 = pd.DataFrame(np.random.randint(low=1, high=7, size=(100, 2)), columns=('Кость 1', 'Кость 2')) dices2['Сумма'] = dices2['Кость 1'] + dices2['Кость 2'] dices3 = pd.DataFrame(np.random.randint(low=1, high=7, size=(1000, 2)), columns=('Кость 1', 'Кость 2')) dices3['Сумма'] = dices3['Кость 1'] + dices3['Кость 2'] fig = go.Figure() fig.add_trace(go.Histogram(x=dices['Сумма'])) fig.add_trace(go.Histogram(x=dices2['Сумма'])) fig.add_trace(go.Histogram(x=dices3['Сумма'])) fig.show()

Все 3 выборки подчиняются одному и тому же распределению, и очевидно, но количество событий сильно отличается, поэтому на нашей гистограмме некоторые столбцы сильно больше других.
Картинку надо «нормализовать». Для этого служит аргумент histnorm.
fig = go.Figure() fig.add_trace(go.Histogram(x=dices['Сумма'], histnorm='probability density')) fig.add_trace(go.Histogram(x=dices2['Сумма'], histnorm='probability density')) fig.add_trace(go.Histogram(x=dices3['Сумма'], histnorm='probability density')) fig.show()

Как и предыдущие виды визуализаций, гистограммы могут иметь оформление:
- подпись графика, подписи осей
- ориентация и положение легенды.
- отступы
fig = go.Figure() fig.add_trace(go.Histogram(x=dices['Сумма'], histnorm='probability density', name='100 бросков v.1')) fig.add_trace(go.Histogram(x=dices2['Сумма'], histnorm='probability density', name='100 бросков v.2')) fig.add_trace(go.Histogram(x=dices3['Сумма'], histnorm='probability density', name='1000 бросков')) fig.update_layout( title="Пример гистограммы на основе бросков пары игральных костей", title_x = 0.5, xaxis_title="сумма очков", yaxis_title="Плотность вероятности", legend=dict(x=.5, xanchor="center", orientation="h"), margin=dict(l=0, r=0, t=30, b=0)) fig.show()

Другой интересны режим оформления — barmode=’overlay’ — он позволяет рисовать столбцы гистограммы одни поверх других.
Имеет смысл использовать его одновременно с аргументом opacity самих гистограмм — он задаёт прозрачность гистограммы (от 0 до 100%).
Однако, большое количество гистограмм в таком случае тяжело визуально интерпретировать, поэтому мы скроем одну.
fig = go.Figure() fig.add_trace(go.Histogram(x=dices['Сумма'], histnorm='probability density', opacity=0.75, name='100 бросков v.1')) fig.add_trace(go.Histogram(x=dices2['Сумма'], histnorm='probability density', opacity=0.75, name='100 бросков v.2')) #fig.add_trace(go.Histogram(x=dices3['Сумма'], histnorm='probability density', opacity=0.75, name='1000 бросков')) fig.update_layout( title="Пример гистограммы на основе бросков пары игральных костей", title_x = 0.5, xaxis_title="сумма очков", yaxis_title="Плотность вероятности", legend=dict(x=.5, xanchor="center", orientation="h"), barmode='overlay', margin=dict(l=0, r=0, t=30, b=0)) fig.show()

Если мы говорим о вероятности, то имеет так же смысл построить и накопительную гистограмму. Например, вероятности выпадения не менее чем X очков на сумме из 2 игральных костей.
Для этого используется аргумент гистограммы cumulative_enabled=True
fig = go.Figure() fig.add_trace(go.Histogram(x=dices['Сумма'], histnorm='probability density', cumulative_enabled=True, name='100 бросков v.1')) fig.add_trace(go.Histogram(x=dices2['Сумма'], histnorm='probability density', cumulative_enabled=True, name='100 бросков v.2')) fig.add_trace(go.Histogram(x=dices3['Сумма'], histnorm='probability density', cumulative_enabled=True, name='1000 бросков')) fig.update_layout( title="Пример накопительной гистограммы на основе бросков пары игральных костей", title_x = 0.5, xaxis_title="сумма очков", yaxis_title="Вероятность", legend=dict(x=.5, xanchor="center", orientation="h"), margin=dict(l=0, r=0, t=30, b=0)) fig.show()

Так же весьма полезно то, что на одной фигуре можно совмещать график, построенный по точкам (go.Scatter) и гистограмму (go.Histogram).
Для демонстрации такого применения, давайте сгенерируем 1000 событий из другого распределения — нормального. Для него легко построить теоретическую кривую. Мы возьмём для этого готовые функции из модуля scipy:
- scipy.stats.norm.rvs — для генерации событий
- scipy.stats.norm.pdf — для получения теоретический функции распределения
В качестве начального и конечного значений аргумента (x) возьмём границы интервала в 3σ
from scipy.stats import norm r = norm.rvs(size=1000) x_norm = np.linspace(norm.ppf(0.01), norm.ppf(0.99), 100) fig = go.Figure() fig.add_trace(go.Histogram(x=r, histnorm='probability density', name='"Экспериментальные" данные')) fig.add_trace(go.Scatter(x=x_norm, y=norm.pdf(x_norm), name='Теоретическая форма нормального распределения')) fig.update_layout( title="Пример гистограммы на основе нормального распределения", title_x = 0.5, legend=dict(x=.5, xanchor="center", orientation="h"), margin=dict(l=0, r=0, t=30, b=0)) fig.show()

Этот пример так же демонстрирует как происходит объединение в столбцы, если величина не дискретная.
В данном случае каждый столбец тем выше, чем больше значений попало в интервал, соответствующий ширине этого столбца.
В свою очередь это означает, что при необходимости мы можем регулировать количество столбцов и их ширину (это 2 взаимосвязанных параметра).
- Вариант 1 — задав ширину столбца — xbins=
- Вариант 2 — задав количество столбцов — nbinsx=200
fig = go.Figure() fig.add_trace(go.Histogram(nbinsx=200, x=r, histnorm='probability density', name='"Экспериментальные" данные')) fig.add_trace(go.Scatter(x=x_norm, y=norm.pdf(x_norm), name='Теоретическая форма нормального распределения')) fig.update_layout( title="Пример гистограммы на основе нормального распределения", title_x = 0.5, legend=dict(x=.5, xanchor="center", orientation="h"), margin=dict(l=0, r=0, t=30, b=0)) fig.show()

Другие столбчатые диаграммы — Bar Charts
Столбчатые диаграммы можно сформировать и своими силами, если сгруппировать данные и вычислить высоты столбцов.
Далее, используя класс go.Bar передаём названия столбцов и их величины в 2 аргумента:
d_grouped = dices.groupby(['Сумма']).count() labels = d_grouped.index values = d_grouped['Кость 1'].values fig = go.Figure(data=[go.Bar(x = labels, y = values)]) fig.show()

Важно!
Как и круговая диаграмма, такая столбчатая в отличие от ранее изученных гистограмм не построит столбец для того, чего нет!
Например, если мы сделаем только 10 бросков по 2 кости, то среди них не может выпасть всех возможных случаев. А значит, они не отобразятся на диаграмме:
Код дырявой диаграммы без крайнего столбца
BAD_d_grouped = dices.head(10).groupby(['Сумма']).count() labels = BAD_d_grouped.index values = BAD_d_grouped['Кость 1'].values fig = go.Figure(data=[go.Bar(x = labels, y = values)]) fig.show()

При необходимости выведения ВСЕХ, даже нулевых столбцов, их следует сформировать самостоятельно.
labels = tuple(range(2,13)) BAD_d_grouped = dices.head(10).groupby(['Сумма']).count() clear = BAD_d_grouped['Кость 1'].append(pd.DataFrame([0]*len(labels), index=labels)).reset_index().drop_duplicates('index').sort_values('index') values = clear[0] fig = go.Figure(data=[go.Bar(x = labels, y = values)]) fig.show()

Создадим парную гистограмму для 2 наборов по 100 бросков, в оба набора добавив на всякий случай колонки с нулями, если их нет.
В зависимости от генерации начальных данных в каких-то местах должна быть только 1 колонка, либо не будет колонок вообще.
labels = tuple(range(2,13)) d_grouped = dices.groupby(['Сумма']).count() clear = d_grouped['Кость 1'].append(pd.DataFrame([0]*len(labels), index=labels)).reset_index().drop_duplicates('index').sort_values('index') values = clear[0] d_grouped2 = dices2.groupby(['Сумма']).count() clear2 = d_grouped2['Кость 1'].append(pd.DataFrame([0]*len(labels), index=labels)).reset_index().drop_duplicates('index').sort_values('index') values2 = clear2[0] fig = go.Figure() fig.add_trace(go.Bar(x = labels, y = values)) fig.add_trace(go.Bar(x = labels, y = values2)) fig.show()

А вот если мы хотим вывести и 3й набор испытаний (1000 бросков), то придётся самостоятельно нормализовать данные, т.к. у go.Bar в отличие от гистограмм нет аргумента вроде histnorm.
Иначе вновь получим странную картинку, когда столбцы третьей серии сильно больше остальных и их нельзя сравнить.
labels = tuple(range(2,13)) d_grouped = dices.groupby(['Сумма']).count() clear = d_grouped['Кость 1'].append(pd.DataFrame([0]*len(labels), index=labels)).reset_index().drop_duplicates('index').sort_values('index') values = clear[0] / clear[0].sum() d_grouped2 = dices2.groupby(['Сумма']).count() clear2 = d_grouped2['Кость 1'].append(pd.DataFrame([0]*len(labels), index=labels)).reset_index().drop_duplicates('index').sort_values('index') values2 = clear2[0] / clear2[0].sum() d_grouped3 = dices3.groupby(['Сумма']).count() clear3 = d_grouped3['Кость 1'].append(pd.DataFrame([0]*len(labels), index=labels)).reset_index().drop_duplicates('index').sort_values('index') values3 = clear3[0] / clear3[0].sum() fig = go.Figure() fig.add_trace(go.Bar(x = labels, y = values)) fig.add_trace(go.Bar(x = labels, y = values2)) fig.add_trace(go.Bar(x = labels, y = values3)) fig.show()

Зато есть у такой диаграммы и серьёзное преимущество — высота столбцов не обязана быть положительной. Это можно использовать, например, для построения диаграммы прибылей и убытков по периодам. Просто вычтем из одного набора значений другой, получим список чисел (некоторые отрицательные, некоторые положительные, а некоторые 0) и используем его для построения.
Так же это отличный повод вспомнить, что для столбчатых диаграмм работают все способы оформления, которые мы отработали на графиках. В данном случае логично выделить нулевой уровень красным цветом.
labels = tuple(range(2,13)) d_grouped = dices.groupby(['Сумма']).count() clear = d_grouped['Кость 1'].append(pd.DataFrame([0]*len(labels), index=labels)).reset_index().drop_duplicates('index').sort_values('index') values = clear[0] d_grouped2 = dices2.groupby(['Сумма']).count() clear2 = d_grouped2['Кость 1'].append(pd.DataFrame([0]*len(labels), index=labels)).reset_index().drop_duplicates('index').sort_values('index') values2 = clear2[0] fig = go.Figure() fig.add_trace(go.Bar(x = labels, y = values - values2)) fig.update_yaxes(zeroline=True, zerolinewidth=3, zerolinecolor='red') fig.update_layout(legend_orientation="h", legend=dict(x=.5, xanchor="center"), title , xaxis_title="Сумма очков", yaxis_title="Разница в числе исходов", margin=dict(l=0, r=0, t=50, b=0)) fig.show()

А ещё можно вывести подписи прямо поверх столбцов. Для этого пригодится пара аргументов:
- text — сюда передаём список тех значений, которые надо вывести (можно заранее сформировать произвольные строки)
- textposition — способ вывода текста:
- ‘auto’ — Plotly самостоятельно пример решение
- ‘outside’ — снаружи, в случае столбчатой диаграммы это будет над столбцом с положительной высотой и под столбцом с отрицательной высотой
- ‘inside’ внутри прямоугольника (если высота прямоугольника = 0, то надпись не будет видно)
- и т.д.
labels = tuple(range(2,13)) d_grouped = dices.groupby(['Сумма']).count() clear = d_grouped['Кость 1'].append(pd.DataFrame([0]*len(labels), index=labels)).reset_index().drop_duplicates('index').sort_values('index') values = clear[0] d_grouped2 = dices2.groupby(['Сумма']).count() clear2 = d_grouped2['Кость 1'].append(pd.DataFrame([0]*len(labels), index=labels)).reset_index().drop_duplicates('index').sort_values('index') values2 = clear2[0] fig = go.Figure() fig.add_trace(go.Bar(x = labels, y = values - values2, text=values - values2, textposition='outside')) fig.update_yaxes(zeroline=True, zerolinewidth=3, zerolinecolor='red') fig.update_layout(legend_orientation="h", legend=dict(x=.5, xanchor="center"), title , xaxis_title="Сумма очков", yaxis_title="Разница в числе исходов", margin=dict(l=0, r=0, t=50, b=0)) fig.show()
А что, если мы хотим вывести не вертикальные столбцы, а горизонтальные полоски?
Это легко сделать надо только:
- Добавить аргумент orientation=’h’
- Поменять местами x и y в данных и подписях (а так же везде, где мы задаём подписи осей, осевые линии и т.п.)
labels = tuple(range(2,13)) d_grouped = dices.groupby(['Сумма']).count() clear = d_grouped['Кость 1'].append(pd.DataFrame([0]*len(labels), index=labels)).reset_index().drop_duplicates('index').sort_values('index') values = clear[0] d_grouped2 = dices2.groupby(['Сумма']).count() clear2 = d_grouped2['Кость 1'].append(pd.DataFrame([0]*len(labels), index=labels)).reset_index().drop_duplicates('index').sort_values('index') values2 = clear2[0] fig = go.Figure() fig.add_trace(go.Bar(y = labels, x = values - values2, orientation='h', text=values - values2, textposition='outside')) fig.update_xaxes(zeroline=True, zerolinewidth=3, zerolinecolor='red') fig.update_layout(legend_orientation="h", legend=dict(x=.5, xanchor="center"), title , yaxis_title="Сумма очков", xaxis_title="Разница в числе исходов", margin=dict(l=0, r=0, t=50, b=0)) fig.show()
Так же при отображении гистограмм 2 наборов данных есть полезный аргумент для слоя — barmode=’stack’.
Он позволяет объединять столбцы в общие колонки. Это полезно, если мы представляем наши данные, как единую серию экспериментов, т.е. мы бросили 100 раз кубики, потом ещё 100 раз и хотим узнать что вышло в итоге, сколько всё-таки каких исходов.
labels = tuple(range(2,13)) d_grouped = dices.groupby(['Сумма']).count() clear = d_grouped['Кость 1'].append(pd.DataFrame([0]*len(labels), index=labels)).reset_index().drop_duplicates('index').sort_values('index') values = clear[0] d_grouped2 = dices2.groupby(['Сумма']).count() clear2 = d_grouped2['Кость 1'].append(pd.DataFrame([0]*len(labels), index=labels)).reset_index().drop_duplicates('index').sort_values('index') values2 = clear2[0] fig = go.Figure() fig.add_trace(go.Bar(x = labels, y = values)) fig.add_trace(go.Bar(x = labels, y = values2)) fig.update_layout(barmode='stack') fig.show()
Ящики с усами (Box Plots)
А что, если требуется более сложный и информативный инструмент? Примером может служить диаграмма размаха или «ящик с усами» (ссылка)
Для примера создадим набор 100 событий с бросками набора других игральных костей. На этот раз 3 4-гранных кости (3d4). Это могло бы быть сравнением 2 игровых мечей с уроном 2d6 и 3d4, однако, любому очевидно, что второй эффективнее (разброс 2-12 против разброса 3-12). Вся ли это информация, которую можно «вытащить» из этих данных?
Конечно нет, ведь у них будут отличаться и меры центральной тенденции (медианы или средние).
Для построения ящиков с усами мы используем класс go.Box. Данные (весь массив «сумм») передаём в единственный аргумент — y.
dices4 = pd.DataFrame(np.random.randint(low=1, high=5, size=(100, 3)), columns=('Кость 1', 'Кость 2', 'Кость 4')) dices4['Сумма'] = dices4['Кость 1'] + dices4['Кость 2'] + dices4['Кость 4'] fig = go.Figure() fig.add_trace(go.Box(y=dices['Сумма'])) fig.add_trace(go.Box(y=dices4['Сумма'])) fig.show()
Не совсем понятно кто есть кто.
Ну или относительно понятно
Примечание. Т.к. мы используем random, то в вашем случае результат может получиться не такой, как у меня при тестовой генерации, однако забавно, что с первой же попытки во время подготовки этого материала я получил вот такую картинку:

Тут ясно, что «усы» левого ящика имеют размах 2-12, значит, это и есть 2d6. Но занятно, что хотя нижняя граница прямого «усы» выше левого, но и верхняя ниже! Это объясняется тем, что 100 событий не так уж и много, а выбросить сразу 3 четвёрки довольно сложно. И медианы у них на одном уровне. Выходит, наше первоначальное предположение о большей эффективности оружия с уроном 3d4 можно считать справедливым лишь по уровню 25% квартиля — он явно выше на правой картинке. Т.е. «ящик с усами» всё же дал нам довольно много легко считываемой и не совсем очевидной первоначально информации.
Однако, как и для других фигур, тут можно задать подписи.
fig = go.Figure() fig.add_trace(go.Box(y=dices['Сумма'], name='2d6')) fig.add_trace(go.Box(y=dices4['Сумма'], name='3d4')) fig.update_layout(title="Сравнение испытаний по 100 бросков игральных костей", xaxis_title="Вид испытаний", yaxis_title="Сумма очков") fig.show()
Иногда вертикальные ящики не очень наглядны (либо сложно прочитать подписи снизу), тогда их можно положить «на бок» так же, как мы делали с обычными столбчатыми диаграммами:
fig = go.Figure() fig.add_trace(go.Box(x=dices['Сумма'], name='2d6')) fig.add_trace(go.Box(x=dices4['Сумма'], name='3d4')) fig.update_layout(title="Сравнение испытаний по 100 бросков игральных костей", yaxis_title="Вид испытаний", xaxis_title="Сумма очков") fig.show()
Иногда полезно для каждого ящика с усами так же отобразить облако точек, формирующий распределение. Это легко сделать с помощью аргумента boxpoints=’all’
fig = go.Figure() fig.add_trace(go.Box(x=dices['Сумма'], name='2d6', boxpoints='all')) fig.add_trace(go.Box(x=dices4['Сумма'], name='3d4', boxpoints='all')) fig.update_layout(title="Сравнение испытаний по 100 бросков игральных костей", yaxis_title="Вид испытаний", xaxis_title="Cумма очков") fig.show()
Географические карты
Plotly поддерживает великое множество разных видов визуализаций, охватить все из которых в одном обзоре довольно трудно (и бессмысленно, т.к. общие принципы будут схожи с ранее показанными)
Полезно будет в завершении лишь показать один из наиболее красивых на мой взгляд «графиков» — Scattermapbox — геокарты.
Для этого возьмём CSV с 1117 населёнными пунктами РФ и их координатами (файл создан на основе github.com/hflabs/city/blob/master/city.csv) — ‘https://raw.githubusercontent.com/hflabs/city/master/city.csv.
Воспользуемся классом go.Scattermapbox и 2 атрибутами:
- lat (широта)
- lon (долгота)
fig.update_layout(mapbox_style="open-street-map")cities = pd.read_csv('https://raw.githubusercontent.com/hflabs/city/master/city.csv') fig = go.Figure(go.Scattermapbox(lat=cities['geo_lat'], lon=cities['geo_lon'])) fig.update_layout(mapbox_style="open-street-map") fig.show()
Как-то криво, правда? Давайте сдвинем центр карты так, чтобы он пришёлся на столицу нашей родины (вернее столицу родины автора этих строк, т.к. у читателя родина может быть иной).
Для этого нам понадобится объект go.layout.mapbox.Center или обычный словарь с 2 аргументами:
- lat
- lon
fig.update_layout( mapbox=dict( center=dict( lat=. lon=. ) ) )fig = go.Figure(go.Scattermapbox(lat=cities['geo_lat'], lon=cities['geo_lon'])) capital = cities[cities['region']=='Москва'] map_center = go.layout.mapbox.Center(lat=capital['geo_lat'].values[0], lon=capital['geo_lon'].values[0]) # Аналог с помощью словаря #map_center = dict(lat=capital['geo_lat'].values[0], lon=capital['geo_lon'].values[0]) fig.update_layout(mapbox_style="open-street-map", mapbox=dict(center=map_center)) fig.show()
Неплохо, но масштаб мелковат (по сути сейчас отображается карта мира на которой 1/6 часть суши занимает далеко не всё полезное место).
Без ущерба для полезной информации можно слегка приблизить картинку.
Для этого используем аргумент zoom=2
fig = go.Figure(go.Scattermapbox(lat=cities['geo_lat'], lon=cities['geo_lon'])) capital = cities[cities['region']=='Москва'] map_center = go.layout.mapbox.Center(lat=capital['geo_lat'].values[0], lon=capital['geo_lon'].values[0]) fig.update_layout(mapbox_style="open-street-map", mapbox=dict(center=map_center, zoom=2)) fig.show()
Увы, на карту попало слишком много Европы без данных и слишком мало отечественного дальнего востока, так что в данном случае центрироваться возможно стоит по геометрическому центру страны (вычислим его весьма «приблизительно»).
fig = go.Figure(go.Scattermapbox(lat=cities['geo_lat'], lon=cities['geo_lon'])) map_center = go.layout.mapbox.Center(lat=(cities['geo_lat'].max()+cities['geo_lat'].min())/2, lon=(cities['geo_lon'].max()+cities['geo_lon'].min())/2) fig.update_layout(mapbox_style="open-street-map", mapbox=dict(center=map_center, zoom=2)) fig.show()
Давайте добавим подписи городов. Для этого используем аргумент text.
Следует заметить, что для нескольких населённых пунктов (города федерального значения) почему-то не заполнено поле city, поэтому для них мы его вручную заполним из address. Не очень красиво, но главное, что не пустота.
cities.loc[cities['city'].isna(), 'city'] = cities.loc[cities['city'].isna(), 'address'] fig = go.Figure(go.Scattermapbox(lat=cities['geo_lat'], lon=cities['geo_lon'], text=cities['city'])) map_center = go.layout.mapbox.Center(lat=(cities['geo_lat'].max()+cities['geo_lat'].min())/2, lon=(cities['geo_lon'].max()+cities['geo_lon'].min())/2) fig.update_layout(mapbox_style="open-street-map", mapbox=dict(center=map_center, zoom=2)) fig.show()
Вспомним, как мы увеличивали плотность информации для обычных графиков. Тут так же можно задать размер маркера, например, от населения.
Следует учесть 2 момента:
- Данные замусорены. Население некоторых городов имеет вид 96[3]. Поэтому колонка с население не численная и нам нужна функция, которая этот мусор обнулит, либо приведёт к какому-то читаемому виду.
- Размер маркера задаётся в пикселях. И 15 миллионов пикселей — слишком большой диаметр. Потому разумно придумать формулу, например, логарифм.
def to_int_size(value): try: return np.log10(int(value)) except: return np.log10(int(value.split('[')[0])) fig = go.Figure(go.Scattermapbox(lat=cities['geo_lat'], lon=cities['geo_lon'], text=cities['city'], marker=dict(size=cities['population'].map(to_int_size)))) map_center = go.layout.mapbox.Center(lat=(cities['geo_lat'].max()+cities['geo_lat'].min())/2, lon=(cities['geo_lon'].max()+cities['geo_lon'].min())/2) fig.update_layout(mapbox_style="open-street-map", mapbox=dict(center=map_center, zoom=2)) fig.show()
Добавим цветовую кодировку. Для этого используем данные о годе основания. Т.к. не для всех городов он точно известен (для некоторых указан век, для некоторых римскими, а не арабскими цифрами), то мы так же вынуждены будем написать свою функцию для обработки годов, но для простоты все проблемные случаи мы будем возвращать None и потом просто удалим все такие города.
Если возвращать, например, np.NaN, то при построении тепловой карты эти значения будут считаться эквивалентными 0 и мы будем считать такие населённые пункты одними из самых старых в стране)
def to_int_year(value): try: return int(value) except: return None cities['foundation_year'] = cities['foundation_year'].map(to_int_year) cities = cities[['region', 'city', 'geo_lat', 'geo_lon', 'foundation_year', 'population']].dropna() fig = go.Figure(go.Scattermapbox(lat=cities['geo_lat'], lon=cities['geo_lon'], text=cities['city'], marker=dict(colorbar=dict(title="Год основания"), color=cities['foundation_year'], size=cities['population'].map(to_int_size)))) map_center = go.layout.mapbox.Center(lat=(cities['geo_lat'].max()+cities['geo_lat'].min())/2, lon=(cities['geo_lon'].max()+cities['geo_lon'].min())/2) fig.update_layout(mapbox_style="open-street-map", mapbox=dict(center=map_center, zoom=2)) fig.show()
А что если мы хотим нанести линию? Без проблем!
Возьмём и добавим новый график на имеющуюся картинку, который будет содержать только 2 точки: Москву и Санкт-Петербург.
Нам понадобится новый атрибут mode = «lines» (у него доступны и другие значения, например «markers+lines»), но мы уже вывели метку города, так что не хотим её дублировать.
Не будем выводить никакой информации при наведении на этот новый график, чтобы она не перебивала эффект наведения на основные точки. hoverinfo=’skip’
fig = go.Figure(go.Scattermapbox(lat=cities['geo_lat'], lon=cities['geo_lon'], text=cities['city'], marker=dict(colorbar=dict(title="Год основания"), color=cities['foundation_year'].map(to_int_year), size=cities['population'].map(to_int_size)))) fig.add_trace(go.Scattermapbox(mode = "lines", hoverinfo='skip', lat=cities[cities['region'].isin(['Санкт-Петербург', 'Москва'])]['geo_lat'], lon=cities[cities['region'].isin(['Санкт-Петербург', 'Москва'])]['geo_lon'])) map_center = go.layout.mapbox.Center(lat=(cities['geo_lat'].max()+cities['geo_lat'].min())/2, lon=(cities['geo_lon'].max()+cities['geo_lon'].min())/2) fig.update_layout(mapbox_style="open-street-map", mapbox=dict(center=map_center, zoom=2)) fig.show()
Ох, кажется полоска тепловой карты наложилась на легенду. Более того, теперь легенда выводится раздельная для точек-городов и линии между Москвой и Санкт-Петербургом.
- Переключим легенду в горизонтальный режим legend_orientation=«h» (в настройках слоя)
- «сгруппируем» легенды вместе. Для этого у каждого графика группы добавим аргумент legendgroup=«group» (можно использовать любые строки, лишь бы они были одинаковые у членов одной группы).
fig = go.Figure(go.Scattermapbox(legendgroup="group", lat=cities['geo_lat'], lon=cities['geo_lon'], text=cities['city'], marker=dict(colorbar=dict(title="Год основания"), color=cities['foundation_year'].map(to_int_year), size=cities['population'].map(to_int_size)))) fig.add_trace(go.Scattermapbox(legendgroup="group", mode = "lines", hoverinfo='skip', lat=cities[cities['region'].isin(['Санкт-Петербург', 'Москва'])]['geo_lat'], lon=cities[cities['region'].isin(['Санкт-Петербург', 'Москва'])]['geo_lon'])) map_center = go.layout.mapbox.Center(lat=(cities['geo_lat'].max()+cities['geo_lat'].min())/2, lon=(cities['geo_lon'].max()+cities['geo_lon'].min())/2) fig.update_layout(legend_orientation="h", mapbox_style="open-street-map", mapbox=dict(center=map_center, zoom=2)) fig.show()
Отлично, теперь они включаются и выключаются вместе. Давайте уберём из легенды «лишний» элемент (линию городов) showlegend=False
А так же подпишем легенду для городов.
fig = go.Figure(go.Scattermapbox(legendgroup="group", name='Города России', lat=cities['geo_lat'], lon=cities['geo_lon'], text=cities['city'], marker=dict(colorbar=dict(title="Год основания"), color=cities['foundation_year'].map(to_int_year), size=cities['population'].map(to_int_size)))) fig.add_trace(go.Scattermapbox(legendgroup="group", showlegend=False, mode = "lines", hoverinfo='skip', lat=cities[cities['region'].isin(['Санкт-Петербург', 'Москва'])]['geo_lat'], lon=cities[cities['region'].isin(['Санкт-Петербург', 'Москва'])]['geo_lon'])) map_center = go.layout.mapbox.Center(lat=(cities['geo_lat'].max()+cities['geo_lat'].min())/2, lon=(cities['geo_lon'].max()+cities['geo_lon'].min())/2) fig.update_layout(legend_orientation="h", mapbox_style="open-street-map", mapbox=dict(center=map_center, zoom=2)) fig.show()
Давайте добавим чуть более осмысленные линии на карту. Для этого воспользуемся маршрутом поезда №002М «Россия» Москва-Владивосток (сайт РЖД)
Я заранее подготовил отдельный файл с городами, на маршруте, разбитом по дням. Это примерная разбивка, т.к. расписание меняется, так что не используйте мою таблицу для оценка когда вы приедете к любимой тёще в гости. Некоторые станции поезда не имеют аналога в нашей оригинальной таблице городов, поэтому они пропущена. Некоторые города указаны 2 раза, т.к. они являются конечной точкой одного дневного перегона и начальной другого дневного перегона.
Наш маршрут будет соединять города, а не вокзалы, так же он не будет совпадать с реальной железной дорогой. Это просто визуализация маршрута, а не инструмент навигации!
train_russia = pd.read_csv('https://gist.githubusercontent.com/lexnekr/2da07b5fc12b5be24068e4d68ed47ca5/raw/d6256765a3223282fbfec7e0b040cbfb21593fff/train_russia.scv') fig = go.Figure(go.Scattermapbox(legendgroup="group", name='Города России', lat=cities['geo_lat'], lon=cities['geo_lon'], text=cities['city'], marker=dict(colorbar=dict(title="Год основания"), color=cities['foundation_year'].map(to_int_year), size=cities['population'].map(to_int_size)))) for df_for_today in train_russia.groupby(['day number']): fig.add_trace(go.Scattermapbox(name='День <>'.format(df_for_today[0]), mode = "lines", hoverinfo='skip', lat=df_for_today[1]['geo_lat'], lon=df_for_today[1]['geo_lon'])) map_center = go.layout.mapbox.Center(lat=(cities['geo_lat'].max()+cities['geo_lat'].min())/2, lon=(cities['geo_lon'].max()+cities['geo_lon'].min())/2) fig.update_layout(title='По России на поезде', legend_orientation="h", mapbox_style="open-street-map", mapbox=dict(center=map_center, zoom=2)) fig.show()
Если мы хотим анимировать процесс появления маршрута по дням, то нам придётся использовать тот же приём, что и ранее с появлением нескольких графиков — заранее вывести все графики или их заглушки невидимыми, а потом на каждом фрейме и шаге слайдера делать их видимыми.
data = [go.Scattermapbox(legendgroup="group", name='Города России', lat=cities['geo_lat'], lon=cities['geo_lon'], text=cities['city'], marker=dict(colorbar=dict(title="Год основания"), color=cities['foundation_year'].map(to_int_year), size=cities['population'].map(to_int_size)))] for df_for_today in train_russia.groupby(['day number']): data.append(go.Scattermapbox(visible=False, name='День <>'.format(df_for_today[0]), mode = "lines", hoverinfo='skip', lat=df_for_today[1]['geo_lat'], lon=df_for_today[1]['geo_lon'])) fig = go.Figure(data) frames=[] for i in range(len(data)+1): temp_frame = go.Frame(name=str(i), data=data) for j in range(1, i): temp_frame['data'][j]['visible']=True frames.append(temp_frame) steps = [] for i in range(len(data)): step = dict( label = str(i), method = "animate", args = [[str(i+1)]] ) steps.append(step) sliders = [dict( currentvalue = >, len = 0.9, x = 0.1, pad = , steps = steps, )] map_center = go.layout.mapbox.Center(lat=(cities['geo_lat'].max()+cities['geo_lat'].min())/2, lon=(cities['geo_lon'].max()+cities['geo_lon'].min())/2) fig.update_layout(title='По России на поезде', legend_orientation="h", mapbox_style="open-street-map", mapbox=dict(center=map_center, zoom=2), updatemenus=[dict(direction="left", pad = , x = 0.1, xanchor = "right", y = 0, yanchor = "top", showactive=False, type="buttons", buttons=[dict(label="►", method="animate", args=[None, ]), dict(label="❚❚", method="animate", args=[[None], , "mode": "immediate", "transition": >])])], ) fig.layout.sliders = sliders fig.frames = frames fig.show()
Безусловно мы разобрали далеко не все виды графиков Plotly. Однако, данного базового набора примеров должно быть достаточно чтобы понять принцип по которому все они работают.
С примерами других визуализаций можно ознакомиться тут — plotly.com/python (обратите внимание, что для каждой категории приведены далеко не все примеры, больше примеров всегда доступно по ссылке «More . »
2. Построение нескольких графиков в одной системе координат
Есть разные способы построения нескольких графиков в одной системе координат. 1 способ. Предположим, нам надо построить в одной системе координат два графика. Тогда перед вызовом функции plotмы должны построить таблицы обеих функций, напримерx1,y1 иx2,y2. А при вызове функцииplotуказать их через запятую в списке аргументов. Пример 3. x=-3:0.1:3; y1=x.^2; y2=x.^2+2; plot(x,y1,x,y2) % переменнаяxобщая для двух графиков Аналогично действуем, если нужно построить более двух графиков. При желании после пары координат графика можно указать символы, управляющие видом этого графика. Пример 4. x1=0:0.1:10; y1=sqrt(x1); x2=-2:0.1:10; y2=sqrt(x2+2); x3=1:0.1:10; y3=sqrt(x3-1); plot(x1,y1,’b’,x2,y2,’r*:’,x3,y3,’gs-.’) Упражнение 2. В одной системе координат построить графики функций, подписать оси, нанести координатную сетку, для каждого графика задать цвет, тип линии и форму маркера:
. 2 способ заключается в том, что создание нового графического окна блокируется с помощью функции hold on. Если к моменту ввода командыhold on, есть открытое графическое окно, то остальные графики будут строиться в нем. Если к моменту ввода командыhold onоткрытого графического окна нет, то окно автоматически будет создано по этой команде,а при каждой новой командеplotв это окно будет добавляться очередной график. Пример 5. x=-2*pi:pi/20:2*pi; y=cos(x); plot(x,y) hold on plot(x,cos(2*x),’g’) plot(x,cos(0.5*x),’r’) grid on xlabel(‘x’),ylabel(‘y’) title(‘Графики функцийy=cos(x),y=cos(2x),y=cos(0,5x)’) Для визуализации координатных осей после построения графика функции можно использовать функцию line([x1 x2],[y1 y2]) Эта функция строит прямую линию, соединяющую точки с координатами
и
. Если мы хотим сделать оси черного цвета, то нужно добавить еще два аргумента: line([x1x2],[y1y1], ‘Сolor’, ‘black’) Пример 5 (продолжение). line ([-8 8],[0 0],’Color’,’black’) line ([0 0],[-1 1],’Color’,’black’) Чтобы отменить режим добавления графика, нужно ввести команду hold off Упражнение 3. Используя команду holdon, в одной системе координат построить графики функций, подписать оси, нанести координатную сетку, для каждого графика задать цвет, тип линии и форму маркера. 1)
на промежутке
; 2)
на промежутке
и
на промежутке
,
на промежутке
(масштаб по осям сделать одинаковым с помощью командыaxis equal).3. Несколько рисунков в одном окне
Чтобы в одном графическом окне создать несколько отдельных рисунков, необходимо прибегнуть к функции subplot, которая позволяет разбить графическое окно на несколько прямоугольных областей равного размера, расположенных подобно элементам матрицы: subplot(row,col,cur). Первые два аргумента задают количество рядов (row) и колонок (col), третий параметр объявляет порядковый номер подобласти, в котором очередная функцияplotбудет стоить свой график. Пример 6. x=-2*pi:pi/20:2*pi; y1=sin(x); y2=cos(x); subplot(2,1,1);plot(x,y1); grid on; title(‘y=sin(x)’) xlabel(‘x’),ylabel(‘y’) axis([-2*pi2*pi-1 1]) %Обратите внимание, как изменилось окно графика (пояснение ниже) line([-2*pi 2*pi],[0 0],’Color’,’black’) line([0 0],[-1 1],’Color’,’black’) subplot(2,1,2); plot(x,y2); grid on; title(‘y=cos(x)’) xlabel(‘x’),ylabel(‘y’) axis([-2*pi 2*pi -1 1]) line([-2*pi 2*pi],[0 0],’Color’,’black’) line([0 0],[-1 1],’Color’,’black’) Пояснение. Функция axis ([x1 x2 y1 y2])изменяет размеры окна графика, преобразуя их к указанным пределам. Это позволяет сделать рисунок более наглядным. Для изменения пределов окна графика также можно воспользоваться функциями xlim([x1 x2])иylim([y1 y2]), которые позволяют задать пределы независимо для каждой из координатных осей. Такой способ полезен в случаях, если масштаб одной из осей заранее не известен. Упражнение 4. Преобразование графиков функций 1) Используя команду subplot, в одном графическом окне создать 6 подобластей (
), в первой из них построить график функции
на промежутке
, где
, в остальных областях на том же промежутке построить графики функций
. В отчет добавить комментарии о том, какими преобразованиями каждый из графиков получается из графика функции
. 2) Используя команду subplot, в одном графическом окне создать 6 подобластей (
), в первой из них построить график функции
на промежутке
, где
, в остальных областях на том же промежутке построить графики функций
. В отчет добавить комментарии о том, какими преобразованиями каждый из графиков получается из графика функции
.
