Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add histogram api #317

Merged
merged 21 commits into from
Sep 20, 2023
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
791f3d4
Added the ability to create histograms for callbacks.
emori-ctc Jul 19, 2023
48db2fa
The process of creating histograms has been moved to Bokeh.
emori-ctc Jul 24, 2023
42ffda3
update bokeh.py
emori-ctc Jul 24, 2023
caa9fb3
Temporary push for source code verification
emori-ctc Aug 4, 2023
bf0447e
Push for code confirmation.
emori-ctc Aug 16, 2023
ae88a7f
Push for code confirmation.
emori-ctc Aug 17, 2023
34b6da9
The error that occurred in pytest has been resolved.
emori-ctc Aug 23, 2023
0f27d62
Merge branch 'main' into add_callback_hist
emori-ctc Aug 24, 2023
aa7609e
Resolved pytest error.
emori-ctc Aug 31, 2023
f4faa59
The points raised in the review have been corrected.
emori-ctc Aug 31, 2023
3e11290
rename variable
emori-ctc Aug 31, 2023
7c5ee50
The points raised in the review have been corrected.
emori-ctc Aug 31, 2023
73a3549
The points raised in the review have been corrected.
emori-ctc Sep 1, 2023
2a6c4eb
The points raised in the review have been corrected.
emori-ctc Sep 1, 2023
6402769
The points raised in the review have been corrected.
emori-ctc Sep 1, 2023
15f28f1
The graph legend has been corrected.
emori-ctc Sep 7, 2023
7b09242
The vertical axis has been changed to a probability notation.
emori-ctc Sep 8, 2023
94b87f7
autoware is now supported.
emori-ctc Sep 14, 2023
f874ae7
Removed unnecessary comment-outs.
emori-ctc Sep 15, 2023
dc53eeb
The conflict has been resolved.
emori-ctc Sep 15, 2023
bba9f86
The pytest error has been resolved.
emori-ctc Sep 15, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/caret_analyze/plot/histogram/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,12 @@

from .histogram import ResponseTimeHistPlot
from .histogram_factory import ResponseTimeHistPlotFactory
from .histogram_plot import HistogramPlot
from .histogram_plot_factory import HistogramPlotFactory

__all__ = [
'ResponseTimeHistPlot',
'ResponseTimeHistPlotFactory'
'ResponseTimeHistPlotFactory',
'HistogramPlotFactory',
'HistogramPlot'
]
118 changes: 118 additions & 0 deletions src/caret_analyze/plot/histogram/histogram_plot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# Copyright 2021 Research Institute of Systems Planning, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import annotations

from collections.abc import Sequence

from bokeh.plotting import Figure

from caret_analyze.record import Frequency, Latency, Period

import pandas as pd

from ..plot_base import PlotBase
from ..visualize_lib import VisualizeLibInterface
from ...exceptions import UnsupportedTypeError
from ...runtime import CallbackBase, Communication

MetricsTypes = Frequency | Latency | Period
HistTypes = CallbackBase | Communication

rokamu623 marked this conversation as resolved.
Show resolved Hide resolved

class HistogramPlot(PlotBase):
"""Class that provides API for histogram data."""

def __init__(
self,
metrics: list[MetricsTypes],
visualize_lib: VisualizeLibInterface,
target_objects: Sequence[HistTypes],
data_type: str
) -> None:
self._metrics = metrics
self._visualize_lib = visualize_lib
self._target_objects = target_objects
self._data_type = data_type

def to_dataframe(
self,
xaxis_type: str
) -> pd.DataFrame:
"""
Get data in pandas DataFrame format.

Parameters
----------
xaxis_type : str
Type of time for timestamp.

Returns
-------
pd.DataFrame

"""
raise NotImplementedError()

def figure(
self,
xaxis_type: str | None = None,
ywheel_zoom: bool | None = None,
full_legends: bool | None = None
) -> Figure:
"""
Get a histogram graph for each object using the bokeh library.

Parameters
----------
xaxis_type : str
Type of x-axis of the line graph to be plotted.
"system_time", "index", or "sim_time" can be specified, by default "system_time".
ywheel_zoom : bool
If True, the drawn graph can be expanded in the y-axis direction
by the mouse wheel, by default True.
full_legends : bool
If True, all legends are drawn
even if the number of legends exceeds the threshold, by default False.

Returns
-------
bokeh.plotting.Figure

Raises
------
UnsupportedTypeError
Argument xaxis_type is not "system_time", "index", or "sim_time".

"""
# Set default value
xaxis_type = xaxis_type or 'system_time'
ywheel_zoom = ywheel_zoom if ywheel_zoom is not None else True
full_legends = full_legends if full_legends is not None else False

# Validate
self._validate_xaxis_type(xaxis_type)

return self._visualize_lib.histogram(
self._metrics,
self._target_objects,
self._data_type
)

def _validate_xaxis_type(self, xaxis_type: str) -> None:
if xaxis_type not in ['system_time', 'sim_time', 'index']:
raise UnsupportedTypeError(
f'Unsupported xaxis_type. xaxis_type = {xaxis_type}. '
'supported xaxis_type: [system_time/sim_time/index]'
)
95 changes: 95 additions & 0 deletions src/caret_analyze/plot/histogram/histogram_plot_factory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Copyright 2021 Research Institute of Systems Planning, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import annotations

from collections.abc import Sequence

from caret_analyze.record import Frequency, Latency, Period

from .histogram_plot import HistogramPlot
from ..visualize_lib import VisualizeLibInterface
from ...common import type_check_decorator
from ...exceptions import UnsupportedTypeError
from ...runtime import CallbackBase, Communication

MetricsType = Frequency | Latency | Period
HistTypes = CallbackBase | Communication


class HistogramPlotFactory:
"""Factory class to create an instance of HistogramPlot."""

@staticmethod
@type_check_decorator
def create_instance(
target_objects: Sequence[HistTypes],
metrics_name: str,
visualize_lib: VisualizeLibInterface
) -> HistogramPlot:
"""
Create an instance of HistogramPlot.

Parameters
----------
target_objects : Sequence[HistTypes]
HistTypes = CallbackBase | Communication
metrics_name : str
Metrics for HistogramPlot data.
supported metrics: [frequency/latency/period]
visualize_lib : VisualizeLibInterface
Instance of VisualizeLibInterface used for visualization.

Returns
-------
HistogramPlot

Raises
------
UnsupportedTypeError
Argument metrics is not "frequency", "latency", or "period".
rokamu623 marked this conversation as resolved.
Show resolved Hide resolved

"""
if metrics_name == 'frequency':
metrics_frequency: list[MetricsType] =\
[Frequency(target_object.to_records()) for target_object in target_objects]
return HistogramPlot(
metrics_frequency,
visualize_lib,
target_objects,
metrics_name
)
elif metrics_name == 'latency':
metrics_latency: list[MetricsType] =\
[Latency(target_object.to_records()) for target_object in target_objects]
return HistogramPlot(
metrics_latency,
visualize_lib,
target_objects,
metrics_name
)
elif metrics_name == 'period':
metrics_period: list[MetricsType] =\
[Period(target_object.to_records()) for target_object in target_objects]
return HistogramPlot(
metrics_period,
visualize_lib,
target_objects,
metrics_name
)
else:
raise UnsupportedTypeError(
'Unsupported metrics specified. '
'Supported metrics: [frequency/latency/period]'
)
110 changes: 109 additions & 1 deletion src/caret_analyze/plot/plot_facade.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from logging import getLogger

from .callback_scheduling import CallbackSchedulingPlot, CallbackSchedulingPlotFactory
from .histogram import ResponseTimeHistPlot, ResponseTimeHistPlotFactory
from .histogram import HistogramPlotFactory, ResponseTimeHistPlot, ResponseTimeHistPlotFactory
from .message_flow import MessageFlowPlot, MessageFlowPlotFactory
from .plot_base import PlotBase
from .stacked_bar import StackedBarPlot, StackedBarPlotFactory
Expand All @@ -30,6 +30,7 @@
logger = getLogger(__name__)

TimeSeriesTypes = CallbackBase | Communication | Publisher | Subscription
HistTypes = CallbackBase | Communication
CallbackSchedTypes = (Application | Executor | Path |
Node | CallbackGroup | Collection[CallbackGroup])

Expand Down Expand Up @@ -63,6 +64,35 @@ def parse_collection_or_unpack(
return parsed_target_objects


def parse_collection_or_unpack_for_hist(
target_arg: tuple[Collection[HistTypes]] | tuple[HistTypes, ...]
) -> list[HistTypes]:
"""
Parse target argument.

To address both cases where the target argument is passed in collection type
or unpacked, this function converts them to the same list format.

Parameters
----------
target_arg : tuple[Collection[HistTypes]] | tuple[HistTypes, ...]
Target objects.

Returns
-------
list[HistTypes]

"""
parsed_target_objects: list[HistTypes]
if isinstance(target_arg[0], Collection):
assert len(target_arg) == 1
parsed_target_objects = list(target_arg[0])
else: # Unpacked case
parsed_target_objects = list(target_arg) # type: ignore

return parsed_target_objects


class Plot:
"""Facade class for plot."""

Expand Down Expand Up @@ -273,3 +303,81 @@ def create_message_flow_plot(
target_path, visualize_lib, granularity, treat_drop_as_delay, lstrip_s, rstrip_s
)
return plot

@staticmethod
def create_frequency_histogram_plot(
*target_objects: HistTypes
) -> PlotBase:
"""
Get frequency histogram plot instance.

Parameters
----------
target_objects : Collection[HistTypes]
HistTypes = CallbackBase | Communication
Instances that are the sources of the plotting.
This also accepts multiple inputs by unpacking.

Returns
-------
PlotBase

"""
visualize_lib = VisualizeLibFactory.create_instance()
plot = HistogramPlotFactory.create_instance(
parse_collection_or_unpack_for_hist(target_objects),
'frequency', visualize_lib
)
return plot

@staticmethod
def create_latency_histogram_plot(
*target_objects: HistTypes
) -> PlotBase:
"""
Get latency histogram plot instance.

Parameters
----------
target_objects : Collection[HistTypes]
HistTypes = CallbackBase | Communication
Instances that are the sources of the plotting.
This also accepts multiple inputs by unpacking.

Returns
-------
PlotBase

"""
visualize_lib = VisualizeLibFactory.create_instance()
plot = HistogramPlotFactory.create_instance(
parse_collection_or_unpack_for_hist(target_objects),
'latency', visualize_lib
)
return plot

@staticmethod
def create_period_histogram_plot(
*target_objects: HistTypes
) -> PlotBase:
"""
Get period histogram plot instance.

Parameters
----------
target_objects : Collection[HistTypes]
HistTypes = CallbackBase | Communication
Instances that are the sources of the plotting.
This also accepts multiple inputs by unpacking.

Returns
-------
PlotBase

"""
visualize_lib = VisualizeLibFactory.create_instance()
plot = HistogramPlotFactory.create_instance(
parse_collection_or_unpack_for_hist(target_objects),
'period', visualize_lib
)
return plot
Loading
Loading