Source code for data_morph.shapes.points
"""Shapes that are composed of points."""
import itertools
from numbers import Number
import numpy as np
from ..data.dataset import Dataset
from .bases.point_collection import PointCollection
[docs]class DotsGrid(PointCollection):
"""
Class representing a 3x3 grid of dots.
.. plot::
:scale: 75
:caption:
This shape is generated using the panda dataset.
from data_morph.data.loader import DataLoader
from data_morph.shapes.points import DotsGrid
_ = DotsGrid(DataLoader.load_dataset('panda')).plot()
Parameters
----------
dataset : Dataset
The starting dataset to morph into other shapes.
"""
def __init__(self, dataset: Dataset) -> None:
xlow, xhigh = dataset.df.x.quantile([0.05, 0.95]).tolist()
ylow, yhigh = dataset.df.y.quantile([0.05, 0.95]).tolist()
xmid = (xhigh + xlow) / 2
ymid = (yhigh + ylow) / 2
super().__init__(
*list(itertools.product([xlow, xmid, xhigh], [ylow, ymid, yhigh]))
)
def __str__(self) -> str:
return 'dots'
[docs]class DownParabola(PointCollection):
"""
Class for the down parabola shape.
.. plot::
:scale: 75
:caption:
This shape is generated using the panda dataset.
from data_morph.data.loader import DataLoader
from data_morph.shapes.points import DownParabola
_ = DownParabola(DataLoader.load_dataset('panda')).plot()
Parameters
----------
dataset : Dataset
The starting dataset to morph into other shapes.
"""
def __init__(self, dataset: Dataset) -> None:
x_bounds = dataset.data_bounds.x_bounds
xmin, xmax = x_bounds
xmid = xmax - x_bounds.range / 2
x_offset = x_bounds.range / 10
xmin += x_offset
xmax -= x_offset
ymin, ymax = dataset.data_bounds.y_bounds
poly = np.polynomial.Polynomial.fit([xmin, xmid, xmax], [ymin, ymax, ymin], 2)
super().__init__(*np.stack(poly.linspace(), axis=1))
def __str__(self) -> str:
return 'down_parab'
[docs]class UpParabola(PointCollection):
"""
Class for the up parabola shape.
.. plot::
:scale: 75
:caption:
This shape is generated using the panda dataset.
from data_morph.data.loader import DataLoader
from data_morph.shapes.points import UpParabola
_ = UpParabola(DataLoader.load_dataset('panda')).plot()
Parameters
----------
dataset : Dataset
The starting dataset to morph into other shapes.
"""
def __init__(self, dataset: Dataset) -> None:
x_bounds = dataset.data_bounds.x_bounds
xmin, xmax = x_bounds
xmid = xmax - x_bounds.range / 2
x_offset = x_bounds.range / 10
xmin += x_offset
xmax -= x_offset
ymin, ymax = dataset.data_bounds.y_bounds
poly = np.polynomial.Polynomial.fit([xmin, xmid, xmax], [ymax, ymin, ymax], 2)
super().__init__(*np.stack(poly.linspace(), axis=1))
def __str__(self) -> str:
return 'up_parab'
[docs]class Scatter(PointCollection):
"""
Class for the scatter shape: a cloud of randomly-scattered points.
.. plot::
:scale: 75
:caption:
This shape is generated using the panda dataset.
from data_morph.data.loader import DataLoader
from data_morph.shapes.points import Scatter
_ = Scatter(DataLoader.load_dataset('panda')).plot()
Parameters
----------
dataset : Dataset
The starting dataset to morph into other shapes.
"""
def __init__(self, dataset: Dataset) -> None:
rng = np.random.default_rng(1)
center = (dataset.df.x.mean(), dataset.df.y.mean())
points = [center]
max_radius = max(dataset.df.x.std(), dataset.df.y.std())
for radius in np.linspace(max_radius // 5, max_radius, num=5):
for angle in np.linspace(0, 360, num=50, endpoint=False):
points.append(
(
center[0]
+ np.cos(angle) * radius
+ rng.standard_normal() * max_radius,
center[1]
+ np.sin(angle) * radius
+ rng.standard_normal() * max_radius,
)
)
super().__init__(*points)
self._alpha = 0.4
[docs] def distance(self, x: Number, y: Number) -> int:
"""
No-op that allows returns 0 so that all perturbations are accepted.
Parameters
----------
x, y : int or float
Coordinates of a point in 2D space.
Returns
-------
int
Always returns 0 to allow for scattering of the points.
"""
return 0