Module redvox.api900.summarize
This module contains functions and classes for summarizing ranges of redvox data.
Expand source code
"""
This module contains functions and classes for summarizing ranges of redvox data.
"""
import datetime
import itertools
import typing
import numpy
import redvox.api900.types as types
import redvox.common.date_time_utils as date_time_utils
def _sensor_name(sensor: typing.Optional[types.RedvoxSensor]) -> str:
"""
Extracts the sensor name from a redvox sensor.
:param sensor: Sensor to get sensor name for.
:return: The sensor name for the given sensor.
"""
if sensor is None:
return ""
if isinstance(sensor, types.time_synchronization_sensor.TimeSynchronizationSensor):
return "time synch"
return sensor.sensor_name()
def _sensor_samples(sensor: typing.Optional[types.RedvoxSensor]) -> int:
"""
Returns the number of samples a sensor contains.
:param sensor: Sensor to test.
:return: Number of samples in sensor.
"""
if sensor is None:
return 0
if isinstance(sensor, (types.microphone_sensor.MicrophoneSensor,
types.time_synchronization_sensor.TimeSynchronizationSensor)):
return len(sensor.payload_values())
return len(sensor.timestamps_microseconds_utc())
# pylint: disable=R0903
class SensorSummary:
"""
This class provides a summary of a sensor.
"""
def __init__(self, sensor: typing.Optional[types.RedvoxSensor]):
self.has_sensor = sensor is not None
self.sensor_name = _sensor_name(sensor)
self.num_samples = _sensor_samples(sensor)
def __str__(self):
if not self.has_sensor:
return "None"
return "{SensorSummary: %s with %d samples} " % (self.sensor_name, self.num_samples)
# pylint: disable=R0902
# pylint: disable=R0903
class WrappedRedvoxPacketSummary:
"""
This class provides a summary of a WrappedRedvoxPacket.
"""
def __init__(self, redvox_packet):
self.redvox_id = redvox_packet.redvox_id()
self.uuid = redvox_packet.uuid()
self.redvox_id_uuid: str = "%s:%s" % (self.redvox_id, self.uuid)
self.start_timestamp_us: int = redvox_packet.start_timestamp_us_utc()
self.end_timestamp_us: int = redvox_packet.end_timestamp_us_utc()
self.duration_s: float = redvox_packet.duration_s()
self.sample_rate_hz = redvox_packet.microphone_sensor().sample_rate_hz()
self.microphone_sensor_summary: SensorSummary = SensorSummary(redvox_packet.microphone_sensor())
self.barometer_sensor_summary: SensorSummary = SensorSummary(redvox_packet.barometer_sensor())
self.location_sensor_summary: SensorSummary = SensorSummary(redvox_packet.location_sensor())
# pylint: disable=C0103
self.time_synchronization_sensor_summary: SensorSummary = SensorSummary(
redvox_packet.time_synchronization_sensor())
self.accelerometer_sensor_summary: SensorSummary = SensorSummary(redvox_packet.accelerometer_sensor())
self.magnetometer_sensor_summary: SensorSummary = SensorSummary(redvox_packet.magnetometer_sensor())
self.gyroscope_sensor_summary: SensorSummary = SensorSummary(redvox_packet.gyroscope_sensor())
self.light_sensor_summary: SensorSummary = SensorSummary(redvox_packet.light_sensor())
self.infrared_sensor_summary: SensorSummary = SensorSummary(redvox_packet.infrared_sensor())
def __str__(self):
return "{WrappedRedvoxPacketSummary: %s %d-%d [%f sec] %f %s %s %s %s %s %s %s %s %s} " % (
self.redvox_id_uuid,
self.start_timestamp_us,
self.end_timestamp_us,
self.duration_s,
self.sample_rate_hz,
str(self.microphone_sensor_summary),
str(self.barometer_sensor_summary),
str(self.location_sensor_summary),
str(self.time_synchronization_sensor_summary),
str(self.accelerometer_sensor_summary),
str(self.magnetometer_sensor_summary),
str(self.gyroscope_sensor_summary),
str(self.light_sensor_summary),
str(self.infrared_sensor_summary)
)
# Type vars for compose function.
# pylint: disable=C0103
X = typing.TypeVar("X")
# pylint: disable=C0103
Y = typing.TypeVar("Y")
# pylint: disable=C0103
Z = typing.TypeVar("Z")
def _compose(fn1: typing.Callable[[X], Y],
fn2: typing.Callable[[Y], Z]) -> typing.Callable[[X], Z]:
"""
Composes two functions together.
:param fn1: A function from X -> Y
:param fn2: A function from Y -> Z
:return: A function from X -> Z
"""
return lambda value: fn2(fn1(value))
def summarize_data(grouped_redvox_packets: typing.Dict[str, typing.List]) -> typing.Dict[
str, typing.List[WrappedRedvoxPacketSummary]]:
"""
Given grouped redvox packets, generate summaries for each device and each packet.
:param grouped_redvox_packets: Grouped packets to generate summary for.
:return: A dictionary which maps redvox ids to summaries of redvox packets for that device.
"""
summarized = {}
for id_uuid, wrapped_redvox_packets in grouped_redvox_packets.items():
summarized[id_uuid] = list(map(WrappedRedvoxPacketSummary, wrapped_redvox_packets))
return summarized
def _subsample(values: typing.Union[numpy.ndarray, typing.List], num_samples: int) -> typing.Union[
numpy.ndarray, typing.List]:
"""
Returns evenly sampled values from an array.
From https://stackoverflow.com/a/50685454 and https://stackoverflow.com/a/9873935
:param values: Sequence to extract evenly sampled samples from.
:param num_samples: Number of samples to extract.
:return: A new, subsampled array.
"""
indexes = numpy.round(numpy.linspace(0, len(values) - 1, num_samples)).astype(int)
if isinstance(values, numpy.ndarray):
return numpy.take(values, indexes)
elif isinstance(values, list):
return [values[i] for i in indexes]
else:
raise RuntimeError("Non sequence type passed to subsample")
# pylint: disable=R0914
def plot_summarized_data(summarized_data: typing.Dict[str, typing.List[WrappedRedvoxPacketSummary]]):
"""
Given summarized data, plot the data to display sensor activity for all passed in summarized data.
:param summarized_data: Data to plot.
"""
try:
import matplotlib.pyplot as plt
all_summaries = list(itertools.chain(*summarized_data.values()))
start_timestamp_us = numpy.min(
list(map(lambda packet_summary: packet_summary.start_timestamp_us, all_summaries)))
end_timestamp_us = numpy.max(list(map(lambda packet_summary: packet_summary.end_timestamp_us, all_summaries)))
start_s = int(date_time_utils.microseconds_to_seconds(start_timestamp_us))
end_s = int(date_time_utils.microseconds_to_seconds(end_timestamp_us))
timestamps = numpy.arange(start_s, end_s + 1)
# Setup the plot
fig, axes = plt.subplots(nrows=1)
axes.set_xlim([timestamps[0], timestamps[-1]])
axes.set_ylim([0, len(summarized_data.keys()) + 1])
# Not for each device, let's plot its ranges
yticks = []
ylabels = []
i = 1
for redvox_id, summaries in summarized_data.items():
for summary in summaries:
summary_start_s = int(date_time_utils.microseconds_to_seconds(summary.start_timestamp_us))
summary_end_s = int(date_time_utils.microseconds_to_seconds(summary.end_timestamp_us))
values = numpy.full(len(timestamps), numpy.nan)
values[timestamps.searchsorted(summary_start_s):timestamps.searchsorted(summary_end_s)] = i
axes.plot(timestamps, values)
yticks.append(i)
ylabels.append(redvox_id.split(":")[0])
i += 1
xticks = _subsample(timestamps, 6)
datetimes = map(datetime.datetime.utcfromtimestamp, xticks)
datetimes = map(lambda dt: dt.strftime("%d %H:%M"), datetimes)
axes.set_xticks(xticks)
axes.set_xticklabels(list(datetimes))
axes.set_yticks(yticks)
axes.set_yticklabels(ylabels)
axes.tick_params(axis='both', which='major', labelsize=8)
axes.tick_params(axis='both', which='minor', labelsize=7)
axes.set_title("RedVox Device Summary")
axes.set_xlabel("Date and Time")
axes.set_ylabel("Device Activity")
fig.text(.5, .01, "UTC %s - %s" % (datetime.datetime.utcfromtimestamp(start_s),
datetime.datetime.utcfromtimestamp(end_s)), ha='center')
plt.show()
except ImportError:
import warnings
warnings.warn("GUI dependencies are not installed. Install the 'GUI' extra to enable this functionality.")
Functions
def plot_summarized_data(summarized_data: Dict[str, List[WrappedRedvoxPacketSummary]])
-
Given summarized data, plot the data to display sensor activity for all passed in summarized data. :param summarized_data: Data to plot.
Expand source code
def plot_summarized_data(summarized_data: typing.Dict[str, typing.List[WrappedRedvoxPacketSummary]]): """ Given summarized data, plot the data to display sensor activity for all passed in summarized data. :param summarized_data: Data to plot. """ try: import matplotlib.pyplot as plt all_summaries = list(itertools.chain(*summarized_data.values())) start_timestamp_us = numpy.min( list(map(lambda packet_summary: packet_summary.start_timestamp_us, all_summaries))) end_timestamp_us = numpy.max(list(map(lambda packet_summary: packet_summary.end_timestamp_us, all_summaries))) start_s = int(date_time_utils.microseconds_to_seconds(start_timestamp_us)) end_s = int(date_time_utils.microseconds_to_seconds(end_timestamp_us)) timestamps = numpy.arange(start_s, end_s + 1) # Setup the plot fig, axes = plt.subplots(nrows=1) axes.set_xlim([timestamps[0], timestamps[-1]]) axes.set_ylim([0, len(summarized_data.keys()) + 1]) # Not for each device, let's plot its ranges yticks = [] ylabels = [] i = 1 for redvox_id, summaries in summarized_data.items(): for summary in summaries: summary_start_s = int(date_time_utils.microseconds_to_seconds(summary.start_timestamp_us)) summary_end_s = int(date_time_utils.microseconds_to_seconds(summary.end_timestamp_us)) values = numpy.full(len(timestamps), numpy.nan) values[timestamps.searchsorted(summary_start_s):timestamps.searchsorted(summary_end_s)] = i axes.plot(timestamps, values) yticks.append(i) ylabels.append(redvox_id.split(":")[0]) i += 1 xticks = _subsample(timestamps, 6) datetimes = map(datetime.datetime.utcfromtimestamp, xticks) datetimes = map(lambda dt: dt.strftime("%d %H:%M"), datetimes) axes.set_xticks(xticks) axes.set_xticklabels(list(datetimes)) axes.set_yticks(yticks) axes.set_yticklabels(ylabels) axes.tick_params(axis='both', which='major', labelsize=8) axes.tick_params(axis='both', which='minor', labelsize=7) axes.set_title("RedVox Device Summary") axes.set_xlabel("Date and Time") axes.set_ylabel("Device Activity") fig.text(.5, .01, "UTC %s - %s" % (datetime.datetime.utcfromtimestamp(start_s), datetime.datetime.utcfromtimestamp(end_s)), ha='center') plt.show() except ImportError: import warnings warnings.warn("GUI dependencies are not installed. Install the 'GUI' extra to enable this functionality.")
def summarize_data(grouped_redvox_packets: Dict[str, List]) ‑> Dict[str, List[WrappedRedvoxPacketSummary]]
-
Given grouped redvox packets, generate summaries for each device and each packet. :param grouped_redvox_packets: Grouped packets to generate summary for. :return: A dictionary which maps redvox ids to summaries of redvox packets for that device.
Expand source code
def summarize_data(grouped_redvox_packets: typing.Dict[str, typing.List]) -> typing.Dict[ str, typing.List[WrappedRedvoxPacketSummary]]: """ Given grouped redvox packets, generate summaries for each device and each packet. :param grouped_redvox_packets: Grouped packets to generate summary for. :return: A dictionary which maps redvox ids to summaries of redvox packets for that device. """ summarized = {} for id_uuid, wrapped_redvox_packets in grouped_redvox_packets.items(): summarized[id_uuid] = list(map(WrappedRedvoxPacketSummary, wrapped_redvox_packets)) return summarized
Classes
class SensorSummary (sensor: Union[EvenlySampledSensor, UnevenlySampledSensor, MicrophoneSensor, BarometerSensor, LocationSensor, TimeSynchronizationSensor, AccelerometerSensor, GyroscopeSensor, MagnetometerSensor, LightSensor, InfraredSensor, ImageSensor, ForwardRef(None)])
-
This class provides a summary of a sensor.
Expand source code
class SensorSummary: """ This class provides a summary of a sensor. """ def __init__(self, sensor: typing.Optional[types.RedvoxSensor]): self.has_sensor = sensor is not None self.sensor_name = _sensor_name(sensor) self.num_samples = _sensor_samples(sensor) def __str__(self): if not self.has_sensor: return "None" return "{SensorSummary: %s with %d samples} " % (self.sensor_name, self.num_samples)
class WrappedRedvoxPacketSummary (redvox_packet)
-
This class provides a summary of a WrappedRedvoxPacket.
Expand source code
class WrappedRedvoxPacketSummary: """ This class provides a summary of a WrappedRedvoxPacket. """ def __init__(self, redvox_packet): self.redvox_id = redvox_packet.redvox_id() self.uuid = redvox_packet.uuid() self.redvox_id_uuid: str = "%s:%s" % (self.redvox_id, self.uuid) self.start_timestamp_us: int = redvox_packet.start_timestamp_us_utc() self.end_timestamp_us: int = redvox_packet.end_timestamp_us_utc() self.duration_s: float = redvox_packet.duration_s() self.sample_rate_hz = redvox_packet.microphone_sensor().sample_rate_hz() self.microphone_sensor_summary: SensorSummary = SensorSummary(redvox_packet.microphone_sensor()) self.barometer_sensor_summary: SensorSummary = SensorSummary(redvox_packet.barometer_sensor()) self.location_sensor_summary: SensorSummary = SensorSummary(redvox_packet.location_sensor()) # pylint: disable=C0103 self.time_synchronization_sensor_summary: SensorSummary = SensorSummary( redvox_packet.time_synchronization_sensor()) self.accelerometer_sensor_summary: SensorSummary = SensorSummary(redvox_packet.accelerometer_sensor()) self.magnetometer_sensor_summary: SensorSummary = SensorSummary(redvox_packet.magnetometer_sensor()) self.gyroscope_sensor_summary: SensorSummary = SensorSummary(redvox_packet.gyroscope_sensor()) self.light_sensor_summary: SensorSummary = SensorSummary(redvox_packet.light_sensor()) self.infrared_sensor_summary: SensorSummary = SensorSummary(redvox_packet.infrared_sensor()) def __str__(self): return "{WrappedRedvoxPacketSummary: %s %d-%d [%f sec] %f %s %s %s %s %s %s %s %s %s} " % ( self.redvox_id_uuid, self.start_timestamp_us, self.end_timestamp_us, self.duration_s, self.sample_rate_hz, str(self.microphone_sensor_summary), str(self.barometer_sensor_summary), str(self.location_sensor_summary), str(self.time_synchronization_sensor_summary), str(self.accelerometer_sensor_summary), str(self.magnetometer_sensor_summary), str(self.gyroscope_sensor_summary), str(self.light_sensor_summary), str(self.infrared_sensor_summary) )