Skip to content

Commit 59885b6

Browse files
authored
Add marginal plots with option to revert to classic plot (#310)
- Add marginal plots to default plotting settings - Original plot is available by passing `--classic` to the CLI or `classic=True` to the `DataMorpher` - Increased the minimum Python version to 3.10 - Increased the minimum Matplotlib version to 3.10 - Increased the minimum NumPy version to 1.23.0 - Increased the minimum pandas version to 2.1
1 parent 10ae6b4 commit 59885b6

File tree

23 files changed

+175
-49
lines changed

23 files changed

+175
-49
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ jobs:
4444
fail-fast: false
4545
matrix:
4646
os: [macos-latest, ubuntu-latest, windows-latest]
47-
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
47+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
4848

4949
env:
5050
MPLBACKEND: Agg # non-interactive backend for matplotlib
-353 KB
Loading

docs/_static/panda-to-star.gif

-250 KB
Loading

docs/quickstart.rst

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,24 @@ This produces the following animation in the newly-created ``morphed_data`` dire
4444
within your current working directory:
4545

4646
.. figure:: _static/panda-to-star.gif
47-
:alt: Morphing the panda dataset into the star shape.
47+
:alt: Morphing the panda dataset into the star shape with marginal plots.
4848
:align: center
4949

50-
Morphing the panda :class:`.Dataset` into the star :class:`.Shape`.
50+
Morphing the panda :class:`.Dataset` into the star :class:`.Shape` with marginal plots.
51+
52+
If you don't want the marginal plots (the histograms on the sides), you can use classic mode:
53+
54+
.. code:: console
55+
56+
$ data-morph --start-shape panda --target-shape star --classic
57+
58+
Animations generated in classic mode include only the scatter plot and the summary statistics:
59+
60+
.. figure:: _static/panda-to-star-classic.gif
61+
:alt: Morphing the panda dataset into the star shape using classic mode.
62+
:align: center
63+
64+
Morphing the panda :class:`.Dataset` into the star :class:`.Shape` using classic mode.
5165

5266
You can smooth the transition with the ``--ease`` or ``--ease-in`` and ``--ease-out`` flags.
5367
The ``--freeze`` flag allows you to start the animation with the specified number of frames

docs/tutorials/custom-datasets.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ Pass the path to the CSV file to use those points as the starting shape:
9999

100100
.. code:: console
101101
102-
$ data-morph --start-shape path/to/points.csv --target-shape wide_lines
102+
$ data-morph --start-shape path/to/points.csv --target-shape wide_lines --classic
103103
104104
Here is an example animation generated from a custom dataset:
105105

pyproject.toml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,13 @@ authors = [
2424
{ name = "Aaron Stevens", email = "[email protected]" },
2525
{ name = "Justin Matejka", email = "[email protected]" },
2626
]
27-
requires-python = ">=3.9"
27+
requires-python = ">=3.10"
2828
classifiers = [
2929
"Development Status :: 4 - Beta",
3030
"Framework :: Matplotlib",
3131
"Intended Audience :: Education",
3232
"Operating System :: OS Independent",
3333
"Programming Language :: Python :: 3 :: Only",
34-
"Programming Language :: Python :: 3.9",
3534
"Programming Language :: Python :: 3.10",
3635
"Programming Language :: Python :: 3.11",
3736
"Programming Language :: Python :: 3.12",
@@ -45,9 +44,9 @@ dynamic = [
4544
]
4645

4746
dependencies = [
48-
"matplotlib>=3.7",
49-
"numpy>=1.20",
50-
"pandas>=1.2",
47+
"matplotlib>=3.10",
48+
"numpy>=1.23.0",
49+
"pandas>=2.1",
5150
"rich>=13.9.4",
5251
]
5352

src/data_morph/bounds/_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def _validate_2d(data: Iterable[Number], name: str) -> Iterable[Number]:
2121
The validated data.
2222
"""
2323
if not (
24-
isinstance(data, (tuple, list))
24+
isinstance(data, tuple | list)
2525
and len(data) == 2
2626
and all(isinstance(x, Number) and not isinstance(x, bool) for x in data)
2727
):

src/data_morph/bounds/bounding_box.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def __init__(
3838
if isinstance(inclusive, bool):
3939
inclusive = [inclusive] * 2
4040
if not (
41-
isinstance(inclusive, (tuple, list))
41+
isinstance(inclusive, tuple | list)
4242
and len(inclusive) == 2
4343
and all(isinstance(x, bool) for x in inclusive)
4444
):
@@ -47,19 +47,19 @@ def __init__(
4747
' or a single Boolean value'
4848
)
4949

50-
self.x_bounds = (
50+
self.x_bounds: Interval = (
5151
x_bounds.clone()
5252
if isinstance(x_bounds, Interval)
5353
else Interval(x_bounds, inclusive[0])
5454
)
55-
"""Interval: The bounds for the x direction."""
55+
"""The bounds for the x direction."""
5656

57-
self.y_bounds = (
57+
self.y_bounds: Interval = (
5858
y_bounds.clone()
5959
if isinstance(y_bounds, Interval)
6060
else Interval(y_bounds, inclusive[1])
6161
)
62-
"""Interval: The bounds for the y direction."""
62+
"""The bounds for the y direction."""
6363

6464
self._bounds = (self.x_bounds, self.y_bounds)
6565

src/data_morph/cli.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,16 @@ def generate_parser() -> argparse.ArgumentParser:
198198
frame_group = parser.add_argument_group(
199199
'Animation Configuration', description='Customize aspects of the animation.'
200200
)
201+
frame_group.add_argument(
202+
'--classic',
203+
default=False,
204+
action='store_true',
205+
help=(
206+
'Whether to plot the original visualization, which consists of a scatter plot '
207+
'and the summary statistics. Otherwise, marginal plots will be included in '
208+
'addition to the classic plot.'
209+
),
210+
)
201211
frame_group.add_argument(
202212
'--ease',
203213
default=False,
@@ -294,6 +304,7 @@ def _morph(
294304
forward_only_animation=args.forward_only,
295305
num_frames=100,
296306
in_notebook=False,
307+
classic=args.classic,
297308
with_median=args.with_median,
298309
)
299310

@@ -409,6 +420,7 @@ def _serialize(args: argparse.Namespace, target_shapes: Sequence[str]) -> None:
409420
forward_only_animation=args.forward_only,
410421
num_frames=100,
411422
in_notebook=False,
423+
classic=args.classic,
412424
with_median=args.with_median,
413425
)
414426

0 commit comments

Comments
 (0)