Gráficos 2D

Índice

Generalidades

SageMath dispone de una amplia panoplia de funciones para crear gráficos en dos dimensiones. La relación completa se halla detallada en la documentación sobre gráficos 2D de SageMath, donde se proporcionan la sintaxis de cada función y multitud de ejemplos de uso. En esta guía nos centraremos en sólo una parte de los recursos que ofrece SageMath.

En un gráfico pueden aparecer figuras como puntos, líneas, polígonos, etc., así como distintos tipos de curvas o regiones planas. Para combinar dos de estos objetos, basta con «sumarlos»: si p y q representan dos objetos gráficos, entonces p + q es el gráfico que une ambos. Por ejemplo, en la siguiente celda se definen tres círculos que luego son mostrados conjuntamente:

In [1]:
centros = [(cos(t), sin(t)) for t in [0,2*pi/3,4*pi/3]]
c1 = circle(centros[0], 1.5, color="red", alpha=0.9, fill=True, axes=False)
c2 = circle(centros[1], 1.5, color="green", alpha=0.7, fill=True)
c3 = circle(centros[2], 1.5, color="blue", alpha=0.5, fill=True)
c1 + c2 + c3   # «sumamos» c1, c2 y c3 para combinarlos
Out[1]:

Si los objetos dependen de un índice o parámetro, se puede sumarlos con la función sum:

In [2]:
flechas = sum(arrow((0,0),(cos(a),sin(a)), color="darkgreen") for a in srange(0,2*pi,pi/6))
flechas.show(aspect_ratio=1, axes=False, frame=True, gridlines=True)

El ejemplo anterior usa el método show para terminar de perfilar el aspecto del gráfico, con las opciones aspect_ratio (número positivo que regula la proporción entre la altura y la anchura del gráfico), axes (controla si se dibujan o no los ejes de coordenadas), frame(dibujo o no de un marco) y gridlines (dibujo de una cuadrícula). La lista completa de opciones admisibles se puede obtener evaluando la expresión Graphics.SHOW_OPTIONS. El método show también puede actuar como función: hubiera sido correcto escribir show(flechas, aspect_ratio=1, axes=False, frame=True).

Otro modo de modificar un objeto gráfico, o de explorar sus características, consiste en utilizar métodos. Si se teclea Graphics. y se pulsa Tab, se abre una ventana con una relación de los métodos aplicables. Alternativamente, la orden dir(Graphics) proporciona una lista todavía más exhaustiva. La información particular sobre cada método se obtiene con Graphics.<método>?, sustituyendo <método> por el método concreto que sea de interés.

Repetimos el último gráfico empleando diversos métodos para cambiar su aspecto:

In [3]:
flechas = sum(arrow((0,0),(cos(a),sin(a)), color="darkgreen") for a in srange(0,2*pi,pi/6))
flechas.set_aspect_ratio(1)
flechas.axes_color("darkred")                # color del marco
flechas.tick_label_color("darkblue")         # color de los números
flechas.axes_labels(["Eje Ox", "Eje Oy"])    # etiquetas en los ejes
flechas.axes_label_color("brown")            # color de las etiquetas
flechas.show(axes=False, frame=True, gridlines=True)

Para guardar una imagen en un fichero se emplea el método save , que tiene un argumento, el nombre del fichero, y las mismas opciones que show. Por ejemplo, con la orden

flechas.save("flechas.pdf", axes=False, frame=True, gridlines=True)

se almacenaría el último gráfico en el fichero flechas.pdf.

Color

La mayoría de las primitivas gráficas admiten una o más opciones para fijar el color del objeto correspondiente. El color se puede indicar, en primer lugar, mediante sus coordenadas en el sistema RGB, esto es, mediante una terna $(r,g,b)$ de números reales pertenecientes al intervalo $[0,1]$. Los números $r$, $g$ y $b$ determinan, respectivamente, la proporción de rojo, verde y azul del color considerado. Tal proporción también se puede indicar en la forma hexadecimal #rrggbb, donde cada par de cifras rr, gg y bb oscila entre 00 y ff (i.e., entre 0 y 255 en base decimal). La celda siguiente, un vez evaluada, permite escoger colores de diversas formas y ver la terna RGB equivalente. Se activa el selector de color pulsando sobre el cuadrado pequeño:

In [4]:
@interact
def _(color=color_selector(widget="colorpicker", label="Color")):
    poligono = polygon([(0,0),(1,0),(1,1),[0,1]], color=color, edgecolor="black")
    rgb = [n(i, digits=5) for i in sage.plot.colors.rgbcolor(color)]
    show(poligono, axes=False, frame=False, figsize=3, title="RGB: "+str(rgb))

Otro método de expresar el color consiste en dar el nombre de uno de los colores incluidos en la lista oficial de colores SVG. Mediante la celda siguiente se puede explorar estos colores y ver su equivalencia en el sistema RGB:

In [5]:
@interact
def _(color=selector(sorted(colors), label="Color")):  
    poligono = polygon([(0,0),(1,0),(1,1),[0,1]], color=color, edgecolor="black")
    rgb = [n(i, digits=5) for i in sage.plot.colors.rgbcolor(color)]
    show(poligono, axes=False, frame=False, figsize=3, title="RGB: "+str(rgb))

Existen asimismo gamas de colores, útiles, sobre todo, en la representación de curvas de nivel o en gráficos tridimensionales. Nuevamente, la celda siguiente permite explorar las gamas predefinidas:

In [6]:
var("x,y")
gamas=sorted(colormaps)
@interact
def _(cmap=selector(gamas, default="jet", label="Gama", buttons=False)):
    show(contour_plot(x^2-y^2, (x,-2,2), (y,-2,2), contours=15, 
                      cmap=cmap, colorbar=True), figsize=5)

La orden sorted(colormaps) genera una lista ordenada de todas las gamas predefinidas. Para mayor información sobre el color en SageMath, cabe consultar la documentación correspondiente.

Figuras

Sagemath proporciona funciones para trazar las siguientes figuras: puntos (point), segmentos y líneas poligonales (line), flechas (arrow), polígonos (polygon), circunferencias y círculos (circle), elipses (ellipse), arcos elípticos (arc) y sectores circulares (disk). Además se puede añadir texto con la función text. Describamos la sintaxis de las funciones más relevantes:

  • point(punto, opciones) o bien point(lista_puntos, opciones)

  • line(lista_vértices, opciones)

  • arrow(pto_inicial, pto_final, opciones)

  • polygon(lista_vértices, opciones)

  • circle(centro, radio, opciones)

  • ellipse(centro, r_1, r_2, ángulo, opciones)

  • text(texto, posición, opciones)

En los argumentos punto, pto_inicial, pto_final, centro o posición se especifican las coordenadas $(x,y)$ del punto correspondiente, expresadas mediante una lista [x,y], una tupla (x,y) o un vector vector([x,y]). Los argumentos lista_puntos y lista_vértices son listas de coordenadas. Los argumentos r_1 y r_2 de ellipse son las longitudes de los semiejes, y ángulo es el ángulo de giro de sus ejes con respecto a la horizontal (por omisión, su valor es $0$; se expresa en radianes); para circle, radio es, obviamente, el radio del círculo.

Todas las funciones cuentan con las opciones color (color de la figura), legend_label (texto de la leyenda) y legend_color (color de la leyenda). En el caso de circle, en vez de color, se pueden usar las opciones edgecolor (color del borde) y facecolor (color del interior); en cambio, para la función polygon, la opción color fija el color de todo el polígono, salvo que se especifique el del borde con edgecolor. Con fill=True se rellena el interior de círculos y polígonos; con fill=False, sólo se traza su borde.

La opción alpha es un número entre $0$ y $1$ que fija el grado de opacidad de la figura ($0=$ transparente, $1=$ opaca). No es opción válida de arrow.

Para line, arrow, circle y polygon, el grosor de las líneas se indica con thickness; el estilo, con linestyle, que puede tomar los valores solid, dashed, dashdot, dotted o "". En el caso de line, se pueden marcar los vértices de la línea poligonal trazada haciendo uso de las opciones marker, markersize, markeredgecolor, markerfacecolor y markeredgewith. La primera opción, marker, controla la forma del punto. Admite muchos valores, como None, , (píxel), . (punto), _ (línea horizontal), | (línea vertical), o (círculo), p (pentágono), s (cuadrado), x, +, *, D y d (diamantes), H y h (hexágonos), etc. La función point también admite la opción marker, pero, en este caso, el tamaño del punto se regula con size.

La función arrow tiene algunas opciones propias: arrowsize (tamaño de la punta de la flecha), width(ancho del astil), head (en qué extremo situar una punta: 0, 1 o 2, para ponerla al principio, al final o en ambos extemos) y arrowshorten (para acortar la longitud de la flecha).

La función text cuenta también con las opciones fontstyle (valores normal, italic, oblique), fontweight (valores ultralight, light, normal, regular, book, medium, roman, semibold, demibold, demi, bold, heavy, extra bold, black), background_color (color del fondo sobre el que se escribe el texto), rotation (ángulo de giro del texto en grados sexagesimales), vertical_alignment (alineamiento vertical del texto, que puede ser top, center, bottom) y horizontal_alignment (alineamiento horizontal; posibles valores: left, center, right).

En fin, el valor de la opción zorder es un entero cualquiera (incluso negativo). Cuando dos o más elementos gráficos se solapan, resulta visible aquél que tenga el valor más alto de zorder.

En las celdas siguientes se ejemplifican muchas de las opciones mencionadas:

In [7]:
# Representación de un triángulo, su círculo circunscrito y su circuncentro.
# Para facilitar el dibujo, los vértices A, B y C del triángulo pertenecen
# a la circunferencia x^2 + y^2 = 16. El circuncentro es (0,0).
# Coordenadas de los vértices:
A, B, C = vector((-3,-sqrt(7))), vector((-1,sqrt(15))), vector((4,0))
# Triángulo, circunferencia circunscrita y circuncentro
triangulo = polygon([A,B,C], color="lightgreen", edgecolor="green", zorder=0)
circunferencia = circle((0,0), 4, thickness=2, fill=True, 
                        edgecolor="darkblue", facecolor="lightcyan", zorder=-1)
circuncentro = point((0,0), color="red", marker="o", size=30, zorder=10)
# Mediatrices de los lados
pmAB = (A+B)/2   # punto medio del lado AB
mAB  = line([-2.2*pmAB,2.2*pmAB], linestyle="dashed", color="dimgray")
pmAC = (A+C)/2   # punto medio del lado AC
mAC  = line([-3.2*pmAC,3.2*pmAC], linestyle="dashed", color="dimgray")
pmBC = (B+C)/2   # punto medio del lado BC
mBC  = line([-1.9*pmBC,1.9*pmBC], linestyle="dashed", color="dimgray")
# Combinamos todo
todo = triangulo + circunferencia + circuncentro + mAB + mAC + mBC
# Añadimos texto
todo += text("A", A, horizontal_alignment="right",vertical_alignment="top", color="black") 
todo += text("B", B, vertical_alignment="bottom", color="black") 
todo += text("C", C, horizontal_alignment="left", color="black")
todo += text("circuncentro", (-0.5,-3.05), horizontal_alignment="center", color="black")
todo += arrow((-0.5,-3),(0,0), arrowshorten=8, arrowsize=3)
# Dibujamos
todo.show(axes=False)
In [8]:
# Representación de las funciones coseno y seno
# mediante dos líneas poligonales
p1 = [(t, cos(t)) for t in srange(-pi,4*pi,pi/8)]
lin1 = line(p1, color="darkgreen", thickness=2, marker="s", 
            markeredgecolor="gray", markerfacecolor="lightgreen",
            legend_label="cos", zorder=10)
p2 = [(t, sin(t)) for t in srange(-pi,4*pi,pi/8)]
lin2 = line(p2, color="darkblue", thickness=2, marker="o", 
            markeredgecolor="gray",markerfacecolor="lightskyblue",
            legend_label="sen", zorder=10)
lin =  lin1 + lin2
lin.set_axes_range(-9*pi/8, 4*pi, -1.1, 1.1)
op_leyenda = dict(numpoints=2, loc=0, handlelength=2, labelspacing=0.4, 
                  back_color=(0.95,0.95,0.95), fancybox=True)
lin.show(frame=True, legend_options=op_leyenda)

Pongamos un ejemplo adicional, que usa además las funciones arc y disk para trazar algunos arcos de circunferencias y un sector circular:

In [9]:
# Dibujo de una cara
# Cabeza, boca y nariz
cara = circle((0,-1), 10, fill=True, facecolor="lightcyan", 
              edgecolor="gray", thickness=2, zorder = -1)
cara += arc((0,-2.5), 6, sector=(3*pi/2-3*pi/10,3*pi/2+3*pi/10), 
            thickness=10, color="firebrick")
cara += disk((0,-2.5), 4, (3*pi/2-pi/6,3*pi/2+pi/6), color="orange", thickness=4)

# Ojos y pestañas
for i in [-4.5,4.5]:
    cara += circle((i,0), 4, fill=True, facecolor="lightgray", 
                   edgecolor="gray", thickness=2)                   # párpados
    cara += ellipse((i,0), 4, 3, fill=True, facecolor="ghostwhite", 
                    edgecolor="gray")                               # globo ocular
    cara += circle((i,0), 3, fill=True, color="green")              # iris
    cara += circle((i,0), 1, fill=True, color="black")              # pupila
    cara += arc((i,1), 4, sector=(pi/2-pi/4,pi/2+pi/4), 
                thickness=4, color="black")                         # pestaña

# Dibujo de la cara
cara.show(axes=False, figsize=5)

Representación de curvas planas

Curvas en forma explícita

Dada una función $f:D\subset\mathbb{R}\to\mathbb{R}$, la representación de la curva $y=f(x)$ en un intervalo $[a,b]$ se realiza mediante la función plot, que se emplea de la forma siguiente:

plot(fun, rango_x, opciones)

donde:

  • fun es una expresión simbólica que proporciona $f(x)$, o bien el nombre asignado a $f$ al definirla previamente como función Python o como función simbólica; por ejemplo, si $f(x)=x\cos x$, cualquiera de las siguientes variantes, entre otras muchas, representa esta función en el intervalo $[-\pi,5\pi]$:
plot(x*cos(x), (x, -pi, 5*pi))       f1(x) = x*cos(x)            def f2(x): return x*cos(x)
                                     plot(f1, (-pi, 5*pi))       plot(f2, (x, -pi, 5*pi))
  • rango_x es el par ($a$,$b$) o la terna (x, $a$, $b$); si se omite, se sobreentiende que $a=-1$ y $b=1$.

Al objeto de representar varias curvas simultáneamente, el argumento fun puede ser asimismo una lista de funciones. No obstante, es posible trazar cada curva con una función plot y sumar luego los gráficos resultantes. Ambas alternativas se ejemplifican en la celda siguiente:

In [10]:
op_leyenda = dict(loc=0, handlelength=2.5, labelspacing=0.3, fancybox=True)

# Primera alternativa: trazado de dos curvas con una sola orden plot
plot([4*sin(x),x*cos(x)], (x,-2*pi,2*pi), color=["red","blue"], 
     linestyle=["solid","dashed"], thickness=2, title="Primera alternativa", 
     legend_label=[r"$4\,\mathrm{sen}\,x$",r"$x\,\cos\,x$"]).show(legend_options=op_leyenda)

# Segunda alternativa: cada curva se traza con su propia orden plot
p1 = plot(4*sin(x), (x,-2*pi,2*pi), color="red", thickness=2, 
          legend_label=r"$4\,\mathrm{sen}\,x$")
p2 = plot(x*cos(x), (x,-2*pi,2*pi), color="blue", linestyle="dashed", thickness=2, 
          legend_label=r"$x\,\cos\,x$")
show(p1+p2, title="Segunda alternativa", legend_options=op_leyenda) # se «suman» p1 y p2 para combinarlos 

La orden plot? o la documentación detallan las múltiples opciones de la función plot. Algunas, como thickness, linestyle, color o legend_label, usadas en la última celda, son idénticas a las de la función line. Mencionemos algunas otras.

La opción plot_points señala el número mínimo de puntos que se han de utilizar para trazar el gráfico; SageMath añade más puntos donde sea necesario para refinar el dibujo:

In [11]:
p1 = plot(cos(x), (x,-pi,4*pi), plot_points=10, color="darkgreen", thickness=2,
          marker="s", markersize=5, markeredgecolor="gray",
          markerfacecolor="lightgreen", legend_label="cos")
p2 = plot(sin(x), (x,-pi,4*pi), plot_points=10, color="darkblue", thickness=2,
          marker="o", markersize=5, markeredgecolor="gray",
          markerfacecolor="lightskyblue", legend_label="sen")
p = p1 + p2
p.set_axes_range(-9*pi/8, 4*pi, -1.1, 1.1)
op_leyenda = dict(numpoints=2, loc=0, handlelength=2, labelspacing=0.4, 
                  back_color=(0.95,0.95,0.95), fancybox=True)
show(p, frame=True, legend_options=op_leyenda)

Las opciones exclude y detect_poles ayudan a tratar los puntos de discontinuidad de la función. Con la primera, se indican, mediante una lista o una ecuación de una sola variable, los puntos en los que no se ha de evaluar la función. La opción detect_poles, por su parte, permite localizar y, en su caso, trazar asíntotas verticales. Sus posibles valores son True, False y "show". Las opciones ymin e ymax acotan inferior y superiormente qué parte del gráfico se muestra. Las siguientes celdas ilustran estas opciones, así como la función graphics_array, que sirve para presentar simultáneamente una matriz de gráficos:

In [12]:
p1 = plot(sin(floor(x)), (x,-6,6), title="Sin exclusiones")
p2 = plot(sin(floor(x)), (x,-6,6), exclude=[-6,-5..6], title="Exclusión de los puntos de discontinuidad")
graphics_array([p1,p2]).show(figsize=[6,3], frame=True, axes=False, gridlines=True, 
                             ticks=[[-6,-4..6],[-1,-1/2..1]])
In [13]:
p1 = plot(tan(pi*x), (x,-2,2), detect_poles=False, ymin=-5, ymax=5, title="detect_poles=False")
p2 = plot(tan(pi*x), (x,-2,2), detect_poles=True, ymin=-5, ymax=5, title="detect_poles=True")
p3 = plot(tan(pi*x), (x,-2,2), detect_poles="show", ymin=-5, ymax=5, title='detect_poles="show"')
graphics_array([p1,p2,p3]).show(aspect_ratio=1/3, figsize=8)

La opción scale controla la escala (lineal o logarítmica) que se ha de usar en cada eje. La celda siguiente compara una representación de la función $f(x)=10^{x-2}$ usando primero una escala lineal en ambos ejes (es la opción por omisión) y luego una escala logarítmica en el eje de ordenadas:

In [14]:
p1 = plot(10^(x-2), (x,0,4))                   # escala lineal en Ox y Oy
p2 = plot(10^(x-2), (x,0,4), scale="semilogy") # escala lineal en Ox y logarítmica en Oy
graphics_array([p1,p2]).show(figsize=[6,3], frame=True, gridlines=True)

Las opciones fill, fillcolor y fillalpha controlan si se rellena, y de qué modo, una región limitada, en parte, por la curva considerada. La variedad de posibilidades es muy amplia. Ilustramos sólo algunas:

In [15]:
p1 = plot(x*cos(x), (x,-2*pi,3*pi), color="red", fill=True, fillcolor="lightgreen", fillalpha=0.25)
p2 = plot(x*cos(x), (x,-2*pi,3*pi), color="red", fill="min", fillcolor="lightgreen", fillalpha=0.5)
p3 = plot(x*cos(x), (x,-2*pi,3*pi), color="red", fill="max", fillcolor="lightgreen", fillalpha=1)
p4 = plot([x*cos(x),sin(x)], (x,-2*pi,3*pi), color=["red","darkgreen"], 
          fill={0:[1]}, fillcolor="lightgreen")

p = graphics_array([[p1,p2],[p3,p4]])
p.show(figsize=8,frame=True, axes=False, gridlines="minors",
       ticks=[[-2*pi,-pi..3*pi],None], tick_formatter=[pi,None])

Las figuras generadas con plot se pueden combinar con otros elementos gráficos, como líneas, flechas, puntos, círculos, polígonos o texto. La celda siguiente representa la función $$ f(x)=\frac{x(x^2-9x+8)}{x^2-1}. $$ Se añaden las dos asíntotas que tiene esta función y texto que las identifica:

In [16]:
f(x) = x*(x^2-9*x+8)/(x^2-1)
x1, x2 = -10, 10
y1, y2 = -50, 30

# Grafo de f
p = plot(f(x), (x,x1,x2), color="brown", exclude=[-1,1], ymin=y1, ymax=y2, thickness=2, 
         legend_label="$y=f(x)$", legend_color="darkblue")
p.set_legend_options(font_size="large", handlelength=2)

# Añadimos las asíntotas
p += line([(x1,x1-9),(x2,x2-9)], linestyle="dashed", color="darkgreen")
p += line([(-1,y1),(-1,y2)], linestyle="dashed", color="darkgreen")

# Y texto con las ecuaciones de las asíntotas
p += text("$y=x-9$", (8,-5), color="darkblue", fontsize="large")
p += text("$x=1$", (-2,25), color="darkblue", fontsize="large")

# Finalmente se traza la gráfica
p.show(frame=True, gridlines=["minor","major"], aspect_ratio=1/5)

Curvas en forma paramétrica

Consideremos una curva dada en forma paramétrica mediante la ecuación $(x,y)=\boldsymbol{\varphi}(t)$, con $\boldsymbol{\varphi}=(\varphi_1,\varphi_2):I\subset\mathbb{R}\to\mathbb{R}^2$. La función parametric_plot traza un arco de esta curva cuando el parámetro $t$ recorre un intervalo $[a,b]$ del dominio de $\boldsymbol{\varphi}$. La sintaxis de esta orden es:

parametric_plot(fun, rango_t, opciones),

donde:

  • fun es una lista, tupla o vector que proporciona la parametrización $\boldsymbol{\varphi}$,
  • rango_t es un par ($a$, $b$) o una terna (t, $a$, $b$).

Las opciones son similares a las de plot. Damos a continuación algunos ejemplos. Trazamos primero la curva $$ \left\{ \begin{aligned} &x=t\cos t, \\ &y=t\,\mathrm{sen}\,t, \end{aligned} \right. \quad t\in[0,8\pi], $$ llamada espiral de Arquímedes:

In [17]:
var("t")
parametric_plot([t*cos(t), t*sin(t)], (0,8*pi))
Out[17]:

Una epicicloide es una curva generada por un punto de una circunferencia de radio $b$ que rueda por el exterior de otra circunferencia de radio $a$. Su parametrización es $$ \renewcommand{\sin}{\operatorname{sen}} \left\{ \begin{aligned} &x = (a + b)\cos t - b\cos((a/b + 1)t), \\ &y = (a + b)\sin t - b\sin((a/b + 1)t). \end{aligned} \right. $$ Representamos el caso $a=10$ y $b=3$:

In [18]:
a, b = 10, 3
phi_1(t) = (a + b)*cos(t) - b*cos((a/b + 1)*t)
phi_2(t) = (a + b)*sin(t) - b*sin((a/b + 1)*t)
parametric_plot((phi_1(t),phi_2(t)), (t,0,6*pi))
Out[18]:

Trazamos ahora una rosa de cuatro pétalos, curva cuya expresión explícita en coordenadas polares es $r=\cos2\theta$. Usamos la opción fill para dar color a la región limitada por esta curva:

In [19]:
# Rosa de cuatro pétalos
var("theta")
def phi(theta):
    r = cos(2*theta)
    return (r*cos(theta), r*sin(theta))
parametric_plot(phi(theta), (theta,0,2*pi), color="darkgreen", fill=True, 
                fillcolor="lightgreen", linestyle="dotted", frame=True, axes=False)
Out[19]:

Como último ejemplo, representamos una curva de Lissajous:

In [20]:
a, b = 3/4, pi/5
parametric_plot([sin(t), sin(a*t + b)], (t,0,8*pi), color="green", 
                fill=True, fillcolor="orange", frame=True, axes=False)
Out[20]:

Curvas en forma implícita

Dadas dos funciones $F, G:D\subset\mathbb{R}^2\to\mathbb{R}$, la función implicit_plot representa la porción de la curva $F(x,y)=G(x,y)$ que se encuentre en un determinado rectángulo $[a_1,b_1]\times[a_2,b_2]$. Esta función admite la siguiente sintaxis:

implicit_plot($F(x,y)$==$G(x,y)$, rango_x, rango_y, opciones)

donde:

  • rango_x es el par ($a_1$, $b_1$) o la terna (x, $a_1$, $b_1$),
  • rango_y es el par ($a_2$, $b_2$) o la terna (y, $a_2$, $b_2$).

Si $G\equiv0$, en el primer argumento se puede escribir simplemente $F(x,y)$, o incluso $F$, en lugar de $F(x,y)$==$0$.

Las opciones de implicit_plot son análogas a las de la función plot. En particular, si fill=True, se rellena la porción del plano que satisface $F(x,y)-G(x,y)<0$ con el color que indique fillcolor. Pongamos algunos ejemplos. Representamos primero la curva $(x^2+y^2)^2=x^2-y^2$:

In [21]:
var("x,y")
implicit_plot((x^2+y^2)^2==x^2-y^2, (x,-1.2,1.2), (y,-0.5,0.5), plot_points=250,
              color="darkgreen", gridlines=True)
Out[21]:

Dada la función $F(x,y)= x^3-2x+y^2-1$, trazamos ahora la curva $F(x,y)=0$ con una línea discontinua y marcamos la región del plano en la que tal función es negativa:

In [22]:
F(x,y) = x^3-2*x+y^2-1
implicit_plot(F(x,y), (-4,2), (-4,4), color='darkgreen', linestyle="dashed", 
              fill=True, fillcolor="lightgreen", frame=True)
Out[22]:

Dibujamos finalmente varios elementos de las familias ortogonales de curvas $(x-c)^2+y^2=c^2$ y $x^2+(y-c)^2=c^2$:

In [23]:
var("x,y")
p = sum(implicit_plot((x-c)^2+y^2-c^2, (x,-5,5), (y,-5,5), color="darkgreen") for c in [-2.5,-1.5..2.5])
q = sum(implicit_plot(x^2+(y-c)^2-c^2, (x,-5,5), (y,-5,5), color="darkred") for c in [-2.5,-1.5..2.5])
show(p+q)

Curvas de nivel

Las curvas de nivel de una función $F:D\subset\mathbb{R}^2\to\mathbb{R}$ son las curvas de ecuación $F(x,y)=c$ para cada $c$ perteneciente al recorrido de $F$. En SageMath se trazan con la función contour_plot, que se usa del siguiente modo:

contour_plot(fun, rango_x, rango_y, opciones),

donde:

  • fun es una expresión o función que proporciona la función $F$,
  • rango_x es un par ($a_1$, $b_1$) o una terna (x, $a_1$, $b_1$),
  • rango_y es un par ($a_2$, $b_2$) o una terna (y, $a_2$, $b_2$).

Se selecciona automáticamente la relación de valores de $c$ para los que se dibujan las correspondientes curvas $F(x,y)=c$; con la opción contours se puede cambiar tanto el número de curvas, proporcionando el entero adecuado, como las lista de valores.

Por omisión, se colorean las curvas de nivel y el espacio entre ellas con una gama de grises. Se cambia la gama con la opción cmap, que puede ser una de las gamas predefinidas (jet, coolwarm, terrain...) o una lista de colores. Con colorbar=True, se añade una barra con el rango de valores y colores asociados. El espacio entre curvas queda en blanco si fill=False.

Análogamente, si labels=True, se anota en cada curva el valor de $c$ asociado. El formato de este número se controla con label_fontsize, label_colors, label_inline, label_inline_spacing y label_fmt.

La opción plot_points indica el número de puntos en los que se evalúa la función para trazar las curvas. Las opciones linewidths y linestyles regulan el aspecto de las curvas; se les puede asignar un único valor, que se emplea para todas las curvas, o una lista de valores, que se aplica entonces cíclicamente.

En la celda siguiente se muestran las curvas de nivel de la función $F(x,y)=x^2-y^2$ de diversos modos. Se puede apreciar así el efecto de muchas de las opciones mencionadas:

In [24]:
var("x,y")
p1 = contour_plot(x^2-y^2, (x,-2,2), (y,-2,2))
p2 = contour_plot(x^2-y^2, (x,-2,2), (y,-2,2), contours=15, cmap=["dimgray"], fill=False,
                  labels=True, label_inline=True, label_fontsize=7)
p3 = contour_plot(x^2-y^2, (x,-2,2), (y,-2,2), contours=15, cmap="coolwarm", colorbar=True)
p4 = contour_plot(x^2-y^2, (x,-2,2), (y,-2,2), contours=15, cmap="coolwarm", colorbar=True,
                  fill=False, linewidths=3, linestyles=["solid","dashed"], 
                  labels=True, label_fmt="%1.1f", label_inline=True, label_inline_space=4, 
                  label_fontsize=7, label_colors="black")
p5 = contour_plot(x^2-y^2, (x,-2,2), (y,-2,2), contours=[-2,0,2], 
                  cmap=["darkblue","blue","gray","red","darkred"], 
                  labels=True, label_fmt={-2:"frío", 0:"templado", 2:"caliente"},
                  label_fontsize=10, label_colors="white")
p6 = contour_plot(x^2-y^2, (x,-2,2), (y,-2,2), contours=[-3..3], cmap="coolwarm", fill=False, 
                  labels=True, label_fmt=lambda x:"$z={:1.0f}$".format(x), 
                  label_inline=True, label_inline_space=2, 
                  label_fontsize=10, label_colors="black")

graphics_array([[p1,p2],[p3,p4],[p5,p6]]).show(figsize=[10,12])

No es posible usar en una misma función contour_plot dos gamas de colores, una para las curvas y otra para el relleno. No obstante, sí es posible superponer dos gráficos, lo cual puede ser conveniente para remarcar las curvas de nivel. Compárense los gráficos a izquierda y derecha:

In [25]:
var("x,y")
f(x,y) = (2*x+3*y^2) / (x^2+y^2+1)^2
p = contour_plot(f(x,y), (x,-2,2), (y,-2,2), contours=15, plot_points=200,
                 cmap=["black"], fill=False, linewidths=0.9)
q = contour_plot(f(x,y), (x,-2,2), (y,-2,2), contours=15, plot_points=200,
                 cmap="jet", colorbar=True)
graphics_array([q,q+p]).show(figsize=[12,5])

Mencionemos, para terminar, la opción region, que permite restringir el dibujo de las curvas a una zona determinada del plano. El valor que se asigne a esta opción ha de ser una función numérica o booleana. Sólo se trazarán curvas allí donde tal función sea positiva, si es numérica, o tome el valor True, si es booleana. Por ejemplo, en la siguiente celda, se limita el dibujo de las curvas de nivel de la función $f(x,y)=\sin(x+y)\cos(x-y)$ a la banda comprendida entre las rectas $y=x-4.5$ e $y=x+4.5$:

In [26]:
def f(x,y):
    return sin(x+y)*cos(x-y)
contour_plot(f(x,y), (x,-3,3), (y,-3,3), contours=15, cmap="jet", colorbar=True,
             region=lambda x,y: x-4.5<y<x+4.5)
Out[26]:

Regiones planas

Ya hemos visto que la opción fill de las funciones plot, parametric_plot e implicit_plot permite dar color a diversas regiones planas. Lo mismo sucede con la función contour_plot. SageMath cuenta, además, con la función region_plot, cuya sintaxis es

region_plot(desig, rango_x, rango_y, opciones),

donde:

  • desig es una expresión o función que indique la desigualdad, o lista de desigualdades, que satisfacen los puntos de la región que se quiere marcar,
  • rango_x es un par ($a_1$, $b_1$) o una terna (x, $a_1$, $b_1$),
  • rango_y es un par ($a_2$, $b_2$) o una terna (y, $a_2$, $b_2$).

Los colores del interior y del exterior de la región se regulan con las opciones incol y outcol; la apariencia del borde se fija con bordercol, borderstyle y borderwidth. Son admisibles más opciones como plot_points o alpha. La celda siguiente muestra cómo representar la región $x^4+y^4-5x^3+3y<1$, con $(x,y)\in[-1,5.2]\times[-3.2,3]$:

In [27]:
var("x,y")
region_plot([x^4+y^4-5*x^3+3*y<1], (x,-1,5.2), (y,-3.2,3), 
            incol="lightgreen", outcol="lightgray", bordercol="darkgreen", frame=True)
Out[27]:

Se representa ahora el conjunto limitado por las circunferencias $x^2+y^2=2x$ y $x^2+y^2=4x$ y las rectas $y=x$ e $y=0$. Cada punto $(x,y)$ de este conjunto satisface las condiciones $$ 2x\leq x^2+y^2 \leq 4x,\quad 0\leq y\leq x. $$ Las cuatro desigualdades se pasan, en forma de lista, como primer argumento de region_plot:

In [28]:
var("x,y")
r = region_plot([x^2+y^2>=2*x, x^2+y^2<=4*x, 0<=y, y<=x], (x,0,4), (y,-0.5,2), 
                plot_points=200, incol="lightgreen", bordercol="darkgreen", borderwidth=3)
p = plot(x, (-0.5,4), linestyle="dashed", color="gray", zorder=0)
q1 = implicit_plot(x^2+y^2==2*x, (x,0,4), (y,0,2), color="gray", linestyle="dashed")
q2 = implicit_plot(x^2+y^2==4*x, (x,0,4), (y,0,2), color="gray", linestyle="dashed")

show(p+q1+q2+r, frame=False, ymin=-0.5, ymax=2.5, aspect_ratio=1)

Se procede análogamente para representar el conjunto $\{ (x,y)\in\mathbb{R}^2 \mid 1\lt x^2-y^2\lt 6,\ x^2+y^2\lt 9\}$:

In [29]:
var("x,y")
region_plot([1<x^2-y^2, x^2-y^2<6, x^2+y^2<9], (x,-3,3), (y,-3,3), plot_points=200,
            incol="lightgreen", bordercol="darkgreen", borderstyle="dashed", frame=True)
Out[29]:

El primer argumento de region_plot puede ser también el nombre de una función booleana más o menos compleja. Por ejemplo, la celda siguiente colorea, dentro del rectángulo $[-3,3]\times[-3,3]$, los puntos $(x,y)$ tales que, o bien $\lvert\sin(x^2-y^2)\rvert\leq 1/2$, o bien $\lvert\cos(x^2+y^2)\rvert\leq 1/2$:

In [30]:
def f(x,y):
    return abs(sin(x^2-y^2))<0.5 or abs(cos(x^2+y^2))<0.5
region_plot(f, (x,-3,3), (y,-3,3), plot_points=400,
            incol="lightgreen", bordercol="darkgreen", frame=True)
Out[30]:
Celda de configuración. La evaluación de la celda siguiente cambia el formato por omisión de este cuaderno.
In [31]:
%%html
<style>
h1{text-align: center; color: rgb(185,74,72);}
h2{text-align: center; color: rgb(0,102,0); padding: 0.25em 0;
   border: 2px solid rgb(0,191,0); border-width: 2px 0;}
h3{border-bottom: 2px solid rgb(153,153,153);} 
h4{color: rgb(58,135,173); font-size: 115%!important;
   font-weight: bold!important;}
.text_cell_render{font-family: "Trebuchet MS",Geneva,sans-serif;
                  font-size: 110%; line-height: 1.5;}
.MathJax_Display{margin: 0.5em 0;}
</style>
Realizado por Juan José Torrens para la asignatura de Cálculo II
Grados de Ciencias y de Ciencia de Datos
Universidad Pública de Navarra
Última versión: 14-1-2021