Source code for pymove.visualization.matplotlib

"""
Matplolib operations.

show_object_id_by_date,
plot_trajectories,
plot_trajectory_by_id,
plot_grid_polygons,
plot_all_features
plot_coords,
plot_bounds,
plot_line

"""
from __future__ import annotations

from typing import TYPE_CHECKING, Any, Callable

import matplotlib.pyplot as plt
from matplotlib.pyplot import axes, figure
from pandas.core.frame import DataFrame
from shapely.geometry import LineString, MultiLineString
from shapely.geometry.base import BaseGeometry

from pymove.core.grid import Grid
from pymove.utils.constants import (
    DATE,
    DAY,
    HOUR,
    LATITUDE,
    LONGITUDE,
    PERIOD,
    POLYGON,
    TRAJ_ID,
)

if TYPE_CHECKING:
    from pymove.core.dask import DaskMoveDataFrame
    from pymove.core.pandas import PandasMoveDataFrame


[docs]def show_object_id_by_date( move_data: 'PandasMoveDataFrame' | 'DaskMoveDataFrame', kind: list | None = None, figsize: tuple[float, float] = (21, 9), return_fig: bool = False, save_fig: bool = False, name: str = 'shot_points_by_date.png', ) -> figure | None: """ Generates four visualizations based on datetime feature. - Bar chart trajectories by day periods - Bar chart trajectories day of the week - Line chart trajectory by date - Line chart of trajectory by hours of the day. Parameters ---------- move_data : pymove.core.MoveDataFrameAbstract subclass. Input trajectory data. kind: list, optional Determines the kinds of each plot, by default None figsize : tuple, optional Represents dimensions of figure, by default (21,9). return_fig : bool, optional Represents whether or not to save the generated picture, by default False. save_fig : bool, optional Represents whether or not to save the generated picture, by default False. name : String, optional Represents name of a file, by default 'shot_points_by_date.png'. Returns ------- figure The generated picture or None References ---------- https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.pyplot.plot.html Examples -------- >>> from pymove.visualization.matplotlib import show_object_id_by_date >>> move_df.head() lat lon datetime id 0 39.984094 116.319236 2008-10-23 05:53:05 1 1 39.984198 116.319322 2008-10-23 05:53:06 1 2 39.984224 116.319402 2008-10-23 05:53:11 1 3 39.984211 116.319389 2008-10-23 05:53:16 2 4 39.984217 116.319422 2008-10-23 05:53:21 2 >>> show_object_id_by_date(move_df) """ if kind is None: kind = ['bar', 'bar', 'line', 'line'] fig, ax = plt.subplots(2, 2, figsize=figsize) columns = move_data.columns move_data.generate_date_features() move_data.generate_hour_features() move_data.generate_time_of_day_features() move_data.generate_day_of_the_week_features() move_data.groupby([PERIOD])[TRAJ_ID].nunique().plot( subplots=True, kind=kind[0], rot=0, ax=ax[0][0], fontsize=12 ) move_data.groupby([DAY])[TRAJ_ID].nunique().plot( subplots=True, kind=kind[1], ax=ax[0][1], rot=0, fontsize=12 ) move_data.groupby([DATE])[TRAJ_ID].nunique().plot( subplots=True, kind=kind[2], grid=True, ax=ax[1][0], rot=90, fontsize=12, ) move_data.groupby([HOUR])[TRAJ_ID].nunique().plot( subplots=True, kind=kind[3], grid=True, ax=ax[1][1], fontsize=12 ) if save_fig: plt.savefig(fname=name) to_drop = list(set(move_data.columns) - set(columns)) move_data.drop(columns=to_drop, inplace=True) if return_fig: return fig
[docs]def plot_trajectories( move_data: DataFrame, markers: str = 'o', markersize: float = 12, figsize: tuple[float, float] = (10, 10), return_fig: bool = False, save_fig: bool = False, name: str = 'trajectories.png', ) -> figure | None: """ Generate a visualization that show trajectories. Parameters ---------- move_data: dataframe Dataframe with trajectories markers : str, optional Represents visualization type marker, by default 'o' markersize : float, optional Represents visualization size marker, by default 12 figsize : tuple(float, float), optional Represents dimensions of figure, by default (10, 10) return_fig : bool, optional Represents whether or not to return the generated picture, by default False save_fig : bool, optional Represents whether or not to save the generated picture, by default False name : str, optional Represents name of a file, by default 'trajectories.png' Returns ------- figure The generated picture or None Examples -------- >>> from pymove.visualization.matplotlib import plot_trajectories >>> move_df.head() lat lon datetime id 0 39.984094 116.319236 2008-10-23 05:53:05 1 1 39.984198 116.319322 2008-10-23 05:53:06 1 2 39.984224 116.319402 2008-10-23 05:53:11 1 3 39.984211 116.319389 2008-10-23 05:53:16 2 4 39.984217 116.319422 2008-10-23 05:53:21 2 >>> plot_trajectories(move_df) """ fig = plt.figure(figsize=figsize) ids = move_data['id'].unique() for id_ in ids: self_id = move_data[move_data['id'] == id_] plt.plot( self_id[LONGITUDE], self_id[LATITUDE], markers, markersize=markersize, ) if save_fig: plt.savefig(fname=name) if return_fig: return fig
[docs]def plot_trajectory_by_id( move_data: DataFrame, id_: int | str, label: str = TRAJ_ID, feature: str | None = None, value: Any | None = None, linewidth: float = 3, markersize: float = 20, figsize: tuple[float, float] = (10, 10), return_fig: bool = False, save_fig: bool = False, name: str | None = None, ) -> figure | None: """ Generate a visualization that shows a trajectory with the specified tid. Parameters ---------- move_data: dataframe Dataframe with trajectories id_ : int, str Represents the trajectory tid label : str, optional Feature with trajectories tids, by default TID feature : str, optional Name of the feature to highlight on plot, by default None value : any, optional Value of the feature to be highlighted as green marker, by default None linewidth : float, optional Represents visualization size line, by default 2 markersize : float, optional Represents visualization size marker, by default 20 figsize : tuple(float, float), optional Represents dimensions of figure, by default (10, 10) return_fig : bool, optional Represents whether or not to return the generated picture, by default False save_fig : bool, optional Represents whether or not to save the generated picture, by default False name : str, optional Represents name of a file, by default None Returns ------- PandasMoveDataFrame', figure Trajectory with the specified tid. The generated picture. Raises ------ KeyError If the dataframe does not contains the TID feature IndexError If there is no trajectory with the tid passed Examples -------- >>> from pymove.visualization.matplotlib import plot_traj_by_id >>> move_df lat lon datetime id 0 39.984094 116.319236 2008-10-23 05:53:05 1 1 39.984198 116.319322 2008-10-23 05:53:06 1 2 39.984224 116.319402 2008-10-23 05:53:11 1 3 39.984211 116.319389 2008-10-23 05:53:16 2 4 39.984217 116.319422 2008-10-23 05:53:21 2 >>> plot_traj_by_id(move_df_3, 1, label='id) >>> plot_traj_by_id(move_df_3, 2, label='id) """ if label not in move_data: raise KeyError('%s feature not in dataframe' % label) df_ = move_data[move_data[label] == id_] if not len(df_): raise IndexError(f'No trajectory with {label} {id_} in dataframe') fig = plt.figure(figsize=figsize) if (not feature) or (not value) or (feature not in df_): plt.plot(df_[LONGITUDE], df_[LATITUDE]) plt.plot( df_.loc[:, LONGITUDE], df_.loc[:, LATITUDE], 'r.', markersize=markersize / 2 ) else: filter_ = df_[feature] == value df_nodes = df_.loc[filter_] df_points = df_.loc[~filter_] plt.plot(df_[LONGITUDE], df_[LATITUDE], linewidth=linewidth) plt.plot( df_nodes[LONGITUDE], df_nodes[LATITUDE], 'gs', markersize=markersize / 2 ) plt.plot( df_points[LONGITUDE], df_points[LATITUDE], 'r.', markersize=markersize / 2 ) plt.plot( df_.iloc[0][LONGITUDE], df_.iloc[0][LATITUDE], 'yo', markersize=markersize ) plt.plot( df_.iloc[-1][LONGITUDE], df_.iloc[-1][LATITUDE], 'yX', markersize=markersize ) if save_fig: if not name: name = 'trajectory_%s.png' % id_ plt.savefig(fname=name) if return_fig: return fig
[docs]def plot_grid_polygons( data: DataFrame, grid: Grid | None = None, markersize: float = 10, linewidth: float = 2, figsize: tuple[int, int] = (10, 10), return_fig: bool = False, save_fig: bool = False, name: str = 'grid.png', ) -> figure | None: """ Generate a visualization with grid polygons. Parameters ---------- data : DataFrame Input trajectory data markersize : float, optional Represents visualization size marker, by default 10 linewidth : float, optional Represents visualization size line, by default 2 figsize : tuple(int, int), optional Represents the size (float: width, float: height) of a figure, by default (10, 10) return_fig : bool, optional Represents whether or not to save the generated picture, by default False save_fig : bool, optional Wether to save the figure, by default False name : str, optional Represents name of a file, by default 'grid.png' Returns ------- figure The generated picture or None Raises ------ If the dataframe does not contains the POLYGON feature IndexError If there is no user with the id passed """ if POLYGON not in data: if grid is None: raise KeyError('POLYGON feature not in dataframe') data = grid.create_all_polygons_to_all_point_on_grid(data) data = data.copy() data.dropna(subset=[POLYGON], inplace=True) fig = plt.figure(figsize=figsize) for _, row in data.iterrows(): xs, ys = row[POLYGON].exterior.xy plt.plot(ys, xs, 'g', linewidth=linewidth, markersize=markersize) xs_start, ys_start = data.iloc[0][POLYGON].exterior.xy xs_end, ys_end = data.iloc[-1][POLYGON].exterior.xy plt.plot(ys_start, xs_start, 'bo', markersize=markersize * 1.5) plt.plot(ys_end, xs_end, 'bX', markersize=markersize * 1.5) if save_fig: plt.savefig(fname=name) if return_fig: return fig
[docs]def plot_all_features( move_data: DataFrame, dtype: Callable = float, figsize: tuple[float, float] = (21, 15), return_fig: bool = False, save_fig: bool = False, name: str = 'features.png', ) -> figure | None: """ Generate a visualization for each columns that type is equal dtype. Parameters ---------- move_data: dataframe Dataframe with trajectories dtype : callable, optional Represents column type, by default np.float64 figsize : tuple(float, float), optional Represents dimensions of figure, by default (21, 15) return_fig : bool, optional Represents whether or not to return the generated picture, by default False save_fig : bool, optional Represents whether or not to save the generated picture, by default False name : str, optional Represents name of a file, by default 'features.png' Returns ------- figure The generated picture or None Raises ------ AttributeError If there are no columns with the specified type Examples -------- >>> from pymove.visualization.matplotlib import plot_all_features >>> move_df.head() lat lon datetime id 0 39.984094 116.319236 2008-10-23 05:53:05 1 1 39.984198 116.319322 2008-10-23 05:53:06 1 2 39.984224 116.319402 2008-10-23 05:53:11 1 3 39.984211 116.319389 2008-10-23 05:53:16 2 4 39.984217 116.319422 2008-10-23 05:53:21 2 >>> plot_all_features(move_df) """ col_dtype = move_data.select_dtypes(include=[dtype]).columns tam = col_dtype.size if not tam: raise AttributeError('No columns with dtype %s.' % dtype) fig, ax = plt.subplots(tam, 1, figsize=figsize) ax_count = 0 for col in col_dtype: ax[ax_count].set_title(col) move_data[col].plot(subplots=True, ax=ax[ax_count]) ax_count += 1 if save_fig: plt.savefig(fname=name) if return_fig: return fig
[docs]def plot_coords(ax: axes, ob: BaseGeometry, color: str = 'r'): """ Plot the coordinates of each point of the object in a 2D chart. Parameters ---------- ax : axes Single axes object ob : geometry object Any geometric object color : str, optional Sets the geometric object color, by default 'r' Example ------- >>> from pymove.visualization.matplotlib import plot_coords >>> import matplotlib.pyplot as plt >>> coords = LineString([(1, 1), (1, 2), (2, 2), (2, 3)]) >>> _, ax = plt.subplots(figsize=(21, 9)) >>> plot_coords(ax, coords) """ x, y = ob.xy ax.plot(x, y, 'o', color=color, zorder=1)
[docs]def plot_bounds(ax: axes, ob: LineString | MultiLineString, color='b'): """ Plot the limits of geometric object. Parameters ---------- ax : axes Single axes object ob : LineString or MultiLineString Geometric object formed by lines. color : str, optional Sets the geometric object color, by default 'b' Example ------- >>> from pymove.visualization.matplotlib import plot_bounds >>> import matplotlib.pyplot as plt >>> bounds = LineString([(1, 1), (1, 2), (2, 2), (2, 3)]) >>> _, ax = plt.subplots(figsize=(21, 9)) >>> plot_bounds(ax, bounds) """ x, y = zip(*list((p.x, p.y) for p in ob.boundary)) ax.plot(x, y, '-', color=color, zorder=1)
[docs]def plot_line( ax: axes, ob: LineString, color: str = 'r', alpha: float = 0.7, linewidth: float = 3, solid_capstyle: str = 'round', zorder: float = 2 ): """ Plot a LineString. Parameters ---------- ax : axes Single axes object ob : LineString Sequence of points. color : str, optional Sets the line color, by default 'r' alpha : float, optional Defines the opacity of the line, by default 0.7 linewidth : float, optional Defines the line thickness, by default 3 solid_capstyle : str, optional Defines the style of the ends of the line, by default 'round' zorder : float, optional Determines the default drawing order for the axes, by default 2 Example ------- >>> from pymove.visualization.matplotlib import plot_line >>> import matplotlib.pyplot as plt >>> line = LineString([(1, 1), (1, 2), (2, 2), (2, 3)]) >>> _, ax = plt.subplots(figsize=(21, 9)) >>> plot_line(ax, line) """ x, y = ob.xy ax.plot( x, y, color=color, alpha=alpha, linewidth=linewidth, solid_capstyle=solid_capstyle, zorder=zorder )