Skip to content

Latest commit

 

History

History
356 lines (259 loc) · 8.88 KB

matplotlib.org

File metadata and controls

356 lines (259 loc) · 8.88 KB

Matplotlib: plotting

This section was adapted from the Scipy lecture notes, which was written by Nicolas Rougier, Mike Müller, and Gaël Varoquaux.

License: Creative Commons Attribution 4.0 International License (CC-by)

Adapted for py4cl by B.Dudson (2019).

The following examples assume that you have already loaded py4cl

(ql:quickload :py4cl)

and then imported python modules as lisp packages using:

(py4cl:import-module "numpy" :as "np")
(py4cl:import-module "matplotlib.pyplot" :as "plt")

Simple plot

./matplotlib_1.png

(let* ((x (np:linspace (- pi) pi 256 :endpoint t))
       (c (np:cos x))
       (s (np:sin x)))

  (plt:plot x c)
  (plt:plot x s))

(plt:show)

Instantiating defaults

;; Create a figure of size 8x6 inches, 80 dots per inch
(plt:figure :figsize '(8 6) :dpi 80)

;; Create a new subplot from a grid of 1x1
(plt:subplot 1 1 1)

(let* ((x (np:linspace (- pi) pi 256 :endpoint t))
       (c (np:cos x))
       (s (np:sin x)))

  ;; Plot cosine with a blue continuous line of width 1 (pixels)
  (plt:plot x c :color "blue" :linewidth 1.0 :linestyle "-")

  ;; Plot sine with a green continuous line of width 1 (pixels)
  (plt:plot x s :color "green" :linewidth 1.0 :linestyle "-")

  ;; Set x limits
  (plt:xlim -4.0 4.0)

  ;; Set x ticks
  (plt:xticks (np:linspace -4 4 9 :endpoint t))

  ;; Set y limits
  (plt:ylim -1.0 1.0)

  ;; Set y ticks
  (plt:yticks (np:linspace -1 1 5 :endpoint t))

  ;; Save figure using 72 dots per inch
  ;; (plt:savefig "exercise_2.png" :dpi 72)

  ;; Show result on screen
  (plt:show))

Moving spines

Spines are the lines connecting the axis tick marks and noting the boundaries of the data area. They can be placed at arbitrary positions and until now, they were on the border of the axis. We’ll change that since we want to have them in the middle. Since there are four of them (top/bottom/left/right), we’ll discard the top and right by setting their color to none and we’ll move the bottom and left ones to coordinate 0 in data space coordinates.

This illustrates the use of py4cl:chain to access data members and methods of python objects.

./matplotlib_2.png

(let* ((x (np:linspace (- pi) pi 256 :endpoint t))
       (c (np:cos x))
       (s (np:sin x)))

  (plt:plot x c)
  (plt:plot x s))

(let ((ax (plt:gca)))  ; gca stands for 'get current axis'
  (py4cl:chain ax spines "right" (set_color "none"))
  (py4cl:chain ax spines "top" (set_color "none"))

  (py4cl:chain ax xaxis (set_ticks_position "bottom"))
  (py4cl:chain ax spines "bottom" (set_position '("data" 0)))

  (py4cl:chain ax yaxis (set_ticks_position "left"))
  (py4cl:chain ax spines "left" (set_position '("data" 0))))

(plt:show)

Annotating some points

Let’s annotate some interesting points using the annotate command. We chose the 2π/3 value and we want to annotate both the sine and the cosine. We’ll first draw a marker on the curve as well as a straight dotted line. Then, we’ll use the annotate command to display some text with an arrow.

./matplotlib_3.png

(py4cl:import-function "dict")

(let* ((x (np:linspace (- pi) pi 256 :endpoint t))
       (c (np:cos x))
       (s (np:sin x)))

  (plt:plot x c)
  (plt:plot x s))

(let ((ax (plt:gca)))  ; gca stands for 'get current axis'
  (py4cl:chain ax spines "right" (set_color "none"))
  (py4cl:chain ax spines "top" (set_color "none"))

  (py4cl:chain ax xaxis (set_ticks_position "bottom"))
  (py4cl:chain ax spines "bottom" (set_position '("data" 0)))

  (py4cl:chain ax yaxis (set_ticks_position "left"))
  (py4cl:chain ax spines "left" (set_position '("data" 0))))

;; Annotate

(let ((y (* 2 pi (/ 3))))
  (plt:plot (vector y y) (vector 0 (cos y)) :color "blue" :linewidth 2.5 :linestyle "--")
  (plt:scatter (list y) (list (cos y)) 50 :color "blue")

  (plt:annotate "$cos(\\frac{2\\pi}{3})=-\\frac{1}{2}$" :xy (list y (cos y)) :xycoords "data"
                :xytext '(-90 -50) :textcoords "offset points" :fontsize 16
                :arrowprops (dict :arrowstyle "->" :connectionstyle "arc3,rad=.2"))
  
  (plt:plot (list y y) (list 0 (sin y)) :color "red" :linewidth 2.5 :linestyle "--")
  (plt:scatter (list y) (list (sin y)) 50 :color "red")

  (plt:annotate "$sin(\\frac{2\\pi}{3})=\\frac{\\sqrt{3}}{2}$" 
                :xy (list y (sin y)) :xycoords "data"
                :xytext '(10 30) :textcoords "offset points" :fontsize 16
                :arrowprops (dict :arrowstyle "->" :connectionstyle "arc3,rad=.2")))

(plt:show)

Plotting a scatter of points

./matplotlib_scatter.png

We’ll load Numpy’s random routines to generate random numbers, in addition to the np and plt modules already loaded:

(py4cl:import-module "numpy.random" :as "nprand")
(let* ((n 1024)
       (x (nprand:normal 0 1 n))
       (y (nprand:normal 0 1 n))
       (theta (np:arctan2 y x)))

  (plt:axes #(0.025 0.025 0.95 0.95))
  (plt:scatter x y :s 75 :c theta :alpha 0.5)

  (plt:xlim -1.5 1.5)
  (plt:xticks #())
  (plt:ylim -1.5 1.5)
  (plt:yticks #()))

(plt:show)

Displaying the contours of a function

./matplotlib_contour.png

(defun f (x y)
  (* 
    (+ 1 (* -0.5 x) (expt x 5) (expt y 3))
    (exp (- 0 (* x x) (* y y)))))

(let* ((n 256)
       (xy (np:meshgrid (np:linspace -3 3 n) 
                        (np:linspace -3 3 n)))
       (x (aref xy 0))
       (y (aref xy 1)))

  (plt:axes #(0.025 0.025 0.95 0.95))

  (plt:contourf x y (py4cl:python-call (np:vectorize #'f) x y) 8 :alpha 0.75)

  (let ((c (plt:contour x y (py4cl:python-call (np:vectorize #'f) x y) 8 :colors "black" :linewidth 0.5)))
    (plt:clabel c :inline 1 :fontsize 10)))

(plt:xticks #())
(plt:yticks #())
(plt:show)

or a slightly more efficient method, using remote-objects:

(defun f (x y)
  (* 
    (+ 1 (* -0.5 x) (expt x 5) (expt y 3))
    (exp (- 0 (* x x) (* y y)))))

(py4cl:remote-objects
(let* ((n 256)
       (xy (np:meshgrid (np:linspace -3 3 n) 
                        (np:linspace -3 3 n)))
       (x (py4cl:chain xy 0))
       (y (py4cl:chain xy 1)))

  (plt:axes #(0.025 0.025 0.95 0.95))

  (plt:contourf x y (py4cl:python-call (np:vectorize #'f) x y) 8 :alpha 0.75)

  (let ((c (plt:contour x y (py4cl:python-call (np:vectorize #'f) x y) 8 :colors "black" :linewidth 0.5)))
    (plt:clabel c :inline 1 :fontsize 10))))

(plt:xticks #())
(plt:yticks #())
(plt:show)

The majority of the time is spent in calling lisp for every point in (x,y). We could just do the calculation in lisp using array-operations:

(ql:quickload :array-operations)
(defun f (x y)
  (* 
    (+ 1 (* -0.5 x) (expt x 5) (expt y 3))
    (exp (- 0 (* x x) (* y y)))))

(let* ((n 256)
       (xy (np:meshgrid (np:linspace -3 3 n) 
                        (np:linspace -3 3 n)))
       (x (aref xy 0))
       (y (aref xy 1))
       (z (aops:vectorize (x y) (f x y))))

  (plt:axes #(0.025 0.025 0.95 0.95))

  (plt:contourf x y z 8 :alpha 0.75)

  (let ((c (plt:contour x y z 8 :colors "black" :linewidth 0.5)))
    (plt:clabel c :inline 1 :fontsize 10)))

(plt:xticks #())
(plt:yticks #())
(plt:show)

3D plots

./matplotlib_3d.png

(py4cl:import-module "numpy" :as "np")
(py4cl:import-module "matplotlib.pyplot" :as "plt")
(py4cl:import-function "Axes3D" :from "mpl_toolkits.mplot3d")

;; Load array-operations (aops) for VECTORIZE macro
(ql:quickload :array-operations)
(let* ((fig (plt:figure))
       (ax (Axes3D fig))
       (xy (np:meshgrid (np:arange -4 4 0.25)
                        (np:arange -4 4 0.25)))
       (x (aref xy 0))
       (y (aref xy 1))

       (r (aops:vectorize (x y) (sqrt (+ (* x x) (* y y)))))
       (z (aops:vectorize (r) (sin r))))

  (py4cl:chain ax (plot_surface x y z :rstride 1 :cstride 1))
  (py4cl:chain ax (contourf x y z :zdir "z" :offset -2))
  (py4cl:chain ax (set_zlim -2 2)))
(plt:show)