Home | History | Annotate | Download | only in common
      1 #    Copyright 2015-2016 ARM Limited
      2 #
      3 # Licensed under the Apache License, Version 2.0 (the "License");
      4 # you may not use this file except in compliance with the License.
      5 # You may obtain a copy of the License at
      6 #
      7 #     http://www.apache.org/licenses/LICENSE-2.0
      8 #
      9 # Unless required by applicable law or agreed to in writing, software
     10 # distributed under the License is distributed on an "AS IS" BASIS,
     11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 # See the License for the specific language governing permissions and
     13 # limitations under the License.
     14 #
     15 
     16 """Utility functions for sheye"""
     17 
     18 import trappy
     19 import numpy as np
     20 
     21 # pylint fails to recognize numpy members.
     22 # pylint: disable=no-member
     23 
     24 def listify(to_select):
     25     """Utitlity function to handle both single and
     26     list inputs
     27     """
     28 
     29     if not isinstance(to_select, list):
     30         to_select = [to_select]
     31 
     32     return to_select
     33 
     34 def init_ftrace(trace):
     35     """Initialize the FTrace Object
     36 
     37     :param trace: Path for the trace file
     38         or a trace object
     39     :type trace: str, :mod:`trappy.ftrace.FTrace`
     40     """
     41 
     42     if isinstance(trace, basestring):
     43         return trappy.FTrace(trace)
     44 
     45     elif isinstance(trace, trappy.BareTrace):
     46         return trace
     47 
     48     raise ValueError("Invalid trace Object")
     49 
     50 def select_window(series, window):
     51     """Helper Function to select a portion of
     52     pandas time series
     53 
     54     :param series: Input Time Series data
     55     :type series: :mod:`pandas.Series`
     56 
     57     :param window: A tuple indicating a time window
     58     :type window: tuple
     59     """
     60 
     61     if not window:
     62         return series
     63 
     64     start, stop = window
     65     ix = series.index
     66     selector = ((ix >= start) & (ix <= stop))
     67     window_series = series[selector]
     68     return window_series
     69 
     70 def area_under_curve(series, sign=None, method="trapz", step="post"):
     71     """Return the area under the time series curve (Integral)
     72 
     73     :param series: The time series to be integrated
     74     :type series: :mod:`pandas.Series`
     75 
     76     :param sign: Clip the data for the area in positive
     77         or negative regions. Can have two values
     78 
     79         - `"+"`
     80         - `"="`
     81     :type sign: str
     82 
     83     :param method: The method for area calculation. This can
     84         be any of the integration methods supported in `numpy`
     85         or `rect`
     86     :type param: str
     87 
     88     :param step: The step behaviour for `rect` method
     89     :type step: str
     90 
     91     *Rectangular Method*
     92 
     93         - Step: Post
     94 
     95             Consider the following time series data
     96 
     97             .. code::
     98 
     99                 2            *----*----*----+
    100                              |              |
    101                 1            |              *----*----+
    102                              |
    103                 0  *----*----+
    104                    0    1    2    3    4    5    6    7
    105 
    106             .. code::
    107 
    108                 import pandas as pd
    109                 a = [0, 0, 2, 2, 2, 1, 1]
    110                 s = pd.Series(a)
    111 
    112             The area under the curve is:
    113 
    114             .. math::
    115 
    116                 \sum_{k=0}^{N-1} (x_{k+1} - {x_k}) \\times f(x_k) \\\\
    117                 (2 \\times 3) + (1 \\times 2) = 8
    118 
    119         - Step: Pre
    120 
    121             .. code::
    122 
    123                 2       +----*----*----*
    124                         |              |
    125                 1       |              +----*----*----+
    126                         |
    127                 0  *----*
    128                    0    1    2    3    4    5    6    7
    129 
    130             .. code::
    131 
    132                 import pandas as pd
    133                 a = [0, 0, 2, 2, 2, 1, 1]
    134                 s = pd.Series(a)
    135 
    136             The area under the curve is:
    137 
    138             .. math::
    139 
    140                 \sum_{k=1}^{N} (x_k - x_{k-1}) \\times f(x_k) \\\\
    141                 (2 \\times 3) + (1 \\times 3) = 9
    142     """
    143 
    144     if sign == "+":
    145         series = series.clip_lower(0)
    146     elif sign == "=":
    147         series = series.clip_upper(0)
    148 
    149     series = series.dropna()
    150 
    151     if method == "rect":
    152 
    153         if step == "post":
    154             values = series.values[:-1]
    155         elif step == "pre":
    156             values = series.values[1:]
    157         else:
    158             raise ValueError("Invalid Value for step: {}".format(step))
    159 
    160         return float((values * np.diff(series.index)).sum())
    161 
    162     if hasattr(np, method):
    163         np_integ_method = getattr(np, method)
    164         return np_integ_method(series.values, series.index)
    165     else:
    166         raise ValueError("Invalid method: {}".format(method))
    167 
    168 def interval_sum(series, value=None, step="post"):
    169     """A function that returns the sum of the
    170     intervals where the value of series is equal to
    171     the expected value. Consider the following time
    172     series data:
    173 
    174     ====== =======
    175      Time   Value
    176     ====== =======
    177       0      0
    178       1      0
    179       2      1
    180       3      1
    181       4      1
    182       5      1
    183       8      0
    184       9      1
    185      10      0
    186      11      1
    187      12      1
    188     ====== =======
    189 
    190     .. note::
    191 
    192         The time/index values, in general, may not be
    193         uniform. This causes difference in the
    194         the values of :func:`interval_sum` for **step-pre**
    195         and **step-post** behaviours
    196 
    197     .. code::
    198 
    199         import pandas
    200 
    201         values = [0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1]
    202         index = [0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 12]
    203         series = pandas.Series(values, index=index)
    204 
    205     The :func:`interval_sum` for the value 1 is calculated differently
    206     for **step-post** and **step-pre** behaviours as follows:
    207 
    208         - **Step-Post**
    209 
    210 
    211           .. code::
    212 
    213             1            *----*----*----*-------------+    *----+    *----*
    214                          |                            |    |    |    |
    215             0  *----*----+                            *----+    *----+
    216                0    1    2    3    4    5    6    7   8    9    10   11   12
    217 
    218           .. math::
    219 
    220             (8-2) + (10-9) + (12-11) = 6 + 1 + 1 = 8
    221 
    222         - **Step-Pre**
    223 
    224           .. code::
    225 
    226             1       +----*----*----*----*              +----*    +----*----*
    227                     |                   |              |    |    |
    228             0  *----*                   +--------------*    +----*
    229                0    1    2    3    4    5    6    7    8    9    10   11   12
    230 
    231           .. math::
    232 
    233                 (5-1) + (9-8) + (12-10) = 4 + 1 + 2 = 7
    234 
    235         .. note::
    236 
    237             The asterisks (*) on the plots above represent the values of the time
    238             series data and these do not vary between the two step styles
    239 
    240     :param series: The time series data
    241     :type series: :mod:`pandas.Series`
    242 
    243     :param value: The value to checked for in the series. If the
    244         value is None, the truth value of the elements in the
    245         series will be used
    246     :type value: element
    247 
    248     :param step: The step behaviour as described above
    249         ::
    250 
    251             step="post"
    252             step="pre
    253     :type step: str
    254     """
    255 
    256     index = series.index
    257     array = series.values
    258 
    259     time_splits = np.append(np.where(np.diff(array) != 0), len(array) - 1)
    260 
    261     prev = 0
    262     time = 0
    263     step_post = True
    264 
    265     if step == "pre":
    266         step_post = False
    267     elif step != "post":
    268         raise ValueError("Invalid value for step: {}".format(step))
    269 
    270     for split in time_splits:
    271 
    272         first_val = series.iloc[split]
    273         check = (first_val == value) if value else first_val
    274         if check:
    275             start = prev
    276             end = split
    277 
    278             if step_post:
    279                 end = split + 1 if split < len(series) - 1 else split
    280             else:
    281                 start = prev - 1 if prev > 1 else prev
    282 
    283             time += index[end] - index[start]
    284 
    285         prev = split + 1
    286 
    287     return float(time)
    288