(requestContext, seriesList, total=None, *nodes)
return resultList
Calculates a percentage of the total of a wildcard series. If `total` is specified,
  each series will be calculated as a percentage of that total. If `total` is not specified,
  the sum of all points in the wildcard series will be used instead.

  A list of nodes can optionally be provided, if so they will be used to match series with their
  corresponding totals following the same logic as :py:func:`groupByNodes <groupByNodes>`.

  When passing `nodes` the `total` parameter may be a series list or `None`.  If it is `None` then
  for each series in `seriesList` the percentage of the sum of series in that group will be returned.

  When not passing `nodes`, the `total` parameter may be a single series, reference the same number
  of series as `seriesList` or be a numeric value.

  Example:

  .. code-block:: none

    # Server01 connections failed and succeeded as a percentage of Server01 connections attempted
    &target=asPercent(Server01.connections.{failed,succeeded}, Server01.connections.attempted)

    # For each server, its connections failed as a percentage of its connections attempted
    &target=asPercent(Server*.connections.failed, Server*.connections.attempted)

    # For each server, its connections failed and succeeded as a percentage of its connections attemped
    &target=asPercent(Server*.connections.{failed,succeeded}, Server*.connections.attempted, 0)

    # apache01.threads.busy as a percentage of 1500
    &target=asPercent(apache01.threads.busy,1500)

    # Server01 cpu stats as a percentage of its total
    &target=asPercent(Server01.cpu.*.jiffies)

    # cpu stats for each server as a percentage of its total
    &target=asPercent(Server*.cpu.*.jiffies, None, 0)

  When using `nodes`, any series or totals that can't be matched will create output series with
  names like ``asPercent(someSeries,MISSING)`` or ``asPercent(MISSING,someTotalSeries)`` and all
  values set to None. If desired these series can be filtered out by piping the result through
  ``|exclude("MISSING")`` as shown below:

  .. code-block:: none

    &target=asPercent(Server{1,2}.memory.used,Server{1,3}.memory.total,0)

    # will produce 3 output series:
    # asPercent(Server1.memory.used,Server1.memory.total) [values will be as expected]
    # asPercent(Server2.memory.used,MISSING) [all values will be None]
    # asPercent(MISSING,Server3.memory.total) [all values will be None]

    &target=asPercent(Server{1,2}.memory.used,Server{1,3}.memory.total,0)|exclude("MISSING")

    # will produce 1 output series:
    # asPercent(Server1.memory.used,Server1.memory.total) [values will be as expected]

  Each node may be an integer referencing a node in the series name or a string identifying a tag.

  .. note::

    When `total` is a seriesList, specifying `nodes` to match series with the corresponding total
    series will increase reliability.
def asPercent(requestContext, seriesList, total=None, *nodes):
  """

  Calculates a percentage of the total of a wildcard series. If `total` is specified,
  each series will be calculated as a percentage of that total. If `total` is not specified,
  the sum of all points in the wildcard series will be used instead.

  A list of nodes can optionally be provided, if so they will be used to match series with their
  corresponding totals following the same logic as :py:func:`groupByNodes <groupByNodes>`.

  When passing `nodes` the `total` parameter may be a series list or `None`.  If it is `None` then
  for each series in `seriesList` the percentage of the sum of series in that group will be returned.

  When not passing `nodes`, the `total` parameter may be a single series, reference the same number
  of series as `seriesList` or be a numeric value.

  Example:

  .. code-block:: none

    # Server01 connections failed and succeeded as a percentage of Server01 connections attempted
    &target=asPercent(Server01.connections.{failed,succeeded}, Server01.connections.attempted)

    # For each server, its connections failed as a percentage of its connections attempted
    &target=asPercent(Server*.connections.failed, Server*.connections.attempted)

    # For each server, its connections failed and succeeded as a percentage of its connections attemped
    &target=asPercent(Server*.connections.{failed,succeeded}, Server*.connections.attempted, 0)

    # apache01.threads.busy as a percentage of 1500
    &target=asPercent(apache01.threads.busy,1500)

    # Server01 cpu stats as a percentage of its total
    &target=asPercent(Server01.cpu.*.jiffies)

    # cpu stats for each server as a percentage of its total
    &target=asPercent(Server*.cpu.*.jiffies, None, 0)

  When using `nodes`, any series or totals that can't be matched will create output series with
  names like ``asPercent(someSeries,MISSING)`` or ``asPercent(MISSING,someTotalSeries)`` and all
  values set to None. If desired these series can be filtered out by piping the result through
  ``|exclude("MISSING")`` as shown below:

  .. code-block:: none

    &target=asPercent(Server{1,2}.memory.used,Server{1,3}.memory.total,0)

    # will produce 3 output series:
    # asPercent(Server1.memory.used,Server1.memory.total) [values will be as expected]
    # asPercent(Server2.memory.used,MISSING) [all values will be None]
    # asPercent(MISSING,Server3.memory.total) [all values will be None]

    &target=asPercent(Server{1,2}.memory.used,Server{1,3}.memory.total,0)|exclude("MISSING")

    # will produce 1 output series:
    # asPercent(Server1.memory.used,Server1.memory.total) [values will be as expected]

  Each node may be an integer referencing a node in the series name or a string identifying a tag.

  .. note::

    When `total` is a seriesList, specifying `nodes` to match series with the corresponding total
    series will increase reliability.

  """
  normalize([seriesList])
  xFilesFactor=requestContext.get('xFilesFactor')

  # if nodes are specified, use them to match series & total
  if nodes:
    keys = []

    # group series together by key
    metaSeries = {}
    for series in seriesList:
      key = aggKey(series, nodes)
      if key not in metaSeries:
        metaSeries[key] = [series]
        keys.append(key)
      else:
        metaSeries[key].append(series)

    # make list of totals
    totalSeries = {}
    # no total seriesList was specified, sum the values for each group of series
    if total is None:
      for key in keys:
        if len(metaSeries[key]) == 1:
          totalSeries[key] = metaSeries[key][0]
        else:
          name = 'sumSeries(%s)' % formatPathExpressions(metaSeries[key])
          (seriesList,start,end,step) = normalize([metaSeries[key]])
          totalValues = [ safe.safeSum(row) for row in izip_longest(*metaSeries[key]) ]
          totalSeries[key] = TimeSeries(name,start,end,step,totalValues,xFilesFactor=xFilesFactor)
    # total seriesList was specified, sum the values for each group of totals
    elif isinstance(total, list):
      for series in total:
        key = aggKey(series, nodes)
        if key not in totalSeries:
          totalSeries[key] = [series]
          if key not in keys:
            keys.append(key)
        else:
          totalSeries[key].append(series)

      for key in totalSeries.keys():
        if len(totalSeries[key]) == 1:
          totalSeries[key] = totalSeries[key][0]
        else:
          name = 'sumSeries(%s)' % formatPathExpressions(totalSeries[key])
          (seriesList,start,end,step) = normalize([totalSeries[key]])
          totalValues = [ safe.safeSum(row) for row in izip_longest(*totalSeries[key]) ]
          totalSeries[key] = TimeSeries(name,start,end,step,totalValues,xFilesFactor=xFilesFactor)
    # trying to use nodes with a total value, which isn't supported because it has no effect
    else:
      raise InputParameterError('total must be None or a seriesList')

    resultList = []
    for key in keys:
      # no series, must have total only
      if key not in metaSeries:
        series2 = totalSeries[key]
        name = "asPercent(%s,%s)" % ('MISSING', series2.name)
        resultValues = [ None for v1 in series2 ]
        resultSeries = TimeSeries(name,start,end,step,resultValues,xFilesFactor=xFilesFactor)
        resultList.append(resultSeries)
        continue

      for series1 in metaSeries[key]:
        # no total
        if key not in totalSeries:
          name = "asPercent(%s,%s)" % (series1.name, 'MISSING')
          resultValues = [ None for v1 in series1 ]
        # series and total
        else:
          series2 = totalSeries[key]
          name = "asPercent(%s,%s)" % (series1.name, series2.name)
          (seriesList,start,end,step) = normalize([(series1, series2)])
          resultValues = [ safe.safeMul(safe.safeDiv(v1, v2), 100.0) for v1,v2 in izip_longest(series1,series2) ]

        resultSeries = TimeSeries(name,start,end,step,resultValues,xFilesFactor=xFilesFactor)
        resultList.append(resultSeries)

    return resultList

  if total is None:
    totalValues = [ safe.safeSum(row) for row in izip_longest(*seriesList) ]
    totalText = "sumSeries(%s)" % formatPathExpressions(seriesList)
  elif type(total) is list:
    if len(total) != 1 and len(total) != len(seriesList):
      raise InputParameterError("asPercent second argument must be missing, a single digit, reference exactly 1 series or reference the same number of series as the first argument")

    if len(total) == 1:
      normalize([seriesList, total])
      totalValues = total[0]
      totalText = totalValues.name
  else:
    totalValues = [total] * len(seriesList[0])
    totalText = str(total)

  resultList = []
  if type(total) is list and len(total) == len(seriesList):
    for series1, series2 in matchSeries(seriesList, total):
      name = "asPercent(%s,%s)" % (series1.name,series2.name)
      (seriesList,start,end,step) = normalize([(series1, series2)])
      resultValues = [ safe.safeMul(safe.safeDiv(v1, v2), 100.0) for v1,v2 in izip_longest(series1,series2) ]
      resultSeries = TimeSeries(name,start,end,step,resultValues,xFilesFactor=xFilesFactor)
      resultList.append(resultSeries)
  else:
    for series in seriesList:
      resultValues = [ safe.safeMul(safe.safeDiv(val, totalVal), 100.0) for val,totalVal in izip_longest(series,totalValues) ]

      name = "asPercent(%s,%s)" % (series.name, totalText or series.pathExpression)
      resultSeries = TimeSeries(name,series.start,series.end,series.step,resultValues,xFilesFactor=xFilesFactor)
      resultList.append(resultSeries)

  return resultList
def asPercent ( requestContext , seriesList , total = None , * nodes ) : normalize ( [ seriesList ] ) xFilesFactor = requestContext . get ( 'xFilesFactor' ) # if nodes are specified, use them to match series & total if nodes : keys = [ ] # group series together by key metaSeries = { } for series in seriesList : key = aggKey ( series , nodes ) if key not in metaSeries : metaSeries [ key ] = [ series ] keys . append ( key ) else : metaSeries [ key ] . append ( series ) # make list of totals totalSeries = { } # no total seriesList was specified, sum the values for each group of series if total is None : for key in keys : if len ( metaSeries [ key ] ) == 1 : totalSeries [ key ] = metaSeries [ key ] [ 0 ] else : name = 'sumSeries(%s)' % formatPathExpressions ( metaSeries [ key ] ) ( seriesList , start , end , step ) = normalize ( [ metaSeries [ key ] ] ) totalValues = [ safe . safeSum ( row ) for row in izip_longest ( * metaSeries [ key ] ) ] totalSeries [ key ] = TimeSeries ( name , start , end , step , totalValues , xFilesFactor = xFilesFactor ) # total seriesList was specified, sum the values for each group of totals elif isinstance ( total , list ) : for series in total : key = aggKey ( series , nodes ) if key not in totalSeries : totalSeries [ key ] = [ series ] if key not in keys : keys . append ( key ) else : totalSeries [ key ] . append ( series ) for key in totalSeries . keys ( ) : if len ( totalSeries [ key ] ) == 1 : totalSeries [ key ] = totalSeries [ key ] [ 0 ] else : name = 'sumSeries(%s)' % formatPathExpressions ( totalSeries [ key ] ) ( seriesList , start , end , step ) = normalize ( [ totalSeries [ key ] ] ) totalValues = [ safe . safeSum ( row ) for row in izip_longest ( * totalSeries [ key ] ) ] totalSeries [ key ] = TimeSeries ( name , start , end , step , totalValues , xFilesFactor = xFilesFactor ) # trying to use nodes with a total value, which isn't supported because it has no effect else : raise InputParameterError ( 'total must be None or a seriesList' ) resultList = [ ] for key in keys : # no series, must have total only if key not in metaSeries : series2 = totalSeries [ key ] name = "asPercent(%s,%s)" % ( 'MISSING' , series2 . name ) resultValues = [ None for v1 in series2 ] resultSeries = TimeSeries ( name , start , end , step , resultValues , xFilesFactor = xFilesFactor ) resultList . append ( resultSeries ) continue for series1 in metaSeries [ key ] : # no total if key not in totalSeries : name = "asPercent(%s,%s)" % ( series1 . name , 'MISSING' ) resultValues = [ None for v1 in series1 ] # series and total else : series2 = totalSeries [ key ] name = "asPercent(%s,%s)" % ( series1 . name , series2 . name ) ( seriesList , start , end , step ) = normalize ( [ ( series1 , series2 ) ] ) resultValues = [ safe . safeMul ( safe . safeDiv ( v1 , v2 ) , 100.0 ) for v1 , v2 in izip_longest ( series1 , series2 ) ] resultSeries = TimeSeries ( name , start , end , step , resultValues , xFilesFactor = xFilesFactor ) resultList . append ( resultSeries ) return resultList if total is None : totalValues = [ safe . safeSum ( row ) for row in izip_longest ( * seriesList ) ] totalText = "sumSeries(%s)" % formatPathExpressions ( seriesList ) elif type ( total ) is list : if len ( total ) != 1 and len ( total ) != len ( seriesList ) : raise InputParameterError ( "asPercent second argument must be missing, a single digit, reference exactly 1 series or reference the same number of series as the first argument" ) if len ( total ) == 1 : normalize ( [ seriesList , total ] ) totalValues = total [ 0 ] totalText = totalValues . name else : totalValues = [ total ] * len ( seriesList [ 0 ] ) totalText = str ( total ) resultList = [ ] if type ( total ) is list and len ( total ) == len ( seriesList ) : for series1 , series2 in matchSeries ( seriesList , total ) : name = "asPercent(%s,%s)" % ( series1 . name , series2 . name ) ( seriesList , start , end , step ) = normalize ( [ ( series1 , series2 ) ] ) resultValues = [ safe . safeMul ( safe . safeDiv ( v1 , v2 ) , 100.0 ) for v1 , v2 in izip_longest ( series1 , series2 ) ] resultSeries = TimeSeries ( name , start , end , step , resultValues , xFilesFactor = xFilesFactor ) resultList . append ( resultSeries ) else : for series in seriesList : resultValues = [ safe . safeMul ( safe . safeDiv ( val , totalVal ) , 100.0 ) for val , totalVal in izip_longest ( series , totalValues ) ] name = "asPercent(%s,%s)" % ( series . name , totalText or series . pathExpression ) resultSeries = TimeSeries ( name , series . start , series . end , series . step , resultValues , xFilesFactor = xFilesFactor ) resultList . append ( resultSeries ) return resultList