test_series.py 34 KB

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