| # Copyright 2015-2016 ARM Limited |
| # |
| # 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. |
| # |
| |
| """Utility functions for sheye""" |
| |
| import trappy |
| import numpy as np |
| |
| # pylint fails to recognize numpy members. |
| # pylint: disable=no-member |
| |
| def listify(to_select): |
| """Utitlity function to handle both single and |
| list inputs |
| """ |
| |
| if not isinstance(to_select, list): |
| to_select = [to_select] |
| |
| return to_select |
| |
| def init_ftrace(trace): |
| """Initialize the FTrace Object |
| |
| :param trace: Path for the trace file |
| or a trace object |
| :type trace: str, :mod:`trappy.ftrace.FTrace` |
| """ |
| |
| if isinstance(trace, basestring): |
| return trappy.FTrace(trace) |
| |
| elif isinstance(trace, trappy.BareTrace): |
| return trace |
| |
| raise ValueError("Invalid trace Object") |
| |
| def select_window(series, window): |
| """Helper Function to select a portion of |
| pandas time series |
| |
| :param series: Input Time Series data |
| :type series: :mod:`pandas.Series` |
| |
| :param window: A tuple indicating a time window |
| :type window: tuple |
| """ |
| |
| if not window: |
| return series |
| |
| start, stop = window |
| ix = series.index |
| selector = ((ix >= start) & (ix <= stop)) |
| window_series = series[selector] |
| return window_series |
| |
| def area_under_curve(series, sign=None, method="trapz", step="post"): |
| """Return the area under the time series curve (Integral) |
| |
| :param series: The time series to be integrated |
| :type series: :mod:`pandas.Series` |
| |
| :param sign: Clip the data for the area in positive |
| or negative regions. Can have two values |
| |
| - `"+"` |
| - `"="` |
| :type sign: str |
| |
| :param method: The method for area calculation. This can |
| be any of the integration methods supported in `numpy` |
| or `rect` |
| :type param: str |
| |
| :param step: The step behaviour for `rect` method |
| :type step: str |
| |
| *Rectangular Method* |
| |
| - Step: Post |
| |
| Consider the following time series data |
| |
| .. code:: |
| |
| 2 *----*----*----+ |
| | | |
| 1 | *----*----+ |
| | |
| 0 *----*----+ |
| 0 1 2 3 4 5 6 7 |
| |
| .. code:: |
| |
| import pandas as pd |
| a = [0, 0, 2, 2, 2, 1, 1] |
| s = pd.Series(a) |
| |
| The area under the curve is: |
| |
| .. math:: |
| |
| \sum_{k=0}^{N-1} (x_{k+1} - {x_k}) \\times f(x_k) \\\\ |
| (2 \\times 3) + (1 \\times 2) = 8 |
| |
| - Step: Pre |
| |
| .. code:: |
| |
| 2 +----*----*----* |
| | | |
| 1 | +----*----*----+ |
| | |
| 0 *----* |
| 0 1 2 3 4 5 6 7 |
| |
| .. code:: |
| |
| import pandas as pd |
| a = [0, 0, 2, 2, 2, 1, 1] |
| s = pd.Series(a) |
| |
| The area under the curve is: |
| |
| .. math:: |
| |
| \sum_{k=1}^{N} (x_k - x_{k-1}) \\times f(x_k) \\\\ |
| (2 \\times 3) + (1 \\times 3) = 9 |
| """ |
| |
| if sign == "+": |
| series = series.clip_lower(0) |
| elif sign == "=": |
| series = series.clip_upper(0) |
| |
| series = series.dropna() |
| |
| if method == "rect": |
| |
| if step == "post": |
| values = series.values[:-1] |
| elif step == "pre": |
| values = series.values[1:] |
| else: |
| raise ValueError("Invalid Value for step: {}".format(step)) |
| |
| return float((values * np.diff(series.index)).sum()) |
| |
| if hasattr(np, method): |
| np_integ_method = getattr(np, method) |
| return np_integ_method(series.values, series.index) |
| else: |
| raise ValueError("Invalid method: {}".format(method)) |
| |
| def interval_sum(series, value=None, step="post"): |
| """A function that returns the sum of the |
| intervals where the value of series is equal to |
| the expected value. Consider the following time |
| series data: |
| |
| ====== ======= |
| Time Value |
| ====== ======= |
| 0 0 |
| 1 0 |
| 2 1 |
| 3 1 |
| 4 1 |
| 5 1 |
| 8 0 |
| 9 1 |
| 10 0 |
| 11 1 |
| 12 1 |
| ====== ======= |
| |
| .. note:: |
| |
| The time/index values, in general, may not be |
| uniform. This causes difference in the |
| the values of :func:`interval_sum` for **step-pre** |
| and **step-post** behaviours |
| |
| .. code:: |
| |
| import pandas |
| |
| values = [0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1] |
| index = [0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 12] |
| series = pandas.Series(values, index=index) |
| |
| The :func:`interval_sum` for the value 1 is calculated differently |
| for **step-post** and **step-pre** behaviours as follows: |
| |
| - **Step-Post** |
| |
| |
| .. code:: |
| |
| 1 *----*----*----*-------------+ *----+ *----* |
| | | | | | |
| 0 *----*----+ *----+ *----+ |
| 0 1 2 3 4 5 6 7 8 9 10 11 12 |
| |
| .. math:: |
| |
| (8-2) + (10-9) + (12-11) = 6 + 1 + 1 = 8 |
| |
| - **Step-Pre** |
| |
| .. code:: |
| |
| 1 +----*----*----*----* +----* +----*----* |
| | | | | | |
| 0 *----* +--------------* +----* |
| 0 1 2 3 4 5 6 7 8 9 10 11 12 |
| |
| .. math:: |
| |
| (5-1) + (9-8) + (12-10) = 4 + 1 + 2 = 7 |
| |
| .. note:: |
| |
| The asterisks (*) on the plots above represent the values of the time |
| series data and these do not vary between the two step styles |
| |
| :param series: The time series data |
| :type series: :mod:`pandas.Series` |
| |
| :param value: The value to checked for in the series. If the |
| value is None, the truth value of the elements in the |
| series will be used |
| :type value: element |
| |
| :param step: The step behaviour as described above |
| :: |
| |
| step="post" |
| step="pre |
| :type step: str |
| """ |
| |
| index = series.index |
| array = series.values |
| |
| time_splits = np.append(np.where(np.diff(array) != 0), len(array) - 1) |
| |
| prev = 0 |
| time = 0 |
| step_post = True |
| |
| if step == "pre": |
| step_post = False |
| elif step != "post": |
| raise ValueError("Invalid value for step: {}".format(step)) |
| |
| for split in time_splits: |
| |
| first_val = series.iloc[split] |
| check = (first_val == value) if value else first_val |
| if check: |
| start = prev |
| end = split |
| |
| if step_post: |
| end = split + 1 if split < len(series) - 1 else split |
| else: |
| start = prev - 1 if prev > 1 else prev |
| |
| time += index[end] - index[start] |
| |
| prev = split + 1 |
| |
| return float(time) |