def polarPlot( rphi, title="", r1=0, r2=1, lpos=1, lsize=0.03, c="blue", bc="k", alpha=1, lw=3, deg=False, vmax=None, fill=True, spline=True, smooth=0, showPoints=True, showDisc=True, showLines=True, showAngles=True, ): """ Polar/radar plot by splining a set of points in polar coordinates. Input is a list of polar angles and radii. :param str title: histogram title :param int bins: number of bins in phi :param float r1: inner radius :param float r2: outer radius :param float lsize: label size :param c: color of the line :param bc: color of the frame and labels :param alpha: alpha of the frame :param int lw: line width in pixels :param bool deg: input array is in degrees :param bool fill: fill convex area with solid color :param bool spline: interpolate the set of input points :param bool showPoints: show data points :param bool showDisc: show the outer ring axis :param bool showLines: show lines to the origin :param bool showAngles: show angular values |polarPlot| |polarPlot.py|_ """ if len(rphi) == 2: #rphi = list(zip(rphi[0], rphi[1])) rphi = np.stack((rphi[0], rphi[1]), axis=1) rphi = np.array(rphi) thetas = rphi[:, 0] radii = rphi[:, 1] k = 180 / np.pi if deg: thetas = np.array(thetas) / k vals = [] for v in thetas: # normalize range t = np.arctan2(np.sin(v), np.cos(v)) if t < 0: t += 2 * np.pi vals.append(t) thetas = np.array(vals) if vmax is None: vmax = np.max(radii) angles = [] labs = [] points = [] for i in range(len(thetas)): t = thetas[i] r = (radii[i]) / vmax * r2 + r1 ct, st = np.cos(t), np.sin(t) points.append([r * ct, r * st, 0]) p0 = points[0] points.append(p0) r2e = r1 + r2 if spline: lines = shapes.KSpline(points, closed=True) else: lines = shapes.Line(points) lines.c(c).lw(lw).alpha(alpha) points.pop() ptsact = None if showPoints: ptsact = shapes.Points(points).c(c).alpha(alpha) filling = None if fill: faces = [] coords = [[0, 0, 0]] + lines.points().tolist() for i in range(1, lines.N()): faces.append([0, i, i + 1]) filling = Mesh([coords, faces]).c(c).alpha(alpha) back = None if showDisc: back = shapes.Disc(r1=r2e, r2=r2e * 1.01, c=bc, res=1, resphi=360) back.z(-0.01).lighting(diffuse=0, ambient=1).alpha(alpha) ti = None if title: ti = shapes.Text(title, (0, 0, 0), s=lsize * 2, depth=0, justify="top-center") ti.pos(0, -r2e * 1.15, 0.01) rays = [] if showDisc: rgap = 0.05 for t in np.linspace(0, 2 * np.pi, num=8, endpoint=False): ct, st = np.cos(t), np.sin(t) if showLines: l = shapes.Line((0, 0, -0.01), (r2e * ct * 1.03, r2e * st * 1.03, -0.01)) rays.append(l) elif showAngles: # just the ticks l = shapes.Line( (r2e * ct * 0.98, r2e * st * 0.98, -0.01), (r2e * ct * 1.03, r2e * st * 1.03, -0.01), ) if showAngles: if 0 <= t < np.pi / 2: ju = "bottom-left" elif t == np.pi / 2: ju = "bottom-center" elif np.pi / 2 < t <= np.pi: ju = "bottom-right" elif np.pi < t < np.pi * 3 / 2: ju = "top-right" elif t == np.pi * 3 / 2: ju = "top-center" else: ju = "top-left" a = shapes.Text(int(t * k), pos=(0, 0, 0), s=lsize, depth=0, justify=ju) a.pos(r2e * ct * (1 + rgap), r2e * st * (1 + rgap), -0.01) angles.append(a) mrg = merge(back, angles, rays, labs, ti) if mrg: mrg.color(bc).alpha(alpha).lighting(diffuse=0, ambient=1) rh = Assembly([lines, ptsact, filling] + [mrg]) rh.base = np.array([0, 0, 0]) rh.top = np.array([0, 0, 1]) return rh
def polarHistogram( values, title="", bins=10, r1=0.25, r2=1, phigap=3, rgap=0.05, lpos=1, lsize=0.05, c=None, bc="k", alpha=1, cmap=None, deg=False, vmin=None, vmax=None, labels=(), showDisc=True, showLines=True, showAngles=True, showErrors=False, ): """ Polar histogram with errorbars. :param str title: histogram title :param int bins: number of bins in phi :param float r1: inner radius :param float r2: outer radius :param float phigap: gap angle btw 2 radial bars, in degrees :param float rgap: gap factor along radius of numeric angle labels :param float lpos: label gap factor along radius :param float lsize: label size :param c: color of the histogram bars, can be a list of length `bins`. :param bc: color of the frame and labels :param alpha: alpha of the frame :param str cmap: color map name :param bool deg: input array is in degrees :param float vmin: minimum value of the radial axis :param float vmax: maximum value of the radial axis :param list labels: list of labels, must be of length `bins` :param bool showDisc: show the outer ring axis :param bool showLines: show lines to the origin :param bool showAngles: show angular values :param bool showErrors: show error bars |polarHisto| |polarHisto.py|_ """ k = 180 / np.pi if deg: values = np.array(values) / k dp = np.pi / bins vals = [] for v in values: # normalize range t = np.arctan2(np.sin(v), np.cos(v)) if t < 0: t += 2 * np.pi vals.append(t - dp) histodata, edges = np.histogram(vals, bins=bins, range=(-dp, 2 * np.pi - dp)) thetas = [] for i in range(bins): thetas.append((edges[i] + edges[i + 1]) / 2) if vmin is None: vmin = np.min(histodata) if vmax is None: vmax = np.max(histodata) errors = np.sqrt(histodata) r2e = r1 + r2 if showErrors: r2e += np.max(errors) / vmax * 1.5 back = None if showDisc: back = shapes.Disc(r1=r2e, r2=r2e * 1.01, c=bc, res=1, resphi=360) back.z(-0.01).lighting(diffuse=0, ambient=1).alpha(alpha) slices = [] lines = [] angles = [] labs = [] errbars = [] for i, t in enumerate(thetas): r = histodata[i] / vmax * r2 d = shapes.Disc((0, 0, 0), r1, r1 + r, res=1, resphi=360) delta = dp - np.pi / 2 - phigap / k d.cutWithPlane(normal=(np.cos(t + delta), np.sin(t + delta), 0)) d.cutWithPlane(normal=(np.cos(t - delta), np.sin(t - delta), 0)) if cmap is not None: cslice = colors.colorMap(histodata[i], cmap, vmin, vmax) d.color(cslice) else: if c is None: d.color(i) elif utils.isSequence(c) and len(c) == bins: d.color(c[i]) else: d.color(c) slices.append(d) ct, st = np.cos(t), np.sin(t) if showErrors: showLines = False err = np.sqrt(histodata[i]) / vmax * r2 errl = shapes.Line( ((r1 + r - err) * ct, (r1 + r - err) * st, 0.01), ((r1 + r + err) * ct, (r1 + r + err) * st, 0.01), ) errl.alpha(alpha).lw(3).color(bc) errbars.append(errl) if showDisc: if showLines: l = shapes.Line((0, 0, -0.01), (r2e * ct * 1.03, r2e * st * 1.03, -0.01)) lines.append(l) elif showAngles: # just the ticks l = shapes.Line( (r2e * ct * 0.98, r2e * st * 0.98, -0.01), (r2e * ct * 1.03, r2e * st * 1.03, -0.01), ) lines.append(l) if showAngles: if 0 <= t < np.pi / 2: ju = "bottom-left" elif t == np.pi / 2: ju = "bottom-center" elif np.pi / 2 < t <= np.pi: ju = "bottom-right" elif np.pi < t < np.pi * 3 / 2: ju = "top-right" elif t == np.pi * 3 / 2: ju = "top-center" else: ju = "top-left" a = shapes.Text(int(t * k), pos=(0, 0, 0), s=lsize, depth=0, justify=ju) a.pos(r2e * ct * (1 + rgap), r2e * st * (1 + rgap), -0.01) angles.append(a) if len(labels) == bins: lab = shapes.Text(labels[i], (0, 0, 0), s=lsize, depth=0, justify="center") lab.pos(r2e * ct * (1 + rgap) * lpos / 2, r2e * st * (1 + rgap) * lpos / 2, 0.01) labs.append(lab) ti = None if title: ti = shapes.Text(title, (0, 0, 0), s=lsize * 2, depth=0, justify="top-center") ti.pos(0, -r2e * 1.15, 0.01) mrg = merge(back, lines, angles, labs, ti) if mrg: mrg.color(bc).alpha(alpha).lighting(diffuse=0, ambient=1) rh = Assembly(slices + errbars + [mrg]) rh.base = np.array([0, 0, 0]) rh.top = np.array([0, 0, 1]) return rh