In [1]:
from astropy.table import Table
from astropy.coordinates import SkyCoord
from astropy import units as u
from astropy.table import hstack
import matplotlib.pyplot as plt
import numpy as np
from astroML.plotting import hist
# for astroML installation see https://www.astroml.org/user_guide/installation.html
In [2]:
### selection tools and numerical analysis
# robust standard deviation
def sigG(arr):
return 0.741*(np.quantile(arr, 0.75)-np.quantile(arr, 0.25))
def checkNobs(d, band):
str1 = band + '_Nobs_old'
arr1 = d[str1]
print(band, 'band, OLD:')
printStats(arr1)
str1 = band + '_Nobs_new'
arr1 = d[str1]
print(' NEW:')
printStats(arr1)
print(' DIFF:')
str2 = 'd' + band
arr2 = d[str2]
printStats(arr2)
return
def printStats(arr):
print(' ', np.min(arr), np.median(arr), np.max(arr))
return
# given vectors x and y, fit medians in bins from xMin to xMax, with Nbin steps,
# and return xBin, medianBin, medianErrBin
def fitMedians(x, y, xMin, xMax, Nbin, verbose=1):
# first generate bins
xEdge = np.linspace(xMin, xMax, (Nbin+1))
xBin = np.linspace(0, 1, Nbin)
nPts = 0*np.linspace(0, 1, Nbin)
medianBin = 0*np.linspace(0, 1, Nbin)
sigGbin = -1+0*np.linspace(0, 1, Nbin)
for i in range(0, Nbin):
xBin[i] = 0.5*(xEdge[i]+xEdge[i+1])
yAux = y[(x>xEdge[i])&(x<=xEdge[i+1])]
if (yAux.size > 0):
nPts[i] = yAux.size
medianBin[i] = np.median(yAux)
# robust estimate of standard deviation: 0.741*(q75-q25)
sigmaG = 0.741*(np.percentile(yAux,75)-np.percentile(yAux,25))
# uncertainty of the median: sqrt(pi/2)*st.dev/sqrt(N)
sigGbin[i] = np.sqrt(np.pi/2)*sigmaG/np.sqrt(nPts[i])
else:
nPts[i] = 0
medianBin[i] = 0
sigGbin[i] = 0
if (verbose):
print('median:', np.median(medianBin[nPts>0]), 'std.dev:', np.std(medianBin[nPts>0]))
return xBin, nPts, medianBin, sigGbin
In [3]:
### plots
# quick plot
def qp(d, Xstr, Xmin, Xmax, Ystr, Ymin, Ymax):
ax = plt.axes()
ax.set_xlabel(Xstr)
ax.set_ylabel(Ystr)
ax.scatter(d[Xstr], d[Ystr], s=0.01, c='blue')
ax.set_xlim(Xmin, Xmax)
ax.set_ylim(Ymin, Ymax)
plt.show()
return
# quick plot - compare three subsamples
def qp3(d1, d2, d3, Xstr, Xmin, Xmax, Ystr, Ymin, Ymax):
ax = plt.axes()
ax.set_xlabel(Xstr)
ax.set_ylabel(Ystr)
ax.scatter(d1[Xstr], d1[Ystr], s=0.01, c='green')
ax.scatter(d2[Xstr], d2[Ystr], s=0.01, c='red')
ax.scatter(d3[Xstr], d3[Ystr], s=0.01, c='blue')
ax.set_xlim(Xmin, Xmax)
ax.set_ylim(Ymin, Ymax)
plt.show()
return
# quick plot - binned median
def qpBM(d, Xstr, Xmin, Xmax, Ystr, Ymin, Ymax, nBin, Nsigma=3, offset=0.01):
ax = plt.axes()
ax.scatter(d[Xstr], d[Ystr], s=0.01, c='black')
# binning
xBinM, nPtsM, medianBinM, sigGbinM = fitMedians(d[Xstr], d[Ystr], Xmin, Xmax, nBin, 1)
# plotting
ax.scatter(xBinM, medianBinM, s=30.0, c='black', alpha=0.8)
ax.scatter(xBinM, medianBinM, s=15.0, c='yellow', alpha=0.3)
#
TwoSigP = medianBinM + Nsigma*sigGbinM
TwoSigM = medianBinM - Nsigma*sigGbinM
ax.plot(xBinM, TwoSigP, c='yellow')
ax.plot(xBinM, TwoSigM, c='yellow')
#
rmsBin = np.sqrt(nPtsM) / np.sqrt(np.pi/2) * sigGbinM
rmsP = medianBinM + rmsBin
rmsM = medianBinM - rmsBin
ax.plot(xBinM, rmsP, c='cyan')
ax.plot(xBinM, rmsM, c='cyan')
#
xL = np.linspace(-100,100)
ax.plot(xL, 0*xL+offset, c='red')
ax.plot(xL, 0*xL-offset, c='red')
#
ax.set_xlabel(Xstr)
ax.set_ylabel(Ystr)
ax.set_xlim(Xmin, Xmax)
ax.set_ylim(Ymin, Ymax)
plt.show()
return
def qphist(arr, xMin, xMax, xLabel, verbose = False):
ax = plt.axes()
hist(arr, bins='knuth', ax=ax, histtype='stepfilled', ec='k', fc='#AAAAAA')
ax.set_xlabel(xLabel)
ax.set_ylabel('n')
ax.set_xlim(xMin, xMax)
plt.show()
if (verbose):
print('Min, max: ', np.min(arr),np.max(arr))
print('Mean, median: ', np.mean(arr),np.median(arr))
print('sigG, st.dev.: ', sigG(arr),np.std(arr))
return
def qpH0(arr, xMin, xMax, xLabel, nBins=0, verbose = False):
ax = plt.axes()
if (nBins>0):
hist, bins = np.histogram(arr, bins=nBins)
center = (bins[:-1]+bins[1:])/2
ax.plot(center, hist, drawstyle='steps', c='blue')
else:
plt.hist(arr, bins='auto', histtype='stepfilled', ec='k', fc='red')
ax.set_xlabel(xLabel)
ax.set_ylabel('n')
ax.set_xlim(xMin, xMax)
ax.plot([-1000, 1000], [0, 0], '--k')
plt.show()
if (verbose):
print('Min, max: ', np.min(arr),np.max(arr))
print('Mean, median: ', np.mean(arr),np.median(arr))
print('sigG, st.dev.: ', sigG(arr),np.std(arr))
return
def qpHdm(d, band, dmMax, xMin, xMax, nBins=50, verbose=False):
str = 'd' + band
dm = 1000*d[str]
dmOK = dm[np.abs(dm)<(1000*dmMax)]
xLabel = str + ' (milimag)'
qpH0(dmOK, 1000*xMin, 1000*xMax, xLabel, nBins, verbose)
return np.mean(dmOK), np.median(dmOK), sigG(dmOK)
def qp2hist(arr1, arr2, xMin, xMax, xLabel, nBins=0, verbose = False):
ax = plt.axes()
if (nBins>0):
hist, bins = np.histogram(arr1, bins=nBins)
center = (bins[:-1]+bins[1:])/2
ax.plot(center, hist, drawstyle='steps', c='red')
hist2, bins2 = np.histogram(arr2, bins=nBins)
center2 = (bins2[:-1]+bins2[1:])/2
ax.plot(center2, hist2, drawstyle='steps', c='blue')
else:
plt.hist(arr1, bins='auto', histtype='stepfilled', ec='k', fc='yellow')
plt.hist(arr2, bins='auto', histtype='stepfilled', ec='red', fc='blue')
ax.set_xlabel(xLabel)
ax.set_ylabel('n')
ax.set_xlim(xMin, xMax)
plt.show()
if (verbose):
print('Min: ', np.min(arr1),np.min(arr2))
print('Median: ', np.median(arr1),np.median(arr2))
print('sigG: ', sigG(arr1),sigG(arr2))
print('Max: ', np.max(arr1),np.max(arr2))
return
In [4]:
ZIdataDir = "/Users/ivezic/Work/Science/CalibrationV2/Data"
# the original SDSS catalog from 2007
sdssOldCat = ZIdataDir + "/" + "stripe82calibStars_v2.6.dat"
# Karun's new catalog from 2020
sdssNewCat = ZIdataDir + "/" + "NEW_stripe82calibStars_v0.dat"
In [5]:
# both new and old files use identical data structure
colnamesSDSS = ['calib_fla', 'ra', 'dec', 'raRMS', 'decRMS', 'nEpochs', 'AR_val',
'u_Nobs', 'u_mMed', 'u_mMean', 'u_mErr', 'u_rms_scatt', 'u_chi2',
'g_Nobs', 'g_mMed', 'g_mMean', 'g_mErr', 'g_rms_scatt', 'g_chi2',
'r_Nobs', 'r_mMed', 'r_mMean', 'r_mErr', 'r_rms_scatt', 'r_chi2',
'i_Nobs', 'i_mMed', 'i_mMean', 'i_mErr', 'i_rms_scatt', 'i_chi2',
'z_Nobs', 'z_mMed', 'z_mMean', 'z_mErr', 'z_rms_scatt', 'z_chi2']
In [6]:
%%time
# old
sdssOld = Table.read(sdssOldCat, format='ascii', names=colnamesSDSS)
np.size(sdssOld)
Out[6]:
In [8]:
%%time
# new
## ZI version
sdssNewCat = '/Users/ivezic/Work/Science/CalibrationV2/SDSS_SSC/Analysis_2020/newSSC.dat'
sdssNewRaw = Table.read(sdssNewCat, format='ascii', names=colnamesSDSS)
# rejects objects with bad Declination, sample size from 1007136 to 1005470
sdssNew = sdssNewRaw[sdssNewRaw['dec']<90]
np.size(sdssNew)
Out[8]:
In [9]:
sdssOld_coords = SkyCoord(ra = sdssOld['ra']*u.degree, dec= sdssOld['dec']*u.degree)
sdssNew_coords = SkyCoord(ra = sdssNew['ra']*u.degree, dec= sdssNew['dec']*u.degree)
# this is matching sdssNew to sdssOld, so that indices are into sdssNew catalog
# makes sense in this case since the sdssOld catalog is (a little bit) bigger
# than sdssNew (1006849 vs 1005470)
idx, d2d, d3d = sdssNew_coords.match_to_catalog_sky(sdssOld_coords)
In [10]:
# object separation is an object with units,
# I add that as a column so that one can
# select based on separation to the nearest matching object
new_old = hstack([sdssNew, sdssOld[idx]], table_names = ['new', 'old'])
new_old['sep_2d_arcsec'] = d2d.arcsec
# since we matched sdssNew to sdssOld, the resulting catalog has the same lengthas sdssNew: 1005470
In [11]:
qphist(d2d.arcsec, 0.0, 0.6, 'radius of match (arcsec)', True)
In [12]:
## first limit astrometric distance and
## require at least 4 epochs as in the old catalog
MAX_DISTANCE_ARCSEC = 0.5
m1 = new_old[(new_old['sep_2d_arcsec'] < MAX_DISTANCE_ARCSEC)]
a1 = m1['g_Nobs_new']
a2 = m1['r_Nobs_new']
a3 = m1['i_Nobs_new']
mOK = m1[(a1>3)&(a2>3)&(a3>3)]
print(len(new_old))
print(len(m1))
print(len(mOK))
In [13]:
for b in ('u', 'g', 'r', 'i', 'z'):
str1 = b + '_Nobs_old'
str2 = b + '_Nobs_new'
print(b, np.sum(mOK[str1]), np.sum(mOK[str2]))
In [14]:
## additional constraints for the u and z band:
## require at least 4 epochs both in the old and new catalogs
mOKu = mOK[(mOK['u_Nobs_old']>3)&(mOK['u_Nobs_new']>3)]
mOKz = mOK[(mOK['z_Nobs_old']>3)&(mOK['u_Nobs_new']>3)]
print(len(mOK))
print(len(mOKu))
print(len(mOKz))
In [15]:
### code for generating new quantities, such as dra, ddec, colors, differences in mags, etc
def derivedColumns(matches):
matches['dra'] = (matches['ra_new']-matches['ra_old'])*3600
matches['ddec'] = (matches['dec_new']-matches['dec_old'])*3600
matches['ra'] = matches['ra_old']
ra = matches['ra']
matches['raW'] = np.where(ra > 180, ra-360, ra)
matches['dec'] = matches['dec_old']
matches['u'] = matches['u_mMed_old']
matches['g'] = matches['g_mMed_old']
matches['r'] = matches['r_mMed_old']
matches['i'] = matches['i_mMed_old']
matches['z'] = matches['z_mMed_old']
matches['ug'] = matches['u_mMed_old'] - matches['g_mMed_old']
matches['gr'] = matches['g_mMed_old'] - matches['r_mMed_old']
matches['ri'] = matches['r_mMed_old'] - matches['i_mMed_old']
matches['gi'] = matches['g_mMed_old'] - matches['i_mMed_old']
matches['du'] = matches['u_mMed_old'] - matches['u_mMed_new']
matches['dg'] = matches['g_mMed_old'] - matches['g_mMed_new']
matches['dr'] = matches['r_mMed_old'] - matches['r_mMed_new']
matches['di'] = matches['i_mMed_old'] - matches['i_mMed_new']
matches['dz'] = matches['z_mMed_old'] - matches['z_mMed_new']
return
In [16]:
derivedColumns(mOK)
derivedColumns(mOKu)
derivedColumns(mOKz)
In [17]:
qp3(mOK, mOKz, mOKu, 'gi', -1.0, 4.5, 'g', 24, 13)
In [18]:
qp(mOKu, 'ug', -0.6, 3.5, 'gr', -0.7, 2.2)
In [19]:
mOKuB = mOKu[mOKu['u']<21]
qp(mOKuB, 'ug', -0.6, 3.5, 'gr', -0.7, 2.2)
In [20]:
qp(mOK, 'gr', -0.7, 2.2, 'ri', -0.7, 2.5)
In [21]:
qp(mOK, 'raW', -60, 70, 'dec', -1.3, 1.3)
In [22]:
checkNobs(mOKu, 'u')
checkNobs(mOK, 'g')
checkNobs(mOK, 'r')
checkNobs(mOK, 'i')
checkNobs(mOKz, 'z')
In [23]:
mOK2 = mOK[mOK['r_chi2_new']<10]
print(np.size(mOK), np.size(mOK2))
In [24]:
mOKB = mOK[mOK['r_mMed_new']<21]
print(np.size(mOK), np.size(mOKB))
In [25]:
mOKc1 = mOK[(mOK['r_chi2_new']<3)&(mOK['i_chi2_new']>20)&(mOK['i_chi2_new']<1000)]
mOKc2 = mOK[(mOK['r_chi2_new']>20)&(mOK['i_chi2_new']<3)&(mOK['r_chi2_new']<1000)]
mOKc3 = mOK[(mOK['r_chi2_new']>20)&(mOK['i_chi2_new']>20)&(mOK['r_chi2_new']<1000)&(mOK['i_chi2_new']<1000)]
print(np.size(mOK), np.size(mOKc1), np.size(mOKc2), np.size(mOKc3))
In [30]:
qp(mOKB, 'r_chi2_new', 0, 5, 'r_mErr_new', 0, 0.1)
In [29]:
qp(mOKB, 'r_chi2_new', 0, 5, 'r_rms_scatt_new', 0, 0.2)
In [46]:
### variables
mOKvar = mOK[(mOK['r_chi2_new']>3) & (mOK['r_Nobs_new']>10)]
print(np.size(mOKvar))
In [47]:
qp(mOKvar, 'ug', -0.6, 3.5, 'gr', -0.7, 2.2)
In [48]:
mOKB['drMM'] = mOKB['r_mMean_new'] - mOKB['r_mMed_new']
np.std(mOKB['drMM'])
qp(mOKB, 'r_chi2_new', 0, 40, 'drMM', 0, 0.12)
In [51]:
qp(mOKB, 'g_chi2_new', 0, 10, 'r_chi2_new', 0, 10)
In [52]:
qp(mOKB, 'i_chi2_new', 0, 10, 'r_chi2_new', 0, 10)
In [54]:
qp(mOK, 'r_chi2_old', 0, 4, 'r_chi2_new', 0, 4)
In [56]:
def makeAllPlots(d, b, dmMax=0.05):
str1 = b + '_Nobs_old'
str2 = b + '_Nobs_new'
str3 = 'Nobs(' + b + ')'
str4 = 'd' + b
str5 = b + '_chi2_old'
str6 = b + '_chi2_new'
str7 = 'chi2(' + b + ')'
qp2hist(d[str1], d[str2], 0, 50, str3, 51, True)
qp2hist(d[str5], d[str6], 0, 5, str7, 51, True)
meanDM, medianDM, sigDM = qpHdm(d, b, 0.1, -0.1, 0.1, 50, True)
# binned medians of dm vs. both coordinates, magnitude and color
qpBM(d, 'raW', -51.5, 60.0, str4, -1*dmMax, dmMax, 500)
qpBM(d, 'dec', -1.3, 1.3, str4, -1*dmMax, dmMax, 120)
qpBM(d, b, 14, 23.0, str4, -1*dmMax, dmMax, 150)
qpBM(d, 'gi', -0.7, 3.5, str4, -1*dmMax, dmMax, 150)
return meanDM, medianDM, sigDM
In [57]:
mOK2 = mOK[(mOK['g_chi2_new']<10)&(mOK['r_chi2_new']<10)&(mOK['i_chi2_new']<10)]
print(np.size(mOK), np.size(mOK2))
In [58]:
for b in ('g', 'r', 'i'):
meanDM, medianDM, sigDM = makeAllPlots(mOK2, b)
print('BAND', b, ': ', meanDM, medianDM, sigDM)
In [59]:
for b in ('g', 'r', 'i'):
meanDM, medianDM, sigDM = makeAllPlots(mOKc1, b)
print('BAND', b, ': ', meanDM, medianDM, sigDM)
In [60]:
for b in ('g', 'r', 'i'):
meanDM, medianDM, sigDM = makeAllPlots(mOKc2, b)
print('BAND', b, ': ', meanDM, medianDM, sigDM)
In [79]:
for b in ('g', 'r', 'i'):
meanDM, medianDM, sigDM = makeAllPlots(mOKc3, b)
print('BAND', b, ': ', meanDM, medianDM, sigDM)
In [61]:
mOKbright = mOK[mOK['g']<20.5]
In [62]:
qpBM(mOKbright, 'dec', -1.3, 1.3, 'dg', -0.03, 0.03, 120)
In [63]:
qpBM(mOKbright, 'dec', -1.3, 1.3, 'dr', -0.03, 0.03, 120)
In [64]:
qpBM(mOKbright, 'dec', -1.3, 1.3, 'di', -0.03, 0.03, 120)
In [65]:
## systematics wrt Dec look very similar in gri; check g-r and r-i systematics
In [66]:
mOKbright['dgr'] = mOKbright['dg'] - mOKbright['dr']
mOKbright['dri'] = mOKbright['dr'] - mOKbright['di']
In [67]:
qpBM(mOKbright, 'dec', -1.3, 1.3, 'dgr', -0.03, 0.03, 120)
In [68]:
qpBM(mOKbright, 'dec', -1.3, 1.3, 'dri', -0.03, 0.03, 120)
In [69]:
### CONCLUSION: need to correct for Dec systematics, at least with a gray correction
# check also u and z bands for systematics
In [70]:
b = 'u'
meanDM, medianDM, sigDM = makeAllPlots(mOKu, b)
print('BAND', b, ': ', meanDM, medianDM, sigDM)
In [71]:
b = 'z'
meanDM, medianDM, sigDM = makeAllPlots(mOKz, b)
print('BAND', b, ': ', meanDM, medianDM, sigDM)
In [72]:
## check bright end for u subsample
mOKuB = mOKu[(mOKu['u']>15.5)&(mOKu['u']<21)]
print(np.size(mOKuB))
b = 'u'
meanDM, medianDM, sigDM = makeAllPlots(mOKuB, b)
print('BAND', b, ': ', meanDM, medianDM, sigDM)
In [73]:
## check u-g color systematics vs. Dec for bright u subsample
mOKuB['dug'] = mOKuB['du'] - mOKuB['dg']
In [74]:
qpBM(mOKuB, 'dec', -1.3, 1.3, 'dug', -0.03, 0.03, 120)
In [75]:
## check color systematics vs. Dec for bright z subsample
mOKzB = mOKz[(mOKz['z']>14)&(mOKz['z']<20)]
print(np.size(mOKzB))
mOKzB['dri'] = mOKzB['dr'] - mOKzB['di']
mOKzB['diz'] = mOKzB['di'] - mOKzB['dz']
In [76]:
qpBM(mOKzB, 'dec', -1.3, 1.3, 'dz', -0.03, 0.03, 120)
In [77]:
qpBM(mOKzB, 'dec', -1.3, 1.3, 'diz', -0.03, 0.03, 120)
In [78]:
qpBM(mOKzB, 'dec', -1.3, 1.3, 'dri', -0.03, 0.03, 120)
In [ ]:
In [ ]:
In [ ]: