def prepare(self, state, dataTable, functionTable, performanceTable, plotRange): """Prepare a plot element for drawing. This stage consists of calculating all quantities and determing the bounds of the data. These bounds may be unioned with bounds from other plot elements that overlay this plot element, so the drawing (which requires a finalized coordinate system) cannot begin yet. This method modifies C{plotRange}. @type state: ad-hoc Python object @param state: State information that persists long enough to use quantities computed in C{prepare} in the C{draw} stage. This is a work-around of lxml's refusal to let its Python instances maintain C{self} and it is unrelated to DataTableState. @type dataTable: DataTable @param dataTable: Contains the data to plot. @type functionTable: FunctionTable @param functionTable: Defines functions that may be used to transform data for plotting. @type performanceTable: PerformanceTable @param performanceTable: Measures and records performance (time and memory consumption) of the drawing process. @type plotRange: PlotRange @param plotRange: The bounding box of plot coordinates that this function will expand. """ self.checkRoles([ "y(x)", "dy/dx", "x(t)", "y(t)", "dx/dt", "dy/dt", "x", "y", "dx", "dy" ]) performanceTable.begin("PlotCurve prepare") self._saveContext(dataTable) yofx = self.xpath("pmml:PlotFormula[@role='y(x)']") dydx = self.xpath("pmml:PlotFormula[@role='dy/dx']") xoft = self.xpath("pmml:PlotFormula[@role='x(t)']") yoft = self.xpath("pmml:PlotFormula[@role='y(t)']") dxdt = self.xpath("pmml:PlotFormula[@role='dx/dt']") dydt = self.xpath("pmml:PlotFormula[@role='dy/dt']") nx = self.xpath("pmml:PlotNumericExpression[@role='x']") ny = self.xpath("pmml:PlotNumericExpression[@role='y']") ndx = self.xpath("pmml:PlotNumericExpression[@role='dx']") ndy = self.xpath("pmml:PlotNumericExpression[@role='dy']") cutExpression = self.xpath("pmml:PlotSelection") if len(yofx) + len(dydx) + len(xoft) + len(yoft) + len(dxdt) + len( dydt) > 0: if len(yofx) == 1 and len(dydx) == 0 and len(xoft) == 0 and len( yoft) == 0 and len(dxdt) == 0 and len(dydt) == 0: expression = (yofx[0].text, ) derivative = (None, ) elif len(yofx) == 1 and len(dydx) == 1 and len(xoft) == 0 and len( yoft) == 0 and len(dxdt) == 0 and len(dydt) == 0: expression = (yofx[0].text, ) derivative = (dydx[0].text, ) elif len(yofx) == 0 and len(dydx) == 0 and len(xoft) == 1 and len( yoft) == 1 and len(dxdt) == 0 and len(dydt) == 0: expression = xoft[0].text, yoft[0].text derivative = None, None elif len(yofx) == 0 and len(dydx) == 0 and len(xoft) == 1 and len( yoft) == 1 and len(dxdt) == 1 and len(dydt) == 1: expression = xoft[0].text, yoft[0].text derivative = dxdt[0].text, dydt[0].text else: raise defs.PmmlValidationError( "The only allowed combinations of PlotFormulae are: \"y(x)\", \"y(x) dy/dx\", \"x(t) y(t)\", and \"x(t) y(t) dx/dt dy/dt\"" ) low = self.get("low", convertType=True) high = self.get("high", convertType=True) if low is None or high is None: raise defs.PmmlValidationError( "The \"low\" and \"high\" attributes are required for PlotCurves defined by formulae" ) samples = self.generateSamples(low, high) loop = self.get("loop", defaultFromXsd=True, convertType=True) state.x, state.y, state.dx, state.dy, xfieldType, yfieldType = self.expressionsToPoints( expression, derivative, samples, loop, functionTable, performanceTable) else: performanceTable.pause("PlotCurve prepare") if len(ndx) == 1: dxdataColumn = ndx[0].evaluate(dataTable, functionTable, performanceTable) else: dxdataColumn = None if len(ndy) == 1: dydataColumn = ndy[0].evaluate(dataTable, functionTable, performanceTable) else: dydataColumn = None performanceTable.unpause("PlotCurve prepare") if len(nx) == 0 and len(ny) == 1: performanceTable.pause("PlotCurve prepare") ydataColumn = ny[0].evaluate(dataTable, functionTable, performanceTable) performanceTable.unpause("PlotCurve prepare") if len(cutExpression) == 1: performanceTable.pause("PlotCurve prepare") selection = cutExpression[0].select( dataTable, functionTable, performanceTable) performanceTable.unpause("PlotCurve prepare") else: selection = NP("ones", len(ydataColumn.data), NP.dtype(bool)) if ydataColumn.mask is not None: selection = NP("logical_and", selection, NP(ydataColumn.mask == defs.VALID), selection) if dxdataColumn is not None and dxdataColumn.mask is not None: selection = NP("logical_and", selection, NP(dxdataColumn.mask == defs.VALID), selection) if dydataColumn is not None and dydataColumn.mask is not None: selection = NP("logical_and", selection, NP(dydataColumn.mask == defs.VALID), selection) yarray = ydataColumn.data[selection] xarray = NP("ones", len(yarray), dtype=NP.dtype(float)) xarray[0] = 0.0 xarray = NP("cumsum", xarray) dxarray, dyarray = None, None if dxdataColumn is not None: dxarray = dxdataColumn.data[selection] if dydataColumn is not None: dyarray = dydataColumn.data[selection] xfieldType = self.xfieldType yfieldType = ydataColumn.fieldType elif len(nx) == 1 and len(ny) == 1: performanceTable.pause("PlotCurve prepare") xdataColumn = nx[0].evaluate(dataTable, functionTable, performanceTable) ydataColumn = ny[0].evaluate(dataTable, functionTable, performanceTable) performanceTable.unpause("PlotCurve prepare") if len(cutExpression) == 1: performanceTable.pause("PlotCurve prepare") selection = cutExpression[0].select( dataTable, functionTable, performanceTable) performanceTable.unpause("PlotCurve prepare") else: selection = NP("ones", len(ydataColumn.data), NP.dtype(bool)) if xdataColumn.mask is not None: selection = NP("logical_and", selection, NP(xdataColumn.mask == defs.VALID), selection) if ydataColumn.mask is not None: selection = NP("logical_and", selection, NP(ydataColumn.mask == defs.VALID), selection) if dxdataColumn is not None and dxdataColumn.mask is not None: selection = NP("logical_and", selection, NP(dxdataColumn.mask == defs.VALID), selection) if dydataColumn is not None and dydataColumn.mask is not None: selection = NP("logical_and", selection, NP(dydataColumn.mask == defs.VALID), selection) xarray = xdataColumn.data[selection] yarray = ydataColumn.data[selection] dxarray, dyarray = None, None if dxdataColumn is not None: dxarray = dxdataColumn.data[selection] if dydataColumn is not None: dyarray = dydataColumn.data[selection] xfieldType = xdataColumn.fieldType yfieldType = ydataColumn.fieldType else: raise defs.PmmlValidationError( "The only allowed combinations of PlotNumericExpressions are: \"y(x)\" and \"x(t) y(t)\"" ) persistentState = {} stateId = self.get("stateId") if stateId is not None: if stateId in dataTable.state: persistentState = dataTable.state[stateId] xarray = NP("concatenate", [xarray, persistentState["x"]]) yarray = NP("concatenate", [yarray, persistentState["y"]]) if dxarray is not None: dxarray = NP("concatenate", [dxarray, persistentState["dx"]]) if dyarray is not None: dyarray = NP("concatenate", [dyarray, persistentState["dy"]]) else: dataTable.state[stateId] = persistentState persistentState["x"] = xarray persistentState["y"] = yarray if dxarray is not None: persistentState["dx"] = dxarray if dyarray is not None: persistentState["dy"] = dyarray smooth = self.get("smooth", defaultFromXsd=True, convertType=True) if not smooth: if dyarray is not None and dxarray is None: dxarray = NP( (NP("roll", xarray, -1) - NP("roll", xarray, 1)) / 2.0) dyarray = dyarray * dxarray loop = self.get("loop", defaultFromXsd=True, convertType=True) if dxarray is not None and not loop: dxarray[0] = 0.0 dxarray[-1] = 0.0 if dyarray is not None and not loop: dyarray[0] = 0.0 dyarray[-1] = 0.0 state.x = xarray state.y = yarray state.dx = dxarray state.dy = dyarray else: smoothingScale = self.get("smoothingScale", defaultFromXsd=True, convertType=True) loop = self.get("loop", defaultFromXsd=True, convertType=True) samples = self.generateSamples(xarray.min(), xarray.max()) state.x, state.y, state.dx, state.dy = self.pointsToSmoothCurve( xarray, yarray, samples, smoothingScale, loop) if plotRange is not None: plotRange.expand(state.x, state.y, xfieldType, yfieldType) performanceTable.end("PlotCurve prepare")
def prepare(self, state, dataTable, functionTable, performanceTable, plotRange): """Prepare a plot element for drawing. This stage consists of calculating all quantities and determing the bounds of the data. These bounds may be unioned with bounds from other plot elements that overlay this plot element, so the drawing (which requires a finalized coordinate system) cannot begin yet. This method modifies C{plotRange}. @type state: ad-hoc Python object @param state: State information that persists long enough to use quantities computed in C{prepare} in the C{draw} stage. This is a work-around of lxml's refusal to let its Python instances maintain C{self} and it is unrelated to DataTableState. @type dataTable: DataTable @param dataTable: Contains the data to plot. @type functionTable: FunctionTable @param functionTable: Defines functions that may be used to transform data for plotting. @type performanceTable: PerformanceTable @param performanceTable: Measures and records performance (time and memory consumption) of the drawing process. @type plotRange: PlotRange @param plotRange: The bounding box of plot coordinates that this function will expand. """ self.checkRoles(["y(x)", "dy/dx", "x(t)", "y(t)", "dx/dt", "dy/dt", "x", "y", "dx", "dy"]) performanceTable.begin("PlotCurve prepare") self._saveContext(dataTable) yofx = self.xpath("pmml:PlotFormula[@role='y(x)']") dydx = self.xpath("pmml:PlotFormula[@role='dy/dx']") xoft = self.xpath("pmml:PlotFormula[@role='x(t)']") yoft = self.xpath("pmml:PlotFormula[@role='y(t)']") dxdt = self.xpath("pmml:PlotFormula[@role='dx/dt']") dydt = self.xpath("pmml:PlotFormula[@role='dy/dt']") nx = self.xpath("pmml:PlotNumericExpression[@role='x']") ny = self.xpath("pmml:PlotNumericExpression[@role='y']") ndx = self.xpath("pmml:PlotNumericExpression[@role='dx']") ndy = self.xpath("pmml:PlotNumericExpression[@role='dy']") cutExpression = self.xpath("pmml:PlotSelection") if len(yofx) + len(dydx) + len(xoft) + len(yoft) + len(dxdt) + len(dydt) > 0: if len(yofx) == 1 and len(dydx) == 0 and len(xoft) == 0 and len(yoft) == 0 and len(dxdt) == 0 and len(dydt) == 0: expression = (yofx[0].text,) derivative = (None,) elif len(yofx) == 1 and len(dydx) == 1 and len(xoft) == 0 and len(yoft) == 0 and len(dxdt) == 0 and len(dydt) == 0: expression = (yofx[0].text,) derivative = (dydx[0].text,) elif len(yofx) == 0 and len(dydx) == 0 and len(xoft) == 1 and len(yoft) == 1 and len(dxdt) == 0 and len(dydt) == 0: expression = xoft[0].text, yoft[0].text derivative = None, None elif len(yofx) == 0 and len(dydx) == 0 and len(xoft) == 1 and len(yoft) == 1 and len(dxdt) == 1 and len(dydt) == 1: expression = xoft[0].text, yoft[0].text derivative = dxdt[0].text, dydt[0].text else: raise defs.PmmlValidationError("The only allowed combinations of PlotFormulae are: \"y(x)\", \"y(x) dy/dx\", \"x(t) y(t)\", and \"x(t) y(t) dx/dt dy/dt\"") low = self.get("low", convertType=True) high = self.get("high", convertType=True) if low is None or high is None: raise defs.PmmlValidationError("The \"low\" and \"high\" attributes are required for PlotCurves defined by formulae") samples = self.generateSamples(low, high) loop = self.get("loop", defaultFromXsd=True, convertType=True) state.x, state.y, state.dx, state.dy, xfieldType, yfieldType = self.expressionsToPoints(expression, derivative, samples, loop, functionTable, performanceTable) else: performanceTable.pause("PlotCurve prepare") if len(ndx) == 1: dxdataColumn = ndx[0].evaluate(dataTable, functionTable, performanceTable) else: dxdataColumn = None if len(ndy) == 1: dydataColumn = ndy[0].evaluate(dataTable, functionTable, performanceTable) else: dydataColumn = None performanceTable.unpause("PlotCurve prepare") if len(nx) == 0 and len(ny) == 1: performanceTable.pause("PlotCurve prepare") ydataColumn = ny[0].evaluate(dataTable, functionTable, performanceTable) performanceTable.unpause("PlotCurve prepare") if len(cutExpression) == 1: performanceTable.pause("PlotCurve prepare") selection = cutExpression[0].select(dataTable, functionTable, performanceTable) performanceTable.unpause("PlotCurve prepare") else: selection = NP("ones", len(ydataColumn.data), NP.dtype(bool)) if ydataColumn.mask is not None: selection = NP("logical_and", selection, NP(ydataColumn.mask == defs.VALID), selection) if dxdataColumn is not None and dxdataColumn.mask is not None: selection = NP("logical_and", selection, NP(dxdataColumn.mask == defs.VALID), selection) if dydataColumn is not None and dydataColumn.mask is not None: selection = NP("logical_and", selection, NP(dydataColumn.mask == defs.VALID), selection) yarray = ydataColumn.data[selection] xarray = NP("ones", len(yarray), dtype=NP.dtype(float)) xarray[0] = 0.0 xarray = NP("cumsum", xarray) dxarray, dyarray = None, None if dxdataColumn is not None: dxarray = dxdataColumn.data[selection] if dydataColumn is not None: dyarray = dydataColumn.data[selection] xfieldType = self.xfieldType yfieldType = ydataColumn.fieldType elif len(nx) == 1 and len(ny) == 1: performanceTable.pause("PlotCurve prepare") xdataColumn = nx[0].evaluate(dataTable, functionTable, performanceTable) ydataColumn = ny[0].evaluate(dataTable, functionTable, performanceTable) performanceTable.unpause("PlotCurve prepare") if len(cutExpression) == 1: performanceTable.pause("PlotCurve prepare") selection = cutExpression[0].select(dataTable, functionTable, performanceTable) performanceTable.unpause("PlotCurve prepare") else: selection = NP("ones", len(ydataColumn.data), NP.dtype(bool)) if xdataColumn.mask is not None: selection = NP("logical_and", selection, NP(xdataColumn.mask == defs.VALID), selection) if ydataColumn.mask is not None: selection = NP("logical_and", selection, NP(ydataColumn.mask == defs.VALID), selection) if dxdataColumn is not None and dxdataColumn.mask is not None: selection = NP("logical_and", selection, NP(dxdataColumn.mask == defs.VALID), selection) if dydataColumn is not None and dydataColumn.mask is not None: selection = NP("logical_and", selection, NP(dydataColumn.mask == defs.VALID), selection) xarray = xdataColumn.data[selection] yarray = ydataColumn.data[selection] dxarray, dyarray = None, None if dxdataColumn is not None: dxarray = dxdataColumn.data[selection] if dydataColumn is not None: dyarray = dydataColumn.data[selection] xfieldType = xdataColumn.fieldType yfieldType = ydataColumn.fieldType else: raise defs.PmmlValidationError("The only allowed combinations of PlotNumericExpressions are: \"y(x)\" and \"x(t) y(t)\"") persistentState = {} stateId = self.get("stateId") if stateId is not None: if stateId in dataTable.state: persistentState = dataTable.state[stateId] xarray = NP("concatenate", [xarray, persistentState["x"]]) yarray = NP("concatenate", [yarray, persistentState["y"]]) if dxarray is not None: dxarray = NP("concatenate", [dxarray, persistentState["dx"]]) if dyarray is not None: dyarray = NP("concatenate", [dyarray, persistentState["dy"]]) else: dataTable.state[stateId] = persistentState persistentState["x"] = xarray persistentState["y"] = yarray if dxarray is not None: persistentState["dx"] = dxarray if dyarray is not None: persistentState["dy"] = dyarray smooth = self.get("smooth", defaultFromXsd=True, convertType=True) if not smooth: if dyarray is not None and dxarray is None: dxarray = NP((NP("roll", xarray, -1) - NP("roll", xarray, 1)) / 2.0) dyarray = dyarray * dxarray loop = self.get("loop", defaultFromXsd=True, convertType=True) if dxarray is not None and not loop: dxarray[0] = 0.0 dxarray[-1] = 0.0 if dyarray is not None and not loop: dyarray[0] = 0.0 dyarray[-1] = 0.0 state.x = xarray state.y = yarray state.dx = dxarray state.dy = dyarray else: smoothingScale = self.get("smoothingScale", defaultFromXsd=True, convertType=True) loop = self.get("loop", defaultFromXsd=True, convertType=True) samples = self.generateSamples(xarray.min(), xarray.max()) state.x, state.y, state.dx, state.dy = self.pointsToSmoothCurve(xarray, yarray, samples, smoothingScale, loop) if plotRange is not None: plotRange.expand(state.x, state.y, xfieldType, yfieldType) performanceTable.end("PlotCurve prepare")