18 VoronoiWeighting class should be imported directly from xboa.bunch.weighting
42 VoronoiWeighting class enables the user to set statistical weights of Hits
43 in a Bunch based on how close they are to neighbouring Hits. The basic
44 principle is that if a Hit is in a sparsely populated region of phase space,
45 but the desired probability distribution would have the Hit in a more
46 densely populated region, we can apply a greater statistical weight to that
47 Hit (i.e. we "count" the Hit more than once in e.g. moment calculations
50 In order to calculate how sparsely populated a particular region is, xboa
51 uses a "Voronoi" tesselation. xboa determines the nearest Hits surrounding a
52 given Hit, draws lines to those neighbours and then bisects the lines. This
53 generates a closed region around the Hit that is closer to that Hit than any
56 xboa calculates the "content" i.e. area/volume/hypervolume of the Voronoi
57 tile for each Hit. Hits which sit in a tile with a larger content are
58 assumed to be in a more sparsely populated region of phase space, while
59 those that sit in a region with a smaller content are assumed to be in a
60 less sparsely populated region of phase space.
62 xboa assigns weights to each Hit according to a multivariate gaussian. In
63 order to prevent Hits on the edge of the distribution from acquiring a
64 disproportionate weighting, it is possible to define a bound around the
65 distribution. Hits outside the bound are disregarded. A few points are
66 defined on the bound, which prevent large Voronoi cells from being
70 def __init__(self, weight_variables, weight_ellipse,
71 weight_mean =
None, voronoi_bound =
None):
73 Initialise the VoronoiWeighting
74 - weight_variables: list of string variable names for call to
75 Bunch.get_hit_variable(...). Should have a length corresponding to the
76 VoronoiWeighting dimension.
77 - weight_ellipse: numpy.array corresponding to the covariance matrix of
78 the desired multivariate gaussian. Should have numpy.shape like
79 (dimension, dimension) where dimension is the dimension of the
81 - weight_mean: numpy.array corresponding to the mean of the desired
82 multivariate gaussian. If set to None, defaults to array of 0s. Should
83 have numpy.shape like (dimension) where dimension is the dimension of
85 - voronoi_bound: BoundingEllipse, corresponding to the maximum bound at
86 which Hits are accepted in the VoronoiWeighting. Hits outside this
87 bound will have weight set to 0. The ellipse bounding_points are used
88 as an external boundary to prevent Voronoi tesselations from getting a
89 big tile content (i.e. area/volume/hypervolume).
91 Requires numpy and scipy to be installed.
93 Returns a VoronoiWeighting object.
97 self.
dim = len(weight_variables)
98 if weight_mean ==
None:
99 weight_mean = numpy.array([0.]*self.
dim)
100 if (self.
dim,) != numpy.shape(weight_mean):
101 raise ValueError(
"weight_mean shape "+\
102 str(numpy.shape(weight_mean))+
" should be "+\
105 if numpy.shape(weight_ellipse) != (self.
dim, self.
dim):
106 raise ValueError(
"weight_ellipse shape "+\
107 str(numpy.shape(weight_ellipse))+
" should be "+\
111 if voronoi_bound !=
None:
112 if voronoi_bound.dim != self.
dim:
113 raise ValueError(
"voronoi_bound dimension should be same as "+\
114 "VoronoiWeighting dimension")
128 Apply a weighting \f$w_i\f$ to each Hit in bunch corresponding to\n
129 \f$ w_i = c_i f(x_i) \f$\n
130 where \f$c_i\f$ is the content of each Voronoi tile and \f$f(x_i)\f$ is
131 the value of a multivariate gaussian evaluated at \f$x_i\f$.
132 - bunch: xboa.bunch.Bunch object. Evaluate weights for all Hits in bunch
133 - global_cut: if set to True, weighting will be applied to the hit's
134 global_weight. If set to False, weighting will be applied to the hit's
136 Returns None (the bunch is weighted "in-place")
145 self.voronoi_bound.bounding_points))
152 tile_index = self.tesselation.point_region[i]
153 region = self.tesselation.regions[tile_index]
154 if -1
in region
or len(region) == 0:
159 weight =
'global_weight'
161 weight =
'local_weight'
163 for i, hit_index
in enumerate(not_cut):
164 hit = bunch[hit_index]
168 for i, hit
in enumerate(bunch):
174 Calculate a multivariate gaussian at point.
175 - bunch: xboa.bunch.Bunch object. Evaluate weights for all Hits in bunch
176 - global_cut: if set to True, weighting will be applied to the hit's
177 global_weight. If set to False, weighting will be applied to the hit's
182 delta_t = numpy.transpose(delta)
183 arg = -0.5*delta.dot(self.weight_ellipse_inv.dot(delta_t))
184 gauss = math.exp(arg)/norm**0.5
188 lower =
None, upper =
None):
190 Plot a 2D projection of the voronoi cells.
191 - projection_axes: list of length two. Determines the projection that
192 will be drawn. Should be a subset of the weight_variables.
193 - fill_option: if set to None or 'none', tiles will not be filled. If
194 set to 'weight', 'content', 'pdf', tiles will be filled according to
195 the calculated point weight, calculated tile content or probability
196 density function for that point.
197 - lower: integer lower bound for slicing of the tesselation regions. If
198 set to None, defaults to 0.
199 - upper: integer upper bound for slicing of the tesselation regions. If
200 set to None, defaults to the end of the tesselation regions list.
202 Requires ROOT to be installed.
204 Returns a tuple of (canvas, histogram, point_graph, tile_graph_list)
205 where canvas is the ROOT TCanvas, histogram is the ROOT TH2D that makes
206 the graph axes, point_graph is the graph with the points plotted,
207 tile_graph_list is a list of graphs, one for each tile.
214 if len(projection_axes) != 2:
215 raise ValueError(
"Expected projection axes of length 2")
218 raise ValueError(
"Projection axes "+str(projection_axes)+\
219 " should be a subset of weight_variables "+
220 str(weight_variables))
222 raise ValueError(
"Need to apply_weights before tesselation can be"+
224 title =
"tesselation_"+str(projection_axes[0])+
"_"+\
225 str(projection_axes[1])
226 canvas = common.make_root_canvas(title)
227 proj = [self.weight_variables.index(x)
for x
in projection_axes]
228 x_list = [vertex[proj[0]]
for vertex
in self.tesselation.vertices]
229 y_list = [vertex[proj[1]]
for vertex
in self.tesselation.vertices]
231 hist, points_graph = common.make_root_graph(title,
232 points[0][lower:upper],
234 points[1][lower:upper],
236 if fill_option !=
None and fill_option !=
'none':
237 hist.SetTitle(
'Color by '+fill_option)
239 points_graph.SetMarkerStyle(6)
242 for point_index, tile_index
in \
243 enumerate(self.tesselation.point_region[lower:upper]):
244 tile = self.tesselation.regions[tile_index]
245 if -1
in tile
or len(tile) == 0:
247 graph = ROOT.TGraph(len(tile)+1)
248 tile_graphs.append(graph)
249 for i, vertex_index
in enumerate(tile):
251 x_list[vertex_index],
252 y_list[vertex_index])
253 graph.SetPoint(len(tile),
256 graph.SetFillColor(fill_colors[point_index])
259 points_graph.Draw(
'p')
261 common._common._hist_persistent += tile_graphs
262 return canvas, hist, points_graph, tile_graphs
265 """Get fill colors for plot_two_d_projection"""
266 if fill_option ==
"weight":
268 elif fill_option ==
"content":
270 elif fill_option ==
"pdf":
273 elif fill_option ==
"none" or fill_option ==
None:
276 raise ValueError(
"Did not recognise fill option "+str(fill_option)+\
277 "should be one of ['weight', 'content', 'pdf', 'none']")
278 n_colors = ROOT.TColor.GetNumberOfColors()
279 bounds = [min(fill_data)+i*(max(fill_data)-min(fill_data))/(n_colors-1)\
280 for i
in range(n_colors)]
281 fill_colors = [bisect.bisect_left(bounds, fill)
for fill
in fill_data]
282 fill_colors = [ROOT.TColor.GetColorPalette(i)
for i
in fill_colors]
286 elements = numpy.zeros([dimension+2, dimension+2])
287 for i
in range(1, dimension+2):
290 for i, vertex_i
in enumerate(simplex):
291 for j, vertex_j
in enumerate(simplex[i+1:]):
293 point_i = triangulation.points[vertex_i]
294 point_j = triangulation.points[vertex_j]
295 square = [(point_i[d] - point_j[d])**2 \
296 for d
in xrange(dimension)]
297 length_sq = sum(square)
298 elements[i+1, j+1] = length_sq
299 elements[j+1, i+1] = length_sq
304 Calculate the tile for the index at point_index
306 Use Cayler-Menger determinant to calculate content of an arbitrary
309 tile_index = self.tesselation.point_region[point_index]
310 region = self.tesselation.regions[tile_index]
311 points = numpy.array([self.tesselation.vertices[i]
for i
in region])
312 dimension = numpy.shape(points)[1]
314 triangulation = scipy.spatial.Delaunay(points)
315 coefficient = (-1)**(dimension+1)/2.**dimension
316 coefficient /= numpy.math.factorial(dimension)**2.
317 for simplex
in triangulation.simplices:
319 det = numpy.linalg.det(elements)
322 this_content = (coefficient*det)**0.5
323 if this_content == this_content:
324 content += this_content
def apply_weights
Apply a weighting to each Hit in bunch corresponding to where is the content of each Voronoi til...
def _tile_content
Calculate the tile for the index at point_index.
VoronoiWeighting class enables the user to set statistical weights of Hits in a Bunch based on how cl...
common module defines common utility data and functions that are used elsewhere
def __init__
Initialise the VoronoiWeighting.
def _get_fill_colors
Get fill colors for plot_two_d_projection.
def get_pdf
Calculate a multivariate gaussian at point.
def plot_two_d_projection
Plot a 2D projection of the voronoi cells.