Ejemplo n.º 1
0
def asPercent(seriesList1, seriesList2orNumber):
    assert len(
        seriesList1
    ) == 1, "asPercent series arguments must reference *exactly* 1 series"
    series1 = seriesList1[0]
    if type(seriesList2orNumber) is list:
        assert len(
            seriesList2orNumber
        ) == 1, "asPercent series arguments must reference *exactly* 1 series"
        series2 = seriesList2orNumber[0]
        name = "asPercent(%s,%s)" % (series1.name, series2.name)
        series = (series1, series2)
        step = reduce(lcm, [s.step for s in series])
        for s in series:
            s.consolidate(step / s.step)
        start = min([s.start for s in series])
        end = max([s.end for s in series])
        end -= (end - start) % step
        values = (safeMul(safeDiv(v1, v2), 100.0) for v1, v2 in izip(*series))
    else:
        number = float(seriesList2orNumber)
        name = "asPercent(%s,%.1f)" % (series1.name, number)
        step = series1.step
        start = series1.start
        end = series1.end
        values = (safeMul(safeDiv(v, number), 100.0) for v in series1)
    series = TimeSeries(name, start, end, step, values)
    series.pathExpression = name
    return [series]
Ejemplo n.º 2
0
def nonNegativeDerivative(
    seriesList,
    maxValue=None
):  # useful for watching counter metrics that occasionally wrap
    results = []
    for series in seriesList:
        newValues = []
        prev = None
        for val in series:
            if None in (prev, val):
                newValues.append(None)
                prev = val
                continue
            diff = val - prev
            if diff >= 0:
                newValues.append(diff)
            elif maxValue is not None and maxValue >= val:
                newValues.append(prev + (maxValue - val))
            else:
                newValues.append(None)
            prev = val
        newName = "nonNegativeDerivative(%s)" % series.name
        newSeries = TimeSeries(newName, series.start, series.end, series.step,
                               newValues)
        newSeries.pathExpression = newName
        results.append(newSeries)
    return results
Ejemplo n.º 3
0
def asPercent(seriesList1,seriesList2orNumber):
  assert len(seriesList1) == 1, "asPercent series arguments must reference *exactly* 1 series"
  series1 = seriesList1[0]
  if type(seriesList2orNumber) is list:
    assert len(seriesList2orNumber) == 1, "asPercent series arguments must reference *exactly* 1 series"
    series2 = seriesList2orNumber[0]
    name = "asPercent(%s,%s)" % (series1.name,series2.name)
    series = (series1,series2)
    step = reduce(lcm,[s.step for s in series])
    for s in series:
      s.consolidate( step / s.step )
    start = min([s.start for s in series])
    end = max([s.end for s in series])
    end -= (end - start) % step
    values = ( safeMul( safeDiv(v1,v2), 100.0 ) for v1,v2 in izip(*series) )
  else:
    number = float(seriesList2orNumber)
    name = "asPercent(%s,%.1f)" % (series1.name,number)
    step = series1.step
    start = series1.start
    end = series1.end
    values = ( safeMul( safeDiv(v,number), 100.0 ) for v in series1 )
  series = TimeSeries(name,start,end,step,values)
  series.pathExpression = name
  return [series]
Ejemplo n.º 4
0
def diffSeries(*seriesLists):
  (seriesList,start,end,step) = normalize(seriesLists)
  name = "diffSeries(%s)" % ','.join(set([s.pathExpression for s in seriesList]))
  values = ( safeDiff(row) for row in izip(*seriesList) )
  series = TimeSeries(name,start,end,step,values)
  series.pathExpression = name
  return [series]
Ejemplo n.º 5
0
def diffSeries(*seriesLists):
    (seriesList, start, end, step) = normalize(seriesLists)
    name = "diffSeries(%s)" % ','.join(
        set([s.pathExpression for s in seriesList]))
    values = (safeDiff(row) for row in izip(*seriesList))
    series = TimeSeries(name, start, end, step, values)
    series.pathExpression = name
    return [series]
Ejemplo n.º 6
0
def averageSeries(*seriesLists):
  (seriesList,start,end,step) = normalize(seriesLists)
  #name = "averageSeries(%s)" % ','.join((s.name for s in seriesList))
  name = "averageSeries(%s)" % ','.join(set([s.pathExpression for s in seriesList]))
  values = ( safeDiv(safeSum(row),safeLen(row)) for row in izip(*seriesList) )
  series = TimeSeries(name,start,end,step,values)
  series.pathExpression = name
  return [series]
Ejemplo n.º 7
0
def boundedAverageSeries(min, max, *seriesLists):
  (seriesList,start,end,step) = normalize(seriesLists)
  #name = "averageSeries(%s)" % ','.join((s.name for s in seriesList))
  name = "averageSeries(%s)" % ','.join(set([s.pathExpression for s in seriesList]))
  bounded_rows = (safeBound(row, min, max) for row in izip(*seriesList))
  values = ( safeDiv(safeSum(row),safeLen(row)) for row in bounded_rows ) #XXX izip
  series = TimeSeries(name,start,end,step,values)
  series.pathExpression = name
  return [series]
Ejemplo n.º 8
0
def averageSeries(*seriesLists):
    (seriesList, start, end, step) = normalize(seriesLists)
    #name = "averageSeries(%s)" % ','.join((s.name for s in seriesList))
    name = "averageSeries(%s)" % ','.join(
        set([s.pathExpression for s in seriesList]))
    values = (safeDiv(safeSum(row), safeLen(row)) for row in izip(*seriesList))
    series = TimeSeries(name, start, end, step, values)
    series.pathExpression = name
    return [series]
Ejemplo n.º 9
0
def stdev(seriesList, time):
    count = 0
    for series in seriesList:
        stddevs = TimeSeries("stddev(%s,%.1f)" % (series.name, float(time)),
                             series.start, series.end, series.step, [])
        stddevs.pathExpression = "stddev(%s,%.1f)" % (series.name, float(time))
        avg = safeDiv(safeSum(series[:time]), time)

        sumOfSquares = sum(map(lambda (x): x * x, series[:time]))
        (sd, sumOfSquares) = doStdDev(sumOfSquares, 0, 0, time, avg)
        stddevs.append(sd)

        for (index, el) in enumerate(series[time:]):
            if el is None:
                continue
            toDrop = series[index]
            if toDrop is None:
                toDrop = 0
            s = safeSum([safeMul(time, avg), el, -toDrop])
            avg = safeDiv(s, time)

            (sd, sumOfSquares) = doStdDev(sumOfSquares, toDrop,
                                          series[index + time], time, avg)
            stddevs.append(sd)
        for i in range(0, time - 1):
            stddevs.insert(0, None)
        seriesList[count] = stddevs
        count = count + 1
    return seriesList
Ejemplo n.º 10
0
def sumSeries(*seriesLists):
  try:
    (seriesList,start,end,step) = normalize(seriesLists)
  except:
    return []
  #name = "sumSeries(%s)" % ','.join((s.name for s in seriesList))
  name = "sumSeries(%s)" % ','.join(set([s.pathExpression for s in seriesList]))
  values = ( safeSum(row) for row in izip(*seriesList) )
  series = TimeSeries(name,start,end,step,values)
  series.pathExpression = name
  return [series]
Ejemplo n.º 11
0
def sumSeries(*seriesLists):
    try:
        (seriesList, start, end, step) = normalize(seriesLists)
    except:
        return []
    #name = "sumSeries(%s)" % ','.join((s.name for s in seriesList))
    name = "sumSeries(%s)" % ','.join(
        set([s.pathExpression for s in seriesList]))
    values = (safeSum(row) for row in izip(*seriesList))
    series = TimeSeries(name, start, end, step, values)
    series.pathExpression = name
    return [series]
Ejemplo n.º 12
0
def evaluateTokens(tokens, timeInterval, originalTime = None):
  if tokens.expression:
    if tokens[0][0][0] == "timeShift":
      delta = timeInterval[0] - parseATTime(tokens[0][0][1][1]['string'].strip('"'))
      delta += timeInterval[1] - timeInterval[0]
      originalTime = timeInterval
      timeInterval = (timeInterval[0] - delta, timeInterval[1] - delta)
    return evaluateTokens(tokens.expression, timeInterval, originalTime)

  elif tokens.pathExpression:
    pathExpr = tokens.pathExpression
    if pathExpr.lower().startswith('graphite.'):
      pathExpr = pathExpr[9:]

    seriesList = []
    (startTime,endTime) = originalTime or timeInterval

    for dbFile in settings.STORE.find(pathExpr):
      log.metric_access(dbFile.metric_path)
      getCacheResults = CarbonLink.sendRequest(dbFile.real_metric)
      dbResults = dbFile.fetch( timestamp(startTime), timestamp(endTime) )
      results = mergeResults(dbResults, getCacheResults())

      if not results:
        continue

      (timeInfo,values) = results
      (start,end,step) = timeInfo
      series = TimeSeries(dbFile.metric_path, start, end, step, values)
      series.pathExpression = pathExpr #hack to pass expressions through to render functions
      series.start = time.mktime(startTime.timetuple())
      series.end = time.mktime(endTime.timetuple())
      seriesList.append(series)

    return seriesList

  elif tokens.call:
    func = SeriesFunctions[tokens.call.func]
    args = [evaluateTokens(arg, timeInterval, originalTime) for arg in tokens.call.args]
    return func(*args)

  elif tokens.number:
    if tokens.number.integer:
      return int(tokens.number.integer)

    elif tokens.number.float:
      return float(tokens.number.float)

  elif tokens.string:
    return str(tokens.string)[1:-1]
Ejemplo n.º 13
0
def log(seriesList, base=10):
  results = []
  for series in seriesList:
    newValues = []
    for val in series:
      if val is None:
        newValues.append(None)
      else:
        newValues.append(math.log(val, base))
    newName = "log(%s, %s)" % (series.name, base)
    newSeries = TimeSeries(newName, series.start, series.end, series.step, newValues)
    newSeries.pathExpression = newName
    results.append(newSeries)
  return results
Ejemplo n.º 14
0
def stdev(seriesList,time):
  count = 0
  for series in seriesList:
    stddevs = TimeSeries("stddev(%s,%.1f)" % (series.name,float(time)),series.start,series.end,series.step,[])
    stddevs.pathExpression = "stddev(%s,%.1f)" % (series.name,float(time))
    avg = safeDiv(safeSum(series[:time]), time)

    sumOfSquares = sum(map(lambda(x): x * x, series[:time]))
    (sd, sumOfSquares) = doStdDev(sumOfSquares, 0, 0, time, avg)
    stddevs.append(sd)

    for (index, el) in enumerate(series[time:]):
      if el is None:
        continue
      toDrop = series[index]
      if toDrop is None:
        toDrop = 0
      s = safeSum([safeMul(time, avg), el, -toDrop])
      avg = safeDiv(s, time)

      (sd, sumOfSquares) = doStdDev(sumOfSquares, toDrop, series[index+time], time, avg)
      stddevs.append(sd)
    for i in range(0, time-1):
      stddevs.insert(0, None)
    seriesList[count] = stddevs
    count = count + 1
  return seriesList
Ejemplo n.º 15
0
def log(seriesList, base=10):
    results = []
    for series in seriesList:
        newValues = []
        for val in series:
            if val is None:
                newValues.append(None)
            else:
                newValues.append(math.log(val, base))
        newName = "log(%s, %s)" % (series.name, base)
        newSeries = TimeSeries(newName, series.start, series.end, series.step,
                               newValues)
        newSeries.pathExpression = newName
        results.append(newSeries)
    return results
Ejemplo n.º 16
0
def integral(seriesList):
  results = []
  for series in seriesList:
    newValues = []
    current = 0.0
    for val in series:
      if val is None:
        newValues.append(None)
      else:
        current += val
        newValues.append(current)
    newName = "integral(%s)" % series.name
    newSeries = TimeSeries(newName, series.start, series.end, series.step, newValues)
    newSeries.pathExpression = newName
    results.append(newSeries)
  return results
Ejemplo n.º 17
0
def evaluateTokens(tokens, timeInterval):
  if tokens.expression:
    return evaluateTokens(tokens.expression, timeInterval)

  elif tokens.pathExpression:
    pathExpr = tokens.pathExpression

    if pathExpr.lower().startswith('graphite.'):
      pathExpr = pathExpr[9:]

    seriesList = []
    (startTime,endTime) = timeInterval

    for dbFile in settings.STORE.find(pathExpr):
      log.metric_access(dbFile.metric_path)
      getCacheResults = CarbonLink.sendRequest(dbFile.real_metric)
      dbResults = dbFile.fetch( timestamp(startTime), timestamp(endTime) )
      results = mergeResults(dbResults, getCacheResults())

      if not results:
        continue

      (timeInfo,values) = results
      (start,end,step) = timeInfo
      series = TimeSeries(dbFile.metric_path, start, end, step, values)
      series.pathExpression = pathExpr #hack to pass expressions through to render functions
      seriesList.append(series)

    return seriesList

  elif tokens.call:
    func = SeriesFunctions[tokens.call.func]
    args = [evaluateTokens(arg, timeInterval) for arg in tokens.call.args]
    try:
      return func(*args)
    except:
      return []

  elif tokens.number:
    if tokens.number.integer:
      return int(tokens.number.integer)

    elif tokens.number.float:
      return float(tokens.number.float)

  elif tokens.string:
    return str(tokens.string)[1:-1]
Ejemplo n.º 18
0
def derivative(seriesList):
  results = []
  for series in seriesList:
    newValues = []
    prev = None
    for val in series:
      if None in (prev,val):
        newValues.append(None)
        prev = val
        continue
      newValues.append(val - prev)
      prev = val
    newName = "derivative(%s)" % series.name
    newSeries = TimeSeries(newName, series.start, series.end, series.step, newValues)
    newSeries.pathExpression = newName
    results.append(newSeries)
  return results
Ejemplo n.º 19
0
def nonNegativeDerivative(seriesList): # useful for watching counter metrics that occasionally wrap
  results = []
  for series in seriesList:
    newValues = []
    prev = None
    for val in series:
      if None in (prev,val):
        newValues.append(None)
        prev = val
        continue
      newValues.append( max(val - prev, 0) )
      prev = val
    newName = "nonNegativeDerivative(%s)" % series.name
    newSeries = TimeSeries(newName, series.start, series.end, series.step, newValues)
    newSeries.pathExpression = newName
    results.append(newSeries)
  return results
Ejemplo n.º 20
0
def integral(seriesList):
    results = []
    for series in seriesList:
        newValues = []
        current = 0.0
        for val in series:
            if val is None:
                newValues.append(None)
            else:
                current += val
                newValues.append(current)
        newName = "integral(%s)" % series.name
        newSeries = TimeSeries(newName, series.start, series.end, series.step,
                               newValues)
        newSeries.pathExpression = newName
        results.append(newSeries)
    return results
Ejemplo n.º 21
0
def derivative(seriesList):
    results = []
    for series in seriesList:
        newValues = []
        prev = None
        for val in series:
            if None in (prev, val):
                newValues.append(None)
                prev = val
                continue
            newValues.append(val - prev)
            prev = val
        newName = "derivative(%s)" % series.name
        newSeries = TimeSeries(newName, series.start, series.end, series.step,
                               newValues)
        newSeries.pathExpression = newName
        results.append(newSeries)
    return results
Ejemplo n.º 22
0
def nonNegativeDerivative(seriesList, maxValue=None): # useful for watching counter metrics that occasionally wrap
  results = []
  for series in seriesList:
    newValues = []
    prev = None
    for val in series:
      if None in (prev,val):
        newValues.append(None)
        prev = val
        continue
      diff = val - prev
      if diff >= 0:
        newValues.append(diff)
      elif maxValue is not None and maxValue >= val:
        newValues.append( prev + (maxValue - val) )
      else:
        newValues.append(None)
      prev = val
    newName = "nonNegativeDerivative(%s)" % series.name
    newSeries = TimeSeries(newName, series.start, series.end, series.step, newValues)
    newSeries.pathExpression = newName
    results.append(newSeries)
  return results
Ejemplo n.º 23
0
def movingAverage(seriesList, time):
    count = 0
    for series in seriesList:
        movAvg = TimeSeries(
            "movingAverage(%s,%.1f)" % (series.name, float(time)),
            series.start, series.end, series.step, [])
        movAvg.pathExpression = "movingAverage(%s,%.1f)" % (series.name,
                                                            float(time))
        avg = safeDiv(safeSum(series[:time]), time)
        movAvg.append(avg)
        for (index, el) in enumerate(series[time:]):
            if el is None:
                continue
            toDrop = series[index]
            if toDrop is None:
                toDrop = 0
            s = safeSum([safeMul(time, avg), el, -toDrop])
            avg = safeDiv(s, time)
            movAvg.append(avg)
        for i in range(0, time - 1):
            movAvg.insert(0, None)
        seriesList[count] = movAvg
        count = count + 1
    return seriesList
Ejemplo n.º 24
0
def movingAverage(seriesList,time):
  count = 0
  for series in seriesList:
    movAvg = TimeSeries("movingAverage(%s,%.1f)" % (series.name,float(time)),series.start,series.end,series.step,[])
    movAvg.pathExpression = "movingAverage(%s,%.1f)" % (series.name,float(time))
    avg = safeDiv(safeSum(series[:time]), time)
    movAvg.append(avg)
    for (index, el) in enumerate(series[time:]):
      if el is None:
        continue
      toDrop = series[index]
      if toDrop is None:
        toDrop = 0
      s = safeSum([safeMul(time, avg), el, -toDrop])
      avg = safeDiv(s, time)
      movAvg.append(avg)
    for i in range(0, time-1):
      movAvg.insert(0, None)
    seriesList[count] = movAvg
    count = count + 1
  return seriesList
Ejemplo n.º 25
0
  def drawLines(self, width=None, dash=None, linecap='butt', linejoin='miter'):
    if not width: width = self.lineWidth
    self.ctx.set_line_width(width)
    originalWidth = width
    width = float(int(width) % 2) / 2
    if dash:
      self.ctx.set_dash(dash,1)
    else:
      self.ctx.set_dash([],0)
    self.ctx.set_line_cap({
      'butt' : cairo.LINE_CAP_BUTT,
      'round' : cairo.LINE_CAP_ROUND,
      'square' : cairo.LINE_CAP_SQUARE,
    }[linecap])
    self.ctx.set_line_join({
      'miter' : cairo.LINE_JOIN_MITER,
      'round' : cairo.LINE_JOIN_ROUND,
      'bevel' : cairo.LINE_JOIN_BEVEL,
    }[linejoin])

    # stack the values
    if self.areaMode == 'stacked':
      total = []
      for series in self.data:
        for i in range(len(series)):
          if len(total) <= i: total.append(0)

          if series[i] is not None:
            original = series[i]
            series[i] += total[i]
            total[i] += original

      self.data = reverse_sort(self.data)

    # setup the clip region
    self.ctx.set_line_width(1.0)
    self.ctx.rectangle(self.area['xmin'], self.area['ymin'], self.area['xmax'] - self.area['xmin'], self.area['ymax'] - self.area['ymin'])
    self.ctx.clip()
    self.ctx.set_line_width(originalWidth)

    if self.params.get('areaAlpha') and self.areaMode == 'first':
      alphaSeries = TimeSeries(None, self.data[0].start, self.data[0].end, self.data[0].step, [x for x in self.data[0]])
      alphaSeries.xStep = self.data[0].xStep
      alphaSeries.color = self.data[0].color
      try:
        alphaSeries.options['alpha'] = float(self.params['areaAlpha'])
      except ValueError:
        pass
      self.data.insert(0, alphaSeries)

    for series in self.data:

      if series.options.has_key('noDraw'):
        continue

      if series.options.has_key('lineWidth'): # adjusts the lineWidth of this line if option is set on the series
        self.ctx.set_line_width(series.options['lineWidth'])

      if series.options.has_key('dashed'): # turn on dashing if dashed option set
        self.ctx.set_dash([ series.options['dashed'] ],1)
      else:
        self.ctx.set_dash([], 0)

      x = float(self.area['xmin']) + (self.lineWidth / 2.0)
      y = float(self.area['ymin'])
      self.setColor( series.color, series.options.get('alpha') or 1.0)

      fromNone = True
      idx = -1
      return_path = []

      for value in series:
        idx = idx+1

        if value is None and self.params.get('drawNullAsZero'):
          value = 0.0

        if series.options.has_key('areaFill'):
           value_btm = series.options['lowBoundData'][idx]
           if value_btm is None:
             value = None

        if value is None:

          if not fromNone:
            if series.options.has_key('areaFill'):
              # close up the shape by drawing to the previous bottom points in reverse
              for pair in reverse_sort(return_path):
                self.ctx.line_to(pair[0], pair[1])
              return_path = []
              self.ctx.close_path()
              self.ctx.fill()
            elif self.areaMode != 'none': #Close off and fill area before unknown interval
              self.ctx.line_to(x, self.area['ymax'])
              self.ctx.close_path()
              self.ctx.fill()
          x += series.xStep
          fromNone = True

        else:
          y = self.area['ymax'] - ((float(value) - self.yBottom) * self.yScaleFactor)
          if y < 0: y = 0

          if series.options.has_key('areaFill'):
             value_btm = series.options['lowBoundData'][idx]
             y_btm = self.area['ymax'] - ((float(value_btm) - self.yBottom) * self.yScaleFactor)
             if y_btm < 0: y_btm = 0
             return_path.append((x + series.xStep, y_btm))

          if series.options.has_key('drawAsInfinite') and value > 0:
            self.ctx.move_to(x, self.area['ymax'])
            self.ctx.line_to(x, self.area['ymin'])
            self.ctx.stroke()
            x += series.xStep
            continue

          if self.lineMode == 'staircase':
            if fromNone:

              if series.options.has_key('areaFill'):
                self.ctx.move_to(x,y_btm)
                self.ctx.line_to(x,y)
              elif self.areaMode != 'none':
                self.ctx.move_to(x,self.area['ymax'])
                self.ctx.line_to(x,y)
              else:
                self.ctx.move_to(x,y)

            else:
              self.ctx.line_to(x,y)

            x += series.xStep
            self.ctx.line_to(x,y)

          elif self.lineMode == 'slope':
            if fromNone:

              if series.options.has_key('areaFill'):
                self.ctx.move_to(x,y_btm)
                self.ctx.line_to(x,y)
              elif self.areaMode != 'none':
                self.ctx.move_to(x,self.area['ymax'])
                self.ctx.line_to(x,y)
              else:
                self.ctx.move_to(x,y)

            x += series.xStep
            self.ctx.line_to(x,y)

          fromNone = False

      if series.options.has_key('areaFill'):
        for pair in reverse_sort(return_path):
          self.ctx.line_to(pair[0], pair[1])
        self.ctx.close_path()
        self.ctx.fill()

      elif self.areaMode != 'none':
        self.ctx.line_to(x, self.area['ymax'])
        self.ctx.close_path()
        self.ctx.fill()

        if self.areaMode == 'first':
          self.areaMode = 'none' #This ensures only the first line is drawn as area

      else:
        self.ctx.stroke()

      self.ctx.set_line_width(originalWidth) # return to the original line width
      if series.options.has_key('dash'): # if we changed the dash setting before, change it back now
        if dash:
          self.ctx.set_dash(dash,1)
        else:
          self.ctx.set_dash([],0)
Ejemplo n.º 26
0
    def drawLines(self,
                  width=None,
                  dash=None,
                  linecap='butt',
                  linejoin='miter'):
        if not width: width = self.lineWidth
        self.ctx.set_line_width(width)
        originalWidth = width
        width = float(int(width) % 2) / 2
        if dash:
            self.ctx.set_dash(dash, 1)
        else:
            self.ctx.set_dash([], 0)
        self.ctx.set_line_cap({
            'butt': cairo.LINE_CAP_BUTT,
            'round': cairo.LINE_CAP_ROUND,
            'square': cairo.LINE_CAP_SQUARE,
        }[linecap])
        self.ctx.set_line_join({
            'miter': cairo.LINE_JOIN_MITER,
            'round': cairo.LINE_JOIN_ROUND,
            'bevel': cairo.LINE_JOIN_BEVEL,
        }[linejoin])

        # stack the values
        if self.areaMode == 'stacked':
            total = []
            for series in self.data:
                for i in range(len(series)):
                    if len(total) <= i: total.append(0)

                    if series[i] is not None:
                        original = series[i]
                        series[i] += total[i]
                        total[i] += original

            self.data = reverse_sort(self.data)

        # setup the clip region
        self.ctx.set_line_width(1.0)
        self.ctx.rectangle(self.area['xmin'], self.area['ymin'],
                           self.area['xmax'] - self.area['xmin'],
                           self.area['ymax'] - self.area['ymin'])
        self.ctx.clip()
        self.ctx.set_line_width(originalWidth)

        if self.params.get('areaAlpha') and self.areaMode == 'first':
            alphaSeries = TimeSeries(None, self.data[0].start,
                                     self.data[0].end, self.data[0].step,
                                     [x for x in self.data[0]])
            alphaSeries.xStep = self.data[0].xStep
            alphaSeries.color = self.data[0].color
            try:
                alphaSeries.options['alpha'] = float(self.params['areaAlpha'])
            except ValueError:
                pass
            self.data.insert(0, alphaSeries)

        for series in self.data:

            if series.options.has_key('noDraw'):
                continue

            if series.options.has_key(
                    'lineWidth'
            ):  # adjusts the lineWidth of this line if option is set on the series
                self.ctx.set_line_width(series.options['lineWidth'])

            if series.options.has_key(
                    'dashed'):  # turn on dashing if dashed option set
                self.ctx.set_dash([series.options['dashed']], 1)
            else:
                self.ctx.set_dash([], 0)

            x = float(self.area['xmin']) + (self.lineWidth / 2.0)
            y = float(self.area['ymin'])
            self.setColor(series.color, series.options.get('alpha') or 1.0)

            fromNone = True
            idx = -1
            return_path = []

            for value in series:
                idx = idx + 1

                if value is None and self.params.get('drawNullAsZero'):
                    value = 0.0

                if series.options.has_key('areaFill'):
                    value_btm = series.options['lowBoundData'][idx]
                    if value_btm is None:
                        value = None

                if value is None:

                    if not fromNone:
                        if series.options.has_key('areaFill'):
                            # close up the shape by drawing to the previous bottom points in reverse
                            for pair in reverse_sort(return_path):
                                self.ctx.line_to(pair[0], pair[1])
                            return_path = []
                            self.ctx.close_path()
                            self.ctx.fill()
                        elif self.areaMode != 'none':  #Close off and fill area before unknown interval
                            self.ctx.line_to(x, self.area['ymax'])
                            self.ctx.close_path()
                            self.ctx.fill()
                    x += series.xStep
                    fromNone = True

                else:
                    y = self.area['ymax'] - (
                        (float(value) - self.yBottom) * self.yScaleFactor)
                    if y < 0: y = 0

                    if series.options.has_key('areaFill'):
                        value_btm = series.options['lowBoundData'][idx]
                        y_btm = self.area['ymax'] - (
                            (float(value_btm) - self.yBottom) *
                            self.yScaleFactor)
                        if y_btm < 0: y_btm = 0
                        return_path.append((x + series.xStep, y_btm))

                    if series.options.has_key('drawAsInfinite') and value > 0:
                        self.ctx.move_to(x, self.area['ymax'])
                        self.ctx.line_to(x, self.area['ymin'])
                        self.ctx.stroke()
                        x += series.xStep
                        continue

                    if self.lineMode == 'staircase':
                        if fromNone:

                            if series.options.has_key('areaFill'):
                                self.ctx.move_to(x, y_btm)
                                self.ctx.line_to(x, y)
                            elif self.areaMode != 'none':
                                self.ctx.move_to(x, self.area['ymax'])
                                self.ctx.line_to(x, y)
                            else:
                                self.ctx.move_to(x, y)

                        else:
                            self.ctx.line_to(x, y)

                        x += series.xStep
                        self.ctx.line_to(x, y)

                    elif self.lineMode == 'slope':
                        if fromNone:

                            if series.options.has_key('areaFill'):
                                self.ctx.move_to(x, y_btm)
                                self.ctx.line_to(x, y)
                            elif self.areaMode != 'none':
                                self.ctx.move_to(x, self.area['ymax'])
                                self.ctx.line_to(x, y)
                            else:
                                self.ctx.move_to(x, y)

                        x += series.xStep
                        self.ctx.line_to(x, y)

                    fromNone = False

            if series.options.has_key('areaFill'):
                for pair in reverse_sort(return_path):
                    self.ctx.line_to(pair[0], pair[1])
                self.ctx.close_path()
                self.ctx.fill()

            elif self.areaMode != 'none':
                self.ctx.line_to(x, self.area['ymax'])
                self.ctx.close_path()
                self.ctx.fill()

                if self.areaMode == 'first':
                    self.areaMode = 'none'  #This ensures only the first line is drawn as area

            else:
                self.ctx.stroke()

            self.ctx.set_line_width(
                originalWidth)  # return to the original line width
            if series.options.has_key(
                    'dash'
            ):  # if we changed the dash setting before, change it back now
                if dash:
                    self.ctx.set_dash(dash, 1)
                else:
                    self.ctx.set_dash([], 0)