-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #15 from khaeru/compat-plotnine
Add .compat.plotnine
- Loading branch information
Showing
14 changed files
with
270 additions
and
47 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
:mod:`ixmp` | ||
*********** | ||
|
||
:doc:`Package documentation <ixmp:index>` | ||
|
||
.. currentmodule:: genno.compat.ixmp | ||
|
||
.. automodule:: genno.compat.ixmp | ||
:members: | ||
|
||
.. automodule:: genno.compat.ixmp.computations | ||
:members: | ||
|
||
.. automodule:: genno.compat.ixmp.reporter | ||
:members: | ||
|
||
.. automodule:: genno.compat.ixmp.util | ||
:members: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
:mod:`plotnine` | ||
*************** | ||
|
||
:doc:`Package documentation <plotnine:index>` | ||
|
||
.. currentmodule:: genno.compat.plotnine | ||
|
||
To use :class:`.Plot`: | ||
|
||
.. ipython:: | ||
|
||
In [1]: from pathlib import Path | ||
...: | ||
...: import xarray as xr | ||
...: import plotnine as p9 | ||
...: | ||
...: from genno import Computer, Quantity | ||
...: from genno.compat.plotnine import Plot | ||
|
||
|
||
1. Create a subclass that overrides :meth:`Plot.generate`, :attr:`Plot.basename`, and optionally :attr:`Plot.inputs`. | ||
|
||
.. ipython:: | ||
|
||
In [1]: class DemoPlot(Plot): | ||
...: basename = "plotnine-demo" | ||
...: suffix = ".svg" | ||
...: | ||
...: def generate(self, x, y): | ||
...: data = x.merge(y, on="t") | ||
...: return ( | ||
...: p9.ggplot(data, p9.aes(x="x", y="y")) | ||
...: + p9.geom_line(color="red") | ||
...: + p9.geom_point(color="blue") | ||
...: ) | ||
|
||
2. Call :meth:`.make_task` to get a task tuple suitable for adding to a :class:`.Computer`: | ||
|
||
.. ipython:: python | ||
# Set up a Computer, including the output path and some data | ||
c = Computer(output_dir=Path(".")) | ||
t = [("t", [-1, 0, 1])] | ||
c.add("x:t", Quantity(xr.DataArray([1.0, 2, 3], coords=t), name="x")) | ||
c.add("y:t", Quantity(xr.DataArray([1.0, 4, 9], coords=t), name="y")) | ||
# Add the plot to the Computer | ||
c.add("plot", DemoPlot.make_task("x:t", "y:t")) | ||
# Show the task that was added | ||
c.graph["plot"] | ||
3. :meth:`.get` the node. The result is the path the the saved plot(s). | ||
|
||
.. ipython:: | ||
|
||
In [1]: c.get("plot") | ||
Out[1]: ./test.svg | ||
|
||
.. image:: ./plotnine-demo.svg | ||
:alt: Demonstration output from genno.compat.plotnine. | ||
|
||
.. automodule:: genno.compat.plotnine | ||
:members: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
:mod:`pyam` | ||
*********** | ||
|
||
:doc:`Package documentation <pyam:index>` | ||
|
||
.. currentmodule:: genno.compat.pyam | ||
|
||
.. automodule:: genno.compat.pyam | ||
:members: | ||
|
||
.. automodule:: genno.compat.pyam.computations | ||
:members: | ||
|
||
.. automodule:: genno.compat.pyam.util | ||
:members: |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
try: | ||
import plotnine # noqa: F401 | ||
except ModuleNotFoundError: # pragma: no cover | ||
HAS_PLOTNINE = False | ||
else: | ||
HAS_PLOTNINE = True | ||
|
||
from .plot import Plot | ||
|
||
__all__ = ["Plot"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import logging | ||
from abc import ABC, abstractmethod | ||
from typing import Hashable, Sequence | ||
|
||
import plotnine as p9 | ||
|
||
log = logging.getLogger(__name__) | ||
|
||
|
||
class Plot(ABC): | ||
"""Class for plotting using :mod:`plotnine`.""" | ||
|
||
#: Filename base for saving the plot. | ||
basename = "" | ||
#: File extension; determines file format. | ||
suffix = ".pdf" | ||
#: Keys for quantities needed by :meth:`generate`. | ||
inputs: Sequence[Hashable] = [] | ||
#: Keyword arguments for :meth:`plotnine.ggplot.save`. | ||
save_args = dict(verbose=False) | ||
|
||
# TODO add static geoms automatically in generate() | ||
__static: Sequence = [] | ||
|
||
def save(self, config, *args, **kwargs): | ||
path = config["output_dir"] / f"{self.basename}{self.suffix}" | ||
|
||
log.info(f"Save to {path}") | ||
|
||
args = map(lambda qty: qty.to_series().rename(qty.name).reset_index(), args) | ||
|
||
plot_or_plots = self.generate(*args, **kwargs) | ||
|
||
try: | ||
# Single plot | ||
plot_or_plots.save(path, **self.save_args) | ||
except AttributeError: | ||
# Iterator containing multiple plots | ||
p9.save_as_pdf_pages(plot_or_plots, path, **self.save_args) | ||
|
||
return path | ||
|
||
@classmethod | ||
def make_task(cls, *inputs): | ||
"""Return a task :class:`tuple` to add to a Computer. | ||
Parameters | ||
---------- | ||
inputs : sequence of :class:`.Key`, :class:`str`, or other hashable, optional | ||
If provided, overrides the :attr:`inputs` property of the class. | ||
Returns | ||
------- | ||
tuple | ||
- The first, callable element of the task is :meth:`save`. | ||
- The second element is ``"config"``, to access the configuration of the | ||
Computer. | ||
- The third and following elements are the `inputs`. | ||
""" | ||
return tuple([cls().save, "config"] + (list(inputs) if inputs else cls.inputs)) | ||
|
||
@abstractmethod | ||
def generate(self, *args, **kwargs): | ||
"""Generate and return the plot. | ||
Must be implemented by subclasses. | ||
Parameters | ||
---------- | ||
args : sequence of :class:`pandas.DataFrame` | ||
Because :mod:`plotnine` operates on pandas data structures, :obj:`Quantity` | ||
are automatically converted before being provided to :meth:`generate`. | ||
""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
from pathlib import Path | ||
|
||
import plotnine as p9 | ||
import pytest | ||
import xarray as xr | ||
|
||
from genno import Computer, Quantity | ||
from genno.compat.plotnine import Plot | ||
|
||
|
||
def test_Plot(tmp_path): | ||
c = Computer(output_dir=tmp_path) | ||
t = [("t", [-1, 0, 1])] | ||
c.add("x:t", Quantity(xr.DataArray([1.0, 2, 3], coords=t), name="x")) | ||
c.add("y:t", Quantity(xr.DataArray([1.0, 2, 3], coords=t), name="y")) | ||
|
||
# Exception raised when the class is incomplete | ||
with pytest.raises( | ||
TypeError, | ||
match=("Can't instantiate abstract class Plot1 with abstract methods generate"), | ||
): | ||
|
||
class Plot1(Plot): | ||
inputs = ["x:t", "y:t"] | ||
|
||
c.add("plot", Plot1.make_task()) | ||
|
||
class Plot2(Plot): | ||
basename = "test" | ||
suffix = ".svg" | ||
|
||
def generate(self, x, y): | ||
return p9.ggplot(x.merge(y, on="t"), p9.aes(x="x", y="y")) + p9.geom_point() | ||
|
||
c.add("plot", Plot2.make_task("x:t", "y:t")) | ||
|
||
# Graph contains the task. Don't compare the callable | ||
assert ("config", "x:t", "y:t") == c.graph["plot"][1:] | ||
assert callable(c.graph["plot"][0]) | ||
|
||
# Plot can be generated | ||
result = c.get("plot") | ||
|
||
# Result is the path to the file | ||
assert isinstance(result, Path) | ||
|
||
# Concrete Plot subclasses can be further subclassed | ||
class Plot3(Plot2): | ||
suffix = ".pdf" | ||
inputs = ["x:t", "y:t"] | ||
|
||
def generate(self, x, y): | ||
# Return an iterable of 2 plots | ||
return (super().generate(x, y), super().generate(x, y)) | ||
|
||
# Multi-page PDFs can be saved | ||
c.add("plot", Plot3.make_task()) | ||
c.get("plot") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters