data_prototype Documentation#
This is prototype development for the next generation of data structures for Matplotlib. This is the version “to throw away”. Everything in this repository should be considered experimental and used at you own risk.
Source : matplotlib/data-prototype
Examples#
Examples#
Examples used in the rapid prototyping of this project.
An simple scatter plot using ax.scatter
An simple scatter plot using PathCollectionWrapper
(Infinitly) Zoomable Mandelbrot Set
Using pint units with PathCollectionWrapper
Note
Go to the end to download the full example code
Errorbar graph#
Using containers.ArrayContainer
and wrappers.ErrorbarWrapper
to plot a graph with error bars.
import matplotlib.pyplot as plt
import numpy as np
from data_prototype.wrappers import ErrorbarWrapper
from data_prototype.containers import ArrayContainer
x = np.arange(10)
y = x**2
yupper = y + np.sqrt(y)
ylower = y - np.sqrt(y)
xupper = x + 0.5
xlower = x - 0.5
ac = ArrayContainer(
x=x, y=y, yupper=yupper, ylower=ylower, xlower=xlower, xupper=xupper
)
fig, ax = plt.subplots()
ew = ErrorbarWrapper(ac)
ax.add_artist(ew)
ax.set_xlim(0, 10)
ax.set_ylim(0, 100)
plt.show()
Note
Go to the end to download the full example code
An simple scatter plot using ax.scatter#
This is a quick comparison between the current Matplotlib scatter and
the version in data_prototype/axes.py
, which uses data containers
and a conversion pipeline.
This is here to show what does work and what does not work with the current implementation of container-based artist drawing.
import data_prototype.axes # side-effect registers projection # noqa
import matplotlib.pyplot as plt
fig = plt.figure()
newstyle = fig.add_subplot(2, 1, 1, projection="data-prototype")
oldstyle = fig.add_subplot(2, 1, 2)
newstyle.scatter([0, 1, 2], [2, 5, 1])
oldstyle.scatter([0, 1, 2], [2, 5, 1])
newstyle.scatter([0, 1, 2], [3, 1, 2])
oldstyle.scatter([0, 1, 2], [3, 1, 2])
# Autoscaling not working
newstyle.set_xlim(oldstyle.get_xlim())
newstyle.set_ylim(oldstyle.get_ylim())
plt.show()
Note
Go to the end to download the full example code
A re-binning histogram#
A containers.HistContainer
which is used with wrappers.StepWrapper
to provide a histogram which recomputes the bins based on a range selected.
import matplotlib.pyplot as plt
import numpy as np
from data_prototype.wrappers import StepWrapper
from data_prototype.containers import HistContainer
hc = HistContainer(
np.concatenate([np.random.randn(5000), 0.1 * np.random.randn(500) + 5]), 25
)
fig, (ax1, ax2) = plt.subplots(1, 2, layout="constrained")
for ax in (ax1, ax2):
ax.add_artist(StepWrapper(hc, lw=0, color="green"))
ax.set_ylim(0, 1)
ax1.set_xlim(-7, 7)
ax1.axvspan(4.5, 5.5, facecolor="none", zorder=-1, lw=5, edgecolor="k")
ax1.set_title("full range")
ax2.set_xlim(4.5, 5.5)
ax2.set_title("zoom to small peak")
plt.show()
Note
Go to the end to download the full example code
An simple scatter plot using PathCollectionWrapper#
A quick scatter plot using containers.ArrayContainer
and
wrappers.PathCollectionWrapper
.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.markers as mmarkers
from data_prototype.containers import ArrayContainer
from data_prototype.wrappers import PathCollectionWrapper
marker_obj = mmarkers.MarkerStyle("o")
cont = ArrayContainer(
x=np.array([0, 1, 2]),
y=np.array([1, 4, 2]),
paths=np.array([marker_obj.get_path()]),
sizes=np.array([12]),
edgecolors=np.array(["k"]),
facecolors=np.array(["C3"]),
)
fig, ax = plt.subplots()
ax.set_xlim(-0.5, 2.5)
ax.set_ylim(0, 5)
lw = PathCollectionWrapper(cont, offset_transform=ax.transData)
ax.add_artist(lw)
plt.show()
Note
Go to the end to download the full example code
A functional line#
Demonstrating the differences between containers.FuncContainer
and
containers.SeriesContainer
using wrappers.LineWrapper
.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from data_prototype.wrappers import LineWrapper
from data_prototype.containers import FuncContainer, SeriesContainer
fc = FuncContainer({"x": (("N",), lambda x: x), "y": (("N",), np.sin)})
lw = LineWrapper(fc, lw=5, color="green", label="sin (function)")
th = np.linspace(0, 2 * np.pi, 16)
sc = SeriesContainer(pd.Series(index=th, data=np.cos(th)), index_name="x", col_name="y")
lw2 = LineWrapper(sc, lw=3, color="blue", label="cos (pandas)")
fig, ax = plt.subplots()
ax.add_artist(lw)
ax.add_artist(lw2)
ax.set_xlim(0, np.pi * 4)
ax.set_ylim(-1.1, 1.1)
plt.show()
Note
Go to the end to download the full example code
A functional 2D image#
A 2D image generated using containers.FuncContainer
and
wrappers.ImageWrapper
.
import matplotlib.pyplot as plt
import numpy as np
from data_prototype.wrappers import ImageWrapper
from data_prototype.containers import FuncContainer
from matplotlib.colors import Normalize
fc = FuncContainer(
{},
xyfuncs={
"xextent": ((2,), lambda x, y: [x[0], x[-1]]),
"yextent": ((2,), lambda x, y: [y[0], y[-1]]),
"image": (
("N", "M"),
lambda x, y: np.sin(x).reshape(1, -1) * np.cos(y).reshape(-1, 1),
),
},
)
norm = Normalize(vmin=-1, vmax=1)
im = ImageWrapper(fc, norm=norm)
fig, ax = plt.subplots()
ax.add_artist(im)
ax.set_xlim(-5, 5)
ax.set_ylim(-5, 5)
fig.colorbar(im)
plt.show()
Note
Go to the end to download the full example code
Show data frame#
Wrapping a pandas.DataFrame
using containers.DataFrameContainer
and wrappers.LineWrapper
.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from data_prototype.wrappers import LineWrapper
from data_prototype.containers import DataFrameContainer
th = np.linspace(0, 4 * np.pi, 256)
dc1 = DataFrameContainer(
pd.DataFrame({"x": th, "y": np.cos(th)}), index_name=None, col_names=lambda n: n
)
df = pd.DataFrame(
{
"cos": np.cos(th),
"sin": np.sin(th),
},
index=th,
)
dc2 = DataFrameContainer(df, index_name="x", col_names={"sin": "y"})
dc3 = DataFrameContainer(df, index_name="x", col_names={"cos": "y"})
fig, (ax1, ax2) = plt.subplots(2, 1)
ax1.add_artist(LineWrapper(dc1, lw=5, color="green", label="sin"))
ax2.add_artist(LineWrapper(dc2, lw=5, color="green", label="sin"))
ax2.add_artist(LineWrapper(dc3, lw=5, color="blue", label="cos"))
for ax in (ax1, ax2):
ax.set_xlim(0, np.pi * 4)
ax.set_ylim(-1.1, 1.1)
plt.show()
Note
Go to the end to download the full example code
(Infinitly) Zoomable Mandelbrot Set#
A mandelbrot set which is computed using a containers.FuncContainer
and represented using a wrappers.ImageWrapper
.
The mandelbrot recomputes as it is zoomed in and/or panned.
import matplotlib.pyplot as plt
import numpy as np
from data_prototype.wrappers import ImageWrapper
from data_prototype.containers import FuncContainer
from matplotlib.colors import Normalize
maxiter = 75
def mandelbrot_set(X, Y, maxiter, *, horizon=3, power=2):
C = X + Y[:, None] * 1j
N = np.zeros_like(C, dtype=int)
Z = np.zeros_like(C)
for n in range(maxiter):
I = abs(Z) < horizon
N += I
Z[I] = Z[I] ** power + C[I]
N[N == maxiter] = -1
return Z, N
fc = FuncContainer(
{},
xyfuncs={
"xextent": ((2,), lambda x, y: [x[0], x[-1]]),
"yextent": ((2,), lambda x, y: [y[0], y[-1]]),
"image": (("N", "M"), lambda x, y: mandelbrot_set(x, y, maxiter)[1]),
},
)
cmap = plt.get_cmap()
cmap.set_under("w")
im = ImageWrapper(fc, norm=Normalize(0, maxiter), cmap=cmap)
fig, ax = plt.subplots()
ax.add_artist(im)
ax.set_xlim(-1, 1)
ax.set_ylim(-1, 1)
ax.set_aspect("equal")
fig.colorbar(im)
plt.show()
Total running time of the script: (0 minutes 1.536 seconds)
Note
Go to the end to download the full example code
Custom bivariate colormap#
Using nu
functions to account for two values when computing the color
of each pixel.
import matplotlib.pyplot as plt
import numpy as np
from data_prototype.wrappers import ImageWrapper
from data_prototype.containers import FuncContainer
from data_prototype.conversion_node import FunctionConversionNode
from matplotlib.colors import hsv_to_rgb
def func(x, y):
return (
(np.sin(x).reshape(1, -1) * np.cos(y).reshape(-1, 1)) ** 2,
np.arctan2(np.cos(y).reshape(-1, 1), np.sin(x).reshape(1, -1)),
)
def image_nu(image):
saturation, angle = image
hue = (angle + np.pi) / (2 * np.pi)
value = np.ones_like(hue)
return np.clip(hsv_to_rgb(np.stack([hue, saturation, value], axis=2)), 0, 1)
fc = FuncContainer(
{},
xyfuncs={
"xextent": ((2,), lambda x, y: [x[0], x[-1]]),
"yextent": ((2,), lambda x, y: [y[0], y[-1]]),
"image": (("N", "M", 2), func),
},
)
im = ImageWrapper(fc, FunctionConversionNode.from_funcs({"image": image_nu}))
fig, ax = plt.subplots()
ax.add_artist(im)
ax.set_xlim(-5, 5)
ax.set_ylim(-5, 5)
plt.show()
Note
Go to the end to download the full example code
Using pint units with PathCollectionWrapper#
Using third party units functionality in conjunction with Matplotlib Axes
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.markers as mmarkers
from data_prototype.containers import ArrayContainer
from data_prototype.conversion_node import DelayedConversionNode
from data_prototype.wrappers import PathCollectionWrapper
import pint
ureg = pint.UnitRegistry()
ureg.setup_matplotlib()
marker_obj = mmarkers.MarkerStyle("o")
cont = ArrayContainer(
x=np.array([0, 1, 2]) * ureg.m,
y=np.array([1, 4, 2]) * ureg.m,
paths=np.array([marker_obj.get_path()]),
sizes=np.array([12]),
edgecolors=np.array(["k"]),
facecolors=np.array(["C3"]),
)
fig, ax = plt.subplots()
ax.set_xlim(-0.5, 7)
ax.set_ylim(0, 5)
# DelayedConversionNode is used to identify the keys which undergo unit transformations
# The actual method which does conversions in this example is added by the
# `Axis`/`Axes`, but `PathCollectionWrapper` does not natively interact with the units.
xconv = DelayedConversionNode.from_keys(("x",), converter_key="xunits")
yconv = DelayedConversionNode.from_keys(("y",), converter_key="yunits")
lw = PathCollectionWrapper(cont, [xconv, yconv], offset_transform=ax.transData)
ax.add_artist(lw)
ax.xaxis.set_units(ureg.feet)
ax.yaxis.set_units(ureg.m)
plt.show()
Note
Go to the end to download the full example code
Simple patch artists#
Draw two fully specified rectangle patches.
Demonstrates patches.RectangleWrapper
using
containers.ArrayContainer
.
import numpy as np
import matplotlib.pyplot as plt
from data_prototype.containers import ArrayContainer
from data_prototype.patches import RectangleWrapper
cont1 = ArrayContainer(
x=np.array([-3]),
y=np.array([0]),
width=np.array([2]),
height=np.array([3]),
angle=np.array([0]),
rotation_point=np.array(["center"]),
edgecolor=np.array([0, 0, 0]),
facecolor=np.array([0.0, 0.7, 0, 0.5]),
linewidth=np.array([3]),
linestyle=np.array(["-"]),
antialiased=np.array([True]),
hatch=np.array(["*"]),
fill=np.array([True]),
capstyle=np.array(["round"]),
joinstyle=np.array(["miter"]),
)
cont2 = ArrayContainer(
x=np.array([0]),
y=np.array([1]),
width=np.array([2]),
height=np.array([3]),
angle=np.array([30]),
rotation_point=np.array(["center"]),
edgecolor=np.array([0, 0, 0]),
facecolor=np.array([0.7, 0, 0]),
linewidth=np.array([6]),
linestyle=np.array(["-"]),
antialiased=np.array([True]),
hatch=np.array([""]),
fill=np.array([True]),
capstyle=np.array(["butt"]),
joinstyle=np.array(["round"]),
)
fig, ax = plt.subplots()
ax.set_xlim(-5, 5)
ax.set_ylim(0, 5)
rect1 = RectangleWrapper(cont1, {})
rect2 = RectangleWrapper(cont2, {})
ax.add_artist(rect1)
ax.add_artist(rect2)
ax.set_aspect(1)
plt.show()
Note
Go to the end to download the full example code
Mapping Line Properties#
Leveraging the converter functions to transform users space data to visualization data.
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.colors import Normalize
from data_prototype.wrappers import LineWrapper, FormattedText
from data_prototype.containers import ArrayContainer
from data_prototype.conversion_node import FunctionConversionNode
cmap = plt.colormaps["viridis"]
cmap.set_over("k")
cmap.set_under("r")
norm = Normalize(1, 8)
line_converter = FunctionConversionNode.from_funcs(
{
# arbitrary functions
"lw": lambda lw: min(1 + lw, 5),
# standard color mapping
"color": lambda j: cmap(norm(j)),
# categorical
"ls": lambda cat: {"A": "-", "B": ":", "C": "--"}[cat[()]],
},
)
text_converter = FunctionConversionNode.from_funcs(
{
"text": lambda j, cat: f"index={j[()]} class={cat[()]!r}",
"y": lambda j: j,
"x": lambda x: 2 * np.pi,
},
)
th = np.linspace(0, 2 * np.pi, 128)
delta = np.pi / 9
fig, ax = plt.subplots()
for j in range(10):
ac = ArrayContainer(
**{
"x": th,
"y": np.sin(th + j * delta) + j,
"j": np.asarray(j),
"lw": np.asarray(j),
"cat": np.asarray({0: "A", 1: "B", 2: "C"}[j % 3]),
}
)
ax.add_artist(
LineWrapper(
ac,
line_converter,
)
)
ax.add_artist(
FormattedText(
ac,
text_converter,
x=2 * np.pi,
ha="right",
bbox={"facecolor": "gray", "alpha": 0.5},
)
)
ax.set_xlim(0, np.pi * 2)
ax.set_ylim(-1.1, 10.1)
plt.show()
Note
Go to the end to download the full example code
Dynamic Downsampling#
Generates a large image with three levels of detail.
When zoomed out, appears as a difference of 2D Gaussians. At medium zoom, a diagonal sinusoidal pattern is apparent. When zoomed in close, noise is visible.
The image is dynamically subsampled using a local mean which hides the finer details.
from typing import Tuple, Dict, Any, Union
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib.colors import Normalize
import numpy as np
from data_prototype.description import Desc, desc_like
from data_prototype.wrappers import ImageWrapper
from skimage.transform import downscale_local_mean
x = y = np.linspace(-3, 3, 3000)
X, Y = np.meshgrid(x, y)
Z1 = np.exp(-(X**2) - Y**2) + 0.08 * np.sin(50 * (X + Y))
Z2 = np.exp(-((X - 1) ** 2) - (Y - 1) ** 2)
Z = (Z1 - Z2) * 2
Z += np.random.random(Z.shape) - 0.5
class Subsample:
def describe(self):
return {
"xextent": Desc([2], float),
"yextent": Desc([2], float),
"image": Desc([], float),
}
def query(
self,
graph,
parent_coordinates="axes",
) -> Tuple[Dict[str, Any], Union[str, int]]:
desc = Desc(("N",), np.dtype("f8"), coordinates="data")
xy = {"x": desc, "y": desc}
data_lim = graph.evaluator(xy, desc_like(xy, coordinates="axes")).inverse
pts = data_lim.evaluate({"x": (0, 1), "y": (0, 1)})
x1, x2 = pts["x"]
y1, y2 = pts["y"]
xi1 = np.argmin(np.abs(x - x1))
yi1 = np.argmin(np.abs(y - y1))
xi2 = np.argmin(np.abs(x - x2))
yi2 = np.argmin(np.abs(y - y2))
xscale = int(np.ceil((xi2 - xi1) / 50))
yscale = int(np.ceil((yi2 - yi1) / 50))
return {
"xextent": [x1, x2],
"yextent": [y1, y2],
"image": downscale_local_mean(Z[xi1:xi2, yi1:yi2], (xscale, yscale)),
}, hash((x1, x2, y1, y2))
sub = Subsample()
cmap = mpl.colormaps["coolwarm"]
norm = Normalize(-2.2, 2.2)
im = ImageWrapper(sub, cmap=cmap, norm=norm)
fig, ax = plt.subplots()
ax.add_artist(im)
ax.set_xlim(-3, 3)
ax.set_ylim(-3, 3)
plt.show()
Note
Go to the end to download the full example code
An animated line#
An animated line using a custom container class,
wrappers.LineWrapper
, and wrappers.FormattedText
.
import time
from typing import Dict, Tuple, Any, Union
from functools import partial
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from data_prototype.conversion_edge import Graph
from data_prototype.description import Desc
from data_prototype.conversion_node import FunctionConversionNode
from data_prototype.wrappers import LineWrapper, FormattedText
class SinOfTime:
N = 1024
# cycles per minutes
scale = 10
def describe(self):
return {
"x": Desc([self.N], float),
"y": Desc([self.N], float),
"phase": Desc([], float),
"time": Desc([], float),
}
def query(
self,
graph: Graph,
parent_coordinates: str = "axes",
) -> Tuple[Dict[str, Any], Union[str, int]]:
th = np.linspace(0, 2 * np.pi, self.N)
def next_time():
cur_time = time.time()
phase = 2 * np.pi * (self.scale * cur_time % 60) / 60
return {
"x": th,
"y": np.sin(th + phase),
"phase": phase,
"time": cur_time,
}, hash(cur_time)
return next_time()
def update(frame, art):
return art
sot_c = SinOfTime()
lw = LineWrapper(sot_c, lw=5, color="green", label="sin(time)")
fc = FormattedText(
sot_c,
FunctionConversionNode.from_funcs(
{"text": lambda phase: f"ϕ={phase:.2f}", "x": lambda: 2 * np.pi, "y": lambda: 1}
),
ha="right",
)
fig, ax = plt.subplots()
ax.add_artist(lw)
ax.add_artist(fc)
ax.set_xlim(0, 2 * np.pi)
ax.set_ylim(-1.1, 1.1)
ani = FuncAnimation(
fig,
partial(update, art=(lw, fc)),
frames=25,
interval=1000 / 60,
# TODO: blitting does not work because wrappers do not inherent from Artist
# blit=True,
)
plt.show()
Total running time of the script: (0 minutes 3.246 seconds)
Note
Go to the end to download the full example code
An animated lissajous ball#
Inspired by https://twitter.com/_brohrer_/status/1584681864648065027
An animated scatter plot using a custom container and wrappers.PathCollectionWrapper
import time
from typing import Dict, Tuple, Any, Union
from functools import partial
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.markers as mmarkers
from matplotlib.animation import FuncAnimation
from data_prototype.conversion_edge import Graph
from data_prototype.description import Desc
from data_prototype.wrappers import PathCollectionWrapper
class Lissajous:
N = 1024
# cycles per minutes
scale = 2
def describe(self):
return {
"x": Desc([self.N], float),
"y": Desc([self.N], float),
"time": Desc([], float),
"sizes": Desc([], float),
"paths": Desc([], float),
"edgecolors": Desc([], str),
"facecolors": Desc([self.N], str),
}
def query(
self,
graph: Graph,
parent_coordinates: str = "axes",
) -> Tuple[Dict[str, Any], Union[str, int]]:
def next_time():
cur_time = time.time()
cur_time = np.array(
[cur_time, cur_time - 0.1, cur_time - 0.2, cur_time - 0.3]
)
phase = 15 * np.pi * (self.scale * cur_time % 60) / 150
marker_obj = mmarkers.MarkerStyle("o")
return {
"x": np.cos(5 * phase),
"y": np.sin(3 * phase),
"sizes": np.array([256]),
"paths": [
marker_obj.get_path().transformed(marker_obj.get_transform())
],
"edgecolors": "k",
"facecolors": ["#4682b4ff", "#82b446aa", "#46b48288", "#8246b433"],
"time": cur_time[0],
}, hash(cur_time[0])
return next_time()
def update(frame, art):
return art
sot_c = Lissajous()
fig, ax = plt.subplots()
ax.set_xlim(-1.1, 1.1)
ax.set_ylim(-1.1, 1.1)
lw = PathCollectionWrapper(sot_c, offset_transform=ax.transData)
ax.add_artist(lw)
# ax.set_xticks([])
# ax.set_yticks([])
ax.set_aspect(1)
ani = FuncAnimation(
fig,
partial(update, art=(lw,)),
frames=60,
interval=1000 / 100 * 15,
# TODO: blitting does not work because wrappers do not inherent from Artist
# blit=True,
)
plt.show()
Total running time of the script: (0 minutes 6.889 seconds)
Note
Go to the end to download the full example code
Slider#
In this example, sliders are used to control the frequency and amplitude of a sine wave.
import inspect
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button
from data_prototype.wrappers import LineWrapper
from data_prototype.containers import FuncContainer
from data_prototype.conversion_node import FunctionConversionNode
class SliderContainer(FuncContainer):
def __init__(self, xfuncs, /, **sliders):
self._sliders = sliders
for slider in sliders.values():
slider.on_changed(
lambda _, sld=slider: sld.ax.figure.canvas.draw_idle(),
)
def get_needed_keys(f, offset=1):
return tuple(inspect.signature(f).parameters)[offset:]
super().__init__(
{
k: (
s,
# this line binds the correct sliders to the functions
# and makes lambdas that match the API FuncContainer needs
lambda x, keys=get_needed_keys(f), f=f: f(
x, *(sliders[k].val for k in keys)
),
)
for k, (s, f) in xfuncs.items()
},
)
def _query_hash(self, graph, parent_coordinates):
key = super()._query_hash(graph, parent_coordinates)
# inject the slider values into the hashing logic
return hash((key, tuple(s.val for s in self._sliders.values())))
# Define initial parameters
init_amplitude = 5
init_frequency = 3
# Create the figure and the line that we will manipulate
fig, ax = plt.subplots()
ax.set_xlim(0, 1)
ax.set_ylim(-7, 7)
ax.set_xlabel("Time [s]")
# adjust the main plot to make room for the sliders
fig.subplots_adjust(left=0.25, bottom=0.25, right=0.75)
# Make a horizontal slider to control the frequency.
axfreq = fig.add_axes([0.25, 0.1, 0.65, 0.03])
freq_slider = Slider(
ax=axfreq,
label="Frequency [Hz]",
valmin=0.1,
valmax=30,
valinit=init_frequency,
)
# Make a vertically oriented slider to control the amplitude
axamp = fig.add_axes([0.1, 0.25, 0.0225, 0.63])
amp_slider = Slider(
ax=axamp,
label="Amplitude",
valmin=0,
valmax=10,
valinit=init_amplitude,
orientation="vertical",
)
# Make a vertically oriented slider to control the phase
axphase = fig.add_axes([0.85, 0.25, 0.0225, 0.63])
phase_slider = Slider(
ax=axphase,
label="Phase [rad]",
valmin=-2 * np.pi,
valmax=2 * np.pi,
valinit=0,
orientation="vertical",
)
# pick a cyclic color map
cmap = plt.get_cmap("twilight")
# set up the data container
fc = SliderContainer(
{
# the x data does not need the sliders values
"x": (("N",), lambda t: t),
"y": (
("N",),
# the y data needs all three sliders
lambda t, amplitude, frequency, phase: amplitude
* np.sin(2 * np.pi * frequency * t + phase),
),
# the color data has to take the x (because reasons), but just
# needs the phase
"color": ((1,), lambda _, phase: phase),
},
# bind the sliders to the data container
amplitude=amp_slider,
frequency=freq_slider,
phase=phase_slider,
)
lw = LineWrapper(
fc,
# color map phase (scaled to 2pi and wrapped to [0, 1])
FunctionConversionNode.from_funcs(
{"color": lambda color: cmap((color / (2 * np.pi)) % 1)}
),
lw=5,
)
ax.add_artist(lw)
# Create a `matplotlib.widgets.Button` to reset the sliders to initial values.
resetax = fig.add_axes([0.8, 0.025, 0.1, 0.04])
button = Button(resetax, "Reset", hovercolor="0.975")
button.on_clicked(
lambda _: [sld.reset() for sld in (freq_slider, amp_slider, phase_slider)]
)
plt.show()
API#
Containers#
- class data_prototype.containers.DataContainer(*args, **kwargs)#
-
- query(graph: Graph, parent_coordinates: str = 'axes', /) Tuple[Dict[str, Any], str | int] #
Query the data container for data.
We are given the data limits and the screen size so that we have an estimate of how finely (or not) we need to sample the data we wrapping.
- Parameters:
- coord_transformmatplotlib.transform.Transform
Must go from axes fraction space -> data space
- size2 integers
xpixels, ypixels
The size in screen / render units that we have to fill.
- Returns:
- dataDict[str, Any]
The values are really array-likes, but 🤷 how to spell that in typing given that the dimension and type will depend on the key / how it is set up and the size may depend on the input values
- cache_keystr
This is a key that clients can use to cache down-stream computations on this data.
- class data_prototype.containers.DataFrameContainer(df: DataFrame, *, col_names: Callable[[str], str] | Dict[str, str], index_name: str | None = None)#
- class data_prototype.containers.DataUnion(*data: DataContainer)#
- describe()#
- class data_prototype.containers.FuncContainer(xfuncs: Dict[str, Tuple[Tuple[int | str, ...], Callable[[Any], Any]]] | None = None, yfuncs: Dict[str, Tuple[Tuple[int | str, ...], Callable[[Any], Any]]] | None = None, xyfuncs: Dict[str, Tuple[Tuple[int | str, ...], Callable[[Any, Any], Any]]] | None = None)#
- exception data_prototype.containers.NoNewKeys#
- class data_prototype.containers.RandomContainer(**shapes)#
Wrappers#
- class data_prototype.wrappers.ErrorbarWrapper(data: DataContainer, converters=None, /, **kwargs)#
- draw(renderer)#
Draw the Artist (and its children) using the given renderer.
This has no effect if the artist is not visible (.Artist.get_visible returns False).
- Parameters:
- renderer~matplotlib.backend_bases.RendererBase subclass.
Notes
This method is overridden in the Artist subclasses.
- set(*, agg_filter=<UNSET>, alpha=<UNSET>, animated=<UNSET>, clip_box=<UNSET>, clip_on=<UNSET>, clip_path=<UNSET>, gid=<UNSET>, in_layout=<UNSET>, label=<UNSET>, mouseover=<UNSET>, path_effects=<UNSET>, picker=<UNSET>, rasterized=<UNSET>, sketch_params=<UNSET>, snap=<UNSET>, transform=<UNSET>, url=<UNSET>, visible=<UNSET>, zorder=<UNSET>)#
Set multiple properties at once.
Supported properties are
- Properties:
agg_filter: a filter function, which takes a (m, n, 3) float array and a dpi value, and returns a (m, n, 3) array and two offsets from the bottom left corner of the image alpha: scalar or None animated: unknown clip_box: unknown clip_on: bool clip_path: unknown figure: unknown gid: str in_layout: bool label: object mouseover: bool path_effects: list of .AbstractPathEffect picker: unknown rasterized: bool sketch_params: unknown snap: unknown transform: unknown url: str visible: bool zorder: float
- class data_prototype.wrappers.FormattedText(data: DataContainer, converters=None, /, **kwargs)#
- draw(renderer)#
- class data_prototype.wrappers.ImageWrapper(data: DataContainer, converters=None, /, cmap=None, norm=None, **kwargs)#
- draw(renderer)#
- class data_prototype.wrappers.LineWrapper(data: DataContainer, converters=None, /, **kwargs)#
- draw(renderer)#
- class data_prototype.wrappers.MultiProxyWrapper(data, converters: ConversionNode | list[ConversionNode] | None, **kwargs)#
- draw(renderer)#
Draw the Artist (and its children) using the given renderer.
This has no effect if the artist is not visible (.Artist.get_visible returns False).
- Parameters:
- renderer~matplotlib.backend_bases.RendererBase subclass.
Notes
This method is overridden in the Artist subclasses.
- get_children()#
Return a list of the child .Artists of this .Artist.
- set(*, agg_filter=<UNSET>, alpha=<UNSET>, animated=<UNSET>, clip_box=<UNSET>, clip_on=<UNSET>, clip_path=<UNSET>, gid=<UNSET>, in_layout=<UNSET>, label=<UNSET>, mouseover=<UNSET>, path_effects=<UNSET>, picker=<UNSET>, rasterized=<UNSET>, sketch_params=<UNSET>, snap=<UNSET>, transform=<UNSET>, url=<UNSET>, visible=<UNSET>, zorder=<UNSET>)#
Set multiple properties at once.
Supported properties are
- Properties:
agg_filter: a filter function, which takes a (m, n, 3) float array and a dpi value, and returns a (m, n, 3) array and two offsets from the bottom left corner of the image alpha: scalar or None animated: bool clip_box: ~matplotlib.transforms.BboxBase or None clip_on: bool clip_path: Patch or (Path, Transform) or None figure: ~matplotlib.figure.Figure gid: str in_layout: bool label: object mouseover: bool path_effects: list of .AbstractPathEffect picker: None or bool or float or callable rasterized: bool sketch_params: (scale: float, length: float, randomness: float) snap: bool or None transform: ~matplotlib.transforms.Transform url: str visible: bool zorder: float
- set_animated(*args, **kwargs)#
broadcasts set_animated to children
- set_clip_box(*args, **kwargs)#
broadcasts set_clip_box to children
- set_clip_path(*args, **kwargs)#
broadcasts set_clip_path to children
- set_figure(*args, **kwargs)#
broadcasts set_figure to children
- set_picker(*args, **kwargs)#
broadcasts set_picker to children
- set_sketch_params(*args, **kwargs)#
broadcasts set_sketch_params to children
- set_snap(*args, **kwargs)#
broadcasts set_snap to children
- set_transform(*args, **kwargs)#
broadcasts set_transform to children
- class data_prototype.wrappers.PathCollectionWrapper(data: DataContainer, converters=None, /, **kwargs)#
- draw(renderer)#
- class data_prototype.wrappers.ProxyWrapper(data, converters: ConversionNode | list[ConversionNode] | None, **kwargs)#
- class data_prototype.wrappers.ProxyWrapperBase(data, converters: ConversionNode | list[ConversionNode] | None, **kwargs)#
- axes: _Axes#
- data: DataContainer#
- draw(renderer)#
- class data_prototype.wrappers.StepWrapper(data: DataContainer, converters=None, /, **kwargs)#
- draw(renderer)#
- class data_prototype.patches.PatchWrapper(data: DataContainer, converters=None, /, **kwargs)#
- draw(renderer)#
- class data_prototype.patches.RectangleWrapper(data: DataContainer, converters=None, /, **kwargs)#
Backmatter#
Installation#
At the command line:
$ pip install git+https://github.com/tacaswell/data-prototype.git@main
Release History#
Initial Release (YYYY-MM-DD)#
Minimum Version of Python and NumPy#
This project supports at least the minor versions of Python initially released 42 months prior to a planned project release date.
The project will always support at least the 2 latest minor versions of Python.
The project will support minor versions of
numpy
initially released in the 24 months prior to a planned project release date or the oldest version that supports the minimum Python version (whichever is higher).The project will always support at least the 3 latest minor versions of NumPy.
The minimum supported version of Python will be set to
python_requires
in setup
. All supported minor versions of
Python will be in the test matrix and have binary artifacts built
for releases.
The project should adjust upward the minimum Python and NumPy version support on every minor and major release, but never on a patch release.
This is consistent with NumPy NEP 29.