# 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)
              )