diff --git a/README.md b/README.md index 9a48115a2..cfaf8d587 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ Key features: with an interface similar in feel and style to the widely used [pandas.DataFrame](https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.html) - Advanced visualization and plotting functions - (see the [gallery](https://pyam-iamc.readthedocs.io/en/stable/examples/index.html)) + (see the [gallery](https://pyam-iamc.readthedocs.io/en/stable/gallery/index.html)) - Diagnostic checks for scripted validation of scenario data and results Data model diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 8e8c1e03a..584ecf5c7 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -2,17 +2,21 @@ ## API changes -Several PRs in this release changed the name of plotting functions -for consistency with matplotlib and seaborn. +Several PRs in this release changed the implementation of the plotting library +for better UX and consistency with **pandas**, **matplotlib** and **seaborn**. - - `stackplot()` for `stack_plot()` - - `barplot()` for `bar_plot()` +Replace the calls to plotting features by the following: + - `plot(...)` (or `plot(kind='line', ...)`) for `line_plot()` + - `plot.stack(...)` for `stack_plot()` + - `plot.bar()` for `bar_plot()` + - ... -These PR also adds an `order` arg to the plotting functions, and the levels +These PRs also add an `order` arg to the plotting functions, and the levels are ordered based on the `run_control()['order']` dictionary by default. ## Individual updates +- [#473](https://github.com/IAMconsortium/pyam/pull/473) Refactor to plotting API following pandas/matplotlib implementation - [#472](https://github.com/IAMconsortium/pyam/pull/472) Add a ´sankey()` example to the plotting gallery - [#470](https://github.com/IAMconsortium/pyam/pull/470) Add two types of `order` arg to `barplot()` - [#467](https://github.com/IAMconsortium/pyam/pull/467) Refactor the GAMS-pyam tutorial to use the gamstransfer module diff --git a/doc/source/.gitignore b/doc/source/.gitignore index fb0ea69ad..9ffffd16b 100644 --- a/doc/source/.gitignore +++ b/doc/source/.gitignore @@ -1,4 +1,5 @@ -examples +# exclude built gallery folder (built by sphinx-gallery module) +gallery modules #* .#* diff --git a/doc/source/api.rst b/doc/source/api.rst index 8678795b8..f77d17cfd 100644 --- a/doc/source/api.rst +++ b/doc/source/api.rst @@ -1,6 +1,6 @@ .. currentmodule:: pyam -API reference +API Reference ============= This page gives an overview of the public |pyam| features, objects, functions diff --git a/doc/source/api/plotting.rst b/doc/source/api/plotting.rst index 41b958692..a119d6061 100644 --- a/doc/source/api/plotting.rst +++ b/doc/source/api/plotting.rst @@ -3,6 +3,39 @@ Plotting library ================ +The plotting API(s) +------------------- + +There are three ways to use the |pyam| plotting library. + +1. Using the plot feature as an attribute of the :class:`IamDataFrame`: + + .. code-block:: python + + IamDataFrame.plot.(**kwargs) + +2. Using the plot feature as a function with a `kind` keyword argument: + + .. code-block:: python + + IamDataFrame.plot(kind='', **kwargs) + + This function defaults to the :meth:`pyam.plotting.line` type. + +3. Calling any function of either the :mod:`plotting` + or the :mod:`figures` module directly via + + .. code-block:: python + + pyam..(df, **kwargs) + + where `df` is either an :class:`IamDataFrame` + or a suitable :class:`pandas.DataFrame`. + +Check out the `Plotting Gallery`_ for examples! + +.. _`Plotting Gallery` : ../gallery/index.html + The RunControl configuration ---------------------------- @@ -27,7 +60,7 @@ or as the path to a yaml file with a similar structure: pyam.run_control().update() -See `this example`_ from the AR6 WG1 work using |pyam| plotting. +See `this example`_ from the AR6 WG1 using |pyam| plotting via a `yaml` file. .. _`this example`: https://github.com/gidden/ar6-wg1-ch6-emissions/blob/master/plotting.yaml @@ -37,6 +70,16 @@ to the RunControl. Plotting functions ------------------ -.. autofunction:: pyam.plotting.stackplot +.. autofunction:: pyam.plotting.line + +.. autofunction:: pyam.plotting.stack + +.. autofunction:: pyam.plotting.bar + +.. autofunction:: pyam.plotting.box + +.. autofunction:: pyam.plotting.scatter + +.. autofunction:: pyam.plotting.pie .. autofunction:: pyam.figures.sankey diff --git a/doc/source/conf.py b/doc/source/conf.py index 022f8ef8f..9a4661cc5 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -320,6 +320,7 @@ 'pint': ('https://pint.readthedocs.io/en/stable', None), 'ixmp': ('https://docs.messageix.org/projects/ixmp/en/stable/', None), 'matplotlib': ('https://matplotlib.org', None), + 'plotly': ('https://plotly.com/python-api-reference/', None), 'pandas_datareader': ('https://pandas-datareader.readthedocs.io/en/stable', None) } @@ -329,19 +330,11 @@ sphinx_gallery_conf = { 'doc_module': ('plotly',), - # path to your examples scripts - 'examples_dirs': 'examples_source', + # path to your example scripts + 'examples_dirs': 'examples', # path where to save gallery generated examples - 'gallery_dirs': 'examples', - 'subsection_order': - ExplicitOrder(['../examples/plot_timeseries', - '../examples/plot_ranges', - '../examples/plot_stack', - '../examples/plot_bar', - '../examples/plot_boxplot', - '../examples/plot_pie', - '../examples/plot_sankey']), - 'reference_url': {'plotly': None,}, + 'gallery_dirs': 'gallery', + 'reference_url': {'plotly': None, 'matplotlib': None, 'pyam': None}, 'image_scrapers': image_scrapers, } diff --git a/doc/source/examples_source/README.txt b/doc/source/examples/README.txt similarity index 100% rename from doc/source/examples_source/README.txt rename to doc/source/examples/README.txt diff --git a/doc/source/examples_source/plot_bar.py b/doc/source/examples/plot_bar.py similarity index 86% rename from doc/source/examples_source/plot_bar.py rename to doc/source/examples/plot_bar.py index 55c139342..3ec3742ee 100644 --- a/doc/source/examples_source/plot_bar.py +++ b/doc/source/examples/plot_bar.py @@ -35,7 +35,7 @@ args = dict(model='WITCH-GLOBIOM 4.4', scenario='CD-LINKS_NPi2020_1000') data = df.filter(**args, variable='Primary Energy|*', region='World') -data.barplot(stacked=True, title='Primary energy mix') +data.plot.bar(stacked=True, title='Primary energy mix') plt.legend(loc=1) plt.tight_layout() plt.show() @@ -46,7 +46,7 @@ # # We can flip that round for a horizontal chart. -data.barplot(stacked=True, orient='h', title='Primary energy mix') +data.plot.bar(stacked=True, orient='h', title='Primary energy mix') plt.legend(loc=1) plt.tight_layout() plt.show() @@ -64,8 +64,8 @@ .filter(region='World', keep=False) ) -data.barplot(bars='region', stacked=True, - title='CO2 emissions by region', cmap='tab20') +data.plot.bar(bars='region', stacked=True, + title='CO2 emissions by region', cmap='tab20') plt.legend(loc=1) plt.tight_layout() plt.show() @@ -80,8 +80,8 @@ from pyam.plotting import add_net_values_to_barplot fig, ax = plt.subplots() -data.barplot(ax=ax, bars='region', stacked=True, - title='CO2 emissions by region', cmap='tab20') +data.plot.bar(ax=ax, bars='region', stacked=True, + title='CO2 emissions by region', cmap='tab20') add_net_values_to_barplot(ax) plt.legend(loc=1) plt.tight_layout() diff --git a/doc/source/examples_source/plot_boxplot.py b/doc/source/examples/plot_boxplot.py similarity index 95% rename from doc/source/examples_source/plot_boxplot.py rename to doc/source/examples/plot_boxplot.py index 2ab636416..c8829659e 100644 --- a/doc/source/examples_source/plot_boxplot.py +++ b/doc/source/examples/plot_boxplot.py @@ -33,7 +33,7 @@ data = df.filter(scenario='CD-LINKS_NPi2020_1000', variable='Emissions|CO2', region='World') -data.boxplot(x='year') +data.plot.box(x='year') plt.tight_layout() plt.show() @@ -49,7 +49,7 @@ .filter(region='World', keep=False) ) -data.boxplot(x='year', by='region', legend=True) +data.plot.box(x='year', by='region', legend=True) # We can use matplotlib arguments to make the figure more appealing. plt.legend(loc=1) diff --git a/doc/source/examples_source/plot_pie.py b/doc/source/examples/plot_pie.py similarity index 94% rename from doc/source/examples_source/plot_pie.py rename to doc/source/examples/plot_pie.py index fbd7957ea..00584b48b 100644 --- a/doc/source/examples_source/plot_pie.py +++ b/doc/source/examples/plot_pie.py @@ -34,7 +34,7 @@ variable='Primary Energy|*', year=2050, region='World') -data.pie_plot() +data.plot.pie() plt.tight_layout() plt.show() @@ -44,7 +44,7 @@ # # Sometimes a legend is preferable to labels, so we can use that instead. -data.pie_plot(labels=None, legend=True) +data.plot.pie(labels=None, legend=True) plt.tight_layout() plt.show() @@ -61,6 +61,6 @@ variable='Emissions|CO2', year=2050) .filter(region='World', keep=False) ) -data.pie_plot(category='region', cmap='tab20') +data.plot.pie(category='region', cmap='tab20') plt.tight_layout() plt.show() diff --git a/doc/source/examples_source/plot_ranges.py b/doc/source/examples/plot_ranges.py similarity index 89% rename from doc/source/examples_source/plot_ranges.py rename to doc/source/examples/plot_ranges.py index 7deead5b3..ffde6e5ec 100644 --- a/doc/source/examples_source/plot_ranges.py +++ b/doc/source/examples/plot_ranges.py @@ -33,7 +33,7 @@ data = df.filter(scenario='CD-LINKS*', variable='Emissions|CO2', region='World') -data.line_plot(color='scenario', fill_between=True) +data.plot(color='scenario', fill_between=True) plt.tight_layout() plt.show() @@ -45,7 +45,7 @@ # or it can be provided specific arguments as a dictionary: # in this illustration, we choose a very low transparency value. -data.line_plot(color='scenario', fill_between=dict(alpha=0.15)) +data.plot(color='scenario', fill_between=dict(alpha=0.15)) plt.tight_layout() plt.show() @@ -57,7 +57,6 @@ # range of data in the final time period using `final_ranges`. Similar to # `fill_between` it can either be true or have specific arguments. -data.line_plot(color='scenario', fill_between=True, - final_ranges=dict(linewidth=5)) +data.plot(color='scenario', fill_between=True, final_ranges=dict(linewidth=5)) plt.tight_layout() plt.show() diff --git a/doc/source/examples_source/plot_sankey.py b/doc/source/examples/plot_sankey.py similarity index 97% rename from doc/source/examples_source/plot_sankey.py rename to doc/source/examples/plot_sankey.py index 31f9fa4bc..f7ad843a9 100644 --- a/doc/source/examples_source/plot_sankey.py +++ b/doc/source/examples/plot_sankey.py @@ -70,6 +70,6 @@ ('Gas Network & Power Generation', 'Gas Demand'), } -fig = df.filter(year=2050).sankey(mapping=sankey_mapping) +fig = df.filter(year=2050).plot.sankey(mapping=sankey_mapping) # calling `show()` is necessary to have the thumbnail in the gallery overview plotly.io.show(fig) diff --git a/doc/source/examples_source/plot_stack.py b/doc/source/examples/plot_stack.py similarity index 92% rename from doc/source/examples_source/plot_stack.py rename to doc/source/examples/plot_stack.py index 7738df971..f63124284 100644 --- a/doc/source/examples_source/plot_stack.py +++ b/doc/source/examples/plot_stack.py @@ -32,7 +32,7 @@ data = df.filter(model=model, scenario=scenario, variable='Primary Energy|*', region='World') -data.stackplot(title=scenario) +data.plot.stack(title=scenario) plt.legend(loc=1) plt.tight_layout() plt.show() @@ -47,8 +47,8 @@ .filter(region='World', keep=False) ) -data.stackplot(stack='region', cmap='tab20', - title=scenario, total=True) +data.plot.stack(stack='region', cmap='tab20', + title=scenario, total=True) plt.legend(loc=1) plt.tight_layout() plt.show() diff --git a/doc/source/examples_source/plot_timeseries.py b/doc/source/examples/plot_timeseries.py similarity index 94% rename from doc/source/examples_source/plot_timeseries.py rename to doc/source/examples/plot_timeseries.py index e40815d43..936fe697e 100644 --- a/doc/source/examples_source/plot_timeseries.py +++ b/doc/source/examples/plot_timeseries.py @@ -36,5 +36,5 @@ .filter(region='World', keep=False) ) -data.line_plot(color='region', title='CO2 emissions by region') +data.plot(color='region', title='CO2 emissions by region') data.timeseries() diff --git a/doc/source/examples_source/sankey_data.csv b/doc/source/examples/sankey_data.csv similarity index 100% rename from doc/source/examples_source/sankey_data.csv rename to doc/source/examples/sankey_data.csv diff --git a/doc/source/examples_source/tutorial_data.csv b/doc/source/examples/tutorial_data.csv similarity index 100% rename from doc/source/examples_source/tutorial_data.csv rename to doc/source/examples/tutorial_data.csv diff --git a/doc/source/index.rst b/doc/source/index.rst index d5f3d7c9e..f729659da 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -65,7 +65,7 @@ The source code for |pyam| is available on `Github`_. data.html .. _`gallery`: - examples/index.html + gallery/index.html .. _`Github`: https://github.com/IAMconsortium/pyam @@ -81,7 +81,7 @@ Table of Contents contributing data tutorials - examples/index + gallery/index api Copyright & License diff --git a/doc/source/tutorials/GAMS_to_pyam.ipynb b/doc/source/tutorials/GAMS_to_pyam.ipynb index 98c783bea..d795dc8ad 100644 --- a/doc/source/tutorials/GAMS_to_pyam.ipynb +++ b/doc/source/tutorials/GAMS_to_pyam.ipynb @@ -914,14 +914,14 @@ } ], "source": [ - "df.filter(variable='supply|*').bar_plot(x='region')" + "df.filter(variable='supply|*').plot.bar(x='region')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Visit the **pyam** [plotting gallery](https://pyam-iamc.readthedocs.io/en/stable/examples/index.html)\n", + "Visit the **pyam** [plotting gallery](https://pyam-iamc.readthedocs.io/en/stable/gallery/index.html)\n", "for more features and options!" ] }, diff --git a/doc/source/tutorials/aggregating_variables_and_plotting_with_negative_values.ipynb b/doc/source/tutorials/aggregating_variables_and_plotting_with_negative_values.ipynb index f3c8724c9..0372709e6 100644 --- a/doc/source/tutorials/aggregating_variables_and_plotting_with_negative_values.ipynb +++ b/doc/source/tutorials/aggregating_variables_and_plotting_with_negative_values.ipynb @@ -64,7 +64,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Pyam's `stack_plot` method plots the stacks in the clearest way possible, even when some emissions are negative. The optional `total` keyword arguments also allows the user to include a total line on their plot." + "Pyam's `stackplot` method plots the stacks in the clearest way possible, even when some emissions are negative. The optional `total` keyword arguments also allows the user to include a total line on their plot." ] }, { @@ -73,7 +73,7 @@ "metadata": {}, "outputs": [], "source": [ - "df.stack_plot();" + "df.plot.stack();" ] }, { @@ -82,7 +82,7 @@ "metadata": {}, "outputs": [], "source": [ - "df.stack_plot(total=True);" + "df.plot.stack(total=True);" ] }, { @@ -98,7 +98,7 @@ "metadata": {}, "outputs": [], "source": [ - "df.stack_plot(alpha=0.5, total={\"color\": \"grey\", \"ls\": \"--\", \"lw\": 2.0});" + "df.plot.stack(alpha=0.5, total={\"color\": \"grey\", \"ls\": \"--\", \"lw\": 2.0});" ] }, { @@ -114,7 +114,7 @@ "metadata": {}, "outputs": [], "source": [ - "df.filter(variable=\"Emissions|CO2|Energy*\").stack_plot(total=True);" + "df.filter(variable=\"Emissions|CO2|Energy*\").plot.stack(total=True);" ] }, { @@ -132,7 +132,7 @@ "source": [ "pdf = df.copy()\n", "afoluluc_vars = [\"Emissions|CO2|LUC\", \"Emissions|CO2|Agg\"]\n", - "fossil_vars = list(set(pdf.variables()) - set(afoluluc_vars))\n", + "fossil_vars = list(set(pdf.variable) - set(afoluluc_vars))\n", "pdf.aggregate(\n", " \"Emissions|CO2|AFOLULUC\", \n", " components=afoluluc_vars, \n", @@ -146,7 +146,7 @@ "pdf.filter(variable=[\n", " \"Emissions|CO2|AFOLULUC\",\n", " \"Emissions|CO2|Fossil\"\n", - "]).stack_plot(total=True);" + "]).plot.stack(total=True);" ] }, { @@ -227,8 +227,8 @@ " variable=\"Emissions|CO2|AFOLU\"\n", ").filter(\n", " region=\"World\", keep=False\n", - ").stack_plot(stack=\"region\", ax=ax)\n", - "df.filter(variable=\"Emissions|CO2|AFOLU\", region=\"World\").line_plot(ax=ax, color=\"black\");" + ").plot.stack(stack=\"region\", ax=ax)\n", + "df.filter(variable=\"Emissions|CO2|AFOLU\", region=\"World\").plot(ax=ax, color=\"black\");" ] }, { @@ -274,8 +274,8 @@ " variable=\"Emissions|CO2|Fossil\"\n", ").filter(\n", " region=\"World\", keep=False\n", - ").stack_plot(stack=\"region\", ax=ax)\n", - "df.filter(variable=\"Emissions|CO2|Fossil\", region=\"World\").line_plot(ax=ax, color=\"black\");" + ").plot.stack(stack=\"region\", ax=ax)\n", + "df.filter(variable=\"Emissions|CO2|Fossil\", region=\"World\").plot(ax=ax, color=\"black\");" ] }, { diff --git a/doc/source/tutorials/iiasa_dbs.ipynb b/doc/source/tutorials/iiasa_dbs.ipynb index bb31d6710..b4389f3ab 100644 --- a/doc/source/tutorials/iiasa_dbs.ipynb +++ b/doc/source/tutorials/iiasa_dbs.ipynb @@ -118,7 +118,7 @@ "metadata": {}, "outputs": [], "source": [ - "ax = df.filter(variable='Emissions|CO2').line_plot(\n", + "ax = df.filter(variable='Emissions|CO2').plot(\n", " color='category', \n", " legend=dict(loc='center left', bbox_to_anchor=(1.0, 0.5))\n", ")" @@ -262,7 +262,7 @@ "metadata": {}, "outputs": [], "source": [ - "ax = df.filter(variable='Primary Energy|Coal').line_plot(\n", + "ax = df.filter(variable='Primary Energy|Coal').plot(\n", " color='scenario', \n", " legend=dict(loc='center left', bbox_to_anchor=(1.0, 0.5))\n", ")" diff --git a/doc/source/tutorials/legends.ipynb b/doc/source/tutorials/legends.ipynb index 9568c626f..3e9ebbcf5 100644 --- a/doc/source/tutorials/legends.ipynb +++ b/doc/source/tutorials/legends.ipynb @@ -47,7 +47,7 @@ "metadata": {}, "outputs": [], "source": [ - "df.line_plot()" + "df.plot()" ] }, { @@ -64,7 +64,7 @@ "metadata": {}, "outputs": [], "source": [ - "df.line_plot(color='model')" + "df.plot(color='model')" ] }, { @@ -80,7 +80,7 @@ "metadata": {}, "outputs": [], "source": [ - "df.line_plot(color='model', legend=dict(loc='center left'))" + "df.plot(color='model', legend=dict(loc='center left'))" ] }, { @@ -105,7 +105,7 @@ "metadata": {}, "outputs": [], "source": [ - "df.line_plot(color='model', legend=OUTSIDE_LEGEND['right'])" + "df.plot(color='model', legend=OUTSIDE_LEGEND['right'])" ] }, { @@ -114,7 +114,7 @@ "metadata": {}, "outputs": [], "source": [ - "df.line_plot(color='model', legend=OUTSIDE_LEGEND['bottom'])" + "df.plot(color='model', legend=OUTSIDE_LEGEND['bottom'])" ] } ], diff --git a/doc/source/tutorials/pyam_first_steps.ipynb b/doc/source/tutorials/pyam_first_steps.ipynb index 2844f7a3c..dacbfd0d2 100644 --- a/doc/source/tutorials/pyam_first_steps.ipynb +++ b/doc/source/tutorials/pyam_first_steps.ipynb @@ -429,7 +429,7 @@ "metadata": {}, "outputs": [], "source": [ - "df.filter(variable='Emissions|CO2', region='World').line_plot()" + "df.filter(variable='Emissions|CO2', region='World').plot()" ] }, { @@ -446,14 +446,14 @@ "metadata": {}, "outputs": [], "source": [ - "df.filter(variable='Emissions|CO2', region='World').line_plot(color='scenario')" + "df.filter(variable='Emissions|CO2', region='World').plot(color='scenario')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "The section on categorization will show more options of the plotting features, as well as a method to set specific colors for different categories. For more information, look at the other tutorials and the [plotting gallery](https://pyam-iamc.readthedocs.io/en/stable/examples/index.html)." + "The section on categorization will show more options of the plotting features, as well as a method to set specific colors for different categories. For more information, look at the other tutorials and the [plotting gallery](https://pyam-iamc.readthedocs.io/en/stable/gallery/index.html)." ] }, { @@ -571,7 +571,7 @@ "metadata": {}, "outputs": [], "source": [ - "df_world.filter(variable='Emissions|CO2').line_plot()" + "df_world.filter(variable='Emissions|CO2').plot()" ] }, { @@ -618,8 +618,8 @@ "\n", "df_world_co2 = df_world.filter(variable='Emissions|CO2')\n", "\n", - "df_world_co2.line_plot(ax=ax[0])\n", - "df_world_co2.filter(exclude=False).line_plot(ax=ax[1])" + "df_world_co2.plot(ax=ax[0])\n", + "df_world_co2.filter(exclude=False).plot(ax=ax[1])" ] }, { @@ -676,7 +676,7 @@ "metadata": {}, "outputs": [], "source": [ - "df.filter(variable='Temperature').line_plot()" + "df.filter(variable='Temperature').plot()" ] }, { diff --git a/pyam/core.py b/pyam/core.py index 0bc82ed75..aa3a0927f 100755 --- a/pyam/core.py +++ b/pyam/core.py @@ -25,7 +25,6 @@ except (ImportError, AttributeError): has_ix = False -from pyam import figures from pyam import plotting from pyam.run_control import run_control from pyam.utils import ( @@ -54,7 +53,7 @@ ) from pyam.read_ixmp import read_ix from pyam.timeseries import fill_series -from pyam.plotting import mpl_args_to_meta_cols +from pyam.plotting import PlotAccessor, mpl_args_to_meta_cols from pyam._aggregate import _aggregate, _aggregate_region, _aggregate_time,\ _aggregate_recursive, _group_and_agg from pyam.units import convert_unit @@ -184,6 +183,9 @@ def _init(self, data, meta=None, **kwargs): if 'exec' in run_control(): self._execute_run_control() + # add the `plot` handler + self.plot = PlotAccessor(self) + def __getitem__(self, key): _key_check = [key] if isstr(key) else key if set(_key_check).issubset(self.meta.columns): @@ -1725,11 +1727,14 @@ def load_meta(self, path, *args, **kwargs): # set column `exclude` to bool self.meta.exclude = self.meta.exclude.astype('bool') - def line_plot(self, x='year', y='value', **kwargs): - """Plot timeseries lines of existing data + def line_plot(self, *args, **kwargs): + """Deprecated, please use `IamDataFrame.plot()`""" + deprecation_warning('Please use `IamDataFrame.plot()`.') + return self.plot(*args, **kwargs) - see pyam.plotting.line_plot() for all available options - """ + def _line_plot(self, x='year', y='value', **kwargs): + """Plot timeseries lines of existing data""" + # TODO merge with `plot.line` and deprecate df = self.as_pandas(meta_cols=mpl_args_to_meta_cols(self, **kwargs)) # pivot data if asked for explicit variable name @@ -1750,68 +1755,37 @@ def line_plot(self, x='year', y='value', **kwargs): if x != 'year' and y != 'year': df = df.drop('year', axis=1) # years causes nan's - ax, handles, labels = plotting.line_plot( - df.dropna(), x=x, y=y, **kwargs) + ax, handles, labels = plotting.line(df.dropna(), x=x, y=y, **kwargs) return ax - def stackplot(self, *args, **kwargs): - """Plot a stacked area chart of timeseries data - - See `pyam.plotting.stackplot `_ - for details. - """ - return plotting.stackplot(self, *args, **kwargs) - def stack_plot(self, *args, **kwargs): - """Deprecated, please use `IamDataFrame.stackplot()`""" - deprecation_warning('Please use `stackplot()`.') - return self.stackplot(*args, **kwargs) - - def barplot(self, *args, **kwargs): - """Plot a grouped or stacked bar chart - - See `pyam.plotting.barplot `_ - for details. - """ - return plotting.barplot(self, *args, **kwargs) + """Deprecated, please use `IamDataFrame.plot.stack()`""" + deprecation_warning('Please use `IamDataFrame.plot.stack()`.') + return self.plot.stack(*args, **kwargs) def bar_plot(self, *args, **kwargs): - """Deprecated, please use `IamDataFrame.barplot()`""" - deprecation_warning('Please use `barplot()`.') - return self.barplot(*args, **kwargs) + """Deprecated, please use `IamDataFrame.plot.bar()`""" + deprecation_warning('Please use `plot.bar()`.') + return self.plot.bar(*args, **kwargs) def boxplot(self, *args, **kwargs): - """Plot boxplot of existing data - - see pyam.plotting.boxplot() for all available options - """ - df = self.as_pandas() - ax = plotting.boxplot(df, *args, **kwargs) - return ax + """Deprecated, please use `IamDataFrame.plot.box()`""" + deprecation_warning('Please use `IamDataFrame.plot.box()`.') + return self.plot.box(**kwargs) def pie_plot(self, *args, **kwargs): - """Plot a pie chart - - see pyam.plotting.pie_plot() for all available options - """ - # TODO: select only relevant meta columns - df = self.as_pandas() - ax = plotting.pie_plot(df, *args, **kwargs) - return ax - - def sankey(self, mapping): - """Plot a sankey diagram - - See `pyam.figures.sankey `_ - for details. - """ - return figures.sankey(self, mapping) - - def scatter(self, x, y, **kwargs): - """Plot a scatter chart using meta indicators as columns - - see pyam.plotting.scatter() for all available options - """ + """Deprecated, please use `IamDataFrame.plot.pie()`""" + deprecation_warning('Please use `IamDataFrame.plot.pie()`.') + return self.plot.pie(*args, **kwargs) + + def scatter(self, *args, **kwargs): + """Deprecated, please use `IamDataFrame.plot.scatter()`""" + deprecation_warning('Please use `IamDataFrame.plot.scatter()`.') + return self.plot.scatter(*args, **kwargs) + + def _scatter(self, x, y, **kwargs): + """Plot a scatter chart using meta indicators as columns""" + # TODO merge with `plot.scatter` and deprecate variables = self.data['variable'].unique() xisvar = x in variables yisvar = y in variables diff --git a/pyam/figures.py b/pyam/figures.py index a0aea877d..455dde24a 100755 --- a/pyam/figures.py +++ b/pyam/figures.py @@ -27,7 +27,7 @@ def sankey(df, mapping): Returns ------- - fig : plotly.graph_objs._figure.Figure + fig : :class:`plotly.graph_objects.Figure` """ # Check for duplicates for col in [name for name in df._data.index.names if name != 'variable']: diff --git a/pyam/plotting.py b/pyam/plotting.py index 97f6e882d..9906cdca1 100644 --- a/pyam/plotting.py +++ b/pyam/plotting.py @@ -12,10 +12,12 @@ from pyam.logging import deprecation_warning from pyam.run_control import run_control +from pyam import figures from pyam.timeseries import cross_threshold from pyam.utils import META_IDX, IAMC_IDX, SORT_IDX, isstr, islistable,\ _raise_data_error + # TODO: this is a hotfix for changes in pandas 0.25.0, per discussions on the # pandas-dev listserv, we should try to ask if matplotlib would make it a # standard feature in their library @@ -70,6 +72,39 @@ } +class PlotAccessor(): + """Make plots of IamDataFrame instances""" + def __init__(self, df): + self._parent = df + + def __call__(self, kind='line', *args, **kwargs): + return getattr(self, kind)(**kwargs) + + def line(self, **kwargs): + self._parent._line_plot(**kwargs) + + def bar(self, **kwargs): + return bar(self._parent, **kwargs) + + def stack(self, **kwargs): + return stack(self._parent, **kwargs) + + def hist(self, **kwargs): + raise NotImplementedError('Histogram plot not implemented yet!') + + def box(self, **kwargs): + return box(self._parent, **kwargs) + + def pie(self, **kwargs): + return pie(self._parent, **kwargs) + + def scatter(self, *args, **kwargs): + return self._parent._scatter(*args, **kwargs) + + def sankey(self, *args, **kwargs): + return figures.sankey(self._parent, *args, **kwargs) + + def reset_default_props(**kwargs): """Reset properties to initial cycle point""" global _DEFAULT_PROPS @@ -210,31 +245,41 @@ def reshape_mpl(df, x, y, idx_cols, **kwargs): return df -def pie_plot(df, value='value', category='variable', - ax=None, legend=False, title=True, cmap=None, - **kwargs): - """Plot data as a bar chart. +def pie(df, value='value', category='variable', legend=False, title=True, + ax=None, cmap=None, **kwargs): + """Plot data as a pie chart. Parameters ---------- - df : pd.DataFrame - Data to plot as a long-form data frame + df : :class:`pyam.IamDataFrame`, :class:`pandas.DataFrame` + Data to be plotted value : string, optional The column to use for data values category : string, optional The column to use for labels - ax : matplotlib.Axes, optional legend : bool, optional - Include a legend + Include a legend. title : bool or string, optional Display a default or custom title. + ax : :class:`matplotlib.axes.Axes`, optional cmap : string, optional - A colormap to use. - kwargs : Additional arguments to pass to the pd.DataFrame.plot() function + The name of a registered colormap. + kwargs + Additional arguments passed to :meth:`pandas.DataFrame.plot`. + + Returns + ------- + ax : :class:`matplotlib.axes.Axes` + Modified `ax` or new instance """ + # cast to DataFrame if necessary + # TODO: select only relevant meta columns + if not isinstance(df, pd.DataFrame): + df = df.as_pandas() + for col in set(SORT_IDX) - set([category]): if len(df[col].unique()) > 1: - msg = 'Can not plot multiple {}s in pie_plot with value={},' +\ + msg = 'Can not plot multiple {}s in a pie plot with value={},' +\ ' category={}' raise ValueError(msg.format(col, value, category)) @@ -273,10 +318,9 @@ def pie_plot(df, value='value', category='variable', return ax -def stackplot(df, x='year', y='value', stack='variable', order=None, - ax=None, legend=True, title=True, cmap=None, total=None, - **kwargs): - """Plot stacked area chart of timeseries data +def stack(df, x='year', y='value', stack='variable', order=None, total=None, + legend=True, title=True, ax=None, cmap=None, **kwargs): + """Plot a stacked area chart of timeseries data Parameters ---------- @@ -292,20 +336,25 @@ def stackplot(df, x='year', y='value', stack='variable', order=None, The order to plot the stack levels and the legend. If not specified, order by :meth:`run_control()['order'][\] ` (where available) or alphabetical. - ax : matplotlib.Axes, optional - legend : bool, optional - Include a legend - title : bool or string, optional - Display a default or custom title. - cmap : string, optional - A colormap to use. total : bool or dict, optional If True, plot a total line with default |pyam| settings. If a dict, then plot the total line using the dict key-value pairs as keyword arguments to :meth:`matplotlib.axes.Axes.plot`. If None, do not plot the total line. + legend : bool, optional + Include a legend. + title : bool or string, optional + Display a default or custom title. + ax : :class:`matplotlib.axes.Axes`, optional + cmap : string, optional + The name of a registered colormap. kwargs - Additional arguments to pass to :meth:`pandas.DataFrame.plot` + Additional arguments passed to :meth:`pandas.DataFrame.plot` + + Returns + ------- + ax : :class:`matplotlib.axes.Axes` + Modified `ax` or new instance """ # cast to DataFrame if necessary # TODO: select only relevant meta columns @@ -405,9 +454,8 @@ def as_series(index, name): return ax -def barplot(df, x='year', y='value', bars='variable', order=None, - bars_order=None, ax=None, orient='v', legend=True, title=True, - cmap=None, **kwargs): +def bar(df, x='year', y='value', bars='variable', order=None, bars_order=None, + orient='v', legend=True, title=True, ax=None, cmap=None, **kwargs): """Plot data as a stacked or grouped bar chart Parameters @@ -425,16 +473,22 @@ def barplot(df, x='year', y='value', bars='variable', order=None, If not specified, order by :meth:`run_control()['order'][\] ` (where available) or alphabetical. - ax : matplotlib.Axes, optional orient : string, optional Vertical or horizontal orientation. legend : bool, optional - Include a legend + Include a legend. title : bool or string, optional Display a default or custom title. + ax : :class:`matplotlib.axes.Axes`, optional cmap : string, optional - A colormap to use. - kwargs : Additional arguments to pass to the pd.DataFrame.plot() function + The name of a registered colormap. + kwargs + Additional arguments passed to :meth:`pandas.DataFrame.plot` + + Returns + ------- + ax : :class:`matplotlib.axes.Axes` + Modified `ax` or new instance """ # cast to DataFrame if necessary # TODO: select only relevant meta columns @@ -443,7 +497,7 @@ def barplot(df, x='year', y='value', bars='variable', order=None, for col in set(SORT_IDX) - set([x, bars]): if len(df[col].unique()) > 1: - msg = 'Can not plot multiple {}s in barplot with x={}, bars={}' + msg = 'Can not plot multiple {}s in bar plot with x={}, bars={}' raise ValueError(msg.format(col, x, bars)) if ax is None: @@ -463,6 +517,10 @@ def barplot(df, x='year', y='value', bars='variable', order=None, c = rc['color'][bars][key] color.append(c) + # change year to str to prevent pandas/matplotlib from auto-ordering (#474) + if _df.index.name == 'year': + _df.index = map(str, _df.index) + # plot data kind = 'bar' if orient.startswith('v') else 'barh' _df.plot(kind=kind, color=color, ax=ax, **kwargs) @@ -497,14 +555,14 @@ def barplot(df, x='year', y='value', bars='variable', order=None, return ax -def boxplot(df, y='value', x='year', by=None, - ax=None, legend=True, title=None, **kwargs): +def box(df, y='value', x='year', by=None, legend=True, title=None, ax=None, + **kwargs): """ Plot boxplot of data using seaborn.boxplot Parameters ---------- - df : pandas.DataFrame - Data to plot as a long-form data frame + df : :class:`pyam.IamDataFrame`, :class:`pandas.DataFrame` + Data to be plotted y : string, optional The column to use for y-axis values representing the distribution within the boxplot @@ -512,16 +570,27 @@ def boxplot(df, y='value', x='year', by=None, The column to use for x-axis points, i.e. the number of boxes the plot will have by : string, optional - The column for grouping y-axis values at each x-axis point, i.e. a 3rd - dimension. - Data should be categorical, not a contiuous variable - ax : matplotlib.Axes, optional + The column for grouping y-axis values at each x-axis point, + i.e. a 3rd dimension. Data should be categorical, not a contiuous + variable. legend : bool, optional - Include a legend + Include a legend. title : bool or string, optional - Display a default or custom title - kwargs : Additional arguments to pass to the pd.DataFrame.plot() + Display a default or custom title. + ax : :class:`matplotlib.axes.Axes`, optional + kwargs + Additional arguments passed to :meth:`pandas.DataFrame.plot`. + + Returns + ------- + ax : :class:`matplotlib.axes.Axes` + Modified `ax` or new instance """ + # cast to DataFrame if necessary + # TODO: select only relevant meta columns + if not isinstance(df, pd.DataFrame): + df = df.as_pandas() + if by: rc = run_control() if 'palette' not in kwargs and 'color' in rc and by in rc['color']: @@ -536,7 +605,7 @@ def boxplot(df, y='value', x='year', by=None, if ax is None: fig, ax = plt.subplots() - # plot + # Create the plot sns.boxplot(x=x, y=y, hue=by, data=df, ax=ax, **kwargs) # Add legend @@ -592,9 +661,9 @@ def add_net_values_to_barplot(axs, color='k'): ax.add_patch(rect) -def scatter(df, x, y, ax=None, legend=None, title=None, - color=None, marker='o', linestyle=None, cmap=None, - groupby=['model', 'scenario'], with_lines=False, **kwargs): +def scatter(df, x, y, legend=None, title=None, color=None, marker='o', + linestyle=None, groupby=['model', 'scenario'], with_lines=False, + ax=None, cmap=None, **kwargs): """Plot data as a scatter chart. Parameters @@ -605,9 +674,10 @@ def scatter(df, x, y, ax=None, legend=None, title=None, column to be plotted on the x-axis y : str column to be plotted on the y-axis - ax : matplotlib.Axes, optional legend : bool, optional - Include a legend (`None` displays legend only if less than 13 entries) + Include a legend. By default, show legend only if less than 13 entries. + If a dictionary is provided, it will be used as keyword arguments + in creating the legend. title : bool or string, optional Display a custom title. color : string, optional @@ -620,13 +690,20 @@ def scatter(df, x, y, ax=None, legend=None, title=None, A valid matplotlib linestyle or column name. If a column name, common values will be provided the same linestyle. default: None - cmap : string, optional - A colormap to use. groupby : list-like, optional Data grouping for plotting. with_lines : bool, optional Make the scatter plot with lines connecting common data. - kwargs : Additional arguments to pass to the pd.DataFrame.plot() function + ax : :class:`matplotlib.axes.Axes`, optional + cmap : string, optional + The name of a registered colormap. + kwargs + Additional arguments passed to :meth:`pandas.DataFrame.plot`. + + Returns + ------- + ax : :class:`matplotlib.axes.Axes` + Modified `ax` or new instance """ if ax is None: fig, ax = plt.subplots() @@ -671,7 +748,7 @@ def scatter(df, x, y, ax=None, legend=None, title=None, labels = sorted(list(set(tuple(legend_data)))) idxs = [legend_data.index(d) for d in labels] handles = [handles[i] for i in idxs] - if legend is None and len(labels) < 13 or legend is not False: + if legend is not False: _add_legend(ax, handles, labels, legend) # add labels and title @@ -683,10 +760,10 @@ def scatter(df, x, y, ax=None, legend=None, title=None, return ax -def line_plot(df, x='year', y='value', ax=None, legend=None, title=True, - color=None, marker=None, linestyle=None, cmap=None, - fill_between=None, final_ranges=None, - rm_legend_label=[], **kwargs): +def line(df, x='year', y='value', legend=None, title=True, + color=None, marker=None, linestyle=None, + fill_between=None, final_ranges=None, + rm_legend_label=[], ax=None, cmap=None, **kwargs): """Plot data as lines with or without markers. Parameters @@ -697,10 +774,10 @@ def line_plot(df, x='year', y='value', ax=None, legend=None, title=True, The column to use for x-axis values y : string, optional The column to use for y-axis values - ax : matplotlib.Axes, optional legend : bool or dictionary, optional - Add a legend. If a dictionary is provided, it will be used as keyword - arguments in creating the legend. + Include a legend. By default, show legend only if less than 13 entries. + If a dictionary is provided, it will be used as keyword arguments + in creating the legend. title : bool or string, optional Display a default or custom title. color : string, optional @@ -712,8 +789,6 @@ def line_plot(df, x='year', y='value', ax=None, legend=None, title=True, linestyle : string, optional A valid matplotlib linestyle or column name. If a column name, common values will be provided the same linestyle. - cmap : string, optional - A colormap to use. fill_between : boolean or dict, optional Fill lines between minima/maxima of the 'color' argument. This can only be used if also providing a 'color' argument. If this is True, then @@ -727,7 +802,16 @@ def line_plot(df, x='year', y='value', ax=None, legend=None, title=True, provided instead of defaults. rm_legend_label : string, list, optional Remove the color, marker, or linestyle label in the legend. - kwargs : Additional arguments to pass to the pd.DataFrame.plot() function + ax : :class:`matplotlib.axes.Axes`, optional + cmap : string, optional + The name of a registered colormap. + kwargs + Additional arguments passed to :meth:`pandas.DataFrame.plot`. + + Returns + ------- + ax : :class:`matplotlib.axes.Axes` + Modified `ax` or new instance """ if ax is None: fig, ax = plt.subplots() diff --git a/pyam/utils.py b/pyam/utils.py index d4728e121..108dc3b13 100644 --- a/pyam/utils.py +++ b/pyam/utils.py @@ -116,6 +116,11 @@ def read_pandas(path, default_sheet='data', *args, **kwargs): if len(xl.sheet_names) > 1 and 'sheet_name' not in kwargs: kwargs['sheet_name'] = default_sheet df = pd.read_excel(path, *args, **kwargs) + + # remove unnamed and empty columns + empty_cols = [c for c in df.columns if str(c).startswith('Unnamed: ') + and all(np.isnan(df[c]))] + df.drop(columns=empty_cols, inplace=True) return df diff --git a/setup.py b/setup.py index a19738654..ffcc4e359 100755 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ REQUIREMENTS = [ 'argparse', - 'iam-units >= 2020.4.12', + 'iam-units>=2020.4.12', 'numpy', 'requests', 'pandas>=0.25.0', diff --git a/tests/conftest.py b/tests/conftest.py index d26002619..5138afb8a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -189,6 +189,7 @@ def add_subannual(_data, name, value): df.set_meta('foo', 'string') yield df + @pytest.fixture(scope="function") def reg_df(): df = IamDataFrame(data=REG_DF) diff --git a/tests/test_core.py b/tests/test_core.py index b7825d0bd..53e47df63 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -97,9 +97,13 @@ def test_init_df_with_extra_col(test_pd_df): df = IamDataFrame(tdf) + # check that extra-cols attribute is set correctly assert df.extra_cols == [extra_col] - pd.testing.assert_frame_equal(df.timeseries().reset_index(), - tdf, check_like=True) + + # check that timeseries data is as expected + obs = df.timeseries().reset_index() + exp = tdf[obs.columns] # get the columns into the right order + pd.testing.assert_frame_equal(obs, exp) def test_init_empty_message(caplog): diff --git a/tests/test_plotting.py b/tests/test_plotting.py index 64556e376..802c8ce04 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -42,7 +42,7 @@ def test_line_plot(plot_df): _plot_df = copy.deepcopy(plot_df) _plot_df.set_meta(meta=[np.nan] * 4, name='test') fig, ax = plt.subplots(figsize=(8, 8)) - _plot_df.line_plot(ax=ax, legend=True) + _plot_df.plot(ax=ax, legend=True) return fig @@ -50,7 +50,7 @@ def test_line_plot_cmap(plot_df): # need to provide cmap and color both _plot_df = copy.deepcopy(plot_df) _plot_df.set_meta(meta=[np.nan] * 4, name='test') - pytest.raises(ValueError, _plot_df.line_plot, cmap='magma') + pytest.raises(ValueError, _plot_df.plot, cmap='magma') @pytest.mark.mpl_image_compare(**MPL_KWARGS) @@ -58,35 +58,35 @@ def test_line_plot_cmap_color_arg(plot_df): _plot_df = copy.deepcopy(plot_df) _plot_df.set_meta(meta=[np.nan] * 4, name='test') fig, ax = plt.subplots(figsize=(8, 8)) - _plot_df.line_plot(ax=ax, legend=True, cmap='magma', color='variable') + _plot_df.plot(ax=ax, legend=True, cmap='magma', color='variable') return fig @pytest.mark.mpl_image_compare(**MPL_KWARGS) def test_line_plot_dict_legend(plot_df): fig, ax = plt.subplots(figsize=(8, 8)) - plot_df.line_plot(ax=ax, legend=dict(loc='outside right')) + plot_df.plot(ax=ax, legend=dict(loc='outside right')) return fig @pytest.mark.mpl_image_compare(**MPL_KWARGS) def test_line_plot_bottom_legend(plot_df): fig, ax = plt.subplots(figsize=(8, 8)) - plot_df.line_plot(ax=ax, legend=dict(loc='outside bottom')) + plot_df.plot(ax=ax, legend=dict(loc='outside bottom')) return fig @pytest.mark.mpl_image_compare(**MPL_KWARGS) def test_line_no_legend(plot_df): fig, ax = plt.subplots(figsize=(8, 8)) - plot_df.line_plot(ax=ax, legend=False) + plot_df.plot(ax=ax, legend=False) return fig @pytest.mark.mpl_image_compare(**MPL_KWARGS) def test_line_color(plot_df): fig, ax = plt.subplots(figsize=(8, 8)) - plot_df.line_plot(ax=ax, color='model', legend=True) + plot_df.plot(ax=ax, color='model', legend=True) return fig @@ -111,14 +111,14 @@ def test_line_PYAM_COLORS(plot_df): df = pyam.IamDataFrame(pd.concat(dfs)) fig, ax = plt.subplots(figsize=(8, 8)) with update_run_control(update): - df.line_plot(ax=ax, color='model', legend=True) + df.plot(ax=ax, color='model', legend=True) return fig @pytest.mark.mpl_image_compare(**MPL_KWARGS) def test_line_color_fill_between(plot_df): fig, ax = plt.subplots(figsize=(8, 8)) - plot_df.line_plot(ax=ax, color='model', fill_between=True, legend=True) + plot_df.plot(ax=ax, color='model', fill_between=True, legend=True) return fig @@ -136,51 +136,51 @@ def test_line_color_fill_between_interpolate(plot_df): newdata = ['test_model1', 'test_scenario1', 'World', 'Primary Energy|Coal', 'EJ/y', 2015, 3.50] df.data.loc[len(df.data) + 1] = newdata - df.line_plot(ax=ax, color='model', fill_between=True, legend=True) + df.plot(ax=ax, color='model', fill_between=True, legend=True) return fig @pytest.mark.mpl_image_compare(**MPL_KWARGS) def test_line_color_final_ranges(plot_df): fig, ax = plt.subplots(figsize=(8, 8)) - plot_df.line_plot(ax=ax, color='model', final_ranges=True, legend=True) + plot_df.plot(ax=ax, color='model', final_ranges=True, legend=True) return fig @pytest.mark.mpl_image_compare(**MPL_KWARGS) def test_line_marker_legend(plot_df): fig, ax = plt.subplots(figsize=(8, 8)) - plot_df.line_plot(ax=ax, marker='model', legend=True) + plot_df.plot(ax=ax, marker='model', legend=True) return fig @pytest.mark.mpl_image_compare(**MPL_KWARGS) def test_line_rm_legend_label(plot_df): fig, ax = plt.subplots(figsize=(8, 8)) - plot_df.line_plot(ax=ax, marker='model', linestyle='scenario', legend=True, - rm_legend_label='marker') + plot_df.plot(ax=ax, marker='model', linestyle='scenario', + legend=True, rm_legend_label='marker') return fig @pytest.mark.mpl_image_compare(**MPL_KWARGS) def test_line_linestyle_legend(plot_df): fig, ax = plt.subplots(figsize=(8, 8)) - plot_df.line_plot(ax=ax, linestyle='model', legend=True) + plot_df.plot(ax=ax, linestyle='model', legend=True) return fig @pytest.mark.mpl_image_compare(**MPL_KWARGS) def test_line_single_color(plot_df): fig, ax = plt.subplots(figsize=(8, 8)) - plot_df.line_plot(ax=ax, color='b', linestyle='model', legend=True) + plot_df.plot(ax=ax, color='b', linestyle='model', legend=True) return fig @pytest.mark.mpl_image_compare(**MPL_KWARGS) def test_line_filter_title(plot_df): fig, ax = plt.subplots(figsize=(8, 8)) - plot_df.filter(variable='Primary Energy|Coal').line_plot( - ax=ax, color='model', marker='scenario', legend=True) + plot_df.filter(variable='Primary Energy|Coal')\ + .plot(ax=ax, color='model', marker='scenario', legend=True) return fig @@ -188,7 +188,7 @@ def test_line_filter_title(plot_df): def test_line_update_rc(plot_df): with update_run_control({'color': {'model': {'test_model1': 'cyan'}}}): fig, ax = plt.subplots(figsize=(8, 8)) - plot_df.line_plot(ax=ax, color='model', legend=True) + plot_df.plot(ax=ax, color='model', legend=True) return fig @@ -197,7 +197,7 @@ def test_line_plot_1_var(plot_df): fig, ax = plt.subplots(figsize=(8, 8)) (plot_df .filter(model='test_model', scenario='test_scenario') - .line_plot(x='Primary Energy', y='year', ax=ax, legend=False) + .plot(x='Primary Energy', y='year', ax=ax, legend=False) ) return fig @@ -207,44 +207,36 @@ def test_line_plot_2_vars(plot_df): fig, ax = plt.subplots(figsize=(8, 8)) (plot_df .filter(model='test_model', scenario='test_scenario') - .line_plot(x='Primary Energy|Coal', y='Primary Energy', ax=ax, legend=False) + .plot(x='Primary Energy|Coal', y='Primary Energy', ax=ax, legend=False) ) return fig def test_barplot_raises(plot_df): - pytest.raises(ValueError, plot_df.barplot) + pytest.raises(ValueError, plot_df.plot.bar) @pytest.mark.mpl_image_compare(**MPL_KWARGS) def test_barplot(plot_df): fig, ax = plt.subplots(figsize=(8, 8)) - (plot_df - .filter(variable='Primary Energy', model='test_model') - .barplot(ax=ax, bars='scenario') - ) + plot_df.filter(variable='Primary Energy', model='test_model')\ + .plot.bar(ax=ax, bars='scenario') return fig @pytest.mark.mpl_image_compare(**MPL_KWARGS) def test_barplot_h(plot_df): fig, ax = plt.subplots(figsize=(8, 8)) - (plot_df - .filter(variable='Primary Energy', model='test_model') - .barplot(ax=ax, bars='scenario', - orient='h') - ) + plot_df.filter(variable='Primary Energy', model='test_model')\ + .plot.bar(ax=ax, bars='scenario', orient='h') return fig @pytest.mark.mpl_image_compare(**MPL_KWARGS) def test_barplot_stacked(plot_df): fig, ax = plt.subplots(figsize=(8, 8)) - (plot_df - .filter(variable='Primary Energy', model='test_model') - .barplot(ax=ax, bars='scenario', - stacked=True) - ) + plot_df.filter(variable='Primary Energy', model='test_model')\ + .plot.bar(ax=ax, bars='scenario', stacked=True) return fig @@ -253,8 +245,8 @@ def test_barplot_stacked_order_by_list(plot_df): fig, ax = plt.subplots(figsize=(8, 8)) plot_df\ .filter(variable='Primary Energy', model='test_model')\ - .barplot(ax=ax, bars='scenario', order=[2015, 2010], - bars_order=['test_scenario1', 'test_scenario'], stacked=True) + .plot.bar(ax=ax, bars='scenario', order=[2015, 2010], + bars_order=['test_scenario1', 'test_scenario'], stacked=True) return fig @@ -266,7 +258,7 @@ def test_barplot_stacked_order_by_rc(plot_df): ( plot_df .filter(variable='Primary Energy', model='test_model') - .barplot(ax=ax, bars='scenario', stacked=True) + .plot.bar(ax=ax, bars='scenario', stacked=True) ) return fig @@ -281,11 +273,9 @@ def test_barplot_stacked_net_line(plot_df): newdata = ['test_model1', 'test_scenario1', 'World', 'Primary Energy|foo', 'EJ/y', y, v] df.data.loc[len(df) + i] = newdata - (df - .filter(variable='Primary Energy|*', model='test_model1', - scenario='test_scenario1', region='World') - .barplot(ax=ax, bars='variable', stacked=True) - ) + df.filter(variable='Primary Energy|*', model='test_model1', + scenario='test_scenario1', region='World')\ + .plot.bar(ax=ax, bars='variable', stacked=True) plotting.add_net_values_to_barplot(ax, color='r') return fig @@ -293,11 +283,8 @@ def test_barplot_stacked_net_line(plot_df): @pytest.mark.mpl_image_compare(**MPL_KWARGS) def test_barplot_title(plot_df): fig, ax = plt.subplots(figsize=(8, 8)) - (plot_df - .filter(variable='Primary Energy', model='test_model') - .barplot(ax=ax, bars='scenario', - title='foo') - ) + plot_df.filter(variable='Primary Energy', model='test_model')\ + .plot.bar(ax=ax, bars='scenario', title='foo') return fig @@ -305,44 +292,38 @@ def test_barplot_title(plot_df): def test_barplot_rc(plot_df): with update_run_control({'color': {'scenario': {'test_scenario': 'black'}}}): fig, ax = plt.subplots(figsize=(8, 8)) - (plot_df - .filter(variable='Primary Energy', model='test_model') - .barplot(ax=ax, bars='scenario') - ) + plot_df.filter(variable='Primary Energy', model='test_model')\ + .plot.bar(ax=ax, bars='scenario') return fig @pytest.mark.mpl_image_compare(**MPL_KWARGS) def test_boxplot(plot_df): fig, ax = plt.subplots(figsize=(8, 8)) - plot_df.boxplot(ax=ax) + plot_df.plot.box(ax=ax) return fig @pytest.mark.mpl_image_compare(**MPL_KWARGS) def test_boxplot_hue(plot_df): fig, ax = plt.subplots(figsize=(8, 8)) - plot_df.boxplot(ax=ax, by='model') + plot_df.plot.box(ax=ax, by='model') return fig @pytest.mark.mpl_image_compare(**MPL_KWARGS) def test_pie_plot_labels(plot_df): fig, ax = plt.subplots(figsize=(8, 8)) - (plot_df - .filter(variable='Primary Energy', model='test_model', year=2010) - .pie_plot(ax=ax, category='scenario') - ) + plot_df.filter(variable='Primary Energy', model='test_model', year=2010)\ + .plot.pie(ax=ax, category='scenario') return fig @pytest.mark.mpl_image_compare(**MPL_KWARGS) def test_pie_plot_legend(plot_df): fig, ax = plt.subplots(figsize=(8, 8)) - (plot_df - .filter(variable='Primary Energy', model='test_model', year=2010) - .pie_plot(ax=ax, category='scenario', labels=None, legend=True) - ) + plot_df.filter(variable='Primary Energy', model='test_model', year=2010)\ + .plot.pie(ax=ax, category='scenario', labels=None, legend=True) return fig @@ -350,9 +331,10 @@ def test_pie_plot_legend(plot_df): def test_pie_plot_other(plot_df): fig, ax = plt.subplots(figsize=(8, 8)) with update_run_control(RC_TEST_DICT): - (plot_df - .filter(variable='Primary Energy', model='test_model', year=2010) - .pie_plot(ax=ax, category='scenario', cmap='viridis', title='foo') + ( + plot_df + .filter(variable='Primary Energy', model='test_model', year=2010) + .plot.pie(ax=ax, category='scenario', cmap='viridis', title='foo') ) return fig @@ -360,21 +342,20 @@ def test_pie_plot_other(plot_df): @pytest.mark.mpl_image_compare(**MPL_KWARGS) def test_stackplot(plot_df): fig, ax = plt.subplots(figsize=(8, 8)) - (plot_df - .filter(variable='Primary Energy', model='test_model') - .stackplot(ax=ax, stack='scenario') - ) + plot_df.filter(variable='Primary Energy', model='test_model')\ + .plot.stack(ax=ax, stack='scenario') return fig @pytest.mark.mpl_image_compare(**MPL_KWARGS) def test_stackplot_order_by_list(plot_df): fig, ax = plt.subplots(figsize=(8, 8)) - (plot_df - .filter(variable='Primary Energy', model='test_model') - .stackplot(ax=ax, stack='scenario', - order=['test_scenario1', 'test_scenario']) - ) + ( + plot_df + .filter(variable='Primary Energy', model='test_model') + .plot.stack(ax=ax, stack='scenario', + order=['test_scenario1', 'test_scenario']) + ) return fig @@ -383,10 +364,8 @@ def test_stackplot_order_by_rc(plot_df): fig, ax = plt.subplots(figsize=(8, 8)) scen_list = ['test_scenario1'] # first list from rc, then alphabetical with update_run_control({'order': {'scenario': scen_list}}): - (plot_df - .filter(variable='Primary Energy', model='test_model') - .stackplot(ax=ax, stack='scenario') - ) + plot_df.filter(variable='Primary Energy', model='test_model')\ + .plot.stack(ax=ax, stack='scenario') return fig @@ -394,10 +373,8 @@ def test_stackplot_order_by_rc(plot_df): def test_stackplot_other(plot_df): fig, ax = plt.subplots(figsize=(8, 8)) with update_run_control({'color': {'scenario': {'test_scenario': 'black'}}}): - (plot_df - .filter(variable='Primary Energy', model='test_model') - .stackplot(ax=ax, stack='scenario', cmap='viridis', title='foo') - ) + plot_df.filter(variable='Primary Energy', model='test_model')\ + .plot.stack(ax=ax, stack='scenario', cmap='viridis', title='foo') return fig @@ -414,14 +391,14 @@ def test_stackplot_negative(): fig, ax = plt.subplots(figsize=(8, 8)) df = pyam.IamDataFrame(TEST_STACKPLOT_NEGATIVE, model='model_a', scenario='scen_a', region='World', unit='foo') - df.stackplot(ax=ax, total=True) + df.plot.stack(ax=ax, total=True) return fig @pytest.mark.mpl_image_compare(**MPL_KWARGS) def test_scatter(plot_df): fig, ax = plt.subplots(figsize=(8, 8)) - plot_df.scatter(ax=ax, x='Primary Energy', y='Primary Energy|Coal') + plot_df.plot.scatter(ax=ax, x='Primary Energy', y='Primary Energy|Coal') return fig @@ -438,16 +415,16 @@ def test_scatter_variables_with_meta_color(plot_df): criteria={'Primary Energy': {'lo': 5, 'year': 2010}}, color='red' ) - plot_df.scatter(ax=ax, x='Primary Energy', y='Primary Energy|Coal', - color='foo') + plot_df.plot.scatter(ax=ax, x='Primary Energy', y='Primary Energy|Coal', + color='foo') return fig @pytest.mark.mpl_image_compare(**MPL_KWARGS) def test_scatter_with_lines(plot_df): fig, ax = plt.subplots(figsize=(8, 8)) - plot_df.scatter(ax=ax, x='Primary Energy', y='Primary Energy|Coal', - with_lines=True) + plot_df.plot.scatter(ax=ax, x='Primary Energy', y='Primary Energy|Coal', + with_lines=True) return fig @@ -458,7 +435,7 @@ def test_scatter_meta(plot_df): .timeseries()[2010], name='Total') plot_df.set_meta(meta=plot_df.filter(variable='Primary Energy|Coal') .timeseries()[2010], name='Coal') - plot_df.scatter(ax=ax, x='Total', y='Coal') + plot_df.plot.scatter(ax=ax, x='Total', y='Coal') return fig @@ -472,36 +449,29 @@ def test_add_panel_label(plot_df): @pytest.mark.mpl_image_compare(**MPL_KWARGS) def test_stackplot_negative_emissions(plot_stackplot_df): fig, ax = plt.subplots(figsize=(8, 8)) - plot_stackplot_df.stackplot(ax=ax) + plot_stackplot_df.plot.stack(ax=ax) return fig @pytest.mark.mpl_image_compare(**MPL_KWARGS) def test_stackplot_negative_emissions_with_total(plot_stackplot_df): fig, ax = plt.subplots(figsize=(8, 8)) - plot_stackplot_df.stackplot(ax=ax, total=True) + plot_stackplot_df.plot.stack(ax=ax, total=True) return fig @pytest.mark.mpl_image_compare(**MPL_KWARGS) def test_stackplot_negative_emissions_kwargs_def_total(plot_stackplot_df): fig, ax = plt.subplots(figsize=(8, 8)) - plot_stackplot_df.stackplot( - alpha=0.5, - total=True, - ax=ax, - ) + plot_stackplot_df.plot.stack(ax=ax, alpha=0.5, total=True) return fig @pytest.mark.mpl_image_compare(**MPL_KWARGS) def test_stackplot_negative_emissions_kwargs_custom_total(plot_stackplot_df): fig, ax = plt.subplots(figsize=(8, 8)) - plot_stackplot_df.stackplot( - alpha=0.5, - total={"color": "grey", "ls": "--", "lw": 2.0}, - ax=ax, - ) + total = {"color": "grey", "ls": "--", "lw": 2.0} + plot_stackplot_df.plot.stack(ax=ax, alpha=0.5, total=total) return fig @@ -518,6 +488,5 @@ def test_stackplot_missing_zero_issue_266(plot_stackplot_df): ), model='model_a', scenario='scen_a', region='World', unit='some_unit') fig, ax = plt.subplots(figsize=(8, 8)) - df.stackplot(ax=ax) - + df.plot.stack(ax=ax) return fig