def __call__(self, value): vmin = self.vmin vmax = self.vmax if type(value) in [IntType, FloatType]: vtype = 'scalar' val = array([value]) else: vtype = 'array' val = asarray(value) if vmin is None or vmax is None: rval = ravel(val) if vmin is None: vmin = nxmin(rval) if vmax is None: vmax = nxmax(rval) if vmin > vmax: raise ValueError("minvalue must be less than or equal to maxvalue") elif vmin==vmax: return 0.*value else: val = where(val<vmin, vmin, val) val = where(val>vmax, vmax, val) result = (1.0/(vmax-vmin))*(val-vmin) if vtype == 'scalar': result = result[0] return result
def __call__(self, value): vmin = self.vmin vmax = self.vmax if type(value) in [IntType, FloatType]: vtype = "scalar" val = array([value]) else: vtype = "array" val = asarray(value) if vmin is None or vmax is None: rval = ravel(val) if vmin is None: vmin = amin(rval) if vmax is None: vmax = amax(rval) if vmin > vmax: raise ValueError("minvalue must be less than or equal to maxvalue") elif vmin == vmax: return 0.0 * value else: val = where(val < vmin, vmin, val) val = where(val > vmax, vmax, val) result = (1.0 / (vmax - vmin)) * (val - vmin) if vtype == "scalar": result = result[0] return result
def __call__(self, X, alpha=1.0): """ X is either a scalar or an array (of any dimension). If scalar, a tuple of rgba values is returned, otherwise an array with the new shape = oldshape+(4,). Any values that are outside the 0,1 interval are clipped to that interval before generating rgb values. Alpha must be a scalar """ alpha = min(alpha, 1.0) # alpha must be between 0 and 1 alpha = max(alpha, 0.0) if type(X) in [IntType, FloatType]: vtype = 'scalar' xa = array([X]) else: vtype = 'array' xa = array(X) xa = where(xa>1.,1.,xa) xa = where(xa<0.,0.,xa) xa = (xa *(self.N-1)).astype(Int) rgba = zeros(xa.shape+(4,), Float) rgba[...,0] = take(self._red_lut, xa) rgba[...,1] = take(self._green_lut, xa) rgba[...,2] = take(self._blue_lut, xa) rgba[...,3] = alpha if vtype == 'scalar': rgba = tuple(rgba[0,:]) return rgba
def __call__(self, value): vmin = self.vmin vmax = self.vmax if type(value) in [IntType, FloatType]: vtype = 'scalar' val = array([value]) else: vtype = 'array' val = array(value) if vmin is None or vmax is None: rval = ravel(val) if vmin is None: vmin = min(rval) if vmax is None: vmax = max(rval) if vmin > vmax: raise ValueError("minvalue must be less than or equal to maxvalue") elif vmin==vmax: return 0.*value else: val = where(val<vmin, vmin, val) val = where(val>vmax, vmax, val) result = divide(val-vmin, vmax-vmin) if vtype == 'scalar': result = result[0] return result
def soft(data, value, substitute=0): mvalue = -value cond_less = numerix.less(data, value) cond_greater = numerix.greater(data, mvalue) data = numerix.where(cond_less & cond_greater, substitute, data) data = numerix.where(cond_less, data + value, data) data = numerix.where(cond_greater, data - value, data) return data
def makeMappingArray(N, data): """Create an N-element 1-d lookup table data represented by a list of x,y0,y1 mapping correspondences. Each element in this list represents how a value between 0 and 1 (inclusive) represented by x is mapped to a corresponding value between 0 and 1 (inclusive). The two values of y are to allow for discontinuous mapping functions (say as might be found in a sawtooth) where y0 represents the value of y for values of x <= to that given, and y1 is the value to be used for x > than that given). The list must start with x=0, end with x=1, and all values of x must be in increasing order. Values between the given mapping points are determined by simple linear interpolation. The function returns an array "result" where result[x*(N-1)] gives the closest value for values of x between 0 and 1. """ try: adata = array(data) except: raise TypeError("data must be convertable to an array") shape = adata.shape if len(shape) != 2 and shape[1] != 3: raise ValueError("data must be nx3 format") x = adata[:,0] y0 = adata[:,1] y1 = adata[:,2] if x[0] != 0. or x[-1] != 1.0: raise ValueError( "data mapping points must start with x=0. and end with x=1") if sometrue(sort(x)-x): raise ValueError( "data mapping points must have x in increasing order") # begin generation of lookup table x = x * (N-1) lut = zeros((N,), Float) xind = arange(float(N)) ind = searchsorted(x, xind)[1:-1] lut[1:-1] = ( divide(xind[1:-1] - take(x,ind-1), take(x,ind)-take(x,ind-1) ) *(take(y0,ind)-take(y1,ind-1)) + take(y1,ind-1)) lut[0] = y1[0] lut[-1] = y0[-1] # ensure that the lut is confined to values between 0 and 1 by clipping it lut = where(lut > 1., 1., lut) lut = where(lut < 0., 0., lut) return lut
def hard(data, value, substitute=0): mvalue = -value cond = numerix.less(data, value) cond &= numerix.greater(data, mvalue) return numerix.where(cond, substitute, data)
def contour_rectgrid(x_grid, y_grid, z, levels): '''calculate the contours of z on the rectangular grid x_grid, y_grid at levels. xarr and yarr are 1d arrays which hold the x/y coordinates of the grid. They do not need to be equispaced nor of same length. z[y, x] holds the value of some scalar function on that grid. levels is a list of float values for which the contours shall be calculated. Returns a list of equal length to levels. Each entry is a set of contour lines for the corresponding level value. A set of contour lines is a list which contains 2-element lists [xarr, yarr]. xarr and yarr hold the points of one contour line in cartesic coordinates. xarr = result[levidx][lineidx][0] yarr = result[levidx][lineidx][1]''' result = [] dx_grid = x_grid[1:] - x_grid[:-1] # x_n+1 - x_n dy_grid = y_grid[1:] - y_grid[:-1] # y_n+1 - y_n for level in levels: lines = contour_graph(z, level) translated_lines = [] for line in lines: interp_y, xind, yind, weights = map(array, line) xcoord = where(interp_y, x_grid[xind], x_grid[xind] + weights*dx_grid[xind-1]) ycoord = where(interp_y, y_grid[yind] + weights*dy_grid[yind-1], y_grid[yind]) translated_lines.append([xcoord, ycoord]) result.append(translated_lines) return result
def __call__(self, X, alpha=1.0): """ X is either a scalar or an array (of any dimension). If scalar, a tuple of rgba values is returned, otherwise an array with the new shape = oldshape+(4,). If the X-values are integers, then they are used as indices into the array. If they are floating point, then they must be in the interval (0.0, 1.0). Alpha must be a scalar. """ if not self._isinit: self._init() alpha = min(alpha, 1.0) # alpha must be between 0 and 1 alpha = max(alpha, 0.0) self._lut[:-3, -1] = alpha mask_bad = None if isinstance(X, (int, float)): vtype = 'scalar' xa = array([X]) else: vtype = 'array' xma = ma.asarray(X) xa = xma.filled(0) mask_bad = ma.getmaskorNone(xma) if typecode(xa) in typecodes['Float']: xa = where(xa == 1.0, 0.9999999, xa) # Tweak so 1.0 is in range. xa = (xa * self.N).astype(Int) mask_under = xa < 0 mask_over = xa > self.N-1 xa = where(mask_under, self._i_under, xa) xa = where(mask_over, self._i_over, xa) if mask_bad is not None: # and sometrue(mask_bad): xa = where(mask_bad, self._i_bad, xa) #print 'types', typecode(self._lut), typecode(xa), xa.shape rgba = take(self._lut, xa) if vtype == 'scalar': rgba = tuple(rgba[0,:]) #print rgba[0,1:10,:] # Now the same for numpy, numeric... return rgba
def __call__(self, X, alpha=1.0): """ X is either a scalar or an array (of any dimension). If scalar, a tuple of rgba values is returned, otherwise an array with the new shape = oldshape+(4,). If the X-values are integers, then they are used as indices into the array. If they are floating point, then they must be in the interval (0.0, 1.0). Alpha must be a scalar. """ if not self._isinit: self._init() alpha = min(alpha, 1.0) # alpha must be between 0 and 1 alpha = max(alpha, 0.0) self._lut[:-3, -1] = alpha mask_bad = None if isinstance(X, (int, float)): vtype = 'scalar' xa = array([X]) else: vtype = 'array' xma = ma.asarray(X) xa = xma.filled(0) mask_bad = ma.getmaskorNone(xma) if typecode(xa) in typecodes['Float']: xa = where(xa == 1.0, 0.9999999, xa) # Tweak so 1.0 is in range. xa = (xa * self.N).astype(Int) mask_under = xa < 0 mask_over = xa > self.N - 1 xa = where(mask_under, self._i_under, xa) xa = where(mask_over, self._i_over, xa) if mask_bad is not None: # and sometrue(mask_bad): xa = where(mask_bad, self._i_bad, xa) #print 'types', typecode(self._lut), typecode(xa), xa.shape rgba = take(self._lut, xa) if vtype == 'scalar': rgba = tuple(rgba[0, :]) #print rgba[0,1:10,:] # Now the same for numpy, numeric... return rgba
def prctile(x, p = (0.0, 25.0, 50.0, 75.0, 100.0)): """ Return the percentiles of x. p can either be a sequence of percentil values or a scalar. If p is a sequence the i-th element of the return sequence is the p(i)-th percentile of x """ x = sort(ravel(x)) Nx = len(x) if not iterable(p): return x[int(p*Nx/100.0)] p = multiply(array(p), Nx/100.0) ind = p.astype(Int) ind = where(ind>=Nx, Nx-1, ind) return take(x, ind)
def prctile(x, p=(0.0, 25.0, 50.0, 75.0, 100.0)): """ Return the percentiles of x. p can either be a sequence of percentil values or a scalar. If p is a sequence the i-th element of the return sequence is the p(i)-th percentile of x """ x = sort(ravel(x)) Nx = len(x) if not iterable(p): return x[int(p * Nx / 100.0)] p = multiply(array(p), Nx / 100.0) ind = p.astype(Int) ind = where(ind >= Nx, Nx - 1, ind) return take(x, ind)
def contour_gencoords(x_grid, y_grid, z, levels): '''calculate the contours of z on the coordinate grid x_grid, y_grid at levels. Let the 2d plane be parametrized by p and q. x_grid[p, q] and y_grid[p,q] have to contain the cartesic coordinates of the point (p, q). z[p, q] holds the value of some scalar function on that grid. levels is a list of float values for which the contours shall be calculated. Returns a list of equal length to levels. Each entry is a set of contour lines for the corresponding level value. A set of contour lines is a list which contains 2-element lists [xarr, yarr]. xarr and yarr hold the points of one contour line in cartesic coordinates. xarr = result[levidx][lineidx][0] yarr = result[levidx][lineidx][1]''' result = [] maxy, maxx = z.shape for level in levels: lines = contour_graph(z, level) coordinates = [] for line in lines: interp_y, x, y, weights = map(array, line) line_translated = [] for grid in x_grid, y_grid: line_translated.append(where(interp_y, # The % operation does not affect the result! # It will avoid index errors but the corresponding # entries will be eaten by where. grid[y,x]*(1-weights) + grid[(y+1)%maxy,x]*weights, grid[y,x]*(1-weights) + grid[y,(x+1)%maxx]*weights )) coordinates.append(line_translated) result.append(coordinates) return result # result[lev][line][0 or 1] -> x / y (interpol. x_grid / y_grid)
def less(data, value, substitute=0): return numerix.where(numerix.greater(data, value), substitute, data)
def pyxplotcontour(canvas, data, x=None, y=None, levels=8, colors='map', colmap=None, color=None, labels=False, labelsize=0.4, **kwargs): """do contour plot. data[y, x]: 2d real array containing z values x, y: None - equidistant point spacing 1d array: rectangular non-equidistant grid 2d array: arbitrary coordinates levels: int (level count) or list of z values default: 8 equispaced levels colors: "map": use colmap (ColorMapper) "map_invert": use colmap but invert color (max visibility on top of bitmap plot) "color_array": color and levels must be arrays of the same length, level i get's the color i "color": use color (as in pyxgraph.pyxplot) labels: draw text labels designating the level value of each contour labelsize: label text size (relative to normal text size) kwargs: are passed to pyxplot """ zmin, zmax = data.min(), data.max() # define a function zcolor(z) that returns the color to use if 'map' in colors: if colmap==None: colmap=ColMapper.ColorMapper("gauss") if 'inv' in colors: def zcolor(z): r, g, b = colmap.colfct((z-zmin)/(zmax-zmin)) return 1-r, 1-g, 1-b else: def zcolor(z): return colmap.colfct((z-zmin)/(zmax-zmin)) elif colors == "color_array": levels = array(levels) assert hasattr(color, "__iter__") # for this color and levels assert hasattr(levels, "__iter__") # have to be lists of the assert len(color)==len(levels) # same length def zcolor(z): return color[argmin(abs(levels - z))] # return most suitable color else: #use color def zcolor(z): return color # calculate level list if isinstance(levels, int): levels = (arange(levels)+0.5)*(zmax-zmin)/levels + zmin # calculate the levels if x is None or y is None: clines = contour_nogrid(data, levels) elif len(x.shape) == 1 and len(y.shape) == 1: clines = contour_rectgrid(x, y, data, levels) elif x.shape == y.shape == data.shape: clines = contour_gencoord(x, y, data, levels) else: raise ValueError, 'pyxplotcontour: x and/or y misshaped' # clines is clines[levidx][lineidx][0 or 1 for x/y] # set default args if 'linetype' not in kwargs and 'lt' not in kwargs: kwargs['linetype']=pyx.style.linestyle.solid if 'style' not in kwargs: kwargs['style'] = 'lines' # plot for levidx, level in enumerate(levels): llabel = '%0.4g'%level levelcolor = zcolor(level) for line in clines[levidx]: canvas.pyxplot((line[0], line[1]), color=levelcolor, title=False, **kwargs) if labels: # find label position x, y = line xy = map(canvas.pos, x, y) canvas_x = array([i[0] for i in xy]) canvas_y = array([i[1] for i in xy]) dcx = canvas_x[1:] - canvas_x[:-1] dcy = canvas_y[1:] - canvas_y[:-1] dcx = where(dcx==0, min(dcx)/10, dcx) # avoid singularity problems ind = argmin(abs(dcy/dcx)) # try to avoid placing labels on plot borders # FIXME: find better method for that? relx = canvas_x[ind] - canvas.xpos if not (labelsize < relx < canvas.width-labelsize): # label at left or right border #if ind < 3 or ind > len(x)-4: # probably a line ending at border ind = len(x) / 2 # label position (graphcoords) lx, ly = x[ind], y[ind] angle = arctan(dcy[ind]/dcx[ind])*180/pi if abs(angle) < 5: angle = 0 # FIXME: do something against labels exceeding plot area canvas.pyxlabel((lx, ly), llabel, graphcoords=True, style=[pyx.trafo.translate(0, 0.05), pyx.trafo.rotate(angle), pyx.trafo.scale(labelsize), pyx.text.halign.center, pyx.text.valign.bottom])
def contour_graph(z, level): '''calculate contour of z at level. z[y,x] has to be a 2d array. Returns a list of 4-element lists. Each entry represents one contour. Each entry is [interp_y, xind, yind, weight] where all four are 1d lists of equal size. Imagine a rectangular grid where the crossings are the points of z. All points of the result are located on this grid. For point n: if interp_y[n] is True, the point is on a vertical line, else on a horizontal line. If it is on a vertical line, xind[n] gives the corresponding index into z. The y position is between yind[n] and yind[n]+1. weight[n] is a number between 0.0 and 1.0 giving the position: 0 means the point is on yind[n] exactly, 1.0 means the point is on yind[n]+1 exactly. If the point is on a horizontal line, weight[n] denotes the position between xind[n] and xind[n]+1. result[lineidx][0..3][pointidx] ''' bw = (z>level) # "black and white" image of z horiz = (bw[:,:-1] != bw[:,1:]) # True where a contour crosses rows vert = (bw[:-1] != bw[1:]) # True where a contour crosses columns # for later readability up, down, left, right = 1, 4, 2, 8 # adjacency & up means crosspoint at y (top), & left: at x (left), # & down: at y+1, & right: at x+1 # adjacency[y, x] is the cell between x...x+1 and y...y+1 adjacency = (horiz[:-1,:]*up | vert[:,:-1]*left | horiz [1:,:]*down | vert[:,1:]*right) # calculate interpolation weights and store in horiz, vert # weight = (level - z[n])/(z[n+1]-z[n]) # -1 => dummy value (no crossing) horiz = where(horiz, (level-z[:,:-1]) / (z[:,1:]-z[:,:-1]), -1) vert = where(vert, (level-z[:-1]) / (z[1:]-z[:-1]), -1) result = [] def _appendpoint(line, x, y, direction): '''append the point adjacency[y,x], direction translated into grid coordinates and remove from adjacency. returns tuple (bool, x, y, weight) bool: False means on horizontal grid, True on vertical grid weight is the position between x and x+1 resp y and y+1''' adjacency[y, x] ^= direction # XOR out direction if direction & (up|down): if direction & down: y += 1 assert horiz[y,x] > -1 # for debugging line[0].append(False) line[1].append(x) line[2].append(y) line[3].append(horiz[y, x]) else: # direction is left or right if direction & right: x += 1 assert vert[y, x] > -1 # for debugging line[0].append(True) line[1].append(x) line[2].append(y) line[3].append(vert[y,x]) # end def opposite = {up:down, down:up, left:right, right:left} while any(adjacency != 0): # first look if there are any contours ending at the borders of z border = True # top border if any(adjacency[0] & up): x0 = nonzero(adjacency[0] & up)[0][0] # just take first one y0 = 0 orig = up # where we come from # bottom elif any(adjacency[-1] & down): x0 = nonzero(adjacency[-1] & down)[0][0] y0 = adjacency.shape[0] - 1 orig = down # left elif any(adjacency[:,0] & left): x0 = 0 y0 = nonzero(adjacency[:,0] & left)[0][0] orig = left # right border elif any(adjacency[:,-1] & right): x0 = adjacency.shape[1] - 1 y0 = nonzero(adjacency[:,-1] & right)[0][0] orig = right else: # pick an arbitrary point (all contours are closed loops now) m = adjacency.argmax() # FIXME: This is not very pretty! forward compatible?! x0, y0 = m%adjacency.shape[1], m/adjacency.shape[1] border = False # did not start from border adj = adjacency[y0, x0] if adj & up: orig = up elif adj & down: orig = down elif adj & left: orig = left else: # There can be only 2 or 4 ends, so adj&right only is an error raise 'Dead end at r%d,c%d -- should be impossible!'%(y0,x0) # okay now we have starting cell x0,y0 and direction orig. endofcontour = False # set when border reached or closed line = [[], [], [], []] while not endofcontour: # append last point _appendpoint(line, x0, y0, orig) # find next point adj = adjacency[y0, x0] if (adj | orig) == 15: # all directions possible, hm... # lets connect cross-wise # this might create weird results with multiple contours cont = opposite[orig] else: # only one continuation - no problem :-D cont = adj newx0 = x0 + {up:0, down:0, left:-1, right:1}[cont] newy0 = y0 + {up:-1, down:1, left:0, right:0}[cont] orig = opposite[cont] # out of bounds if border and ((not 0<=newx0<adjacency.shape[1]) or (not 0<=newy0<adjacency.shape[0])): _appendpoint(line, x0, y0, cont) endofcontour = True else: adjacency[y0, x0] ^= cont x0, y0 = newx0, newy0 # loop closed if adjacency[y0, x0] & orig == 0: _appendpoint(line, x0, y0, orig) adjacency[y0, x0] ^= orig endofcontour = True result.append(line) return result