Loading [MathJax]/extensions/TeX/AMSsymbols.js
LALInference 4.1.9.1-5e288d3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
cbcBayesPPAnalysis.py
Go to the documentation of this file.
1##python
2# -*- coding: utf-8 -*-
3#
4# cbcBayesPPAnalysis.py
5#
6# Copyright 2013
7# Benjamin Farr <bfarr@u.northwestern.edu>,
8# Will M. Farr <will.farr@ligo.org>,
9# SalvatoreVitale <salvatore.vitale@ligo.org>
10# Vivien Raymond <vivien.raymond@ligo.org>
11#
12# This program is free software; you can redistribute it and/or modify
13# it under the terms of the GNU General Public License as published by
14# the Free Software Foundation; either version 2 of the License, or
15# (at your option) any later version.
16#
17# This program is distributed in the hope that it will be useful,
18# but WITHOUT ANY WARRANTY; without even the implied warranty of
19# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20# GNU General Public License for more details.
21#
22# You should have received a copy of the GNU General Public License
23# along with this program; if not, write to the Free Software
24# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25# MA 02110-1301, USA.
26
27from igwn_ligolw import lsctables
28from igwn_ligolw import utils
29import matplotlib
30matplotlib.use("Agg")
31import matplotlib.pyplot as pp
32import numpy as np
33import optparse
34import os
35import os.path
36import scipy.stats as ss
37import string
38from lalinference import bayespputils as bppu
39
40posterior_name_to_latex_name = {
41 'm1' : r'$m_1$',
42 'm2' : r'$m_2$',
43 'eta' : r'$\eta$',
44 'q' : r'$q$',
45 'mc' : r'$\mathcal{M}$',
46 'distance' : r'$d$',
47 'time' : r'$t$',
48 'ra' : r'$\alpha$',
49 'dec' : r'$\delta$',
50 'phi_orb' : r'$\phi_\mathrm{orb}$',
51 'psi' : r'$\psi$',
52 'iota' : r'$\iota$',
53 'a1' : r'$a_1$',
54 'a2' : r'$a_2$',
55 'theta1' : r'$\theta_1$',
56 'theta2' : r'$\theta_2$',
57 'phi1' : r'$\phi_1$',
58 'phi2' : r'$\phi_2$',
59 'phi12':r'$\phi_{12}$',
60 'phi_jl':r'$\phi_{jl}$',
61 'theta_jn':r'$\theta_{jn}$',
62 'tilt1':r'$\tau_1$',
63 'tilt2':r'$\tau_2$',
64 'dchiMinus2':r'$d\chi_{-2}$',
65 'dchiMinus1':r'$d\chi_{-1}$',
66 'dchi0':r'$d\chi_0$',
67 'dchi1':r'$d\chi_1$',
68 'dchi2':r'$d\chi_2$',
69 'dchi3':r'$d\chi_3$',
70 'dchi3S':r'$d\chi_{3S}$',
71 'dchi3NS':r'$d\chi_{3NS}$',
72 'dchi4':r'$d\chi_4$',
73 'dchi4S':r'$d\chi_{4S}$',
74 'dchi4NS':r'$d\chi_{4NS}$',
75 'dchi5':r'$d\chi_5$',
76 'dchi5S':r'$d\chi_{5S}$',
77 'dchi5NS':r'$d\chi_{5NS}$',
78 'dchi5l':r'$d\chi_{5l}$',
79 'dchi5lS':r'$d\chi_{5lS}$',
80 'dchi5lNS':r'$d\chi_{5lNS}$',
81 'dchi6':r'$d\chi_6$',
82 'dchi6S':r'$d\chi_{6S}$',
83 'dchi6NS':r'$d\chi_{6NS}$',
84 'dchi6l':r'$d\chi_{6l}$',
85 'dchi7':r'$d\chi_7$',
86 'dchi7S':r'$d\chi_{7S}$',
87 'dchi7NS':r'$d\chi_{7NS}$',
88 'dsigma1':r'$d\sigma_1$',
89 'dsigma2':r'$d\sigma_2$',
90 'dsigma3':r'$d\sigma_3$',
91 'dsigma4':r'$d\sigma_4$',
92 'dalpha1':r'$d\alpha_1$',
93 'dalpha2':r'$d\alpha_2$',
94 'dalpha3':r'$d\alpha_3$',
95 'dalpha4':r'$d\alpha_4$',
96 'dalpha5':r'$d\alpha_5$',
97 'dbeta1':r'$d\beta_1$',
98 'dbeta2':r'$d\beta_2$',
99 'dbeta3':r'$d\beta_3$',
100 'dbeta4':r'$d\beta_4$',
101 'dchikappaS':r'$d\chi_{\kappa_{S}}$',
102 'dchikappaA':r'$d\chi_{\kappa_{A}}$'
103}
104
105def fractional_rank(x, xs):
106 """Returns the fraction of samples, ``xs``, that fall below the value
107 ``x``.
108
109 """
110 nbelow = np.sum(xs < x)
111
112 return float(nbelow)/float(xs.shape[0])
113
114def pp_plot(ps, title=None, outfile=None):
115 """Generates a p-p plot for the given ps.
116
117 :param ps: The p-values whose cumulative distribution is to be
118 plotted.
119
120 :param title: An (optional) title for the plot.
121
122 :param outfile: An (optional) basename for the plot file; both
123 basename.png and basename.pdf will be created.
124
125 """
126
127 ps = np.atleast_1d(ps)
128 ps = np.sort(ps)
129 ys = np.zeros(ps.shape[0]+2)
130 ys[:-1] = np.linspace(0, 1, ps.shape[0]+1)
131 ys[-1] = 1.0
132 xs = np.zeros(ps.shape[0]+2)
133 xs[-1] = 1.0
134 xs[1:-1] = ps
135
136 pp.figure(figsize=(6,6), dpi=100)
137
138 for i in range(10):
139 syn_ps = np.random.uniform(size=ps.shape[0])
140 syn_xs = np.zeros(ps.shape[0]+2)
141 syn_xs[-1] = 1.0
142 syn_xs[1:-1] = np.sort(syn_ps)
143 pp.plot(syn_xs, ys, '-', color='0.9')
144
145 pp.plot(xs, ys, '-k')
146 pp.plot(ys, ys, '--k')
147
148 pp.xlabel(r'$p$')
149 pp.ylabel(r'$P(p)$')
150 if title is not None:
151 pp.title(title)
152
153 if outfile is not None:
154 pp.savefig(outfile + '.png')
155 pp.savefig(outfile + '.pdf')
156
157def pp_kstest_pvalue(ps):
158 """Returns the K-S p-value for the test of the given ``ps`` against a
159 uniform distribution on [0,1].
160
161 """
162 stat, p = ss.kstest(ps, lambda x: x)
163
164 return p
165
166def read_posterior_samples(f,injrow):
167 """Returns a bppu posterior sample object
168 """
169 peparser=bppu.PEOutputParser('common')
170 commonResultsObj=peparser.parse(open(f,'r'))
171 data = bppu.Posterior(commonResultsObj,SimInspiralTableEntry=injrow,injFref=100.0)
172 # add tilts, comp masses, tidal...
173 try:
174 data.extend_posterior()
175 except Exception as e:
176 pass
177 return data
178
179def output_html(outdir, ks_pvalues, injnum,skypp=False):
180 """Outputs the HTML page summarizing the results.
181
182 """
183 table_row_template = string.Template("""<tr> <td> ${name} </td>
184 <td> ${pvalue} </td>
185 <td> <img src="${name}.png" alt="${name} p-p plot" width="300" height="300" /> </td> <td> <a href="${name}.png">PNG</a> <a href="${name}.pdf">PDF</a> <a href="${name}-ps.dat">p-values</a> </td> </tr>
186
187 """)
188
189 html_template = string.Template("""<!DOCTYPE html>
190 <html>
191 <head>
192 <title> LALInference P-P Plots </title>
193 </head>
194
195 <body>
196
197 <p>This page was generated with the output of ${injnum} simulations.</p>
198 ${linkstr}
199 <br>
200 <table border="1">
201 <tr>
202 <th> Parameter </th> <th> K-S p-value </th> <th> p-p Plot </th> <th> Links </th>
203 </tr>
204
205 ${tablerows}
206 ${skypp}
207 </table>
208
209 </body>
210 </html>
211
212 """)
213
214 # If this script is run with lalinference_pp_pipe then the following directory structure should exist
215 links="<ul>"
216 if os.path.isdir(os.path.join(outdir,'prior')):
217 links+='<li><a href="prior/">Prior Samples used in this test</a>'
218 if os.path.isdir(os.path.join(outdir,'injections')):
219 links+='<li><a href="injections/">Posteriors for each injection</a>'
220 links+='</ul>'
221
222 tablerows = []
223 for par, pv in ks_pvalues.items():
224 tablerows.append(table_row_template.substitute(name=par, pvalue=str(pv)))
225 tablerows = '\n'.join(tablerows)
226
227 if skypp is False:
228 skysub=''
229 else:
230 skysub='<tr> <td> sky </td> <td> See Plot </td> <td> <img src="sky_p-p.png" alt="sky p-p plot" width="300" height="300" /> </td> <td> <a href="sky_p-p.png">PNG</a> <a href="sky_p-p.pdf">PDF</a></td> </tr>'
231
232 html = html_template.substitute(tablerows=tablerows, injnum=str(injnum), linkstr=links,skypp=skysub)
233
234 with open(os.path.join(outdir, 'index.html'), 'w') as out:
235 out.write(html)
236
237if __name__ == '__main__':
238 USAGE='''%prog [options] posfile1.dat posfile2.dat ...
239 Generate PP analysis for a set of injections. posfiles must be in same order as injections.'''
240 parser = optparse.OptionParser(USAGE)
241 parser.add_option('--injXML', action='store', type='string', dest='injxml',
242 help='sim_inspiral XML file for injections')
243 parser.add_option('--outdir', action='store', type='string',
244 help='output directory')
245
246 parser.add_option('--postsamples', action='store', type='string',
247 default='posterior_samples.dat',
248 help='filename for posterior samples files')
249
250 parser.add_option('--par', action='append', default=[], type='string',
251 help='parameter names for the p-p plot')
252 parser.add_option('--skyPPfolder', action='store',dest='skypp',type='string',default=None,help='Path to folder containing png/pdf with 2D skyarea PP plots')
253
254 (options, args) = parser.parse_args()
255
256 injs = lsctables.SimInspiralTable.get_table(utils.load_filename(options.injxml))
257
258 if options.par == []:
259 parameters = ['m1', 'm2', 'mc', 'eta', 'q', 'theta_jn', 'a1', 'a2', 'tilt1', 'tilt2', 'phi12', 'phi_jl', 'ra', 'dec', 'distance', 'time', 'phi_orb', 'psi']
260 else:
261 parameters = options.par
262
263 try:
264 os.mkdir(options.outdir)
265 except:
266 pass
267
268 pvalues = { }
269 posfiles=args
270 Ninj=0
271 for index,posfile in enumerate(posfiles):
272 try:
273 psamples = read_posterior_samples(posfile,injs[index])
274 Ninj+=1
275 except:
276 # Couldn't read the posterior samples or the XML.
277 continue
278
279 for par in parameters:
280 try:
281 samples = psamples[par].samples
282 true_value=psamples[par].injval
283 p = fractional_rank(true_value, samples)
284 try:
285 pvalues[par].append(p)
286 except:
287 pvalues[par] = [p]
288 except:
289 # Couldn't read samples for parameter or injection
290 continue
291
292 # Generate plots, K-S tests
293 ks_pvalues = {}
294 for par, ps in pvalues.items():
295 print("Trying to create the plot for",par,".")
296 try:
297 pp_plot(ps, title=posterior_name_to_latex_name[par], outfile=os.path.join(options.outdir, par))
298 pp.clf()
299 ks_pvalues[par] = pp_kstest_pvalue(ps)
300 np.savetxt(os.path.join(options.outdir, par + '-ps.dat'), np.reshape(ps, (-1, 1)))
301 except:
302 print("Could not create the plot for",par,"!!!")
303
304 skypp=False
305 if options.skypp is not None:
306 found=0
307 if os.path.isdir(options.skypp):
308 for i in ['png','pdf']:
309 if os.path.isfile(os.path.join(options.skypp,'p-p.%s'%i)):
310 inf=os.path.join(os.path.realpath(options.skypp),'p-p.%s'%i)
311 outf=os.path.join(options.outdir,'sky_p-p.%s'%i)
312 os.system('cp %s %s'%(inf,outf))
313 found+=1
314 else:
315 print("could not find %s\n"%os.path.join(options.skypp,'p-p.%s'%i))
316 else:
317 print("skyPPfolder %s doesn't seem to be a valid folder or cannot be read. Skipping skyPP plot\n"%os.path.realpath(options.skypp))
318
319 if found>0:
320 skypp=True
321
322 output_html(options.outdir, ks_pvalues, Ninj ,skypp=skypp )
def read_posterior_samples(f, injrow)
Returns a bppu posterior sample object.
def pp_plot(ps, title=None, outfile=None)
Generates a p-p plot for the given ps.
def pp_kstest_pvalue(ps)
Returns the K-S p-value for the test of the given ps against a uniform distribution on [0,...
def output_html(outdir, ks_pvalues, injnum, skypp=False)
Outputs the HTML page summarizing the results.
def fractional_rank(x, xs)
Returns the fraction of samples, xs, that fall below the value x.