show_ts_distribution.py 6.75 KB
#! /usr/bin/env python
# ==========================================================================
# This script displays the TS distribution values generated using cstsdist.
#
# Required 3rd party modules:
# - matplotlib
# - numpy
#
# Copyright (C) 2011 Jurgen Knodlseder
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# ==========================================================================
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
import numpy as np
import sys
import csv
import math


# ==================== #
# Read TS distribution #
# ==================== #
def read_ts(filename, tsname="TS"):
	"""
	Read TS distribution values from CSV file.
	"""
	# Initialise list
	values = []
	
	# Open reader
	reader = csv.reader(open(filename, 'r'), delimiter=',')
	
	# Read rows
	first = True
	index = -1
	for row in reader:
		
		# Get column index if first row
		if first:
			try:
				index = row.index(tsname)
			except:
				sys.stdout.write('ERROR: Column "TS" not found in file\n')
				sys.stdout.write(row+"\n")
				raise NameError("TS")

		# Handle data rows
		else:
			values.append(float(row[index]))
		
		# Flag that first row has been passed
		first = False
	
	# Create numpy array
	a = np.array(values)
	
	# Return array
	return a


# =========== #
# Compute erf #
# =========== #
def erf(x):
	"""
	Compute error function.
	"""
	# save the sign of x
	sign = 1
	if x < 0: 
		sign = -1
	x = abs(x)

	# constants
	a1 =  0.254829592
	a2 = -0.284496736
	a3 =  1.421413741
	a4 = -1.453152027
	a5 =  1.061405429
	p  =  0.3275911
	
	# A&S formula 7.1.26
	t = 1.0/(1.0 + p*x)
	y = 1.0 - (((((a5*t + a4)*t) + a3)*t + a2)*t + a1)*t*math.exp(-x*x)
	
	# Return result
	return sign*y # erf(-x) = -erf(x)


# =============================== #
# Display cumulative distribution #
# =============================== #
def dist_cdf(values, nbins, title):
	"""
	Displays cumulative TS distribution and overplots expectation.
	"""
	# Set range and adapt number of bins. We make sure that we have a
	# histogram bin centred on 0 that will capture the TS=0 part.
	max_value = max(values)
	binsize   = max_value/nbins
	min_value = 0.5*binsize
	while min_value > min(values):
		min_value -= binsize
		nbins     += 1
	
	# Create histogram
	hist_x      = []
	hist_y      = []
	hist_expect = []
	norm        = 0.5*len(values)
	for i in range(nbins):
		x_min = min_value+i*binsize
		x_max = min_value+(i+1)*binsize
		x_val = 0.5*(x_min+x_max)
		y_val = 0.0
		for ts in values:
			if ts > x_min:
				y_val += 1.0
		hist_x.append(x_min)
		hist_y.append(y_val)
		hist_x.append(x_max)
		hist_y.append(y_val)
		if x_min > 0.0:
			value = norm*erf(math.sqrt(0.5*x_min)) + norm
			hist_expect.append(len(values)-value)
			hist_expect.append(len(values)-value)
		else:
			hist_expect.append(norm)
			hist_expect.append(norm)

	# Show expected distribution (half)
	l = plt.semilogy(hist_x, hist_y,      'r-', linewidth=2, label="simulations")
	l = plt.semilogy(hist_x, hist_expect, 'k-', linewidth=2, label="expectation")
	l = plt.semilogy(hist_x, hist_y,      'r-', linewidth=2)

	# Set plot
	plt.xlabel('TS')
	plt.ylabel('Number of trials > TS')
	plt.title(title)
	plt.grid(True)
	plt.legend(loc="upper right")

	# Show histogram
	plt.show()
	
	# Return
	return


# ======================================== #
# Display probability density distribution #
# ======================================== #
def dist_pdf(values, nbins, title):
	"""
	Display probability density TS distribution and overplots
	expectation.
	"""
	# Set range and adapt number of bins. We make sure that we have a
	# histogram bin centred on 0 that will capture the TS=0 part.
	max_value = max(values)
	binsize   = max_value/nbins
	min_value = 0.5*binsize
	while min_value > min(values):
		min_value -= binsize
		nbins     += 1
	
	# Create histogram
	n, bins, patches = plt.hist(values, nbins, range=[min_value, max_value], \
	                            align='mid', facecolor='green', \
								log=True, label="simulations")

	# Create expected distribution (onyl for positive TS). We compute the
	# full and half of the distribution, as for positively constrained
	# quantities half of the values have TS=0
	x      = []
	y_full = []
	y_half = []
	width  = bins[1]-bins[0]
	norm   = len(values)/(math.sqrt(2.0)*math.sqrt(math.pi))*width
	x.append(0.0)
	y_full.append(len(values))
	y_half.append(0.5*len(values))
	for bin in bins:
		ts = bin + 0.5*width
		if ts > 0:
			y  = norm*math.pow(ts,-0.5)*math.exp(-0.5*ts)
			x.append(ts)
			y_full.append(y)
			y_half.append(0.5*y)
	
	# Show expected distribution (half)
	l = plt.semilogy(x, y_half, 'ro', linewidth=2, label="expectation")

	# Set plot
	plt.xlabel('TS')
	plt.ylabel('Number of trials')
	plt.title(title)
	plt.grid(True)
	plt.legend(loc="upper right")

	# Show histogram
	plt.show()
		
	
# ======================== #
# Main routine entry point #
# ======================== #
if __name__ == '__main__':
	"""
	Display TS distribution generated using cstsdist using matplotlib.
	"""
	# Print usage information
	usage = "Usage: show_ts_distribution filename" \
	        " [-n bins] [-c column] [-t title] [-p plot]"
	if len(sys.argv) < 2:
		sys.stdout.write(usage+"\n")
		sys.exit()

	# Extract parameters
	filename = sys.argv[1]

	# Set default parameters
	nbins  = 30
	tsname = "TS"
	title  = "TS distribution"
	plot   = "pdf"

	# Parameter dictionnary
	pars = [{'option': '-n', 'value': nbins}, \
	        {'option': '-c', 'value': tsname}, \
	        {'option': '-t', 'value': title}, \
			{'option': '-p', 'value': plot}]
	
	# Gather parameters from command line
	i = 2
	while i < len(sys.argv):
		
		# Search for options
		for par in pars:
			if sys.argv[i] == par['option']:
				if len(sys.argv) > i+1:
					i      += 1
					try:
						par['value'] = sys.argv[i]
					except:
						sys.stdout.write(usage+"\n")
						sys.exit()
				else:
					sys.stdout.write(usage+"\n")
					sys.exit()
		
		# Next item
		i += 1
	
	# Recover parameters
	nbins  = int(pars[0]['value'])
	tsname = pars[1]['value']
	title  = pars[2]['value']
	plot   = pars[3]['value']
	
	# Read values from CSV file
	values = read_ts(filename, tsname=tsname)
	sys.stdout.write(len(values), "values read.\n")
	
	# Show histogram
	if plot == "pdf":
		dist_pdf(values, nbins, title)
	else:
		dist_cdf(values, nbins, title)