def compute_luminosity_function(z, m, M, m_max, archive_file): """Compute the luminosity function and archive in the given file. If the file exists, then the saved results are returned. """ Mmax = m_max - (m - M) zmax = z_mu(m_max - M) if not os.path.exists(archive_file): print ("- computing bootstrapped luminosity function ", "for %i points" % len(z)) zbins = np.linspace(0.08, 0.12, 21) Mbins = np.linspace(-24, -20.2, 21) dist_z, err_z, dist_M, err_M = bootstrap_Cminus(z, M, zmax, Mmax, zbins, Mbins, Nbootstraps=20, normalize=True) np.savez(archive_file, zbins=zbins, dist_z=dist_z, err_z=err_z, Mbins=Mbins, dist_M=dist_M, err_M=err_M) else: print "- using precomputed bootstrapped luminosity function results" archive = np.load(archive_file) zbins = archive["zbins"] dist_z = archive["dist_z"] err_z = archive["err_z"] Mbins = archive["Mbins"] dist_M = archive["dist_M"] err_M = archive["err_M"] return zbins, dist_z, err_z, Mbins, dist_M, err_M
def compute_luminosity_function(z, m, M, m_max, archive_file): """Compute the luminosity function and archive in the given file. If the file exists, then the saved results are returned. """ Mmax = m_max - (m - M) zmax = z_mu(m_max - M) if not os.path.exists(archive_file): print("- computing bootstrapped luminosity function ", "for {0} points".format(len(z))) zbins = np.linspace(0.08, 0.12, 21) Mbins = np.linspace(-24, -20.2, 21) dist_z, err_z, dist_M, err_M = bootstrap_Cminus(z, M, zmax, Mmax, zbins, Mbins, Nbootstraps=20, normalize=True) np.savez(archive_file, zbins=zbins, dist_z=dist_z, err_z=err_z, Mbins=Mbins, dist_M=dist_M, err_M=err_M) else: print("- using precomputed bootstrapped luminosity function results") archive = np.load(archive_file) zbins = archive['zbins'] dist_z = archive['dist_z'] err_z = archive['err_z'] Mbins = archive['Mbins'] dist_M = archive['dist_M'] err_M = archive['err_M'] return zbins, dist_z, err_z, Mbins, dist_M, err_M
def compute_luminosity_function(z, m, M, m_max, archive_file, Nbootstraps=20, bin_type='freedman', z_mu=None): ''' Compute the luminosity function and archive in the given file. If the file exists, then the saved results are returned. Parameters ---------- z : array-like Redshifts of galaxies. m : array-like Apparent magnitudes of galaxies. M : array-like Absolute magnitudes of galaxies. m_max : float Maximum sensitivity in apparent magnitudes. archive_file : str Name of file saved for bootstrapping. Nbootstraps : int Number of bootstraps to perform. bin_type : str Type of binning method to use. Options are 'freedman' or 'scott' z_mu : func Distance modulus as a function of redshift. Returns ------- zbins : array-like Redshift bin centers. dist_z : array-like Redshift count distribution. err_z : array-like Redshift count errors. Mbins : array-like Absolute magnitude bin centers. dist_M : array-like Absolute magnitude count distribution. err_M : array-like Absolute magnitude count errors. ''' from astroML.density_estimation import scotts_bin_width as scott_bin from astroML.density_estimation import freedman_bin_width as free_bin from astroML.lumfunc import binned_Cminus, bootstrap_Cminus Mmax = m_max - (m - M) zmax = z_mu(m_max - M) if not os.path.exists(archive_file): print("- computing bootstrapped luminosity function ", "for %i points" % len(z)) if bin_type == 'freedman': zbin_width, zbins = free_bin(z, return_bins=True) Mbin_width, Mbins = free_bin(M, return_bins=True) elif bin_type == 'scott': zbin_width, zbins = scott_bin(z, return_bins=True) Mbin_width, Mbins = scott_bin(M, return_bins=True) dist_z, err_z, dist_M, err_M = bootstrap_Cminus(z, M, zmax, Mmax, zbins, Mbins, Nbootstraps=20, normalize=True) np.savez(archive_file, zbins=zbins, dist_z=dist_z, err_z=err_z, Mbins=Mbins, dist_M=dist_M, err_M=err_M) else: print "- using precomputed bootstrapped luminosity function results" archive = np.load(archive_file) zbins = archive['zbins'] dist_z = archive['dist_z'] err_z = archive['err_z'] Mbins = archive['Mbins'] dist_M = archive['dist_M'] err_M = archive['err_M'] return zbins, dist_z, err_z, Mbins, dist_M, err_M
ymax[ymax > 1] = 1 # cutoff at y=1 # truncate the data flag = (x < xmax) & (y < ymax) x = x[flag] y = y[flag] xmax = xmax[flag] ymax = ymax[flag] x_fit = np.linspace(0, 1, 21) y_fit = np.linspace(0, 1, 21) #------------------------------------------------------------ # compute the Cminus distributions (with bootstrap) x_dist, dx_dist, y_dist, dy_dist = bootstrap_Cminus(x, y, xmax, ymax, x_fit, y_fit, Nbootstraps=20, normalize=True) x_mid = 0.5 * (x_fit[1:] + x_fit[:-1]) y_mid = 0.5 * (y_fit[1:] + y_fit[:-1]) #------------------------------------------------------------ # Plot the results fig = plt.figure(figsize=(5, 2)) fig.subplots_adjust(bottom=0.2, top=0.95, left=0.1, right=0.92, wspace=0.25) # First subplot is the true & inferred 1D distributions ax = fig.add_subplot(121) ax.plot(x_mid, x_pdf.pdf(x_mid), '-k', label='$p(x)$') ax.plot(y_mid, y_pdf.pdf(y_mid), '--k', label='$p(y)$')
# truncate the data flag = (x < xmax) & (y < ymax) x = x[flag] y = y[flag] xmax = xmax[flag] ymax = ymax[flag] x_fit = np.linspace(0, 1, 21) y_fit = np.linspace(0, 1, 21) #------------------------------------------------------------ # compute the Cminus distributions (with bootstrap) x_dist, dx_dist, y_dist, dy_dist = bootstrap_Cminus(x, y, xmax, ymax, x_fit, y_fit, Nbootstraps=20, normalize=True) x_mid = 0.5 * (x_fit[1:] + x_fit[:-1]) y_mid = 0.5 * (y_fit[1:] + y_fit[:-1]) #------------------------------------------------------------ # Plot the results fig = plt.figure(figsize=(5, 2)) fig.subplots_adjust(bottom=0.2, top=0.95, left=0.1, right=0.92, wspace=0.25) # First subplot is the true & inferred 1D distributions ax = fig.add_subplot(121) ax.plot(x_mid, x_pdf.pdf(x_mid), '-k', label='$p(x)$')
def doAstr511HW2(truth, obs, diskLF, haloLF): """Make a six-panel plot illustrating Astr511 homework #2""" # a) Define a ``gold parallax sample" by piObs/piErr > 10 and compare distance # modulus from the parallax measurement (D/kpc=1 milliarcsec/piObs) # to the distance modulus determined from r and Mr listed in the ``truth" file piObs = obs[14] piErr = obs[15] piSNR = piObs / piErr D = np.where(piObs>0, 1/piObs, 1.0e6) mask = (piSNR>10) Dok = D[mask] DM = 5*np.log10(Dok/0.01) DMtrue = truth[4] - truth[8] diffDM = DM - DMtrue[mask] if (0): print ' mean =', np.mean(diffDM) print ' rms =', np.std(diffDM) med, sigG = median_sigmaG(diffDM) print 'median =', med print 'sigmaG =', sigG ### make vectors of x=distance modulus and y=absolute magnitude to run Cminus # sample faint limit rFaintLimit = 24.5 # use either true distances, or estimated from parallax if (0): # using "truth" values of distance modulus, DM, to compute LF Mr = truth[8] rTrue = truth[4] DMtrue = rTrue - Mr # type could be estimated from colors... typeWD = truth[13] # here the sample is defined using true apparent magnitude maskC = (rTrue < rFaintLimit) & (typeWD == 1) xC = DMtrue[maskC] yC = Mr[maskC] else: # observed apparent magnitude rObs = obs[6] if (1): # fit a 4-th order polynomial to Mr(gr) relation (high-SNR sample) A, B, C, D, E = fitPhotomParallax(truth, obs) # and now apply gr = obs[4]-obs[6] MrPP = WDppFit(gr, A, B, C, D, E) else: # testing here: take true Mr... MrPP = truth[8] # here the sample is defined using observed apparent magnitude DMobs = rObs - MrPP # type could be estimated from colors... typeWD = truth[13] maskC = (rObs < rFaintLimit) & (typeWD == 1) xC = DMobs[maskC] yC = MrPP[maskC] ## the sample boundary is a simple linear function because using DM xCmax = rFaintLimit - yC yCmax = rFaintLimit - xC ## split disk and halo, assume populations are known pop = truth[14] popC = pop[maskC] maskD = (popC==1) xCD = xC[maskD] yCD = yC[maskD] xCmaxD = xCmax[maskD] yCmaxD = yCmax[maskD] maskH = (popC==2) xCH = xC[maskH] yCH = yC[maskH] xCmaxH = xCmax[maskH] yCmaxH = yCmax[maskH] print 'observed sample size of xCD (disk)', np.size(xCD) print 'observed sample size of xCH (halo)', np.size(xCH) ### and now corrected Mr distributions: application of the Cminus method ## note that Cminus returns **cumulative** distribution functions ## however, bootstrap_Cminus (which calls Cminus) then does conversion ## and returns **differential** distribution functions #### this is where we run Cminus method (or use stored results) #### archive_file = 'CminusZI.npz' if not os.path.exists(archive_file): print ("- computing bootstrapped differential luminosity function ") #------------------------------------------------------------ # aux arrays x_fit = np.linspace(0, 15, 42) y_fit = np.linspace(10, 17, 42) #------------------------------------------------------------ ## compute the Cminus distributions (with bootstrap) # disk subsample print ("Disk sample with %i points" % len(xCD)) xD_dist, dxD_dist, yD_dist, dyD_dist = bootstrap_Cminus(xCD, yCD, xCmaxD, yCmaxD, x_fit, y_fit, Nbootstraps=20, normalize=True) # halo subsample print ("Halo sample with %i points" % len(xCH)) xH_dist, dxH_dist, yH_dist, dyH_dist = bootstrap_Cminus(xCH, yCH, xCmaxH, yCmaxH, x_fit, y_fit, Nbootstraps=20, normalize=True) np.savez(archive_file, x_fit=x_fit, xD_dist=xD_dist, dxD_dist=dxD_dist, y_fit=y_fit, yD_dist=yD_dist, dyD_dist=dyD_dist) archive_file = 'CminusZIhalo.npz' np.savez(archive_file, xH_dist=xH_dist, dxH_dist=dxH_dist, yH_dist=yH_dist, dyH_dist=dyH_dist) #------------------------------------------------------------ else: print "- using ZI's precomputed bootstrapped luminosity function results" # disk archive_file = 'CminusZI.npz' archive = np.load(archive_file) x_fit = archive['x_fit'] xD_dist = archive['xD_dist'] dxD_dist = archive['dxD_dist'] y_fit = archive['y_fit'] yD_dist = archive['yD_dist'] dyD_dist = archive['dyD_dist'] # halo archive_file = 'CminusZIhalo.npz' archive = np.load(archive_file) xH_dist = archive['xH_dist'] dxH_dist = archive['dxH_dist'] yH_dist = archive['yH_dist'] dyH_dist = archive['dyH_dist'] # bin mid positions for plotting the results: x_mid = 0.5 * (x_fit[1:] + x_fit[:-1]) y_mid = 0.5 * (y_fit[1:] + y_fit[:-1]) ### determine normalization of luminosity functions and number density distributions ## we need to renormalize LFs: what we have are differential distributions ## normalized so that their cumulative LF is 1 at the last point; ## we need to account for the fractional sky coverage and for flux selection effect ## and do a bit of extrapolation to our position (DM=0) at the end # 1) the fraction of covered sky: we know that the sample is defined by bGal > bMin bMin = 60.0 # in degrees # in steradians, given b > 60deg, dOmega = 2*pi*[1-cos(90-60)] = 0.8418 sr dOmega = 2*math.radians(180)*(1-math.cos(math.radians(90-bMin))) # 2) renormalization constants due to selection effects # these correspond to the left-hand side of eq. 12 from lecture 4 (page 26) DMmax0 = 11.0 MrMax0 = rFaintLimit - DMmax0 NmaskD = ((xCD < DMmax0) & (yCD < MrMax0)) Ndisk = np.sum(NmaskD) NmaskH = ((xCH < DMmax0) & (yCH < MrMax0)) Nhalo = np.sum(NmaskH) print 'Size of volume-limited samples D and H:', Ndisk, ' and ', Nhalo, '(DM<', DMmax0, ', Mr<', MrMax0, ')' ## Size of volume-limited samples D and H: 49791 and 2065 (DM< 11.0 , Mr< 13.5 ) # now we need to find what fraction of the total sample (without flux selection effects) # is found for x < DMmax0 and y < MrMax0. To do so, we need to integrate differential # distributions returned by bootstrap_Cminus up to x=DMmax0 and y=MrMax0 # (NB: we could have EASILY read off these from the original cumulative distributions returned # from Cminus, but unfortunately bootstrap_Cminus returns only differential distributions; # a design bug??) # n.b. verified that all four distributions integrate to 1 XfracD = IntDiffDistr(x_fit, xD_dist, DMmax0) YfracD = IntDiffDistr(y_fit, yD_dist, MrMax0) XfracH = IntDiffDistr(x_fit, xH_dist, DMmax0) YfracH = IntDiffDistr(y_fit, yH_dist, MrMax0) # and use eq. 12 to obtain normalization constant C for both subsamples Cdisk = Ndisk / (XfracD * YfracD) Chalo = Nhalo / (XfracH * YfracH) print 'Cdisk = ', Cdisk, 'Chalo = ', Chalo ## Cdisk = 608,082 Chalo = 565,598 ## number density normalization # need to go from per DM to per pc: dDM/dV = 5 / [ln(10) * dOmega * D^3] Dpc = 10 * 10**(0.2*x_mid) # the units are "number of stars per pc^3" (of all absolute magnitudes Mr) # note that here we assume isotropic sky distribution (division by dOmega!) rhoD = Cdisk * 5/np.log(10)/dOmega / Dpc**3 * xD_dist rhoH = Chalo * 5/np.log(10)/dOmega / Dpc**3 * xH_dist # same fractional errors remain after renormalization dxD_distN = rhoD * dxD_dist / xD_dist dxH_distN = rhoH * dxH_dist / xH_dist # differential luminosity function: per pc3 and mag, ** at the solar position ** # to get proper normalization, we simply multiply differential distributions (whose # integrals are already normalized to 1) by the local number density distributions) # Therefore, we need to extrapolate rhoD and rhoH to D=0 # and then use these values as normalization factors # The choice of extrapolation function is tricky (much more so than in case of # interpolation): based on our "prior knowledge", we choose # for disk: fit exponential disk, ln(rho) = ln(rho0) - H*D for 100 < D/pc < 500 xFitExpDisk = Dpc[(Dpc > 100) & (Dpc < 500)] yFitExpDisk = np.log(rhoD[(Dpc > 100) & (Dpc < 500)]) A, H = curve_fit(ExpDiskDensity, xFitExpDisk, yFitExpDisk)[0] rhoD0 = np.exp(A) print 'rhoD0', rhoD0, ' scale height H (pc) =', 1/H # and for halo: simply take the median value for 500 < D/pc < 2000 rhoH0 = np.median(rhoH[(Dpc > 500) & (Dpc < 2000)]) print 'rhoH0', rhoH0 # and now renormalize (rhoD0, rhoH0 = 0.0047 and 0.0000217) # the units are "number of stars per pc^3 AND per mag" (at D=0) yD_distN = yD_dist * rhoD0 yH_distN = yH_dist * rhoH0 # same fractional errors remain after renormalization dyD_distN = yD_distN * dyD_dist / yD_dist dyH_distN = yH_distN * dyH_dist / yH_dist ### PLOTTING ### plot_kwargs = dict(color='k', linestyle='none', marker='.', markersize=1) plt.subplots_adjust(bottom=0.09, top=0.96, left=0.09, right=0.96, wspace=0.31, hspace=0.32) # plot the distribution of the distance modulus difference and compute its median # and root-mean-square scatter (hint: beware of outliers and clip at 3sigma!) hist, bins = np.histogram(diffDM, bins=50) ## N.B. diffDM defined at the top center = (bins[:-1]+bins[1:])/2 ax1 = plt.subplot(3,2,1) ax1.plot(center, hist, drawstyle='steps') ax1.set_xlim(-1, 1) ax1.set_xlabel(r'$\mathrm{\Delta DM (mag)}$') ax1.set_ylabel(r'$\mathrm{dN/d(\Delta DM)}$') ax1.set_title('parallax SNR>10') ax1.plot(label=r'mpj legend') plt.text(0.25, 0.9, r'$\bar{x}=-0.023$', fontsize=12, bbox=dict(ec='k', fc='w', alpha=0.9), ha='center', va='center', transform=plt.gca().transAxes) plt.text(0.75, 0.9, r'$med.=-0.035$', fontsize=12, bbox=dict(ec='k', fc='w', alpha=0.9), ha='center', va='center', transform=plt.gca().transAxes) plt.text(0.2, 0.7, r'$\sigma=0.158$', fontsize=12, bbox=dict(ec='k', fc='w', alpha=0.9), ha='center', va='center', transform=plt.gca().transAxes) plt.text(0.75, 0.7, r'$\sigma_G=0.138$', fontsize=12, bbox=dict(ec='k', fc='w', alpha=0.9), ha='center', va='center', transform=plt.gca().transAxes) # plot uncorrected Mr vs. DM for disk ax2 = plt.subplot(3,2,2) ax2.set_xlim(4, 15) ax2.set_ylim(17, 10) ax2.set_xlabel(r'$\mathrm{DM}$') ax2.set_ylabel(r'$\mathrm{M_{r}}$') scatter_contour(xCD, yCD, threshold=20, log_counts=True, ax=ax2, histogram2d_args=dict(bins=40), plot_args=dict(marker='.', linestyle='none', markersize=1, color='black'), contour_args=dict(cmap=plt.cm.bone)) # ax2.plot(xCmaxD, yCD, color='blue') plt.text(0.75, 0.20, r'$disk$', fontsize=22, bbox=dict(ec='k', fc='w', alpha=0.3), ha='center', va='center', transform=plt.gca().transAxes) # plot uncorrected Mr vs. DM for halo ax3 = plt.subplot(3,2,3) ax3.set_xlim(4, 15) ax3.set_ylim(17, 10) ax3.set_xlabel(r'$\mathrm{DM}$') ax3.set_ylabel(r'$\mathrm{M_{r}}$') scatter_contour(xCH, yCH, threshold=20, log_counts=True, ax=ax3, histogram2d_args=dict(bins=40), plot_args=dict(marker='.', linestyle='none', markersize=1, color='black'), contour_args=dict(cmap=plt.cm.bone)) # ax3.plot(xCH, yCmaxH, '.k', markersize=0.1, color='blue') plt.text(0.75, 0.20, r'$halo$', fontsize=22, bbox=dict(ec='k', fc='w', alpha=0.3), ha='center', va='center', transform=plt.gca().transAxes) # uncorrected Mr distributions ax4 = plt.subplot(3,2,4) ax4.hist(yCD, bins=30, log=True, histtype='step', color='red') ax4.hist(yCH, bins=30, log=True, histtype='step', color='blue') ax4.set_xlim(10, 17) ax4.set_ylim(1, 50000) ax4.set_xlabel(r'$\mathrm{M_r}$') ax4.set_ylabel(r'$\mathrm{dN/dM_r}$') plt.text(0.52, 0.20, r'$uncorrected \, M_r \, distribution$', fontsize=12, bbox=dict(ec='k', fc='w', alpha=0.3), ha='center', va='center', transform=plt.gca().transAxes) ## now plot the two remaining panels: corrected rho and Mr distributions # density distributions ax5 = plt.subplot(325, yscale='log') ax5.errorbar(Dpc, rhoD, dxD_distN, fmt='^k', ecolor='k', lw=1, color='red', label='disk') ax5.errorbar(Dpc, rhoH, dxH_distN, fmt='^k', ecolor='k', lw=1, color='blue', label='halo') ax5.set_xlim(0, 5000) ax5.set_ylim(0.000001, 0.01) ax5.set_xlabel(r'$\mathrm{D (pc)}$') ax5.set_ylabel(r'$\mathrm{\rho(D)} (pc^{-3}) $') ax5.legend(loc='upper right') # luminosity functions ax6 = plt.subplot(326, yscale='log') ax6.errorbar(y_mid, yD_distN, dyD_distN, fmt='^k', ecolor='k', lw=1, color='red') ax6.errorbar(y_mid, yH_distN, dyH_distN, fmt='^k', ecolor='k', lw=1, color='blue') # true luminosity functions MbinD = diskLF[0] psiD = diskLF[1] MbinH = haloLF[0] psiH = haloLF[1] / 200 ax6.plot(MbinD, psiD, color='red') ax6.plot(MbinH, psiH, color='blue') plt.text(0.62, 0.15, r'$\Psi \, in \, pc^{-3} mag^{-1}$', fontsize=12, bbox=dict(ec='k', fc='w', alpha=0.3), ha='center', va='center', transform=plt.gca().transAxes) ax6.set_xlim(10, 17) ax6.set_xlabel(r'$\mathrm{M_r}$') ax6.set_ylabel(r'$\Psi(M_r)=\mathrm{dN^2/dV \,dM_r}$') plt.savefig('Astr511HW2.png') plt.show()
def compute_luminosity_function(z, m, M, m_max, archive_file, Nbootstraps=20, bin_type='freedman', z_mu=None): ''' Compute the luminosity function and archive in the given file. If the file exists, then the saved results are returned. Parameters ---------- z : array-like Redshifts of galaxies. m : array-like Apparent magnitudes of galaxies. M : array-like Absolute magnitudes of galaxies. m_max : float Maximum sensitivity in apparent magnitudes. archive_file : str Name of file saved for bootstrapping. Nbootstraps : int Number of bootstraps to perform. bin_type : str Type of binning method to use. Options are 'freedman' or 'scott' z_mu : func Distance modulus as a function of redshift. Returns ------- zbins : array-like Redshift bin centers. dist_z : array-like Redshift count distribution. err_z : array-like Redshift count errors. Mbins : array-like Absolute magnitude bin centers. dist_M : array-like Absolute magnitude count distribution. err_M : array-like Absolute magnitude count errors. ''' from astroML.density_estimation import scotts_bin_width as scott_bin from astroML.density_estimation import freedman_bin_width as free_bin from astroML.lumfunc import binned_Cminus, bootstrap_Cminus Mmax = m_max - (m - M) zmax = z_mu(m_max - M) if not os.path.exists(archive_file): print ("- computing bootstrapped luminosity function ", "for %i points" % len(z)) if bin_type == 'freedman': zbin_width, zbins = free_bin(z, return_bins=True) Mbin_width, Mbins = free_bin(M, return_bins=True) elif bin_type == 'scott': zbin_width, zbins = scott_bin(z, return_bins=True) Mbin_width, Mbins = scott_bin(M, return_bins=True) dist_z, err_z, dist_M, err_M = bootstrap_Cminus(z, M, zmax, Mmax, zbins, Mbins, Nbootstraps=20, normalize=True) np.savez(archive_file, zbins=zbins, dist_z=dist_z, err_z=err_z, Mbins=Mbins, dist_M=dist_M, err_M=err_M) else: print "- using precomputed bootstrapped luminosity function results" archive = np.load(archive_file) zbins = archive['zbins'] dist_z = archive['dist_z'] err_z = archive['err_z'] Mbins = archive['Mbins'] dist_M = archive['dist_M'] err_M = archive['err_M'] return zbins, dist_z, err_z, Mbins, dist_M, err_M