Введение в научный Python-1. Введение в научный Python
Скачать 6.2 Mb.
|
get current axes), которая возвращает ссылку на активный объект axes рисунка fig. Если объект рисунка создается автоматически при вызове какой–либо графической функции, то доступ к нему (активному графическому окну) можно получить с помощью инструкции fig=plt.gcf() (get current figure). Координатная сетка на рисунке появилась в результате выполнения команды plt.grid(True). Эта сетка связана с делениями координатных осей (ticks). В matplotlib существуют главные деления/засечки (major ticks) и вспомогательные (minor ticks) для каждой координатной оси. По умолчанию рисуются только главные деления и связанные с ними линии сетки. Узнать положение основных засечек можно следующим образом: xax = ax.xaxis xlocs = xax.get_ticklocs() xlocs 117 array([-3., -2., -1., 0., 1., 2., 3., 4., 5.]) Как видите, положение засечек является массивом. Его можно изменить следующими командами: xlocs =np.linspace(-3,5,17) ax.set_xticks( xlocs, minor = False) # minor = False – главные засечки Для экономии места мы привели только нижнюю часть рисунка, на котором изменилось количество засечек на оси X и густота вертикальных линий координатной сетки. Для самой сетки можно задать опции цвета, стиля, толщины и т.д. Например, следующая инструкция задает опции сетки для первой (и, как правило, единственной) графической области. ax[0].grid(color='b',alpha=0.5,line,linewidth=0.5) Оформление засечек на оси можно выполнить следующей командой. ax.tick_params(axis='x', which='major', labelleft='off', labelright='off', labeltop='on', labelbottom='on', direction='in', length=10, width=4, colors='r') Здесь мы говорим, что будем оформлять главные засечки координатной оси X (axis='x',which='major'), расположенные на верхней и нижней стороне области рисунка (labeltop='on',labelbottom='on'). Засечки будут направлены внутрь рисунка (direction='in'), иметь длину 10 и толщину 4 (length=10, width=4), и будут красного цвета (colors='r'). Можно поменять подписи у меток, стоящих возле засечек. В коде примера после строки «# Метка 1 для вставки» добавьте следующий код: xax = ax.xaxis # ссылка на объект горизонтальной координатной оси xlabels = xax.get_ticklabels() # итератор меток (не список) на оси X for lbl in xlabels: lbl.set_text('x = '+ lbl.get_text()) # меняем текст каждой метки # меняем список меток на оси X ax.set_xticklabels(xlabels, color='green', rotation=335) yax = ax.yaxis # ссылка на объект вертикальной координатной оси ylabels = yax.get_ticklabels() # итератор меток for lbl in ylabels: lbl.set_text('y = '+ lbl.get_text()) # меняем текст каждой метки # меняем список меток на оси Y ax.set_yticklabels(ylabels, color='green') 118 В результате его выполнения подписи к засечкам примут вид, показанный на следующем рисунке. Для экономии места мы привели только нижнюю часть рисунка. Команды xax=ax.get_xaxis() и yax=ax.yaxis предоставляют доступ к объекту axis горизонтальной координатной оси. Через него можно получить доступ ко всем методам и атрибутам, управляющим внешним видом оси без использования функции ax.tick_params(). Объектов axes на графике можно создать несколько, и каждый из них будет содержать свой график. В следующем коде мы рисуем график функции 2 x y , как основную кривую, и график обратной функции x y , как вспомогательный. import matplotlib.pyplot as plt import numpy as np x = np.linspace(0, 5, 10) y = x**2 fig = plt.figure(facecolor='white') ax1 = fig.add_axes([0.1, 0.1, 0.8, 0.8]) # основной объект axes ax2 = fig.add_axes([0.2, 0.5, 0.4, 0.3]) # внутренний объект axes # Главный график ax1.plot(x, y, 'r',linewidth=3) ax1.set_title('title') # внутренний график ax2.plot(y, x, 'g',linewidth=2) ax2.set_xlabel('y') ax2.set_ylabel('x') ax2.set_title('inner title'); Здесь при создании объектов ax1 и ax2 методами fig.add_axes() использовались относительные координаты и размеры прямоугольных областей, которые представляют эти объекты. ■ 119 Цвет графического элемента задается при его создании каким–либо аргументом (по умолчанию или явно). Его имя (название опции) может быть различным: color (или colors), facecolor (или facecolors), backgroundcolor и т.д. У многих графических объектов имеются методы, которые устанавливают цвет его элементов, например, set_color(цвет). Однако все они используют одинаковые способы задания цвета. Для основных цветов можно использовать три формата задания: длинное или короткое имя, заключенное в кавычки, а также числовой триплет. Так черный цвет можно задать строками „black‟, „k‟ или кортежем (0,0,0). Cоздать графическое окно с белым фоном можно любой из следующих инструкций: fig = plt.figure(facecolor='white') fig = plt.figure(facecolor='w') fig = plt.figure(facecolor=(1,1,1)) Для обозначения сложных цветов используются кортежи из трех дробных чисел из интервала [0, 1]. Например, (0.5,0.5,0.5) – серый цвет, (0.5,0,0) – темно–красный, а (0.49,1.0,.0.83) – аквамариновый. Первое число задает относительную интенсивность красной составляющей цвета, второе – зеленой, третье – синей. Получить массив относительных интенсивностей из абсолютных можно следующим образом: rgb = np.array([204,255,51])/255 Яркость серых цветов можно задать с помощью строки, содержащей число в интервале от 0 до 1 (1 – белый, 0 – черный), например, color='0.75'. Эти числовые значения можно использовать вместе с цветовой картой „gray‟, в которой нулю отвечает черный цвет, единице – белый, а промежуточным числам – различные оттенки серого (от темно-серого до светло-серого). В следующем примере функция plt.pcolor() строит псевдоцветное изображение двумерного массива, используя заданную нами цветовую карту. В таких картах (палитрах) одному числовому значению отвечает некоторый цвет. Список всех палитр можно получить командой plt.cm.datad. Задание конкретной палитры, используемой графическим объектом, можно выполнить командой объект.set_cmap('название_палитры') или с помощью подходящей опции графической функции. В нашем примере используются только оттенки серого цвета, содержащиеся в палитре „gray‟. A=np.array([[0,0.1,0.2],[0.4,1,0.6],[0.7,0.8,0.9]], dtype=float) im=plt.pcolor(A) # изображение массива im.set_cmap('gray') # выбор цветовой карты изображения fig=plt.gcf() fig.set_facecolor('w') ax=fig.gca() ax.axis('equal') # одинаковый масштаб по осм X и Y fig.colorbar(im) # создание палитры цветов справа 120 Обратите внимание на то, что квадрат отвечающий элементу массива A[0][0], расположен в левом нижнем углу изображения. Инструкция fig.colorbar(im) нарисовала справа палитру цветов, показывающую связь между числовым значением и цветом. Заметим, что для изображения массива аналогичным способом можно использовать функции plt.pcolormesh(A) и plt.matshow(A). В следующем примере каждый цвет задается списком из трех чисел, представляющих относительные интенсивности основных цветов B=np.array([[[0,0,0],[0,0,1],[0,1,0],[0,1,1],[1,0,0],[1,0,1],[1,1,0],[1,1,1]], [[0,0,0.25],[0,0,0.5],[0,0.5,0],[0,0.5,0.5],[0.5,0,0],[0.5,0,0.5], [0.5,0.5,0],[0.5,0.5,0.5]], [[1,0,0.25],[1,0,0.5],[1,0.5,0],[1,0.5,0.5],[0.5,1,0],[0.5,1,0.5], [0.5,0.75,1],[0.5,0.5,1]], [[1,0.5,0.25],[1,0.5,0.5],[0.5,0.5,0],[1,0.25,0.5],[0.25,1,0], [0.25,1,0.5],[0.25,0.75,0.75],[0.25,0.5,1]]]) im=plt.imshow(B, interpolation='nea rest') # следующий рисунок слева fig=plt.gcf() fig.set_facecolor('w') ax=fig.gca() ax.set_xticks([]) # удаляем засечки на оси X ax.set_yticks([]) # удаляем засечки на оси Y Основные цвета записаны в элементе B[0] матрицы и на рисунке слева представлены в верхней строке. Массив A может иметь размеры MxN, MxNx3 или MxNx4. Здесь функция plt.imshow(B, interpolation='nearest') используется для представления «двумерного» массива B, элементами которого являются трехэлементные вектора цветов. Опция interpolation='nearest' необходима для того, чтобы функция не создавала плавного перехода цветов между соседними квадратами. Если эту опцию удалить, то вы получите изображение, показанное на предыдущем рисунке справа. Обычно функция plt.imshow(A[,...]) используется для отображения массивов цветов, прочитанных из графических файлов функцией plt.imread(имя_файла). Например, 121 import matplotlib.cbook as cbook image_file = cbook.get_sample_data( r'D:\W ork\Python\StartProgs\Graphics\PogorelovDesk01.png') image = plt.imread(image_file) plt.imshow(image) Того же результата можно добиться следующим кодом: from scipy.ndimage import imread im = imread( r'D:\W ork\Python\StartProgs\Graphics\PogorelovD esk01.png') plt.imshow(im) Цвет можно задавать RGB строкой, содержащей шестнадцатеричное число, перед которым стоит символ '#' (решетка). Например, color='#E0A9F3'. Первые две цифры задают абсолютную яркость красной составляющей (от 0 до 255=0xff), вторые – зеленой, третьи – синей. Пример. Нарисуем таблицу, состоящую из квадратов, цвет заливки которых задается шестнадцатеричной строкой. %matplotlib qt import numpy as np import matplotlib.pyplot as plt from matplotlib.patches import Rectangle fig, ax = plt.subplots() fig.set_facecolor('white') n=3 rg=range(n) for r in rg: cr=r*127 for g in rg: cg=g*127 for b in rg: cb=b*127 clr='#'+'{0:02x}'.format(cr)+ \ '{0:02x}'.format(cg)+'{0:02x}'.format(cb) x=n*r+g y=b rect=Rectangle((x, y),1, 1,facecolor=clr) ax.add_patch(rect) ax.text(x+0.5,y+0.5,clr,color='w', \ verticalalignment='center',horizontalalignment='center') 122 ax.set_xlim(0, 9) ax.set_ylim(0, 3) ax.set_aspect('equal') В этом примере в тройном цикле составляющие основных цветов (cr,cg,cb) принимают по 3 значения: 0, 127, 254. Из них компонуется строка цвета clr. Она получается конкатенацией 4–х строк. Первая строка состоит из одного символа „#‟. Остальные три являются форматированными строками вида'{0:02x}'.format(cr). Здесь десятичное число cr преобразуется к 16- тиричному виду (на это указывает символ „x‟). Под него выделяется две позиции (двойка перед „x‟), и символом заполнения свободных позиций является 0. Таким образом, строка „02x‟ означает вывод двухсимвольного шестнадцатеричного числа с ведущим нулем, если он потребуется. Начальный ноль, стоящий перед двоеточием {0:...}, является номером аргумента метода format(...) , который будет выводиться (у нас других аргументов нет). Созданная строка цвета clr используется при задании цвета фона квадрата (facecolor=clr), а также печатается белым цветом в его центре. Координата y левой нижней вершины каждого квадрата соответствует номеру индекса b в списке значений синей составляющей цвета [0, 127, 254]. Координата x конструируется по формуле x=n*r+g (у нас n=3), где r и g – индексы в списке значений красной и зеленой составляющей цвета. В результате красная составляющая цвета левых 3x3 квадратов равна нулю, средних 3x3 квадратов равна 127, а правых 3x3 квадратов равна 254 (т.е. 0xfe). ■ Набор цветов можно разнообразить за счѐт различной степени прозрачности, которая обычно задается числовым параметром alpha (от полностью прозрачного – 0, до непрозрачного – 1). Однако эта возможность чаще используется в трехмерном случае, где мы ее и рассмотрим. Вернемся к обсуждению функций, используемых при построении двумерных графиков. Параметрические графики. Последовательность x координат точек у функции plot() не обязана быть монотонно возрастающей. 123 plot([0,1,2,0],[0,2,1,0],linewidth=4); axis('equal'); # задание одинаковых масштабов по осям Это позволяет строить кривые по их параметрическому уравнению. t=np.linspace(0,2*np.pi,100) x=np.sin(t)**3 y=np.cos(t)**3 # астроида plot(x,y,linewidth=4); axis('equal'); # следующий рисунок слева В следующем примере использование функции axes().set_aspect(1) позволяет построить окружность, которая выглядит как окружности, а не как эллипс. t=np.linspace(0,2*np.pi,100) plot(np.cos(t),np.sin(t),linewidth=4) axes().set_aspect(1) # предыдущий рисунок справа График функции в полярных координатах строится с помощью функции plt.polar(angle, radius). У нее первый аргумент представляет массив углов, второй – радиусов. Параметры и функции оформления такие же, как у функции plot(). t=np.linspace(0,4*np.pi,100) plt.polar(t,t, linewidth=4, color='green') Функция plt.polar() автоматически выравнивает вертикальный и горизонтальный масштабы. В следующих примерах окружность похожа сама на себя, а не на овал. t=np.linspace(0,2*np.pi,100) r = [1 for a in t] polar(t,r,linewidth=4,color='red') # следующий рисунок слева 124 Cдвинутая окружность тоже выглядит как окружность. t=np.linspace(0,2*np.pi,100) polar(t,2*np.cos(t),linewidth=4,color='blue') # предыдущий рисунок справа Функция plt.scatter() рисует точки (график рассеяния). Ей надо передать массивы x и y координат этих точек. plt.scatter([0,1,2,1.5,0.5,0.25,0.5,1],[0,2,1,1.5,1,0.5,0.25,0.5], linewidths=8,c='red',edgecolors='blue'); # следующий рисунок слева n = 1024 X = np.random.normal(0,1,n) Y = np.random.normal(0,1,n) plt.scatter(X,Y) # предыдущий рисунок справа plt.axis('equal'); Области между кривыми. Для построения графика с залитой областью между кривой и горизонтальной осью можно использовать функцию plt.fill(). x = np.linspace(0, 1) y = np.sin(4 *np.pi*x)*np.exp( -5*x) plt.fill(x, y, 'r') plt.grid(True) Функция fill предназначена для заливки замкнутых многоугольников. Поэтому она соединяет последнюю точку с первой (полагает, что массивы координат x,y задают замкнутый многоугольник), но это не всегда совпадает с осью Ox. x=np.array([0,1,2,1]) y=np.array([1,2,1,0]) plt.fill(x, y, 'c') 125 plt.show() Использование двух функций при построении залитых графиков допустимо. t = np.linspace(0, np.pi,100) x1=np.cos(t) y1=np.sin(t) x2=2*np.cos(t) y2=1.5*np.sin(t) plt.fill(x2, y2,'y',x1,y1, 'b') plt.grid(True) plt.axes().set_aspect('equal') Однако, если здесь использовать массивы в другом порядке fill(x1,y1,'r',x2,y2,'b') , то вы увидите только бо'льшую область, поскольку меньшая будет полностью закрыта бо̀льшей. Функция plt.fill() старается залить область между кривой и осью X. Чтобы залить область между двумя кривыми нужно использовать функцию fill_between(x,y1,y2,опции), где y1 и y2 являются массивами точек ограничивающих кривых. x = np.linspace(0, 1) y1 = np.sin(4 *np.pi*x)*np.exp( -5*x) y2=np.sin(x*np.pi) plt.fill_between(x,y1,y2,color='c') Графические примитивы. На двумерные рисунки можно добавлять множество графических примитивов: ломаных, прямоугольников, многоугольников, кругов, секторов и т.д. Часто их называют «патчами» (patch – пятно на рисунке). Мы будем использовать термины «графический примитив», 126 а также графический фрагмент или фигура. В следующем примере демонстрируются способы рисования некоторых фигур. Пример. Рисование графических примитивов. # -*- coding: utf-8 -*- import numpy as np import matplotlib from matplotlib.patches import Circle, W edge, Polygon, Rectangle from matplotlib.collections import PatchCollection import matplotlib.pyplot as plt fig, ax = plt.subplots() fig.set_facecolor('white') patches = [ ] circle = Circle((5, 4), 3) # круг: центр, радиус patches.append(circle) # добавление круга в набор фигур wedge = W edge((7, 8), # сектор: центр 2, # радиус 45, # начальный угол 120) # конечный угол patches.append(wedge) # добавление сектора в набор фигур # Создание и добавление нескольких фигур patches += [ W edge((10, 10), 1, 0, 360), # круг W edge((12, .7), 2, 0, 360, width=0.5), # кольцо W edge((4, 14), 3, 0, 45), # сектор W edge((17, 5), 2, 45, 270, width=1)] # кольцевой сектор # многоугольник polygon = Polygon([[12,12],[14,17],[16,16],[19,17],[14,10]]) patches.append(polygon) # добавление многоугольника в набор фигур # прямоугольники rect1=Rectangle((7, 16), # координаты вершины 5, # ширина прямоугольника 3) # высота прямоугольника patches.append(rect1) # добавление прямоугольника в набор фигур rect2=Rectangle((9, 4),5, 3, angle=30, # угол поворота в градусах facecolor='cyan' # цвет фона #fill=False) # нет фона ax.add_patch(rect2) # добавление прямоугольника сразу на рисунок rect3=Rectangle((3, 8),5, 3,angle=80, facecolor='yellow', # цвет заливки hatch='x', # шриховка, допустимо: '/','//',+','-','o','0','.','*' 127 edgecolor="#0000ff", # цвет контура linewidth=4, # толщина контура line) # допустимо: 'solid', 'dashed','dashdot','dotted' ax.add_patch(rect3) # добавление прямоугольника в графическую область # создание коллекции фигур p=PatchCollection (patches,cmap=matplotlib.cm.jet) # задание цвета в коллекции фигур colors = 80*np.random.rand(len(patches)) p.set_array(np.array(colors)) ax.add_collection(p) # добавление коллекции фигур на рисунок # оформление рисунка: пределы, пропорции, засечки и координатная сетка ax.set_xlim(0, 20) ax.set_ylim(0, 20) ax.set_aspect('equal') xlocs =np.linspace(0,20,21) ax.set_xticks(xlocs, minor = False) # положение главных засечек по оси X ax.set_yticks(xlocs, minor = False) # положение главных засечек по оси Y ax.grid(True) Для создания графического примитива нужно выполнить три шага: создать «математический» примитив, задав его геометрические характеристики; преобразовать «математический» примитив в графический, задав его цвет, стиль, толщину линий и т.д.; перенести графический примитив на рисунок, используя функцию add_patch(). В нашем примере эта последовательность действий была выполнена двумя разными способами. Вначале из модуля matplotlib.patches мы импортировали функции Circle, Wedge, Polygon, Rectangle, которые создают примитивы: круг, сектор, многоугольник и прямоугольник. Затем мы создали список patches. Элементы списка могут иметь любой тип, и в нашем 128 случае они являются объектами примитивов. Мы добавили все объекты в список patches, который с помощью функции PatchCollection(...) преобразовали в коллекцию графических примитивов. Затем, используя инструкцию ax.add_collection(...), мы добавили коллекцию фигур на рисунок. Второй способ состоял в создании одного примитива, например, прямоугольника rect2=Rectangle(...), который сразу с помощью инструкции ax.add_patch(rect2) переносился на рисунок. Шаги, описанные выше, иногда объединяются. Например, графический примитив – прямоугольник rect2 (первые два шага) был создан сразу функцией Rectangle(...). Кроме того, все три шага иногда можно записать одной строкой. Например, чтобы нарисовать стрелку от точки (0,0) до точки (1, 1) можно выполнить инструкцию gca().add_patch(Arrow(0,0,1,1)). В следующем примере мы демонстрируем способ работы с графическим примитивом Path («путь»), который является ломаной. Если ломаная замкнутая, то ее внутренняя область может быть залита цветом, и тогда Path будет представлять многоугольник. import matplotlib.pyplot as plt from matplotlib.path import Path import matplotlib.patches as patches fig =plt.figure(facecolor='white') ax = fig.add_subplot(111) coords=[(0,0),(2,1),(1,2),(1,2)] linecmds=[Path.MOVETO,Path.LINETO,Path.LINETO, Path.CLOSEPOLY] path=Path(coords,linecmds) # создаем путь # создаем графический примитив закрашенный треугольник patch=patches.PathPatch(path,lw=4,facecolor='c',edgecolor='r') # нет заливки #patch=patches.PathPatch(path,lw=4,facecolor='none',edgecolor='r') #patch=patches.PathPatch(path,lw=4,fill=False,edgecolor='r') ax.add_patch(patch) # добавляем фигуру в графическую область рисунка ax.text(0.85,1,'Kharkov',fontsize=24) # пишем текст ax.set_xlim(0,2) ax.set_ylim(0,2) В следующем примере мы иллюстрируем работу с графическим примитивом Arrow. 129 import numpy as np import matplotlib.pypl ot as plt import matplotlib.patches as patches # стрелки по окружности t=np.linspace(0,2*np.pi,9) x=np.cos(t) y=np.sin(t) fig =plt.figure(facecolor='white') ax = fig.add_subplot(111) arrows=[(x0,y0,dx,dy) for (x0,y0,dx,dy) in zip(x,y,np.diff(x),np.diff(y))] for x0,y0,dx,dy in arrows: ax.add_patch(patches.Arrow(x0,y0,dx,dy,width=0.4)) ax.plot(x,y,'r',linewidth=2) Приведем еще пример использования графических примитивов. import numpy as np import matplotlib.pyplot as plt import matplotlib.patches as patches from matplotlib.collections import PatchCollection fig, ax = plt.subplots() fig.set_facecolor('white') ptchs = [ ] ptchs.append(patches.Circle((2,2), 1,color='#ff00A0')) ptchs.append(patches.Re ctangle((1,4), 1, 2, facecolor='g', edgecolor='r',linewidth=3)) ptchs.append(patches.W edge((2,8), 1, 30, 270, ec="none", facecolor='c')) # сектор # RegularPolygon( (xc,yc), numVertices, radius=5,...) Правильный многоугольник ptchs.append(pat ches.RegularPolygon((5,2), 7, 1,color='y')) # Ellipse((xc,yc), width, height, angle=0.0,...) ptchs.append(patches.Ellipse((5,5), 3, 1.5,45,facecolor='r', edgecolor='k',linewidth=3)) # Arrow( xstart, ystart, dx, dy, width=1.0,...) ptchs.append(patche s.Arrow(4, 7, 2, 2, width=1)) # FancyBboxPatch((xleft,ybottom), width, height, box,...) ptchs.append(patches.FancyBboxPatch((8,4),1,2, boxstyle=('round,pad=0.5'))) ptchs.append(patches.FancyBboxPatch((7.5,1.5),2,1, 130 facecolor='#ff7700', boxstyle=patches.BoxStyle("roundtooth", pad=0.5))) ptchs.append(patches.FancyBboxPatch((7.5,7.5),1.5,1.5, facecolor='#ff7777', boxstyle=patches.BoxStyle("sawtooth", pad=0.5))) collection = PatchCollection(ptchs, match_original=True) ax.add_collection(collection) ax.axis('image'); ax.set_xlim(0, 10); ax.set_ylim(0, 10); Формат функций, создающих большинство графических примитивов, очевиден. Скажем несколько слов о примитиве FancyBboxPatch. Он представляет собой прямоугольник, вокруг которого рисуется фигурная область (полоса). Размеры прямоугольника передаются аргументами функции. Тип и размеры полосы определяются опцией boxstyle, которая может принимать строковое значение, либо объект класса patches.BoxStyle. |