test_contour.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. import datetime
  2. import re
  3. import numpy as np
  4. from matplotlib.testing.decorators import image_comparison
  5. from matplotlib import pyplot as plt
  6. from numpy.testing import assert_array_almost_equal
  7. from matplotlib.colors import LogNorm
  8. import pytest
  9. def test_contour_shape_1d_valid():
  10. x = np.arange(10)
  11. y = np.arange(9)
  12. z = np.random.random((9, 10))
  13. fig, ax = plt.subplots()
  14. ax.contour(x, y, z)
  15. def test_contour_shape_2d_valid():
  16. x = np.arange(10)
  17. y = np.arange(9)
  18. xg, yg = np.meshgrid(x, y)
  19. z = np.random.random((9, 10))
  20. fig, ax = plt.subplots()
  21. ax.contour(xg, yg, z)
  22. @pytest.mark.parametrize("args, message", [
  23. ((np.arange(9), np.arange(9), np.empty((9, 10))),
  24. 'Length of x (9) must match number of columns in z (10)'),
  25. ((np.arange(10), np.arange(10), np.empty((9, 10))),
  26. 'Length of y (10) must match number of rows in z (9)'),
  27. ((np.empty((10, 10)), np.arange(10), np.empty((9, 10))),
  28. 'Number of dimensions of x (2) and y (1) do not match'),
  29. ((np.arange(10), np.empty((10, 10)), np.empty((9, 10))),
  30. 'Number of dimensions of x (1) and y (2) do not match'),
  31. ((np.empty((9, 9)), np.empty((9, 10)), np.empty((9, 10))),
  32. 'Shapes of x (9, 9) and z (9, 10) do not match'),
  33. ((np.empty((9, 10)), np.empty((9, 9)), np.empty((9, 10))),
  34. 'Shapes of y (9, 9) and z (9, 10) do not match'),
  35. ((np.empty((3, 3, 3)), np.empty((3, 3, 3)), np.empty((9, 10))),
  36. 'Inputs x and y must be 1D or 2D, not 3D'),
  37. ((np.empty((3, 3, 3)), np.empty((3, 3, 3)), np.empty((3, 3, 3))),
  38. 'Input z must be 2D, not 3D'),
  39. (([[0]],), # github issue 8197
  40. 'Input z must be at least a (2, 2) shaped array, but has shape (1, 1)'),
  41. (([0], [0], [[0]]),
  42. 'Input z must be at least a (2, 2) shaped array, but has shape (1, 1)'),
  43. ])
  44. def test_contour_shape_error(args, message):
  45. fig, ax = plt.subplots()
  46. with pytest.raises(TypeError, match=re.escape(message)):
  47. ax.contour(*args)
  48. def test_contour_empty_levels():
  49. x = np.arange(9)
  50. z = np.random.random((9, 9))
  51. fig, ax = plt.subplots()
  52. with pytest.warns(UserWarning) as record:
  53. ax.contour(x, x, z, levels=[])
  54. assert len(record) == 1
  55. def test_contour_Nlevels():
  56. # A scalar levels arg or kwarg should trigger auto level generation.
  57. # https://github.com/matplotlib/matplotlib/issues/11913
  58. z = np.arange(12).reshape((3, 4))
  59. fig, ax = plt.subplots()
  60. cs1 = ax.contour(z, 5)
  61. assert len(cs1.levels) > 1
  62. cs2 = ax.contour(z, levels=5)
  63. assert (cs1.levels == cs2.levels).all()
  64. def test_contour_badlevel_fmt():
  65. # Test edge case from https://github.com/matplotlib/matplotlib/issues/9742
  66. # User supplied fmt for each level as a dictionary, but Matplotlib changed
  67. # the level to the minimum data value because no contours possible.
  68. # This was fixed in https://github.com/matplotlib/matplotlib/pull/9743
  69. x = np.arange(9)
  70. z = np.zeros((9, 9))
  71. fig, ax = plt.subplots()
  72. fmt = {1.: '%1.2f'}
  73. with pytest.warns(UserWarning) as record:
  74. cs = ax.contour(x, x, z, levels=[1.])
  75. ax.clabel(cs, fmt=fmt)
  76. assert len(record) == 1
  77. def test_contour_uniform_z():
  78. x = np.arange(9)
  79. z = np.ones((9, 9))
  80. fig, ax = plt.subplots()
  81. with pytest.warns(UserWarning) as record:
  82. ax.contour(x, x, z)
  83. assert len(record) == 1
  84. @image_comparison(['contour_manual_labels'],
  85. savefig_kwarg={'dpi': 200}, remove_text=True, style='mpl20')
  86. def test_contour_manual_labels():
  87. x, y = np.meshgrid(np.arange(0, 10), np.arange(0, 10))
  88. z = np.max(np.dstack([abs(x), abs(y)]), 2)
  89. plt.figure(figsize=(6, 2), dpi=200)
  90. cs = plt.contour(x, y, z)
  91. pts = np.array([(1.5, 3.0), (1.5, 4.4), (1.5, 6.0)])
  92. plt.clabel(cs, manual=pts)
  93. @image_comparison(['contour_labels_size_color.png'],
  94. remove_text=True, style='mpl20')
  95. def test_contour_labels_size_color():
  96. x, y = np.meshgrid(np.arange(0, 10), np.arange(0, 10))
  97. z = np.max(np.dstack([abs(x), abs(y)]), 2)
  98. plt.figure(figsize=(6, 2))
  99. cs = plt.contour(x, y, z)
  100. pts = np.array([(1.5, 3.0), (1.5, 4.4), (1.5, 6.0)])
  101. plt.clabel(cs, manual=pts, fontsize='small', colors=('r', 'g'))
  102. @image_comparison(['contour_manual_colors_and_levels.png'], remove_text=True)
  103. def test_given_colors_levels_and_extends():
  104. _, axs = plt.subplots(2, 4)
  105. data = np.arange(12).reshape(3, 4)
  106. colors = ['red', 'yellow', 'pink', 'blue', 'black']
  107. levels = [2, 4, 8, 10]
  108. for i, ax in enumerate(axs.flat):
  109. filled = i % 2 == 0.
  110. extend = ['neither', 'min', 'max', 'both'][i // 2]
  111. if filled:
  112. # If filled, we have 3 colors with no extension,
  113. # 4 colors with one extension, and 5 colors with both extensions
  114. first_color = 1 if extend in ['max', 'neither'] else None
  115. last_color = -1 if extend in ['min', 'neither'] else None
  116. c = ax.contourf(data, colors=colors[first_color:last_color],
  117. levels=levels, extend=extend)
  118. else:
  119. # If not filled, we have 4 levels and 4 colors
  120. c = ax.contour(data, colors=colors[:-1],
  121. levels=levels, extend=extend)
  122. plt.colorbar(c, ax=ax)
  123. @image_comparison(['contour_datetime_axis.png'],
  124. remove_text=False, style='mpl20')
  125. def test_contour_datetime_axis():
  126. fig = plt.figure()
  127. fig.subplots_adjust(hspace=0.4, top=0.98, bottom=.15)
  128. base = datetime.datetime(2013, 1, 1)
  129. x = np.array([base + datetime.timedelta(days=d) for d in range(20)])
  130. y = np.arange(20)
  131. z1, z2 = np.meshgrid(np.arange(20), np.arange(20))
  132. z = z1 * z2
  133. plt.subplot(221)
  134. plt.contour(x, y, z)
  135. plt.subplot(222)
  136. plt.contourf(x, y, z)
  137. x = np.repeat(x[np.newaxis], 20, axis=0)
  138. y = np.repeat(y[:, np.newaxis], 20, axis=1)
  139. plt.subplot(223)
  140. plt.contour(x, y, z)
  141. plt.subplot(224)
  142. plt.contourf(x, y, z)
  143. for ax in fig.get_axes():
  144. for label in ax.get_xticklabels():
  145. label.set_ha('right')
  146. label.set_rotation(30)
  147. @image_comparison(['contour_test_label_transforms.png'],
  148. remove_text=True, style='mpl20')
  149. def test_labels():
  150. # Adapted from pylab_examples example code: contour_demo.py
  151. # see issues #2475, #2843, and #2818 for explanation
  152. delta = 0.025
  153. x = np.arange(-3.0, 3.0, delta)
  154. y = np.arange(-2.0, 2.0, delta)
  155. X, Y = np.meshgrid(x, y)
  156. Z1 = np.exp(-(X**2 + Y**2) / 2) / (2 * np.pi)
  157. Z2 = (np.exp(-(((X - 1) / 1.5)**2 + ((Y - 1) / 0.5)**2) / 2) /
  158. (2 * np.pi * 0.5 * 1.5))
  159. # difference of Gaussians
  160. Z = 10.0 * (Z2 - Z1)
  161. fig, ax = plt.subplots(1, 1)
  162. CS = ax.contour(X, Y, Z)
  163. disp_units = [(216, 177), (359, 290), (521, 406)]
  164. data_units = [(-2, .5), (0, -1.5), (2.8, 1)]
  165. CS.clabel()
  166. for x, y in data_units:
  167. CS.add_label_near(x, y, inline=True, transform=None)
  168. for x, y in disp_units:
  169. CS.add_label_near(x, y, inline=True, transform=False)
  170. @image_comparison(['contour_corner_mask_False.png',
  171. 'contour_corner_mask_True.png'],
  172. remove_text=True)
  173. def test_corner_mask():
  174. n = 60
  175. mask_level = 0.95
  176. noise_amp = 1.0
  177. np.random.seed([1])
  178. x, y = np.meshgrid(np.linspace(0, 2.0, n), np.linspace(0, 2.0, n))
  179. z = np.cos(7*x)*np.sin(8*y) + noise_amp*np.random.rand(n, n)
  180. mask = np.random.rand(n, n) >= mask_level
  181. z = np.ma.array(z, mask=mask)
  182. for corner_mask in [False, True]:
  183. plt.figure()
  184. plt.contourf(z, corner_mask=corner_mask)
  185. def test_contourf_decreasing_levels():
  186. # github issue 5477.
  187. z = [[0.1, 0.3], [0.5, 0.7]]
  188. plt.figure()
  189. with pytest.raises(ValueError):
  190. plt.contourf(z, [1.0, 0.0])
  191. def test_contourf_symmetric_locator():
  192. # github issue 7271
  193. z = np.arange(12).reshape((3, 4))
  194. locator = plt.MaxNLocator(nbins=4, symmetric=True)
  195. cs = plt.contourf(z, locator=locator)
  196. assert_array_almost_equal(cs.levels, np.linspace(-12, 12, 5))
  197. @pytest.mark.parametrize("args, cls, message", [
  198. ((), TypeError,
  199. 'function takes exactly 6 arguments (0 given)'),
  200. ((1, 2, 3, 4, 5, 6), ValueError,
  201. 'Expected 2-dimensional array, got 0'),
  202. (([[0]], [[0]], [[]], None, True, 0), ValueError,
  203. 'x, y and z must all be 2D arrays with the same dimensions'),
  204. (([[0]], [[0]], [[0]], None, True, 0), ValueError,
  205. 'x, y and z must all be at least 2x2 arrays'),
  206. ((*[np.arange(4).reshape((2, 2))] * 3, [[0]], True, 0), ValueError,
  207. 'If mask is set it must be a 2D array with the same dimensions as x.'),
  208. ])
  209. def test_internal_cpp_api(args, cls, message): # Github issue 8197.
  210. import matplotlib._contour as _contour
  211. with pytest.raises(cls, match=re.escape(message)):
  212. _contour.QuadContourGenerator(*args)
  213. def test_internal_cpp_api_2():
  214. import matplotlib._contour as _contour
  215. arr = [[0, 1], [2, 3]]
  216. qcg = _contour.QuadContourGenerator(arr, arr, arr, None, True, 0)
  217. with pytest.raises(
  218. ValueError, match=r'filled contour levels must be increasing'):
  219. qcg.create_filled_contour(1, 0)
  220. def test_circular_contour_warning():
  221. # Check that almost circular contours don't throw a warning
  222. with pytest.warns(None) as record:
  223. x, y = np.meshgrid(np.linspace(-2, 2, 4), np.linspace(-2, 2, 4))
  224. r = np.hypot(x, y)
  225. plt.figure()
  226. cs = plt.contour(x, y, r)
  227. plt.clabel(cs)
  228. assert len(record) == 0
  229. @image_comparison(['contour_log_extension.png'],
  230. remove_text=True, style='mpl20')
  231. def test_contourf_log_extension():
  232. # Test that contourf with lognorm is extended correctly
  233. fig = plt.figure(figsize=(10, 5))
  234. fig.subplots_adjust(left=0.05, right=0.95)
  235. ax1 = fig.add_subplot(131)
  236. ax2 = fig.add_subplot(132)
  237. ax3 = fig.add_subplot(133)
  238. # make data set with large range e.g. between 1e-8 and 1e10
  239. data_exp = np.linspace(-7.5, 9.5, 1200)
  240. data = np.power(10, data_exp).reshape(30, 40)
  241. # make manual levels e.g. between 1e-4 and 1e-6
  242. levels_exp = np.arange(-4., 7.)
  243. levels = np.power(10., levels_exp)
  244. # original data
  245. c1 = ax1.contourf(data,
  246. norm=LogNorm(vmin=data.min(), vmax=data.max()))
  247. # just show data in levels
  248. c2 = ax2.contourf(data, levels=levels,
  249. norm=LogNorm(vmin=levels.min(), vmax=levels.max()),
  250. extend='neither')
  251. # extend data from levels
  252. c3 = ax3.contourf(data, levels=levels,
  253. norm=LogNorm(vmin=levels.min(), vmax=levels.max()),
  254. extend='both')
  255. cb = plt.colorbar(c1, ax=ax1)
  256. assert cb.ax.get_ylim() == (1e-8, 1e10)
  257. cb = plt.colorbar(c2, ax=ax2)
  258. assert cb.ax.get_ylim() == (1e-4, 1e6)
  259. cb = plt.colorbar(c3, ax=ax3)
  260. assert_array_almost_equal(cb.ax.get_ylim(),
  261. [3.162277660168379e-05, 3162277.660168383], 2)
  262. @image_comparison(['contour_addlines.png'],
  263. remove_text=True, style='mpl20', tol=0.03)
  264. # tolerance is because image changed minutely when tick finding on
  265. # colorbars was cleaned up...
  266. def test_contour_addlines():
  267. fig, ax = plt.subplots()
  268. np.random.seed(19680812)
  269. X = np.random.rand(10, 10)*10000
  270. pcm = ax.pcolormesh(X)
  271. # add 1000 to make colors visible...
  272. cont = ax.contour(X+1000)
  273. cb = fig.colorbar(pcm)
  274. cb.add_lines(cont)
  275. assert_array_almost_equal(cb.ax.get_ylim(), [114.3091, 9972.30735], 3)
  276. @image_comparison(baseline_images=['contour_uneven'],
  277. extensions=['png'], remove_text=True, style='mpl20')
  278. def test_contour_uneven():
  279. z = np.arange(24).reshape(4, 6)
  280. fig, axs = plt.subplots(1, 2)
  281. ax = axs[0]
  282. cs = ax.contourf(z, levels=[2, 4, 6, 10, 20])
  283. fig.colorbar(cs, ax=ax, spacing='proportional')
  284. ax = axs[1]
  285. cs = ax.contourf(z, levels=[2, 4, 6, 10, 20])
  286. fig.colorbar(cs, ax=ax, spacing='uniform')