Skip to content

Commit

Permalink
Use IPython display
Browse files Browse the repository at this point in the history
1. Makes it possible to avoid string output that follows the output image.
2. Gained the ability to control the interactive figure format from
   within plotnine.

closes #694
  • Loading branch information
has2k1 committed Jan 10, 2024
1 parent 32f491a commit 4bdd7cc
Show file tree
Hide file tree
Showing 9 changed files with 259 additions and 128 deletions.
1 change: 1 addition & 0 deletions doc/_quartodoc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,7 @@ quartodoc:
- current_theme
- dpi
- figure_size
- figure_format
- get_option
- set_option

Expand Down
22 changes: 22 additions & 0 deletions doc/changelog.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,25 @@ title: Changelog
### API Changes

- Requires python >= 3.9

- The name of the calculated aesthetic of
[](:class:`~plotnine.stats.stat_function`) changed from `y` to `fx`.

- [](:class:`~plotnine.stats.stat_ecdf`) has gained the `pad` parameter. The
default is set to `True`, which pads the domain with `-inf` and `inf` so
that the ECDF does not have discontinuities at the extremes. To get the
behaviour, set `pad` to `False`. ({{< issue 725 >}})

- Removed the environment parameter from `ggplot`.

- When a ggplot object is the last in a jupyter cell, the output image will
not be followed by string meta information about the figure/image.

This will happen even if the backend is set to an interactive one.

If you set the backend to an interactive one, use
[](:class:`~plotnine.ggplot.show`) to draw the plot.

### New

- Added symmetric logarithm transformation scales
Expand Down Expand Up @@ -51,6 +62,17 @@ title: Changelog
accept lists/tuples to set the values on individual text objects.
({{< issue 724 >}})

- Gained the option [](:attr:`~plotnine.options.figure_format`) to set the format
of the inline figures in an interactive session. e.g.

```python
from plotnine.options import set_option

set_option("figure_format", "svg")
```

will output all subsequent figures in svg format.

### Bug Fixes

- Fixed handling of minor breaks in
Expand Down
12 changes: 0 additions & 12 deletions plotnine/_utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
from typing import Any, Callable

import numpy.typing as npt
from IPython.core.interactiveshell import InteractiveShell
from matplotlib.typing import ColorType
from typing_extensions import TypeGuard

Expand Down Expand Up @@ -1186,17 +1185,6 @@ def __exit__(self, type, value, traceback):
return self._cm.__exit__(type, value, traceback)


def get_ipython() -> "InteractiveShell | None":
"""
Return running IPython instance or None
"""
try:
from IPython.core.getipython import get_ipython
except ImportError:
return None
return get_ipython()


def simple_table(
rows: list[tuple[str, str]], headers: tuple[str, str], **kwargs
):
Expand Down
67 changes: 67 additions & 0 deletions plotnine/_utils/context.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from __future__ import annotations

Check warning on line 1 in plotnine/_utils/context.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/context.py#L1

Added line #L1 was not covered by tests

from typing import TYPE_CHECKING

Check warning on line 3 in plotnine/_utils/context.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/context.py#L3

Added line #L3 was not covered by tests

import pandas as pd

Check warning on line 5 in plotnine/_utils/context.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/context.py#L5

Added line #L5 was not covered by tests

if TYPE_CHECKING:
from typing_extensions import Self

Check warning on line 8 in plotnine/_utils/context.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/context.py#L7-L8

Added lines #L7 - L8 were not covered by tests

from plotnine import ggplot

Check warning on line 10 in plotnine/_utils/context.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/context.py#L10

Added line #L10 was not covered by tests


class plot_context:

Check warning on line 13 in plotnine/_utils/context.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/context.py#L13

Added line #L13 was not covered by tests
"""
Context to setup the environment within with the plot is built
Parameters
----------
plot :
ggplot object to be built within the context.
exits.
show :
Whether to show the plot.
"""

def __init__(self, plot: ggplot, show: bool = False):
self.plot = plot
self.show = show

Check warning on line 28 in plotnine/_utils/context.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/context.py#L26-L28

Added lines #L26 - L28 were not covered by tests

def __enter__(self) -> Self:

Check warning on line 30 in plotnine/_utils/context.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/context.py#L30

Added line #L30 was not covered by tests
"""
Enclose in matplolib & pandas environments
"""
import matplotlib as mpl

Check warning on line 34 in plotnine/_utils/context.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/context.py#L34

Added line #L34 was not covered by tests

self.plot.theme._targets = {}
self.rc_context = mpl.rc_context(self.plot.theme.rcParams)

Check warning on line 37 in plotnine/_utils/context.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/context.py#L36-L37

Added lines #L36 - L37 were not covered by tests

# Pandas deprecated is_copy, and when we create new dataframes
# from slices we do not want complaints. We always uses the
# new frames knowing that they are separate from the original.
self.pd_option_context = pd.option_context(

Check warning on line 42 in plotnine/_utils/context.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/context.py#L42

Added line #L42 was not covered by tests
"mode.chained_assignment", None
)
self.rc_context.__enter__()
self.pd_option_context.__enter__()
return self

Check warning on line 47 in plotnine/_utils/context.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/context.py#L45-L47

Added lines #L45 - L47 were not covered by tests

def __exit__(self, exc_type, exc_value, exc_traceback):

Check warning on line 49 in plotnine/_utils/context.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/context.py#L49

Added line #L49 was not covered by tests
"""
Exit matplotlib & pandas environments
"""
import matplotlib.pyplot as plt

Check warning on line 53 in plotnine/_utils/context.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/context.py#L53

Added line #L53 was not covered by tests

if exc_type is None:
if self.show:
plt.show()

Check warning on line 57 in plotnine/_utils/context.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/context.py#L55-L57

Added lines #L55 - L57 were not covered by tests
else:
plt.close(self.plot.figure)

Check warning on line 59 in plotnine/_utils/context.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/context.py#L59

Added line #L59 was not covered by tests
else:
# There is an exception, close any figure
if hasattr(self.plot, "figure"):
plt.close(self.plot.figure)

Check warning on line 63 in plotnine/_utils/context.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/context.py#L62-L63

Added lines #L62 - L63 were not covered by tests

self.rc_context.__exit__(exc_type, exc_value, exc_traceback)
self.pd_option_context.__exit__(exc_type, exc_value, exc_traceback)
delattr(self.plot.theme, "_targets")

Check warning on line 67 in plotnine/_utils/context.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/context.py#L65-L67

Added lines #L65 - L67 were not covered by tests
78 changes: 78 additions & 0 deletions plotnine/_utils/ipython.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
from __future__ import annotations

Check warning on line 1 in plotnine/_utils/ipython.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/ipython.py#L1

Added line #L1 was not covered by tests

from typing import TYPE_CHECKING

Check warning on line 3 in plotnine/_utils/ipython.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/ipython.py#L3

Added line #L3 was not covered by tests

if TYPE_CHECKING:
from typing import Callable, Literal, TypeAlias

Check warning on line 6 in plotnine/_utils/ipython.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/ipython.py#L5-L6

Added lines #L5 - L6 were not covered by tests

from IPython.core.interactiveshell import InteractiveShell

Check warning on line 8 in plotnine/_utils/ipython.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/ipython.py#L8

Added line #L8 was not covered by tests

FigureFormat: TypeAlias = Literal[

Check warning on line 10 in plotnine/_utils/ipython.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/ipython.py#L10

Added line #L10 was not covered by tests
"png", "retina", "jpeg", "jpg", "svg", "pdf"
]


def get_ipython() -> "InteractiveShell":

Check warning on line 15 in plotnine/_utils/ipython.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/ipython.py#L15

Added line #L15 was not covered by tests
"""
Return running IPython instance or None
"""
try:
from IPython.core.getipython import get_ipython as _get_ipython
except ImportError as err:
raise type(err)("IPython is has not been installed.") from err

Check warning on line 22 in plotnine/_utils/ipython.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/ipython.py#L19-L22

Added lines #L19 - L22 were not covered by tests

ip = _get_ipython()
if ip is None:
raise RuntimeError("Not running in a juptyer session.")

Check warning on line 26 in plotnine/_utils/ipython.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/ipython.py#L24-L26

Added lines #L24 - L26 were not covered by tests

return ip

Check warning on line 28 in plotnine/_utils/ipython.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/ipython.py#L28

Added line #L28 was not covered by tests


def is_inline_backend():

Check warning on line 31 in plotnine/_utils/ipython.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/ipython.py#L31

Added line #L31 was not covered by tests
"""
Return True if the inline_backend is on
This can only be True if also running in an jupyter/ipython session.
"""
import matplotlib as mpl

Check warning on line 37 in plotnine/_utils/ipython.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/ipython.py#L37

Added line #L37 was not covered by tests

return "matplotlib_inline.backend_inline" in mpl.get_backend()

Check warning on line 39 in plotnine/_utils/ipython.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/ipython.py#L39

Added line #L39 was not covered by tests


def get_display_function(format: FigureFormat) -> Callable[[bytes], None]:

Check warning on line 42 in plotnine/_utils/ipython.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/ipython.py#L42

Added line #L42 was not covered by tests
"""
Return a function that will display the plot image
"""
from IPython.display import (

Check warning on line 46 in plotnine/_utils/ipython.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/ipython.py#L46

Added line #L46 was not covered by tests
SVG,
Image,
display_jpeg,
display_pdf,
display_png,
display_svg,
)

def png(b: bytes):
display_png(Image(b, format="png"))

Check warning on line 56 in plotnine/_utils/ipython.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/ipython.py#L55-L56

Added lines #L55 - L56 were not covered by tests

def retina(b: bytes):
display_png(Image(b, format="png", retina=True))

Check warning on line 59 in plotnine/_utils/ipython.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/ipython.py#L58-L59

Added lines #L58 - L59 were not covered by tests

def jpeg(b: bytes):
display_jpeg(Image(b, format="jpeg"))

Check warning on line 62 in plotnine/_utils/ipython.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/ipython.py#L61-L62

Added lines #L61 - L62 were not covered by tests

def svg(b: bytes):
display_svg(SVG(b))

Check warning on line 65 in plotnine/_utils/ipython.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/ipython.py#L64-L65

Added lines #L64 - L65 were not covered by tests

def pdf(b: bytes):
display_pdf(b, raw=True)

Check warning on line 68 in plotnine/_utils/ipython.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/ipython.py#L67-L68

Added lines #L67 - L68 were not covered by tests

lookup = {

Check warning on line 70 in plotnine/_utils/ipython.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/ipython.py#L70

Added line #L70 was not covered by tests
"png": png,
"retina": retina,
"jpeg": jpeg,
"jpg": jpeg,
"svg": svg,
"pdf": pdf,
}
return lookup[format]

Check warning on line 78 in plotnine/_utils/ipython.py

View check run for this annotation

Codecov / codecov/patch

plotnine/_utils/ipython.py#L78

Added line #L78 was not covered by tests
Loading

0 comments on commit 4bdd7cc

Please sign in to comment.