Module pyangstrom.visualization.region
Expand source code
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.axes import Axes
from matplotlib.patches import Rectangle, Wedge, Circle
from matplotlib.animation import Animation, FuncAnimation
from pyangstrom.transform import (
find_heat_source_direction,
Direction,
CartesianGeometry,
PolarGeometry,
Geometry,
Region,
collapse_region,
)
from pyangstrom.visualization.recording import plot_recording
MARGINS_MULTIPLIERS = {
'm': 1.,
'meters': 1.,
'mm': 1e3,
'millimeters': 1e3,
}
def add_cartesian_geometry(
ax: Axes,
geometry: CartesianGeometry,
) -> Axes:
ax.add_patch(Rectangle(
(geometry['min_x_pixels'], geometry['min_y_pixels']),
geometry['max_x_pixels'] - geometry['min_x_pixels'],
geometry['max_y_pixels'] - geometry['min_y_pixels'],
hatch='..',
edgecolor='red',
facecolor='none',
))
match find_heat_source_direction(geometry):
case Direction.LESSER_X:
ax.plot(
[geometry['heat_source_x_pixels'], geometry['heat_source_x_pixels']],
[geometry['min_y_pixels'], geometry['max_y_pixels']],
linewidth=2,
color='blue',
)
case Direction.GREATER_X:
ax.plot(
[geometry['heat_source_x_pixels'], geometry['heat_source_x_pixels']],
[geometry['min_y_pixels'], geometry['max_y_pixels']],
linewidth=2,
color='blue',
)
case Direction.LESSER_Y:
ax.plot(
[geometry['min_x_pixels'], geometry['max_x_pixels']],
[geometry['heat_source_y_pixels'], geometry['heat_source_y_pixels']],
linewidth=2,
color='blue',
)
case Direction.GREATER_Y:
ax.plot(
[geometry['min_x_pixels'], geometry['max_x_pixels']],
[geometry['heat_source_y_pixels'], geometry['heat_source_y_pixels']],
linewidth=2,
color='blue',
)
return ax
def add_polar_geometry(
ax: Axes,
geometry: PolarGeometry,
) -> Axes:
ax.add_patch(Wedge(
(geometry['center']['x_pixels'], geometry['center']['y_pixels']),
geometry['max_r_pixels'],
geometry['min_theta_degrees'],
geometry['max_theta_degrees'],
hatch='..',
edgecolor='red',
facecolor='none',
))
ax.add_patch(Circle(
(geometry['center']['x_pixels'], geometry['center']['y_pixels']),
geometry['min_r_pixels'],
linewidth=2,
edgecolor='r',
linestyle='solid',
facecolor='none',
))
ax.add_patch(Circle(
(geometry['center']['x_pixels'], geometry['center']['y_pixels']),
geometry['max_r_pixels'],
linewidth=2,
edgecolor='r',
linestyle='solid',
facecolor='none',
))
return ax
def add_geometry(
ax: Axes,
geometry: Geometry | list[Geometry],
) -> Axes:
if isinstance(geometry, list):
for g in geometry:
ax = add_geometry(ax, g)
return ax
match geometry:
case {'min_x_pixels': _}:
return add_cartesian_geometry(ax, geometry)
case {'center': _}:
return add_polar_geometry(ax, geometry)
def plot_geometry(
ax: Axes,
df_recording: pd.DataFrame,
geometry: Geometry | list[Geometry],
) -> Axes:
ax = plot_recording(ax, df_recording)
ax = add_geometry(ax, geometry)
return ax
def plot_spatiotemporal_heat_map(
ax: Axes,
region: Region,
) -> Axes:
region = collapse_region(region)
ax.imshow(region.temperatures_kelvin)
ax.set_title("Spatiotemporal Heat Map of Analysis Region")
ax.set_xlabel("Displacement from heat source (pixels)")
ax.set_ylabel("Frames elapsed")
return ax
def idx_displacement_to_label(idx_displacement: int):
idx_displacement += 1
if idx_displacement == 0:
return 'Line N'
elif idx_displacement < 0:
return f'Line N{idx_displacement}'
else:
return f'Line {idx_displacement}'
def plot_isotherms(
ax: Axes,
region: Region,
idx_displacements: list[int] = [0, -1],
use_timestamps: bool = False,
) -> Axes:
region = collapse_region(region)
if use_timestamps:
x = region.timestamps
ax.set_xlabel("Time")
else:
x = region.margins.seconds_elapsed
ax.set_xlabel("Time elapsed (seconds)")
labels = map(idx_displacement_to_label, idx_displacements)
for idx, label in zip(idx_displacements, labels):
ax.plot(x, region.temperatures_kelvin[:, idx], label=label)
ax.set_ylabel("Temperature (kelvin)")
ax.set_title("Temperatures of Isotherms Over Time")
ax.legend()
return ax
def plot_groups(ax: Axes, region: Region, use_timestamps: bool = False) -> Axes:
if use_timestamps:
x = region.timestamps
ax.set_xlabel("Time")
else:
x = region.margins.seconds_elapsed
ax.set_xlabel("Time elapsed (seconds)")
ax.plot(x, region.temperatures_kelvin.mean(axis=1), alpha=0.5)
ax.set_ylabel("Temperature (kelvin)")
ax.set_title("Average Temperatures of Grouped Nodes Over Time")
return ax
def animate_region(region: Region, displacements_unit: str = 'mm') -> Animation:
ax: Axes = None
fig, ax = plt.subplots()
region = collapse_region(region)
x = (MARGINS_MULTIPLIERS[displacements_unit]
* region.margins.displacements_meters)
ln, = ax.plot(x, region.temperatures_kelvin[0])
ax.set_ylim(
region.temperatures_kelvin.min(),
region.temperatures_kelvin.max(),
)
ax.set_title(region.timestamps[0].strftime('(%b %d) %H:%M:%S.%f'))
ax.set_xlabel(f"Displacement from heat source ({displacements_unit})")
ax.set_ylabel("Temperature (kelvin)")
def update(frame):
time, temps = frame
ln.set_data(x, temps)
ax.set_title(time.strftime('(%b %d) %H:%M:%S.%f'))
interval = 1e3 * (
region.timestamps
.to_series()
.diff()
.mode()
.item()
.total_seconds()
)
anim = FuncAnimation(
fig,
update,
iter(zip(region.timestamps, region.temperatures_kelvin)),
interval=interval,
repeat=False,
cache_frame_data=False,
)
return anim
Functions
def add_cartesian_geometry(ax: matplotlib.axes._axes.Axes, geometry: CartesianGeometry) ‑> matplotlib.axes._axes.Axes
-
Expand source code
def add_cartesian_geometry( ax: Axes, geometry: CartesianGeometry, ) -> Axes: ax.add_patch(Rectangle( (geometry['min_x_pixels'], geometry['min_y_pixels']), geometry['max_x_pixels'] - geometry['min_x_pixels'], geometry['max_y_pixels'] - geometry['min_y_pixels'], hatch='..', edgecolor='red', facecolor='none', )) match find_heat_source_direction(geometry): case Direction.LESSER_X: ax.plot( [geometry['heat_source_x_pixels'], geometry['heat_source_x_pixels']], [geometry['min_y_pixels'], geometry['max_y_pixels']], linewidth=2, color='blue', ) case Direction.GREATER_X: ax.plot( [geometry['heat_source_x_pixels'], geometry['heat_source_x_pixels']], [geometry['min_y_pixels'], geometry['max_y_pixels']], linewidth=2, color='blue', ) case Direction.LESSER_Y: ax.plot( [geometry['min_x_pixels'], geometry['max_x_pixels']], [geometry['heat_source_y_pixels'], geometry['heat_source_y_pixels']], linewidth=2, color='blue', ) case Direction.GREATER_Y: ax.plot( [geometry['min_x_pixels'], geometry['max_x_pixels']], [geometry['heat_source_y_pixels'], geometry['heat_source_y_pixels']], linewidth=2, color='blue', ) return ax
def add_geometry(ax: matplotlib.axes._axes.Axes, geometry: CartesianGeometry | PolarGeometry | list[CartesianGeometry | PolarGeometry]) ‑> matplotlib.axes._axes.Axes
-
Expand source code
def add_geometry( ax: Axes, geometry: Geometry | list[Geometry], ) -> Axes: if isinstance(geometry, list): for g in geometry: ax = add_geometry(ax, g) return ax match geometry: case {'min_x_pixels': _}: return add_cartesian_geometry(ax, geometry) case {'center': _}: return add_polar_geometry(ax, geometry)
def add_polar_geometry(ax: matplotlib.axes._axes.Axes, geometry: PolarGeometry) ‑> matplotlib.axes._axes.Axes
-
Expand source code
def add_polar_geometry( ax: Axes, geometry: PolarGeometry, ) -> Axes: ax.add_patch(Wedge( (geometry['center']['x_pixels'], geometry['center']['y_pixels']), geometry['max_r_pixels'], geometry['min_theta_degrees'], geometry['max_theta_degrees'], hatch='..', edgecolor='red', facecolor='none', )) ax.add_patch(Circle( (geometry['center']['x_pixels'], geometry['center']['y_pixels']), geometry['min_r_pixels'], linewidth=2, edgecolor='r', linestyle='solid', facecolor='none', )) ax.add_patch(Circle( (geometry['center']['x_pixels'], geometry['center']['y_pixels']), geometry['max_r_pixels'], linewidth=2, edgecolor='r', linestyle='solid', facecolor='none', )) return ax
def animate_region(region: Region, displacements_unit: str = 'mm') ‑> matplotlib.animation.Animation
-
Expand source code
def animate_region(region: Region, displacements_unit: str = 'mm') -> Animation: ax: Axes = None fig, ax = plt.subplots() region = collapse_region(region) x = (MARGINS_MULTIPLIERS[displacements_unit] * region.margins.displacements_meters) ln, = ax.plot(x, region.temperatures_kelvin[0]) ax.set_ylim( region.temperatures_kelvin.min(), region.temperatures_kelvin.max(), ) ax.set_title(region.timestamps[0].strftime('(%b %d) %H:%M:%S.%f')) ax.set_xlabel(f"Displacement from heat source ({displacements_unit})") ax.set_ylabel("Temperature (kelvin)") def update(frame): time, temps = frame ln.set_data(x, temps) ax.set_title(time.strftime('(%b %d) %H:%M:%S.%f')) interval = 1e3 * ( region.timestamps .to_series() .diff() .mode() .item() .total_seconds() ) anim = FuncAnimation( fig, update, iter(zip(region.timestamps, region.temperatures_kelvin)), interval=interval, repeat=False, cache_frame_data=False, ) return anim
def idx_displacement_to_label(idx_displacement: int)
-
Expand source code
def idx_displacement_to_label(idx_displacement: int): idx_displacement += 1 if idx_displacement == 0: return 'Line N' elif idx_displacement < 0: return f'Line N{idx_displacement}' else: return f'Line {idx_displacement}'
def plot_geometry(ax: matplotlib.axes._axes.Axes, df_recording: pandas.core.frame.DataFrame, geometry: CartesianGeometry | PolarGeometry | list[CartesianGeometry | PolarGeometry]) ‑> matplotlib.axes._axes.Axes
-
Expand source code
def plot_geometry( ax: Axes, df_recording: pd.DataFrame, geometry: Geometry | list[Geometry], ) -> Axes: ax = plot_recording(ax, df_recording) ax = add_geometry(ax, geometry) return ax
def plot_groups(ax: matplotlib.axes._axes.Axes, region: Region, use_timestamps: bool = False) ‑> matplotlib.axes._axes.Axes
-
Expand source code
def plot_groups(ax: Axes, region: Region, use_timestamps: bool = False) -> Axes: if use_timestamps: x = region.timestamps ax.set_xlabel("Time") else: x = region.margins.seconds_elapsed ax.set_xlabel("Time elapsed (seconds)") ax.plot(x, region.temperatures_kelvin.mean(axis=1), alpha=0.5) ax.set_ylabel("Temperature (kelvin)") ax.set_title("Average Temperatures of Grouped Nodes Over Time") return ax
def plot_isotherms(ax: matplotlib.axes._axes.Axes, region: Region, idx_displacements: list[int] = [0, -1], use_timestamps: bool = False) ‑> matplotlib.axes._axes.Axes
-
Expand source code
def plot_isotherms( ax: Axes, region: Region, idx_displacements: list[int] = [0, -1], use_timestamps: bool = False, ) -> Axes: region = collapse_region(region) if use_timestamps: x = region.timestamps ax.set_xlabel("Time") else: x = region.margins.seconds_elapsed ax.set_xlabel("Time elapsed (seconds)") labels = map(idx_displacement_to_label, idx_displacements) for idx, label in zip(idx_displacements, labels): ax.plot(x, region.temperatures_kelvin[:, idx], label=label) ax.set_ylabel("Temperature (kelvin)") ax.set_title("Temperatures of Isotherms Over Time") ax.legend() return ax
def plot_spatiotemporal_heat_map(ax: matplotlib.axes._axes.Axes, region: Region) ‑> matplotlib.axes._axes.Axes
-
Expand source code
def plot_spatiotemporal_heat_map( ax: Axes, region: Region, ) -> Axes: region = collapse_region(region) ax.imshow(region.temperatures_kelvin) ax.set_title("Spatiotemporal Heat Map of Analysis Region") ax.set_xlabel("Displacement from heat source (pixels)") ax.set_ylabel("Frames elapsed") return ax