Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ call suptitle("figure title")
- `table` for a block of text below (or above) the axes
- `streamplot` for the streamlines of a vector field
- `add_frame` / `save_animation(file, fps=, loop=)` to write an animated GIF
- `matshow`, `eventplot` and `broken_barh`
- `spy` (sparsity pattern of a dense matrix or COO indices), `matshow`, `eventplot` and `broken_barh`
- `hist2d` and `hexbin` for counting points into square or hexagonal bins
- `imshow(interpolation="bilinear")` to smooth an image instead of
showing it as blocks
Expand Down
70 changes: 70 additions & 0 deletions docs/gallery/plot_spy.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
! Spy: sparsity patterns
! ======================
!
! The nonzero structure of a matrix, as matplotlib's
! `spy <https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.spy.html>`_
! draws it: one square marker per stored entry, row 1 at the top and the
! column numbers along the top edge.
!
! :f:subr:`spy` takes either a dense matrix, whose entries above ``precision``
! in magnitude are marked, or the COO indices of a sparse one as a
! ``(2, nnz)`` integer array of rows and columns. Indices start at 1.

program plot_spy
use fplotlib
implicit none

integer, parameter :: n = 30
real(dp) :: a(n, n)
integer, allocatable :: ij(:, :)
integer :: i, k

! %%
! A dense matrix: the tridiagonal of a 1D Laplacian, plus two corner entries
! and an anti-diagonal of tiny values that ``precision`` filters out.

a = 0.0_dp
do i = 1, n
a(i, i) = 2.0_dp
if (i > 1) a(i, i - 1) = -1.0_dp
if (i < n) a(i, i + 1) = -1.0_dp
a(i, n + 1 - i) = a(i, n + 1 - i) + 1.0e-9_dp
end do
a(1, n) = -1.0_dp
a(n, 1) = -1.0_dp

call figure(figsize=[10.0_dp, 5.0_dp])
call subplot(1, 2, 1)
call spy(a, precision=1.0e-6_dp, markersize=4.0_dp)
call title("dense, precision = 1e-6")

! %%
! A sparse matrix given only by its COO indices: a 5-point stencil on a
! 6 x 6 grid. The shape ``m``, ``n`` is passed so that empty trailing rows or
! columns still show.

k = 0
allocate (ij(2, 5 * 36))
block
integer :: gx, gy, r
do gy = 1, 6
do gx = 1, 6
r = (gy - 1) * 6 + gx
k = k + 1; ij(:, k) = [r, r]
if (gx > 1) then; k = k + 1; ij(:, k) = [r, r - 1]; end if
if (gx < 6) then; k = k + 1; ij(:, k) = [r, r + 1]; end if
if (gy > 1) then; k = k + 1; ij(:, k) = [r, r - 6]; end if
if (gy < 6) then; k = k + 1; ij(:, k) = [r, r + 6]; end if
end do
end do
end block

call subplot(1, 2, 2)
call spy(ij(:, 1:k), m=36, n=36, markersize=5.0_dp, color="C1")
call title("COO indices")

call savefig("spy.svg")

print *, "wrote spy.svg"

end program plot_spy
74 changes: 73 additions & 1 deletion src/fplotlib.f90
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ module fplotlib
public :: bar, barh, hist, fill_between, fill_betweenx, stackplot
public :: errorbar, axhline, axvline, axline
public :: pcolormesh, pcolor, hist2d, hexbin
public :: matshow, eventplot, broken_barh, streamplot, table
public :: spy, matshow, eventplot, broken_barh, streamplot, table
public :: add_axes, secondary_xaxis, secondary_yaxis
public :: add_rectangle, add_circle, add_ellipse, add_polygon
public :: add_arrow, add_path
Expand Down Expand Up @@ -197,6 +197,11 @@ module fplotlib
module procedure imshow_z, imshow_rgb
end interface imshow

! Sparsity pattern of a dense matrix, or of a COO index array.
interface spy
module procedure spy_dense, spy_coo
end interface spy

! One dataset or a whole block of them, a row to each.
interface boxplot
module procedure boxplot_one, boxplot_many
Expand Down Expand Up @@ -2704,6 +2709,73 @@ subroutine imshow_rgb(z, extent, origin, aspect)
end if
end subroutine imshow_rgb

! matplotlib's spy for a dense matrix: a marker at every entry whose
! magnitude exceeds precision (default 0, i.e. every non-zero entry).
! Indices are Fortran's, so the first row and column sit at 1.
subroutine spy_dense(a, precision, markersize, color, marker)
real(dp), intent(in) :: a(:, :)
real(dp), intent(in), optional :: precision, markersize
character(len=*), intent(in), optional :: color, marker
real(dp) :: tol
logical, allocatable :: mask(:, :)
integer, allocatable :: ij(:, :)
integer :: i, j, k

tol = 0.0_dp
if (present(precision)) tol = abs(precision)
allocate (mask(size(a, 1), size(a, 2)))
mask = abs(a) > tol
allocate (ij(2, count(mask)))
k = 0
do j = 1, size(a, 2)
do i = 1, size(a, 1)
if (mask(i, j)) then
k = k + 1
ij(:, k) = [i, j]
end if
end do
end do
call spy_coo(ij, size(a, 1), size(a, 2), markersize, color, marker)
end subroutine spy_dense

! Sparsity pattern from COO indices: ij(1, :) are the rows and ij(2, :)
! the columns of the stored entries, 1-based. m and n are the matrix
! shape; without them the largest index in each is used.
subroutine spy_coo(ij, m, n, markersize, color, marker)
integer, intent(in) :: ij(:, :)
integer, intent(in), optional :: m, n
real(dp), intent(in), optional :: markersize
character(len=*), intent(in), optional :: color, marker
integer :: nr, nc
real(dp) :: ms
character(len=:), allocatable :: mk

if (size(ij, 1) /= 2) error stop "fplotlib: spy needs a (2, nnz) index array"
nr = 0
nc = 0
if (size(ij, 2) > 0) then
nr = maxval(ij(1, :))
nc = maxval(ij(2, :))
end if
if (present(m)) nr = m
if (present(n)) nc = n
ms = 10.0_dp
if (present(markersize)) ms = markersize
mk = "s"
if (present(marker)) mk = marker

call ensure_fig()
if (size(ij, 2) > 0) then
call plot(real(ij(2, :), dp), real(ij(1, :), dp), marker=mk, &
linestyle="None", color=color, markersize=ms)
end if
call xlim(0.5_dp, real(nc, dp) + 0.5_dp)
call ylim(0.5_dp, real(nr, dp) + 0.5_dp)
ax(cur_i)%y_inv = .true.
ax(cur_i)%x_top = .true.
call set_aspect(1.0_dp, "box")
end subroutine spy_coo

! matshow: an image of a matrix. The first row is at the top, the
! cells are square and the column numbers run along the top, which is
! how a matrix is written down.
Expand Down
2 changes: 2 additions & 0 deletions tests/fingerprints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ interp 640 480 00000000000000000000000000000000000000000000000000000000000000000
hist2d 640 480 4041a024a1408408b2be65f4e8f4b0b67cc8f82c0babc84d0a4645ca7e988f58d994304b3d33dd453f6d8d6b6238624271ca4f372bea702e58ba63044e5861147da1e388fb32744ed88ee843cb9b8d0814252e2df217d18c501c62262e5d5ad05f416b56220b7207a821074c2f6aba9df04383d12a242f9a9b83430147f196987de7a1a00bf1a60930384d27aac7b6df24695566e48f9d9b2d83614c228de1ab22659fd578d296507370313c70f61555802ca0470b50440ceec919ae3c78eef65bc7f5e6aefef5aab1b2ab8d18181498c5e13f856b05c04010d5ffb3dbf44e40664862ef15129c037c03286631713065b3b19704034a2ba0b1e31e8e59ee8e56d38bc2e9955e3222a66adfd2aa13d5499910dafc0656a15c85605cbdd2275e0e3ac380fd56e3a4e7db600f67944500136414f24bc8fa63e2b9d4196a4e322bfa587d62c2e7045523f87c6359f549420f1c993003086110082205844ab7e174200338bae7e7e6781af66afa8f2407a262ec71a966c006340ecd31ec6675cde9b8ac5f9aade059bce0120da2b02dfcdb4b3d1bdc07a246726217b2d9f0905c1f0c487325a60c42d53621f43d9e37790b3800c2c400085444c297ad71b432368ba26b85999e05070eca26974bfbab957b1168d2d2be79480d0ede644eec8b5ba0752503aa246d065c66c226f84a8d450ce97165589ad149a2faf54500846f3ab3bf
hexbin 640 480 99b30fc0fb6c26fa6b0ac75f8750ad84afd225facaa312911eae6b8e1147bd12206262f1348f73b5f3324344e81fe043e70a0791528a781c16491874ad5264974252b65e2bb0996ad9f163d966983b88d362951b2c3b99fd19ac23ee173b96e393f1285ccf1b665691044f9cae5d2a9e066b34d07a21261bb22a09259113103596a27af462140464114d33ad11e8a2cfd49c7d0072aa2c00f65b094481536e3dcb41a46a6d4c0872a24617b1c470493fd19fcae2177fa3e236083cca6c5322db4b3281d2772ea03c5d15b6f9b214345245f0804dbf253f67d2a6ebd526f4d888c13e6d251c575b7f382008792235aa8921b9c45258d9ddf15f21dcd4ab574f298e182f9d82c67b86eebbe29e95712ffe77bd969572f30511628e61de7304024f24636db949feae604e5a7c266b4ba168a0bb22a22f00dac7d105331fa322f1dd2719d31d35c8813cdbb39a41c712f74a521cf639bbbbf10cd7c79b6cdcebd0e12c8936af783982b94d5247703532995e7b0158ed6b183df348348f4e638f505df5284f4ab34d16c3291f9cab23b5c1e59d77105e3adc14d2377cc382688cc91f0d1cfd2396476abc90d03752632f9ab06067bb1d5653154fabc718e463a94a44de07b388248af882012efdc8c3032dc88a14d2ee9613da8831ecd19ee3a6f9e66f787f1bf34dabc6274e8199c9dc08e90116a552f3024a0d3d394440f1e6099d
matshow 640 480 0000000000000000c1f68b49611d1876986106d3089f6b02e98149e9b46f3298085c49f68f7eed97b10e4210dc621374fdbb9977ee00e71000000000000000000000000000000000d8c64c7df3935f7abf242648a0005daf8e9ea78807bd9d6eff4d1a76a081f49ef40077f2b384f49fdc134541f8b4f55c000000000000000000000000000000006c8a4986bed62f6d0863022214a004009500c1010059581606d404c4cde74009a16a024224a4263a8d50298c65561a650000000000000000000000000000000052335479d4708a8228000236048125042020431142020002408a002034284242412010608680800181b07cdde6fd70b00000000000000000000000000000000001aa17bd55a6165408408b842c8008020129220010040080018001231d3a900008284a44a5d489009a173264033709bf000000000000000000000000000000007ec1071f89d2708540000091ab8c100f405240520000100825c8c022a0207a0acab2bfa439931cc4431f41589f1e64a0000000000000000000000000000000001b47500a4108cef61891232a943593221840809904d4481048ce4b7f0fe405918b537d8a464b63783d83e18e91c8533d000000000000000000000000000000006a2e4ca12666f2f5e8276fccb099ad081b64144b04af8dce600ffea7b95202b34e4d9e0bf908c28fe21eaaa0783e9c740000000000000000
spy_dense 640 480 070b551691b94f3dd058af2c1396cff796b670ab97129db4649308f79ce832988b7a46291f6ea7058c290af8ef56ae935e72fc48d082b86765ae61ad337599a5c49bc08167376fccccc40107209da90588005c700c4e17b827098d900c6b41d901452a4208db47295276f945b304fdd25293a1ea828285c3baf53bfd16dcefc17101407e64101804dc5a2e30c74e3ae22f87f52aca141c1e0000000000000000000000000000000000000000000000000000000000000000d1b326e7f4fcbf8f7ce262184e01cd1db7495b2512e113a700000000000000000000000000000000000000000000000000000000000000000000000000000000a8dcd1a316ed0287778b03054cdb512131b886bc8666833c00000000000000000000000000000000000000000000000000000000000000000000000000000000bb6a6bd70a94057d2da5868b1cb92291cb38c34b6d7e65400000000000000000c21b82c6550d0aa60e8cd3ddcb124e1900000000000000001c533293527ad8b8f1449c6d30d95b5e3370b23b73aade2c79157ebec5002952378ecba9879ce568caf8653c03deff98ba7cc37721ac02c54f456f98ed0ca7e91061cfcf4eaf535c228f2e88c0ce736a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
spy_coo 640 480 000000000000000000000000000000000465ec06debc7efd121055f6285fe6282c8ba4012a049b7009b7770101b8eaaf02009400002020000000000000000000000000000000000000400014200040008d864e267378e4fc25db74127621f3f00b835ee019a572f5ec2609428b1e249a10827c018fac3ca20000000000000000000000000000000080004000282409001a4fba7e89bcbee700000000000000009a3395d70483c498ffff51ee1d2c36161951008767273794000000000000000000000000000000000000040100081088285b1e4a587f3b8b00000000000000000000000080000000c2d783c4c2086560e8702e96a521625b000000000000000000000000000000008010000011000000315e7006ceeb4ec600000000000000000000000000000000bdff9e7e67a29e2c3a10032dc0b41410000000000000000000000000000000000000214000020034296fdb84f1b7c3c7547615404964ffdb0000000000000000337260e050081e93032a47330c51e800000000000000000000000000000000002c202200028022009e68a62c80d4b49f00000000000000000000000000000000a3e6148089d8e04e444b546488826b6000000000000000000000000000000000000000000000000058793d7de4bc834fc42d084a25d3befea24e392a9727b215ea7e950e4aa003f1d01010800c2100000000000000000000
eventplot 640 480 4001a02401400000ed1952e619b0e9b0a0c37cfae9f0cb73257cf3d338ba82b5ba1d1c2f1d583fb931f68bf7b6b9c555b3f93ad9bb4292d516491874ad52649751701daaa5a3c9f8ad3cf7db776fd496729cf567f2db6b4825db74127621f3f0ab835ae019a572d578176ae5641fcee21b80061009890390a359466920cac7cda7764185f5ae3a4835bde8a3578e7c2cfd84f44db00ae93b15b341ab4c16e85612829d37f23653dd86e700fa95b6b1459e0701520b3845f8d1b326e7f4fcbf8f85d522cf984a473cea6141ec322a8b2c19429fda9257ff2c0cbaf04fe9e66523c7c67d5b6dc4860c2b6987816f2262a819dff2fbe5950404a8dcd1a316ed0287550c77f6fae7c39f1d0397a1d804f77b6b17e358a38a05019df9d03c3fa5f8680339923f8f0c6735b8a82dcc7851ac790d3ab560d3782963bb6a6bd70a94057d529a8dcdcd842ced4f014962ee6e95bafd3e09a482ccd546cc347c5bcbff013f89b15e31c243608b79870f86b125f35ed70c520ac7017c0ef1449c6d30d95b5ed01a248c444ab0e7e8b9c871ae993ac2000000000000000000000000000000000000000000000000000000000000000000000000000000004cb261193db438d30201b01800000a416215fe4ee35fd93c81d163a5bff7c197ece1d342dc07c9cda97770045fd29a86abf30555ba2a9fcb22f5fe654c3218d46fc51c434a25072e
broken_barh 640 480 4001a02401400000ed1952e619b0e9b0a0c37cfae9f0cb73a3d27805fa4a833db82411efb921440331f68bf7b6b9c555b3f93ad9bb4292d516491874ad5264971090d532cf35236f076fcba7022744f0f8e55dcfb707664773d1556cf8aa32ab23fab2f08a79781a4a22cfee60873198e4e13cd7e4612024a359466920cac7cd7602d92db3ed0dc3f9951315b0b243c20000000200200400f3b0bf28b1ebfc84b5095a1b3626aea80100110210100010bc831c3e55df4a01d1b326e7f4fcbf8f2a36094e0318a533705a48cbbd01a057ca52520950b9366e7513b421082a4877fc6551605a0aa87dfc9e327a5cb9751b5dd05298fc5fc82ba8dcd1a316ed0287c9ffc8d8f9b39b158f0174fd361d627c5ab98eb1be894e5b9cf5510699e06f7fced23ed2af2b8466a624c1dfe739f5b066f2783677d884d5bb6a6bd70a94057def0334f6ab4b7a7e156b3898b1d244e9e85ce835e763e8b12a63cd8933e2272f7e1040b0250ae2dc246c37ac1119616f10aadb6288b1071cf1449c6d30d95b5eaba825cbdab28af0b6e549c4b9e291d49ec8195458d4c2a3d443919fcb1c042c045d2b8b338117aa6a3a8bc6ef964428fc0207fd63247ba14cb261193db438d30201b00800000241de6024a4347b3cb65195cfa43f2c0de4a1cdcf4ee9f86e884194f737133831c970d2cf7a6713b2c1e53691fc93d134a5398796d272358e67
streamplot 640 480 8b511cf775db026cfd11d0d69991f3b2a4c17dfa68f0c353dcdea2f1a007391f2deed5f97b9652023076c7b7eab0c54562b22bf6b83790d416491874ad526497947ab6eb1494826d0dd76b26db659273c53668666d8706cd1f7ed99cb383f61317b28ded290ff913b358e4c129ea51b5141af6e4d373472f04df34b829c01bdaeb63816b52123a2b338e126c578a3b4f961ea413140907a30ea74f8cd36042602e548c435e17818b85bfb79b5c884efab52223bfdf5aa7f2b032f6788e5e4b71468f74d88fae5ce3c304f74e6570b863de63ff9330bde0ac810eb0f326a3d93646572a49dc56879f1bd0cc9a0e6bba3ff16953c1e5eef569a3379df45f9614ce49b0b18393a8cbd6c5105f7e6dfca1e2cc32924711df44aeecef58f6e3e8c135b94d507a5488f4427958f5c0ebef939b0c62408392622570a6cadd0f6c60eb25f1693d9cbd1dc89ac68f5b5957c2cdbeb9b7b86a370b2faa029b80ea77ab93be7fcca7ae41a9a1b24288a989247d3d9ba7aaf42a9a743325b743f0dc96df23f10ca6dd07162e2d8dfc78d2e51df226b059bea507431f18be86fbea273e91a7a0809150dd5b3a353a3f7272664cb8060d568a099c33e1bd35c6b728daf07be7c1efdba1ebee3180aad35cbccdb1df16d18bfea8aa31045b9bac600343c926888677cde16c33a203c8413ab29cc5004aea212b2bd526a1114fe9ac0dfc894404e5
Expand Down
8 changes: 8 additions & 0 deletions tests/test_plots.f90
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,14 @@ program test_plots
call matshow(mz)
call save_all("matshow")

call figure()
call spy(reshape([1.0_dp, 0.0_dp, 0.0_dp, 2.0_dp, 0.0_dp, 3.0_dp], [2, 3]))
call save_all("spy_dense")

call figure()
call spy(reshape([1, 1, 2, 3, 4, 2], [2, 3]), m=5, n=4)
call save_all("spy_coo")

! 70) a row of events
call clf()
do i = 1, 40
Expand Down
Loading