test_series.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850
  1. """ Test cases for Series.plot """
  2. from datetime import datetime
  3. from itertools import chain
  4. import numpy as np
  5. import pytest
  6. from pandas.compat import is_platform_linux
  7. from pandas.compat.numpy import np_version_gte1p24
  8. import pandas.util._test_decorators as td
  9. import pandas as pd
  10. from pandas import (
  11. DataFrame,
  12. Series,
  13. date_range,
  14. plotting,
  15. )
  16. import pandas._testing as tm
  17. from pandas.tests.plotting.common import (
  18. TestPlotBase,
  19. _check_plot_works,
  20. )
  21. @pytest.fixture
  22. def ts():
  23. return tm.makeTimeSeries(name="ts")
  24. @pytest.fixture
  25. def series():
  26. return tm.makeStringSeries(name="series")
  27. @pytest.fixture
  28. def iseries():
  29. return tm.makePeriodSeries(name="iseries")
  30. @td.skip_if_no_mpl
  31. class TestSeriesPlots(TestPlotBase):
  32. @pytest.mark.slow
  33. def test_plot(self, ts):
  34. _check_plot_works(ts.plot, label="foo")
  35. _check_plot_works(ts.plot, use_index=False)
  36. axes = _check_plot_works(ts.plot, rot=0)
  37. self._check_ticks_props(axes, xrot=0)
  38. ax = _check_plot_works(ts.plot, style=".", logy=True)
  39. self._check_ax_scales(ax, yaxis="log")
  40. ax = _check_plot_works(ts.plot, style=".", logx=True)
  41. self._check_ax_scales(ax, xaxis="log")
  42. ax = _check_plot_works(ts.plot, style=".", loglog=True)
  43. self._check_ax_scales(ax, xaxis="log", yaxis="log")
  44. _check_plot_works(ts[:10].plot.bar)
  45. _check_plot_works(ts.plot.area, stacked=False)
  46. def test_plot_iseries(self, iseries):
  47. _check_plot_works(iseries.plot)
  48. @pytest.mark.parametrize(
  49. "kind",
  50. [
  51. "line",
  52. "bar",
  53. "barh",
  54. pytest.param("kde", marks=td.skip_if_no_scipy),
  55. "hist",
  56. "box",
  57. ],
  58. )
  59. def test_plot_series_kinds(self, series, kind):
  60. _check_plot_works(series[:5].plot, kind=kind)
  61. def test_plot_series_barh(self, series):
  62. _check_plot_works(series[:10].plot.barh)
  63. def test_plot_series_bar_ax(self):
  64. ax = _check_plot_works(Series(np.random.randn(10)).plot.bar, color="black")
  65. self._check_colors([ax.patches[0]], facecolors=["black"])
  66. def test_plot_6951(self, ts):
  67. # GH 6951
  68. ax = _check_plot_works(ts.plot, subplots=True)
  69. self._check_axes_shape(ax, axes_num=1, layout=(1, 1))
  70. ax = _check_plot_works(ts.plot, subplots=True, layout=(-1, 1))
  71. self._check_axes_shape(ax, axes_num=1, layout=(1, 1))
  72. ax = _check_plot_works(ts.plot, subplots=True, layout=(1, -1))
  73. self._check_axes_shape(ax, axes_num=1, layout=(1, 1))
  74. def test_plot_figsize_and_title(self, series):
  75. # figsize and title
  76. _, ax = self.plt.subplots()
  77. ax = series.plot(title="Test", figsize=(16, 8), ax=ax)
  78. self._check_text_labels(ax.title, "Test")
  79. self._check_axes_shape(ax, axes_num=1, layout=(1, 1), figsize=(16, 8))
  80. def test_dont_modify_rcParams(self):
  81. # GH 8242
  82. key = "axes.prop_cycle"
  83. colors = self.plt.rcParams[key]
  84. _, ax = self.plt.subplots()
  85. Series([1, 2, 3]).plot(ax=ax)
  86. assert colors == self.plt.rcParams[key]
  87. def test_ts_line_lim(self, ts):
  88. fig, ax = self.plt.subplots()
  89. ax = ts.plot(ax=ax)
  90. xmin, xmax = ax.get_xlim()
  91. lines = ax.get_lines()
  92. assert xmin <= lines[0].get_data(orig=False)[0][0]
  93. assert xmax >= lines[0].get_data(orig=False)[0][-1]
  94. tm.close()
  95. ax = ts.plot(secondary_y=True, ax=ax)
  96. xmin, xmax = ax.get_xlim()
  97. lines = ax.get_lines()
  98. assert xmin <= lines[0].get_data(orig=False)[0][0]
  99. assert xmax >= lines[0].get_data(orig=False)[0][-1]
  100. def test_ts_area_lim(self, ts):
  101. _, ax = self.plt.subplots()
  102. ax = ts.plot.area(stacked=False, ax=ax)
  103. xmin, xmax = ax.get_xlim()
  104. line = ax.get_lines()[0].get_data(orig=False)[0]
  105. assert xmin <= line[0]
  106. assert xmax >= line[-1]
  107. self._check_ticks_props(ax, xrot=0)
  108. tm.close()
  109. # GH 7471
  110. _, ax = self.plt.subplots()
  111. ax = ts.plot.area(stacked=False, x_compat=True, ax=ax)
  112. xmin, xmax = ax.get_xlim()
  113. line = ax.get_lines()[0].get_data(orig=False)[0]
  114. assert xmin <= line[0]
  115. assert xmax >= line[-1]
  116. self._check_ticks_props(ax, xrot=30)
  117. tm.close()
  118. tz_ts = ts.copy()
  119. tz_ts.index = tz_ts.tz_localize("GMT").tz_convert("CET")
  120. _, ax = self.plt.subplots()
  121. ax = tz_ts.plot.area(stacked=False, x_compat=True, ax=ax)
  122. xmin, xmax = ax.get_xlim()
  123. line = ax.get_lines()[0].get_data(orig=False)[0]
  124. assert xmin <= line[0]
  125. assert xmax >= line[-1]
  126. self._check_ticks_props(ax, xrot=0)
  127. tm.close()
  128. _, ax = self.plt.subplots()
  129. ax = tz_ts.plot.area(stacked=False, secondary_y=True, ax=ax)
  130. xmin, xmax = ax.get_xlim()
  131. line = ax.get_lines()[0].get_data(orig=False)[0]
  132. assert xmin <= line[0]
  133. assert xmax >= line[-1]
  134. self._check_ticks_props(ax, xrot=0)
  135. def test_area_sharey_dont_overwrite(self, ts):
  136. # GH37942
  137. fig, (ax1, ax2) = self.plt.subplots(1, 2, sharey=True)
  138. abs(ts).plot(ax=ax1, kind="area")
  139. abs(ts).plot(ax=ax2, kind="area")
  140. assert self.get_y_axis(ax1).joined(ax1, ax2)
  141. assert self.get_y_axis(ax2).joined(ax1, ax2)
  142. def test_label(self):
  143. s = Series([1, 2])
  144. _, ax = self.plt.subplots()
  145. ax = s.plot(label="LABEL", legend=True, ax=ax)
  146. self._check_legend_labels(ax, labels=["LABEL"])
  147. self.plt.close()
  148. _, ax = self.plt.subplots()
  149. ax = s.plot(legend=True, ax=ax)
  150. self._check_legend_labels(ax, labels=[""])
  151. self.plt.close()
  152. # get name from index
  153. s.name = "NAME"
  154. _, ax = self.plt.subplots()
  155. ax = s.plot(legend=True, ax=ax)
  156. self._check_legend_labels(ax, labels=["NAME"])
  157. self.plt.close()
  158. # override the default
  159. _, ax = self.plt.subplots()
  160. ax = s.plot(legend=True, label="LABEL", ax=ax)
  161. self._check_legend_labels(ax, labels=["LABEL"])
  162. self.plt.close()
  163. # Add lebel info, but don't draw
  164. _, ax = self.plt.subplots()
  165. ax = s.plot(legend=False, label="LABEL", ax=ax)
  166. assert ax.get_legend() is None # Hasn't been drawn
  167. ax.legend() # draw it
  168. self._check_legend_labels(ax, labels=["LABEL"])
  169. def test_boolean(self):
  170. # GH 23719
  171. s = Series([False, False, True])
  172. _check_plot_works(s.plot, include_bool=True)
  173. msg = "no numeric data to plot"
  174. with pytest.raises(TypeError, match=msg):
  175. _check_plot_works(s.plot)
  176. @pytest.mark.parametrize("index", [None, tm.makeDateIndex(k=4)])
  177. def test_line_area_nan_series(self, index):
  178. values = [1, 2, np.nan, 3]
  179. d = Series(values, index=index)
  180. ax = _check_plot_works(d.plot)
  181. masked = ax.lines[0].get_ydata()
  182. # remove nan for comparison purpose
  183. exp = np.array([1, 2, 3], dtype=np.float64)
  184. tm.assert_numpy_array_equal(np.delete(masked.data, 2), exp)
  185. tm.assert_numpy_array_equal(masked.mask, np.array([False, False, True, False]))
  186. expected = np.array([1, 2, 0, 3], dtype=np.float64)
  187. ax = _check_plot_works(d.plot, stacked=True)
  188. tm.assert_numpy_array_equal(ax.lines[0].get_ydata(), expected)
  189. ax = _check_plot_works(d.plot.area)
  190. tm.assert_numpy_array_equal(ax.lines[0].get_ydata(), expected)
  191. ax = _check_plot_works(d.plot.area, stacked=False)
  192. tm.assert_numpy_array_equal(ax.lines[0].get_ydata(), expected)
  193. def test_line_use_index_false(self):
  194. s = Series([1, 2, 3], index=["a", "b", "c"])
  195. s.index.name = "The Index"
  196. _, ax = self.plt.subplots()
  197. ax = s.plot(use_index=False, ax=ax)
  198. label = ax.get_xlabel()
  199. assert label == ""
  200. _, ax = self.plt.subplots()
  201. ax2 = s.plot.bar(use_index=False, ax=ax)
  202. label2 = ax2.get_xlabel()
  203. assert label2 == ""
  204. @pytest.mark.xfail(
  205. np_version_gte1p24 and is_platform_linux(),
  206. reason="Weird rounding problems",
  207. strict=False,
  208. )
  209. def test_bar_log(self):
  210. expected = np.array([1e-1, 1e0, 1e1, 1e2, 1e3, 1e4])
  211. _, ax = self.plt.subplots()
  212. ax = Series([200, 500]).plot.bar(log=True, ax=ax)
  213. tm.assert_numpy_array_equal(ax.yaxis.get_ticklocs(), expected)
  214. tm.close()
  215. _, ax = self.plt.subplots()
  216. ax = Series([200, 500]).plot.barh(log=True, ax=ax)
  217. tm.assert_numpy_array_equal(ax.xaxis.get_ticklocs(), expected)
  218. tm.close()
  219. # GH 9905
  220. expected = np.array([1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1e0, 1e1])
  221. _, ax = self.plt.subplots()
  222. ax = Series([0.1, 0.01, 0.001]).plot(log=True, kind="bar", ax=ax)
  223. ymin = 0.0007943282347242822
  224. ymax = 0.12589254117941673
  225. res = ax.get_ylim()
  226. tm.assert_almost_equal(res[0], ymin)
  227. tm.assert_almost_equal(res[1], ymax)
  228. tm.assert_numpy_array_equal(ax.yaxis.get_ticklocs(), expected)
  229. tm.close()
  230. _, ax = self.plt.subplots()
  231. ax = Series([0.1, 0.01, 0.001]).plot(log=True, kind="barh", ax=ax)
  232. res = ax.get_xlim()
  233. tm.assert_almost_equal(res[0], ymin)
  234. tm.assert_almost_equal(res[1], ymax)
  235. tm.assert_numpy_array_equal(ax.xaxis.get_ticklocs(), expected)
  236. def test_bar_ignore_index(self):
  237. df = Series([1, 2, 3, 4], index=["a", "b", "c", "d"])
  238. _, ax = self.plt.subplots()
  239. ax = df.plot.bar(use_index=False, ax=ax)
  240. self._check_text_labels(ax.get_xticklabels(), ["0", "1", "2", "3"])
  241. def test_bar_user_colors(self):
  242. s = Series([1, 2, 3, 4])
  243. ax = s.plot.bar(color=["red", "blue", "blue", "red"])
  244. result = [p.get_facecolor() for p in ax.patches]
  245. expected = [
  246. (1.0, 0.0, 0.0, 1.0),
  247. (0.0, 0.0, 1.0, 1.0),
  248. (0.0, 0.0, 1.0, 1.0),
  249. (1.0, 0.0, 0.0, 1.0),
  250. ]
  251. assert result == expected
  252. def test_rotation(self):
  253. df = DataFrame(np.random.randn(5, 5))
  254. # Default rot 0
  255. _, ax = self.plt.subplots()
  256. axes = df.plot(ax=ax)
  257. self._check_ticks_props(axes, xrot=0)
  258. _, ax = self.plt.subplots()
  259. axes = df.plot(rot=30, ax=ax)
  260. self._check_ticks_props(axes, xrot=30)
  261. def test_irregular_datetime(self):
  262. from pandas.plotting._matplotlib.converter import DatetimeConverter
  263. rng = date_range("1/1/2000", "3/1/2000")
  264. rng = rng[[0, 1, 2, 3, 5, 9, 10, 11, 12]]
  265. ser = Series(np.random.randn(len(rng)), rng)
  266. _, ax = self.plt.subplots()
  267. ax = ser.plot(ax=ax)
  268. xp = DatetimeConverter.convert(datetime(1999, 1, 1), "", ax)
  269. ax.set_xlim("1/1/1999", "1/1/2001")
  270. assert xp == ax.get_xlim()[0]
  271. self._check_ticks_props(ax, xrot=30)
  272. def test_unsorted_index_xlim(self):
  273. ser = Series(
  274. [0.0, 1.0, np.nan, 3.0, 4.0, 5.0, 6.0],
  275. index=[1.0, 0.0, 3.0, 2.0, np.nan, 3.0, 2.0],
  276. )
  277. _, ax = self.plt.subplots()
  278. ax = ser.plot(ax=ax)
  279. xmin, xmax = ax.get_xlim()
  280. lines = ax.get_lines()
  281. assert xmin <= np.nanmin(lines[0].get_data(orig=False)[0])
  282. assert xmax >= np.nanmax(lines[0].get_data(orig=False)[0])
  283. def test_pie_series(self):
  284. # if sum of values is less than 1.0, pie handle them as rate and draw
  285. # semicircle.
  286. series = Series(
  287. np.random.randint(1, 5), index=["a", "b", "c", "d", "e"], name="YLABEL"
  288. )
  289. ax = _check_plot_works(series.plot.pie)
  290. self._check_text_labels(ax.texts, series.index)
  291. assert ax.get_ylabel() == "YLABEL"
  292. # without wedge labels
  293. ax = _check_plot_works(series.plot.pie, labels=None)
  294. self._check_text_labels(ax.texts, [""] * 5)
  295. # with less colors than elements
  296. color_args = ["r", "g", "b"]
  297. ax = _check_plot_works(series.plot.pie, colors=color_args)
  298. color_expected = ["r", "g", "b", "r", "g"]
  299. self._check_colors(ax.patches, facecolors=color_expected)
  300. # with labels and colors
  301. labels = ["A", "B", "C", "D", "E"]
  302. color_args = ["r", "g", "b", "c", "m"]
  303. ax = _check_plot_works(series.plot.pie, labels=labels, colors=color_args)
  304. self._check_text_labels(ax.texts, labels)
  305. self._check_colors(ax.patches, facecolors=color_args)
  306. # with autopct and fontsize
  307. ax = _check_plot_works(
  308. series.plot.pie, colors=color_args, autopct="%.2f", fontsize=7
  309. )
  310. pcts = [f"{s*100:.2f}" for s in series.values / series.sum()]
  311. expected_texts = list(chain.from_iterable(zip(series.index, pcts)))
  312. self._check_text_labels(ax.texts, expected_texts)
  313. for t in ax.texts:
  314. assert t.get_fontsize() == 7
  315. # includes negative value
  316. series = Series([1, 2, 0, 4, -1], index=["a", "b", "c", "d", "e"])
  317. with pytest.raises(ValueError, match="pie plot doesn't allow negative values"):
  318. series.plot.pie()
  319. # includes nan
  320. series = Series([1, 2, np.nan, 4], index=["a", "b", "c", "d"], name="YLABEL")
  321. ax = _check_plot_works(series.plot.pie)
  322. self._check_text_labels(ax.texts, ["a", "b", "", "d"])
  323. def test_pie_nan(self):
  324. s = Series([1, np.nan, 1, 1])
  325. _, ax = self.plt.subplots()
  326. ax = s.plot.pie(legend=True, ax=ax)
  327. expected = ["0", "", "2", "3"]
  328. result = [x.get_text() for x in ax.texts]
  329. assert result == expected
  330. def test_df_series_secondary_legend(self):
  331. # GH 9779
  332. df = DataFrame(np.random.randn(30, 3), columns=list("abc"))
  333. s = Series(np.random.randn(30), name="x")
  334. # primary -> secondary (without passing ax)
  335. _, ax = self.plt.subplots()
  336. ax = df.plot(ax=ax)
  337. s.plot(legend=True, secondary_y=True, ax=ax)
  338. # both legends are drawn on left ax
  339. # left and right axis must be visible
  340. self._check_legend_labels(ax, labels=["a", "b", "c", "x (right)"])
  341. assert ax.get_yaxis().get_visible()
  342. assert ax.right_ax.get_yaxis().get_visible()
  343. tm.close()
  344. # primary -> secondary (with passing ax)
  345. _, ax = self.plt.subplots()
  346. ax = df.plot(ax=ax)
  347. s.plot(ax=ax, legend=True, secondary_y=True)
  348. # both legends are drawn on left ax
  349. # left and right axis must be visible
  350. self._check_legend_labels(ax, labels=["a", "b", "c", "x (right)"])
  351. assert ax.get_yaxis().get_visible()
  352. assert ax.right_ax.get_yaxis().get_visible()
  353. tm.close()
  354. # secondary -> secondary (without passing ax)
  355. _, ax = self.plt.subplots()
  356. ax = df.plot(secondary_y=True, ax=ax)
  357. s.plot(legend=True, secondary_y=True, ax=ax)
  358. # both legends are drawn on left ax
  359. # left axis must be invisible and right axis must be visible
  360. expected = ["a (right)", "b (right)", "c (right)", "x (right)"]
  361. self._check_legend_labels(ax.left_ax, labels=expected)
  362. assert not ax.left_ax.get_yaxis().get_visible()
  363. assert ax.get_yaxis().get_visible()
  364. tm.close()
  365. # secondary -> secondary (with passing ax)
  366. _, ax = self.plt.subplots()
  367. ax = df.plot(secondary_y=True, ax=ax)
  368. s.plot(ax=ax, legend=True, secondary_y=True)
  369. # both legends are drawn on left ax
  370. # left axis must be invisible and right axis must be visible
  371. expected = ["a (right)", "b (right)", "c (right)", "x (right)"]
  372. self._check_legend_labels(ax.left_ax, expected)
  373. assert not ax.left_ax.get_yaxis().get_visible()
  374. assert ax.get_yaxis().get_visible()
  375. tm.close()
  376. # secondary -> secondary (with passing ax)
  377. _, ax = self.plt.subplots()
  378. ax = df.plot(secondary_y=True, mark_right=False, ax=ax)
  379. s.plot(ax=ax, legend=True, secondary_y=True)
  380. # both legends are drawn on left ax
  381. # left axis must be invisible and right axis must be visible
  382. expected = ["a", "b", "c", "x (right)"]
  383. self._check_legend_labels(ax.left_ax, expected)
  384. assert not ax.left_ax.get_yaxis().get_visible()
  385. assert ax.get_yaxis().get_visible()
  386. tm.close()
  387. @pytest.mark.parametrize(
  388. "input_logy, expected_scale", [(True, "log"), ("sym", "symlog")]
  389. )
  390. def test_secondary_logy(self, input_logy, expected_scale):
  391. # GH 25545
  392. s1 = Series(np.random.randn(30))
  393. s2 = Series(np.random.randn(30))
  394. # GH 24980
  395. ax1 = s1.plot(logy=input_logy)
  396. ax2 = s2.plot(secondary_y=True, logy=input_logy)
  397. assert ax1.get_yscale() == expected_scale
  398. assert ax2.get_yscale() == expected_scale
  399. def test_plot_fails_with_dupe_color_and_style(self):
  400. x = Series(np.random.randn(2))
  401. _, ax = self.plt.subplots()
  402. msg = (
  403. "Cannot pass 'style' string with a color symbol and 'color' keyword "
  404. "argument. Please use one or the other or pass 'style' without a color "
  405. "symbol"
  406. )
  407. with pytest.raises(ValueError, match=msg):
  408. x.plot(style="k--", color="k", ax=ax)
  409. @td.skip_if_no_scipy
  410. def test_kde_kwargs(self, ts):
  411. sample_points = np.linspace(-100, 100, 20)
  412. _check_plot_works(ts.plot.kde, bw_method="scott", ind=20)
  413. _check_plot_works(ts.plot.kde, bw_method=None, ind=20)
  414. _check_plot_works(ts.plot.kde, bw_method=None, ind=np.int_(20))
  415. _check_plot_works(ts.plot.kde, bw_method=0.5, ind=sample_points)
  416. _check_plot_works(ts.plot.density, bw_method=0.5, ind=sample_points)
  417. _, ax = self.plt.subplots()
  418. ax = ts.plot.kde(logy=True, bw_method=0.5, ind=sample_points, ax=ax)
  419. self._check_ax_scales(ax, yaxis="log")
  420. self._check_text_labels(ax.yaxis.get_label(), "Density")
  421. @td.skip_if_no_scipy
  422. def test_kde_missing_vals(self):
  423. s = Series(np.random.uniform(size=50))
  424. s[0] = np.nan
  425. axes = _check_plot_works(s.plot.kde)
  426. # gh-14821: check if the values have any missing values
  427. assert any(~np.isnan(axes.lines[0].get_xdata()))
  428. @pytest.mark.xfail(reason="Api changed in 3.6.0")
  429. def test_boxplot_series(self, ts):
  430. _, ax = self.plt.subplots()
  431. ax = ts.plot.box(logy=True, ax=ax)
  432. self._check_ax_scales(ax, yaxis="log")
  433. xlabels = ax.get_xticklabels()
  434. self._check_text_labels(xlabels, [ts.name])
  435. ylabels = ax.get_yticklabels()
  436. self._check_text_labels(ylabels, [""] * len(ylabels))
  437. @td.skip_if_no_scipy
  438. @pytest.mark.parametrize(
  439. "kind",
  440. plotting.PlotAccessor._common_kinds + plotting.PlotAccessor._series_kinds,
  441. )
  442. def test_kind_both_ways(self, kind):
  443. s = Series(range(3))
  444. _, ax = self.plt.subplots()
  445. s.plot(kind=kind, ax=ax)
  446. self.plt.close()
  447. _, ax = self.plt.subplots()
  448. getattr(s.plot, kind)()
  449. self.plt.close()
  450. @pytest.mark.parametrize("kind", plotting.PlotAccessor._common_kinds)
  451. def test_invalid_plot_data(self, kind):
  452. s = Series(list("abcd"))
  453. _, ax = self.plt.subplots()
  454. msg = "no numeric data to plot"
  455. with pytest.raises(TypeError, match=msg):
  456. s.plot(kind=kind, ax=ax)
  457. @td.skip_if_no_scipy
  458. @pytest.mark.parametrize("kind", plotting.PlotAccessor._common_kinds)
  459. def test_valid_object_plot(self, kind):
  460. s = Series(range(10), dtype=object)
  461. _check_plot_works(s.plot, kind=kind)
  462. @pytest.mark.parametrize("kind", plotting.PlotAccessor._common_kinds)
  463. def test_partially_invalid_plot_data(self, kind):
  464. s = Series(["a", "b", 1.0, 2])
  465. _, ax = self.plt.subplots()
  466. msg = "no numeric data to plot"
  467. with pytest.raises(TypeError, match=msg):
  468. s.plot(kind=kind, ax=ax)
  469. def test_invalid_kind(self):
  470. s = Series([1, 2])
  471. with pytest.raises(ValueError, match="invalid_kind is not a valid plot kind"):
  472. s.plot(kind="invalid_kind")
  473. def test_dup_datetime_index_plot(self):
  474. dr1 = date_range("1/1/2009", periods=4)
  475. dr2 = date_range("1/2/2009", periods=4)
  476. index = dr1.append(dr2)
  477. values = np.random.randn(index.size)
  478. s = Series(values, index=index)
  479. _check_plot_works(s.plot)
  480. def test_errorbar_asymmetrical(self):
  481. # GH9536
  482. s = Series(np.arange(10), name="x")
  483. err = np.random.rand(2, 10)
  484. ax = s.plot(yerr=err, xerr=err)
  485. result = np.vstack([i.vertices[:, 1] for i in ax.collections[1].get_paths()])
  486. expected = (err.T * np.array([-1, 1])) + s.to_numpy().reshape(-1, 1)
  487. tm.assert_numpy_array_equal(result, expected)
  488. msg = (
  489. "Asymmetrical error bars should be provided "
  490. f"with the shape \\(2, {len(s)}\\)"
  491. )
  492. with pytest.raises(ValueError, match=msg):
  493. s.plot(yerr=np.random.rand(2, 11))
  494. tm.close()
  495. @pytest.mark.slow
  496. def test_errorbar_plot(self):
  497. s = Series(np.arange(10), name="x")
  498. s_err = np.abs(np.random.randn(10))
  499. d_err = DataFrame(
  500. np.abs(np.random.randn(10, 2)), index=s.index, columns=["x", "y"]
  501. )
  502. # test line and bar plots
  503. kinds = ["line", "bar"]
  504. for kind in kinds:
  505. ax = _check_plot_works(s.plot, yerr=Series(s_err), kind=kind)
  506. self._check_has_errorbars(ax, xerr=0, yerr=1)
  507. ax = _check_plot_works(s.plot, yerr=s_err, kind=kind)
  508. self._check_has_errorbars(ax, xerr=0, yerr=1)
  509. ax = _check_plot_works(s.plot, yerr=s_err.tolist(), kind=kind)
  510. self._check_has_errorbars(ax, xerr=0, yerr=1)
  511. ax = _check_plot_works(s.plot, yerr=d_err, kind=kind)
  512. self._check_has_errorbars(ax, xerr=0, yerr=1)
  513. ax = _check_plot_works(s.plot, xerr=0.2, yerr=0.2, kind=kind)
  514. self._check_has_errorbars(ax, xerr=1, yerr=1)
  515. ax = _check_plot_works(s.plot, xerr=s_err)
  516. self._check_has_errorbars(ax, xerr=1, yerr=0)
  517. # test time series plotting
  518. ix = date_range("1/1/2000", "1/1/2001", freq="M")
  519. ts = Series(np.arange(12), index=ix, name="x")
  520. ts_err = Series(np.abs(np.random.randn(12)), index=ix)
  521. td_err = DataFrame(np.abs(np.random.randn(12, 2)), index=ix, columns=["x", "y"])
  522. ax = _check_plot_works(ts.plot, yerr=ts_err)
  523. self._check_has_errorbars(ax, xerr=0, yerr=1)
  524. ax = _check_plot_works(ts.plot, yerr=td_err)
  525. self._check_has_errorbars(ax, xerr=0, yerr=1)
  526. # check incorrect lengths and types
  527. with tm.external_error_raised(ValueError):
  528. s.plot(yerr=np.arange(11))
  529. s_err = ["zzz"] * 10
  530. with tm.external_error_raised(TypeError):
  531. s.plot(yerr=s_err)
  532. @pytest.mark.slow
  533. def test_table(self, series):
  534. _check_plot_works(series.plot, table=True)
  535. _check_plot_works(series.plot, table=series)
  536. @pytest.mark.slow
  537. @td.skip_if_no_scipy
  538. def test_series_grid_settings(self):
  539. # Make sure plot defaults to rcParams['axes.grid'] setting, GH 9792
  540. self._check_grid_settings(
  541. Series([1, 2, 3]),
  542. plotting.PlotAccessor._series_kinds + plotting.PlotAccessor._common_kinds,
  543. )
  544. @pytest.mark.parametrize("c", ["r", "red", "green", "#FF0000"])
  545. def test_standard_colors(self, c):
  546. from pandas.plotting._matplotlib.style import get_standard_colors
  547. result = get_standard_colors(1, color=c)
  548. assert result == [c]
  549. result = get_standard_colors(1, color=[c])
  550. assert result == [c]
  551. result = get_standard_colors(3, color=c)
  552. assert result == [c] * 3
  553. result = get_standard_colors(3, color=[c])
  554. assert result == [c] * 3
  555. def test_standard_colors_all(self):
  556. from matplotlib import colors
  557. from pandas.plotting._matplotlib.style import get_standard_colors
  558. # multiple colors like mediumaquamarine
  559. for c in colors.cnames:
  560. result = get_standard_colors(num_colors=1, color=c)
  561. assert result == [c]
  562. result = get_standard_colors(num_colors=1, color=[c])
  563. assert result == [c]
  564. result = get_standard_colors(num_colors=3, color=c)
  565. assert result == [c] * 3
  566. result = get_standard_colors(num_colors=3, color=[c])
  567. assert result == [c] * 3
  568. # single letter colors like k
  569. for c in colors.ColorConverter.colors:
  570. result = get_standard_colors(num_colors=1, color=c)
  571. assert result == [c]
  572. result = get_standard_colors(num_colors=1, color=[c])
  573. assert result == [c]
  574. result = get_standard_colors(num_colors=3, color=c)
  575. assert result == [c] * 3
  576. result = get_standard_colors(num_colors=3, color=[c])
  577. assert result == [c] * 3
  578. def test_series_plot_color_kwargs(self):
  579. # GH1890
  580. _, ax = self.plt.subplots()
  581. ax = Series(np.arange(12) + 1).plot(color="green", ax=ax)
  582. self._check_colors(ax.get_lines(), linecolors=["green"])
  583. def test_time_series_plot_color_kwargs(self):
  584. # #1890
  585. _, ax = self.plt.subplots()
  586. ax = Series(np.arange(12) + 1, index=date_range("1/1/2000", periods=12)).plot(
  587. color="green", ax=ax
  588. )
  589. self._check_colors(ax.get_lines(), linecolors=["green"])
  590. def test_time_series_plot_color_with_empty_kwargs(self):
  591. import matplotlib as mpl
  592. def_colors = self._unpack_cycler(mpl.rcParams)
  593. index = date_range("1/1/2000", periods=12)
  594. s = Series(np.arange(1, 13), index=index)
  595. ncolors = 3
  596. _, ax = self.plt.subplots()
  597. for i in range(ncolors):
  598. ax = s.plot(ax=ax)
  599. self._check_colors(ax.get_lines(), linecolors=def_colors[:ncolors])
  600. def test_xticklabels(self):
  601. # GH11529
  602. s = Series(np.arange(10), index=[f"P{i:02d}" for i in range(10)])
  603. _, ax = self.plt.subplots()
  604. ax = s.plot(xticks=[0, 3, 5, 9], ax=ax)
  605. exp = [f"P{i:02d}" for i in [0, 3, 5, 9]]
  606. self._check_text_labels(ax.get_xticklabels(), exp)
  607. def test_xtick_barPlot(self):
  608. # GH28172
  609. s = Series(range(10), index=[f"P{i:02d}" for i in range(10)])
  610. ax = s.plot.bar(xticks=range(0, 11, 2))
  611. exp = np.array(list(range(0, 11, 2)))
  612. tm.assert_numpy_array_equal(exp, ax.get_xticks())
  613. def test_custom_business_day_freq(self):
  614. # GH7222
  615. from pandas.tseries.offsets import CustomBusinessDay
  616. s = Series(
  617. range(100, 121),
  618. index=pd.bdate_range(
  619. start="2014-05-01",
  620. end="2014-06-01",
  621. freq=CustomBusinessDay(holidays=["2014-05-26"]),
  622. ),
  623. )
  624. _check_plot_works(s.plot)
  625. @pytest.mark.xfail(
  626. reason="GH#24426, see also "
  627. "github.com/pandas-dev/pandas/commit/"
  628. "ef1bd69fa42bbed5d09dd17f08c44fc8bfc2b685#r61470674"
  629. )
  630. def test_plot_accessor_updates_on_inplace(self):
  631. ser = Series([1, 2, 3, 4])
  632. _, ax = self.plt.subplots()
  633. ax = ser.plot(ax=ax)
  634. before = ax.xaxis.get_ticklocs()
  635. ser.drop([0, 1], inplace=True)
  636. _, ax = self.plt.subplots()
  637. after = ax.xaxis.get_ticklocs()
  638. tm.assert_numpy_array_equal(before, after)
  639. @pytest.mark.parametrize("kind", ["line", "area"])
  640. def test_plot_xlim_for_series(self, kind):
  641. # test if xlim is also correctly plotted in Series for line and area
  642. # GH 27686
  643. s = Series([2, 3])
  644. _, ax = self.plt.subplots()
  645. s.plot(kind=kind, ax=ax)
  646. xlims = ax.get_xlim()
  647. assert xlims[0] < 0
  648. assert xlims[1] > 1
  649. def test_plot_no_rows(self):
  650. # GH 27758
  651. df = Series(dtype=int)
  652. assert df.empty
  653. ax = df.plot()
  654. assert len(ax.get_lines()) == 1
  655. line = ax.get_lines()[0]
  656. assert len(line.get_xdata()) == 0
  657. assert len(line.get_ydata()) == 0
  658. def test_plot_no_numeric_data(self):
  659. df = Series(["a", "b", "c"])
  660. with pytest.raises(TypeError, match="no numeric data to plot"):
  661. df.plot()
  662. @pytest.mark.parametrize(
  663. "data, index",
  664. [
  665. ([1, 2, 3, 4], [3, 2, 1, 0]),
  666. ([10, 50, 20, 30], [1910, 1920, 1980, 1950]),
  667. ],
  668. )
  669. def test_plot_order(self, data, index):
  670. # GH38865 Verify plot order of a Series
  671. ser = Series(data=data, index=index)
  672. ax = ser.plot(kind="bar")
  673. expected = ser.tolist()
  674. result = [
  675. patch.get_bbox().ymax
  676. for patch in sorted(ax.patches, key=lambda patch: patch.get_bbox().xmax)
  677. ]
  678. assert expected == result
  679. def test_style_single_ok(self):
  680. s = Series([1, 2])
  681. ax = s.plot(style="s", color="C3")
  682. assert ax.lines[0].get_color() == "C3"
  683. @pytest.mark.parametrize(
  684. "index_name, old_label, new_label",
  685. [(None, "", "new"), ("old", "old", "new"), (None, "", "")],
  686. )
  687. @pytest.mark.parametrize("kind", ["line", "area", "bar", "barh", "hist"])
  688. def test_xlabel_ylabel_series(self, kind, index_name, old_label, new_label):
  689. # GH 9093
  690. ser = Series([1, 2, 3, 4])
  691. ser.index.name = index_name
  692. # default is the ylabel is not shown and xlabel is index name (reverse for barh)
  693. ax = ser.plot(kind=kind)
  694. if kind == "barh":
  695. assert ax.get_xlabel() == ""
  696. assert ax.get_ylabel() == old_label
  697. elif kind == "hist":
  698. assert ax.get_xlabel() == ""
  699. assert ax.get_ylabel() == "Frequency"
  700. else:
  701. assert ax.get_ylabel() == ""
  702. assert ax.get_xlabel() == old_label
  703. # old xlabel will be overridden and assigned ylabel will be used as ylabel
  704. ax = ser.plot(kind=kind, ylabel=new_label, xlabel=new_label)
  705. assert ax.get_ylabel() == new_label
  706. assert ax.get_xlabel() == new_label
  707. @pytest.mark.parametrize(
  708. "index",
  709. [
  710. pd.timedelta_range(start=0, periods=2, freq="D"),
  711. [pd.Timedelta(days=1), pd.Timedelta(days=2)],
  712. ],
  713. )
  714. def test_timedelta_index(self, index):
  715. # GH37454
  716. xlims = (3, 1)
  717. ax = Series([1, 2], index=index).plot(xlim=(xlims))
  718. assert ax.get_xlim() == (3, 1)