In [58]:
from IPython.display import Image, display

Is there something going on with goats and $\alpha$?

I think that I can construct an analogy that is simple enough to think about, yet retains all of the relevant pieces to solve a problem in astrophysics. Meaning, if you can think through how to solve this analogy, we will then be able to use the same reasoning and methods to solve the astrophysics case.

Here is the analogous problem.

Imagine a strange planet -- similar to Earth in many ways except that it is covered with grass and rolling hills everywhere. The only inhabitants of this planet are about ~30 species of goats (I know it should probably be breeds, but work with me). Each of these species of goats appears on hills all over the planet (and each goat lives its entire life on the particular hill that it was born on). The goats eat a mixture of grass and hay and alfalfa ($\alpha$), and this mixture is fixed per hill (but in principle it could have different mixtures at each hill).

The happy goats might look like:


In [59]:
display(Image("../img/goats-on-a-hill.jpg"))
print("Image Credit: Medena Rosa https://unsplash.com/@daisy66")


Image Credit: Medena Rosa https://unsplash.com/@daisy66

Let's assume that each species of goat grows to a known precise height on Earth, and that identifying each species is trivial.

The goat's Earth height - data might look like:

species height [m]
A 1.3020405
B 2.1000003222

It turns out that for each of the species of goat, the final height they grow to depends on one parameter only: the relative concentration of ($\alpha$), and these height measurements were based on the concentration of $\alpha$ on Earth. Let's center this relative measure on zero so that we're thinking about the difference in this value. So a $\Delta \alpha/\alpha_{Earth} = 0$ means that the concentration is the same as on Earth. I'll use daa as shorthand for $\Delta \alpha/\alpha_{Earth}$. Elaborating, a daa = 0.05 means that the concentration of alfalfa is 5% higher on this planet than Earth, while daa = -0.05 means that the concentration of alfalfa is 5% lower on this planet than Earth.

Further, there is a well-established and well-motivated theory that connects daa and the respective heights. Again, for each species, there is a parameter the ties in how sensitively their final height will be for a given daa. Let's call those q-values and for each species, there is a $q_a = 1000$ (strongly positive), $q_b = -750$ (strongly negative), and $q_c = 3$ (very insensitive to daa). This gives us a formula:

$$\Delta v_i = daa * q_i$$

for the vertical change in height for each species (i) for a given daa value and scaled by its corresponding q-value_i.

Hopefully I have confused neither you nor myself with this analogy so far.

These goats have another trick up their sleeve -- it turns out that they wear altimeters on their heads, which will send out altitude of their head to nanometer precision. Remember, however, that this planet has rolling hills, and we care to the nanometer scale about their relative heights not their altitudes.

In an attempt to measure the value of $\alpha$ on these different hills, scientists went to locations all over this planet and measured these goats. Device K made measurements in roughly the Northern hemisphere, and Device V made measurements in the roughly Southern hemisphere. The scientists took measurements of the goats they found on various hills by having the goats stand on a single plank of very smooth and very flat wood (Device K), or two pieces of very smooth and very flat wood (for Device V). Once the goats were standing on the wood, they recorded the altitude measurement of each goat, and moved on to the next hill.

A total of 293 measurements were taken: 140 measurements with K, and 153 measurements with V.

In principle, the data could look something like this:

site species altitude [m] error [m]
site_PHL957 A 6382507.0023141 0.0000003
site_PHL957 B 6382506.1312993 0.0000008
site_PHL957 F 6382507.1233276 0.0000003
site_PHL957 G 6382508.8100211 0.0000005
site_J2000 B 6382205.2741312 0.0000006
site_J2000 E 6382203.3621233 0.00000035
site_J2000 R 6382204.5678100 0.0000004

But, because the value they were most interested in was this daa value, they fit the respective altimeters in each of these measurements to give a single daa, and reported it like this:

site daa error
site_PHL957 -1.2e-6 2.1e-6
site_J2000 2.6e-6 5.2e-6

Obviously the altitude doesn't matter, but since the hypothesis was to check for a possible daa they fit a model to the relative heights for a best-fit daa.

Further, they found that looking at measurements from K, that the values of daa were slightly negative, while measurements from V found slightly positive. So they fit a dipole to the data (the alfalfa concentrations were highest in, say Europe and lowest in Australia). This gives a 1-parameter model for the relative concentration of $\alpha$ as a function of position on the planet (given by angle from the location from the peak daa location).

$$daa = A * \cos(\theta) + B)$$

with $A$ being the dipole coefficient, $B$ being the monopole [or constant offset term], and $\theta$ being the angle between the peak and the point under consideration.

Later, a scientist working with these devices found some problems with the pieces of wood used to bring the goats to the same altitude. The two-pieces of wood were particularly tricky to align and sometimes there would be a slight tilt to the levels. Fitting for the best-fit daa assumes that daa is the best model to fit discrepant relative heights.

The task at hand

The way that the scientists measure daa can actually give a best-fit for relative v-shifts. What that means is that there is a mode that allows them to re-run these measurements, getting $\Delta v$ and errors for each species. Is there a statistically rigorous way to go from altitudes to testing whether a global daa model is a better fit to the data, or whether two systematic effects are a more likely explanation of the data?

So, I can generate data in any combination of the two following ways:

A. generate data (with noise) from a model that has a non-zero daa value, and B. generate data (with noise) from a model that has a systematic error in the measuring device.

Note that for each system, one of the vshifts is set to zero (and the remaining vshifts are relative to that). The data will be available in the following form:


In [200]:
sys0a


Out[200]:
system #J2000 source wavelength vshift sigma qval rest_wave x
1 0 J000149-015940 Keck 5171.257827 0.000000 62.167126 270.0 1670.788610 -2.704805e+14
2 0 J000149-015940 Keck 4725.312789 62.007272 64.585653 47.0 1526.707631 -4.302337e+13
3 0 J000149-015940 Keck 5595.981560 -7.242025 55.136028 526.0 1808.013169 -5.702142e+14
4 0 J000149-015940 Keck 6364.330443 -130.203369 60.892969 -1110.0 2056.260038 1.368522e+15
5 0 J000149-015940 Keck 6382.826953 -64.161215 75.407935 -1168.0 2062.236100 1.444215e+15
6 0 J000149-015940 Keck 6394.983372 -132.597043 58.140072 -1360.0 2066.163734 1.684823e+15
7 0 J000149-015940 Keck 4978.316232 -99.474564 79.955683 -1165.0 1608.450852 1.123529e+15
8 0 J000149-015940 Keck 5291.395402 36.434070 62.883790 -20.0 1709.604020 2.050106e+13
9 0 J000149-015940 Keck 5390.280350 20.323841 74.996257 -1400.0 1741.552890 1.461892e+15
10 0 J000149-015940 Keck 5422.353633 -5.212064 55.970077 -700.0 1751.915490 7.352955e+14
11 0 J000149-015940 Keck 6271.096907 27.540317 57.799044 2488.0 2026.137090 -3.022525e+15
12 0 J000149-015940 Keck 6384.140359 103.311666 52.630599 1585.0 2062.660450 -1.960233e+15

And can be plotted in two "spaces", wavelength space (which would be where the goats were standing on the wooden plank space), or x space (the slope of which would be


In [201]:
Image("../img/xvswave.png")


Out[201]:

Given some data of the similar form, can you devise a test that quantifies the relative likelihood of either hypothesis A or hypothesis B?


In [209]:
import statsmodels.api as sm
import warnings
warnings.filterwarnings('ignore')

In [ ]:


In [321]:
def VLT_distortion(measured_wave, cutoff=10000.):
    slope1 = .0600
    intercept1 = -100
    slope2 = .160
    intercept2 = -1500
    if measured_wave < cutoff:
        return measured_wave * slope1 + intercept1
    else:
        return measured_wave * slope2 + intercept2

def Keck_distortion(measured_wave, cutoff=10000.):
    slope1 = .0600
    intercept1 = -100
    slope2 = .160
    intercept2 = -1500
    if measured_wave < cutoff:
        return measured_wave * slope1 + intercept1
    else:
        return measured_wave * slope2 + intercept2

    
def distorted_velocity(row, measured_wave):
    if row.source == "VLT":
        return VLT_distortion(measured_wave)
    elif row.source == "Keck":
        return Keck_distortion(measured_wave)

In [103]:
DIP_RA = 17.3
DIP_RA_ERR = 1.0
DIP_DEC = -61.0
DIP_DEC_ERR = 10.0
DIP_AMPLITUDE = 0.97e-5
DIP_AMPLITUDE_ERR = 0.21e-5 # average of asymmetric errors
DIP_MONOPOLE = -0.178e-5
DIP_MONOPOLE_ERR  = 0.084e-5

dipole = SkyCoord(DIP_RA, DIP_DEC, unit=(u.hourangle, u.deg))

In [104]:
def parse_j2000(name):
    return ' '.join([name[1:3], name[3:5], name[5:7], name[7:10], name[10:12], name[12:]])

In [102]:
def dipole_alpha(name):
    c = SkyCoord(parse_j2000(name), unit=(u.hourangle, u.deg))
    theta = float(c.separation(dipole).to_string(decimal=True))
    return (DIP_AMPLITUDE * np.cos(np.deg2rad(theta)) + DIP_MONOPOLE) * 1e6

In [138]:
# \omega_0 in cm^{-1}
# 62171.623009591349 = 1.0e8 / qvals.loc['FeII1608'].wave
# q in cm^{-1}
daa = -5e-6

In [ ]:


In [187]:
def generate_dataset(gen_dipole_alpha=True, 
                     wavelength_distortion=False,
                     seed=228,
                    ):
    df = pd.DataFrame(columns=['system',
                               '#J2000',
                               'source',
                               'wavelength',
                               'vshift', 
                               'sigma', 
                               'qval',
                               'rest_wave'
                              ])
    count = 0
    abs_count = 0
    for index, row in full_parse.iterrows():
        waves = []
        rest_waves = []
        vshifts = []
        qvals_list = []
        for tran in row['transition'].split():
            vshift = 0.0
            rest_wave = qvals.loc[codes.loc[tran].trans].wave
            measured_wave = rest_wave * (1 + row.zabs)
            qval = qvals.loc[codes.loc[tran].trans].qval
            if gen_dipole_alpha:
                vshift += shifted_velocity(dipole_alpha(row['#J2000']),
                                           qval,
                                           rest_wave)
            if wavelength_distortion:
                vshift += distorted_velocity(row, measured_wave)
            waves.append(measured_wave)
            rest_waves.append(rest_wave)
            vshifts.append(vshift)
            qvals_list.append(qval)
        vshifts = np.array(vshifts)
        errors = sigmas(vshifts)
        vshifts += errors * np.random.randn(len(vshifts))
        vshifts = vshifts - vshifts[0]
        for single in range(len(vshifts)):
            abs_count += 1
            df.loc[abs_count] = [int(index), #system
                                 row['#J2000'], #j2000
                                 row.source,
                                 waves[single], #wavelength 
                                 vshifts[single],
                                 errors[single],
                                 qvals_list[single], # qvalues
                                 rest_waves[single],
                                ]
    df['system'] = df.system.astype(int)
    df['x'] = -2.0 * c * df['qval'] * df['rest_wave']
    return df

In [188]:
full_parse.head()


Out[188]:
#J2000 zem zabs da eda sample source sigflag imrotator transition
0 J000149-015940 2.31 2.09510 0.34 7.27 B1 Keck 2 0 d g h i j k l s t u v w
1 J000149-015940 2.31 2.15390 36.05 39.54 B1 Keck 1 0 d f g l
2 J000322-260316 4.11 1.43420 -12.53 11.67 C Keck 1 1 b c p r
3 J000322-260316 4.11 3.38970 -78.43 35.48 C Keck 1 1 d g l m
4 J000520+052410 1.90 0.59137 -31.05 24.33 C Keck 1 0 b c n p q r

In [260]:
df = generate_dataset()

In [261]:
df.plot.scatter('wavelength', 'vshift')


Out[261]:
<matplotlib.axes._subplots.AxesSubplot at 0x116f7aa58>

In [323]:
df2 = generate_dataset(gen_dipole_alpha=False, wavelength_distortion=True)

In [324]:
df2.plot.scatter('wavelength', 'vshift')


Out[324]:
<matplotlib.axes._subplots.AxesSubplot at 0x11b195a58>

In [193]:
df.head(20)


Out[193]:
system #J2000 source wavelength vshift sigma qval rest_wave x
1 0 J000149-015940 Keck 5171.257827 0.000000 62.167126 270.0 1670.788610 -2.704805e+14
2 0 J000149-015940 Keck 4725.312789 62.007272 64.585653 47.0 1526.707631 -4.302337e+13
3 0 J000149-015940 Keck 5595.981560 -7.242025 55.136028 526.0 1808.013169 -5.702142e+14
4 0 J000149-015940 Keck 6364.330443 -130.203369 60.892969 -1110.0 2056.260038 1.368522e+15
5 0 J000149-015940 Keck 6382.826953 -64.161215 75.407935 -1168.0 2062.236100 1.444215e+15
6 0 J000149-015940 Keck 6394.983372 -132.597043 58.140072 -1360.0 2066.163734 1.684823e+15
7 0 J000149-015940 Keck 4978.316232 -99.474564 79.955683 -1165.0 1608.450852 1.123529e+15
8 0 J000149-015940 Keck 5291.395402 36.434070 62.883790 -20.0 1709.604020 2.050106e+13
9 0 J000149-015940 Keck 5390.280350 20.323841 74.996257 -1400.0 1741.552890 1.461892e+15
10 0 J000149-015940 Keck 5422.353633 -5.212064 55.970077 -700.0 1751.915490 7.352955e+14
11 0 J000149-015940 Keck 6271.096907 27.540317 57.799044 2488.0 2026.137090 -3.022525e+15
12 0 J000149-015940 Keck 6384.140359 103.311666 52.630599 1585.0 2062.660450 -1.960233e+15
13 1 J000149-015940 Keck 5269.500197 0.000000 72.663045 270.0 1670.788610 -2.704805e+14
14 1 J000149-015940 Keck 5875.022867 36.509975 55.055246 224.0 1862.780325 -2.501845e+14
15 1 J000149-015940 Keck 4815.083198 -119.710572 70.446110 47.0 1526.707631 -4.302337e+13
16 1 J000149-015940 Keck 5072.893142 -1.351393 66.368306 -1165.0 1608.450852 1.123529e+15
17 2 J000322-260316 Keck 6806.884386 0.000000 53.685667 212.0 2796.353786 -3.554501e+14
18 2 J000322-260316 Keck 6824.355116 80.854645 70.804039 121.0 2803.530982 -2.033955e+14
19 2 J000322-260316 Keck 5800.124117 -35.757468 73.070638 1505.0 2382.763995 -2.150147e+15
20 2 J000322-260316 Keck 6329.338960 -0.813990 73.200343 1370.0 2600.172114 -2.135863e+15

In [254]:
sys0a = df[df.system==0].copy()

In [255]:
sys0w = df2[df2.system==0].copy()

Plot vs wavelength, and vs X


In [256]:
sys0a


Out[256]:
system #J2000 source wavelength vshift sigma qval rest_wave x
1 0 J000149-015940 Keck 5171.257827 0.000000 62.167126 270.0 1670.788610 -2.704805e+14
2 0 J000149-015940 Keck 4725.312789 62.007272 64.585653 47.0 1526.707631 -4.302337e+13
3 0 J000149-015940 Keck 5595.981560 -7.242025 55.136028 526.0 1808.013169 -5.702142e+14
4 0 J000149-015940 Keck 6364.330443 -130.203369 60.892969 -1110.0 2056.260038 1.368522e+15
5 0 J000149-015940 Keck 6382.826953 -64.161215 75.407935 -1168.0 2062.236100 1.444215e+15
6 0 J000149-015940 Keck 6394.983372 -132.597043 58.140072 -1360.0 2066.163734 1.684823e+15
7 0 J000149-015940 Keck 4978.316232 -99.474564 79.955683 -1165.0 1608.450852 1.123529e+15
8 0 J000149-015940 Keck 5291.395402 36.434070 62.883790 -20.0 1709.604020 2.050106e+13
9 0 J000149-015940 Keck 5390.280350 20.323841 74.996257 -1400.0 1741.552890 1.461892e+15
10 0 J000149-015940 Keck 5422.353633 -5.212064 55.970077 -700.0 1751.915490 7.352955e+14
11 0 J000149-015940 Keck 6271.096907 27.540317 57.799044 2488.0 2026.137090 -3.022525e+15
12 0 J000149-015940 Keck 6384.140359 103.311666 52.630599 1585.0 2062.660450 -1.960233e+15

In [263]:
def plot_system(system, dataframe):
    plotdf = dataframe[dataframe.system == system]
    fig, (ax1, ax2) = plt.subplots(figsize=(12, 10), nrows=2)
    sns.regplot('wavelength', 'vshift', data=plotdf, ax=ax1)
    sns.regplot('x', 'vshift', data=plotdf, ax=ax2)
    fig.tight_layout()

In [346]:
plot_system(230, df)



In [363]:
sns.color_palette()


Out[363]:
[(0.2980392156862745, 0.4470588235294118, 0.6901960784313725),
 (0.3333333333333333, 0.6588235294117647, 0.40784313725490196),
 (0.7686274509803922, 0.3058823529411765, 0.3215686274509804),
 (0.5058823529411764, 0.4470588235294118, 0.6980392156862745),
 (0.8, 0.7254901960784313, 0.4549019607843137),
 (0.39215686274509803, 0.7098039215686275, 0.803921568627451)]

In [365]:
def plot_systems(system, dataframe1, dataframe2):
    plotdf1 = dataframe1[dataframe1.system == system]
    plotdf2 = dataframe2[dataframe2.system == system]
    fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(figsize=(14, 10), nrows=2, ncols=2)
    
    sns.regplot('wavelength', 'vshift', data=plotdf1, ax=ax1, color=sns.color_palette()[0])
    sns.regplot('x', 'vshift', data=plotdf1, ax=ax3, color=sns.color_palette()[1])
    ax1.set_title("X-generating process")
    
    sns.regplot('wavelength', 'vshift', data=plotdf2, ax=ax2, color=sns.color_palette()[0])
    sns.regplot('x', 'vshift', data=plotdf2, ax=ax4, color=sns.color_palette()[1])
    ax2.set_title("W-generating process")
    fig.tight_layout()


plot_systems(230, df, df2)



In [ ]:


In [267]:
plot_system(2, df)



In [ ]:


In [257]:
def plot_vs_x(system):
    fig, (ax1, ax2) = plt.subplots(figsize=(12, 10), nrows=2)
#     system.plot.scatter('wavelength', 'vshift', ax=ax, s=50)
    sns.regplot('wavelength', 'vshift', data=system, ax=ax1)
    sns.regplot('x', 'vshift', data=system, ax=ax2)
    fig.tight_layout()
#     x = -2 * c * row.qval * row.rest_wave
#     v / (del_alpha * 1e-14) = x

In [258]:
plot_vs_x(sys0a)



In [335]:
plot_vs_x()



In [ ]:


In [207]:
g


Out[207]:
system #J2000 source wavelength vshift sigma qval rest_wave x
1970 292 J235034-432559 VLT 7819.164456 0.000000 53.728017 212.0 2796.353786 -3.554501e+14
1971 292 J235034-432559 VLT 6662.684683 -112.862286 60.472046 1505.0 2382.763995 -2.150147e+15
1972 292 J235034-432559 VLT 5186.137211 48.080248 55.884143 458.0 1854.708966 -5.093214e+14
1973 292 J235034-432559 VLT 5208.706345 21.494520 56.604046 224.0 1862.780325 -2.501845e+14
1974 292 J235034-432559 VLT 4268.979878 2.187284 57.391969 47.0 1526.707631 -4.302337e+13

In [217]:
g.sigma


Out[217]:
1970    53.728017
1971    60.472046
1972    55.884143
1973    56.604046
1974    57.391969
Name: sigma, dtype: float64

In [ ]:
model = sm.OLS(g.vshift, g.x, )
results = model.fit()
print(results.rsquared_adj)

In [284]:
g.sigma


Out[284]:
1970    5.138509
1971    7.539942
1972    7.301934
1973    5.918203
1974    6.017853
Name: sigma, dtype: float64

In [ ]:
# mod_wls = sm.WLS(y, X, weights=1./w)
# res_wls = mod_wls.fit()

In [294]:
X = sm.add_constant(g.wavelength)
model = sm.WLS(g.vshift, X, weights=1.0/g.sigma)
results = model.fit()
np.sum((g.vshift - results.fittedvalues)**2.0 / (g.sigma) ** 2.0) / (len(g.sigma) - 3.0)


Out[294]:
24.898503105984808

In [295]:
X = sm.add_constant(g.x)
model = sm.WLS(g.vshift, X, weights=1.0/g.sigma)
results = model.fit()
np.sum((g.vshift - results.fittedvalues)**2.0 / (g.sigma) ** 2.0) / (len(g.sigma) - 3.0)


Out[295]:
10.8793118893269

In [334]:
plt.scatter(g.wavelength, g.vshift)
plt.scatter(g.x, g.vshift)


Out[334]:
<matplotlib.collections.PathCollection at 0x11bb356d8>

In [274]:
results.summary()


Out[274]:
OLS Regression Results
Dep. Variable: vshift R-squared: 0.077
Model: OLS Adj. R-squared: -0.153
Method: Least Squares F-statistic: 0.3358
Date: Wed, 08 Feb 2017 Prob (F-statistic): 0.593
Time: 15:34:03 Log-Likelihood: -23.057
No. Observations: 5 AIC: 48.11
Df Residuals: 4 BIC: 47.72
Df Model: 1
Covariance Type: nonrobust
coef std err t P>|t| [95.0% Conf. Int.]
wavelength -0.0012 0.002 -0.579 0.593 -0.007 0.004
Omnibus: nan Durbin-Watson: 2.521
Prob(Omnibus): nan Jarque-Bera (JB): 1.645
Skew: -1.403 Prob(JB): 0.439
Kurtosis: 3.126 Cond. No. 1.00

In [317]:
df.system.nunique()


Out[317]:
293

In [330]:
alpha = []
distortion = []
for (index, g) in df.groupby('system'):
    X = sm.add_constant(g.x)
    model = sm.WLS(g.vshift, X, weights=1.0/g.sigma)
    results = model.fit()
    chisq = np.sum((g.vshift - results.fittedvalues)**2.0 / (g.sigma) ** 2.0)
#     if chisq < 1e7:
    alpha.append(chisq)
    
    X = sm.add_constant(g.wavelength)
    model = sm.WLS(g.vshift, X, weights=1.0/g.sigma)
    results = model.fit()
    chisq = np.sum((g.vshift - results.fittedvalues)**2.0 / (g.sigma) ** 2.0)
#     if chisq < 1e7:
    distortion.append(chisq)

alpha = np.array(alpha)
distortion = np.array(distortion)
fig, ax = plt.subplots(figsize=(12, 8))
ax.hist(alpha / distortion, bins=np.linspace(0, 5, 50), label='Alpha', alpha=0.5)
# ax.set_xlim(0, 2.0, )
# ax.legend()
# fig.tight_layout()

alpha = []
distortion = []
for (index, g) in df2.groupby('system'):
    X = sm.add_constant(g.x)
    model = sm.WLS(g.vshift, X, weights=1.0/g.sigma)
    results = model.fit()
    chisq = np.sum((g.vshift - results.fittedvalues)**2.0 / (g.sigma) ** 2.0)
    if chisq < 1e7:
        alpha.append(chisq)
    
    X = sm.add_constant(g.wavelength)
    model = sm.WLS(g.vshift, X, weights=1.0/g.sigma)
    results = model.fit()
    chisq = np.sum((g.vshift - results.fittedvalues)**2.0 / (g.sigma) ** 2.0)
    if chisq < 1e10:
        distortion.append(chisq)

alpha = np.array(alpha)
distortion = np.array(distortion)
# ax.hist(alpha, alpha=0.5, label='Alpha hypothesis')
# ax.hist(distortion, alpha=0.5, label='Distortion hypothesis')
ax.hist(alpha / distortion, bins=np.linspace(0, 5, 50), label='Wavelength', alpha=0.5)
ax.set_xlim(0, 5.0, )
ax.legend()
fig.tight_layout()



In [332]:
alpha = []
distortion = []
for (index, g) in df.groupby('system'):
    X = sm.add_constant(g.x)
    model = sm.WLS(g.vshift, X, weights=1.0/g.sigma)
    results = model.fit()
    chisq = np.sum((g.vshift - results.fittedvalues)**2.0 / (g.sigma) ** 2.0)
#     if chisq < 1e7:
    alpha.append(chisq)
    
    X = sm.add_constant(g.wavelength)
    model = sm.WLS(g.vshift, X, weights=1.0/g.sigma)
    results = model.fit()
    chisq = np.sum((g.vshift - results.fittedvalues)**2.0 / (g.sigma) ** 2.0)
#     if chisq < 1e7:
    distortion.append(chisq)

alpha = np.array(alpha)
distortion = np.array(distortion)
fig, ax = plt.subplots(figsize=(12, 8))
ax.hist(alpha / distortion, bins=np.linspace(0, 5, 50), label='Alpha', alpha=0.5)
# ax.set_xlim(0, 2.0, )
# ax.legend()
# fig.tight_layout()

alpha = []
distortion = []
for (index, g) in df2.groupby('system'):
    X = sm.add_constant(g.x)
    model = sm.WLS(g.vshift, X, weights=1.0/g.sigma)
    results = model.fit()
    chisq = np.sum((g.vshift - results.fittedvalues)**2.0 / (g.sigma) ** 2.0)
    if chisq < 1e7:
        alpha.append(chisq)
    
    X = sm.add_constant(g.wavelength)
    model = sm.WLS(g.vshift, X, weights=1.0/g.sigma)
    results = model.fit()
    chisq = np.sum((g.vshift - results.fittedvalues)**2.0 / (g.sigma) ** 2.0)
    if chisq < 1e10:
        distortion.append(chisq)

alpha = np.array(alpha)
distortion = np.array(distortion)
# ax.hist(alpha, alpha=0.5, label='Alpha hypothesis')
# ax.hist(distortion, alpha=0.5, label='Distortion hypothesis')
ax.hist(alpha / distortion, bins=np.linspace(0, 5, 50), label='Wavelength', alpha=0.5)
ax.set_xlim(0, 5.0, )
ax.legend()
fig.tight_layout()



In [329]:
alpha = []
distortion = []
for (index, g) in df2.groupby('system'):
    X = sm.add_constant(g.x)
    model = sm.WLS(g.vshift, X, weights=1.0/g.sigma)
    results = model.fit()
    chisq = np.sum((g.vshift - results.fittedvalues)**2.0 / (g.sigma) ** 2.0)
    if chisq < 1e7:
        alpha.append(chisq)
    
    X = sm.add_constant(g.wavelength)
    model = sm.WLS(g.vshift, X, weights=1.0/g.sigma)
    results = model.fit()
    chisq = np.sum((g.vshift - results.fittedvalues)**2.0 / (g.sigma) ** 2.0)
    if chisq < 1e10:
        distortion.append(chisq)

alpha = np.array(alpha)
distortion = np.array(distortion)
fig, ax = plt.subplots(figsize=(12, 8))

ax.hist(alpha, alpha=0.5, 
                bins=np.linspace(0, 5, 50),
        label='Alpha hypothesis')
ax.hist(distortion, alpha=0.5, 
                        bins=np.linspace(0, 5, 50),
        label='Distortion hypothesis')
# ax.hist(alpha / distortion, 
#         bins=np.linspace(0, 5, 50), 
#         label='Wavelength',
#         alpha=0.5)
# ax.set_xlim(0, 5.0, )
ax.legend()
fig.tight_layout()



In [ ]:


In [301]:
# 32
# 32
# 167
# 167
# 176
# 176
# 210
# 210
# 251
# 251
df[df.system==32]


Out[301]:
system #J2000 source wavelength vshift sigma qval rest_wave x
195 32 J034943-381031 Keck 6144.540203 0.000000 5.238996 47.0 1526.707631 -4.302337e+13
196 32 J034943-381031 Keck 6473.532144 -13.956842 6.842105 -1165.0 1608.450852 1.123529e+15

In [242]:
sm.add_constant(g.x)


Out[242]:
const x
1970 1.0 -3.554501e+14
1971 1.0 -2.150147e+15
1972 1.0 -5.093214e+14
1973 1.0 -2.501845e+14
1974 1.0 -4.302337e+13

In [305]:
distortion


Out[305]:
[1.3918058677651755,
 0.85794330672576469,
 2.2433336634264869,
 0.16363763447977964,
 0.48549924321780713,
 0.57312365660583908,
 0.39097068502115173,
 1.0429737671215054,
 1.0049174522842399,
 1.9697098311674008,
 0.25153803643288458,
 1.1472806566903568,
 1.5423659817475395,
 0.56714970781840812,
 0.11332662277097248,
 0.79202774376165463,
 0.1908214077103709,
 2.1732523960984231,
 1.5460171727697634,
 0.5641499051373805,
 0.3072237392120436,
 1.2990827262925528,
 1.3553811236220445,
 0.034374882951023929,
 9.4796681206651012,
 0.55979999706688144,
 1.6668377098289746,
 0.93139713384270528,
 1.6032659688628754,
 0.17947053796997431,
 0.12078599479165324,
 0.49245272605460605,
 0.83609156613368418,
 0.41776210071173636,
 0.58776693914306977,
 1.1901333233538687,
 0.64241063941261456,
 1.0391944771335555,
 0.76362751258744088,
 0.001558165534518047,
 2.1231982093791646,
 0.46687248467022696,
 1.156962355675007,
 1.2852502690662813,
 3.7625229217291509,
 0.63612405474087219,
 0.31113832558507248,
 0.065362360726511984,
 8.1781366170723508,
 1.1872266456777547,
 2.9880533398797087,
 0.12463949888372232,
 0.92649183794396828,
 0.43794960810254907,
 1.0226977572353191,
 1.0035674188393477,
 0.68994972234684782,
 0.26389934969008894,
 0.82254601016768447,
 2.2164972920669999,
 0.50519846210149799,
 0.074102938080351427,
 0.52568321986788336,
 1.2038641015462888,
 0.53981019443510003,
 1.046279929491686,
 0.4306579386831475,
 0.22365124993415067,
 0.00037630088283854677,
 1.6303338149052118,
 0.15120268994516445,
 0.17086190984585317,
 3.8136561447845421,
 0.38958207984101589,
 1.143753773786869,
 0.57078252937541951,
 0.60690879899721972,
 2.3779080640419892,
 0.58752769770219271,
 1.1177343852037942,
 1.6943148664456342,
 0.74218541349237099,
 0.78474698777355778,
 1.206433921786723,
 1.0370120216518508,
 1.5845247147574988,
 0.57229123577220886,
 0.67618509266590388,
 0.091710620145820723,
 1.5444997829051257,
 0.58738511786948022,
 0.4756246653464628,
 0.93381454874476399,
 2.7509088073246608,
 0.89423437234483949,
 0.678174396826416,
 3.4659464106618003,
 1.5271228016518579,
 1.2040264534631238,
 0.61293533007231893,
 0.05966355004485991,
 0.12383816021618217,
 0.12564597823916424,
 0.6789204441692952,
 0.70902815077760928,
 1.2342903927511526,
 0.17828543665810739,
 0.215691532625778,
 0.92300732133192653,
 0.65717180031798084,
 0.81198194550167291,
 1.6848116492822713,
 1.0949601496969905,
 0.68103811825075267,
 1.087543262810009,
 0.79018342380976703,
 1.6480709446568524,
 0.20721476333581068,
 0.97871261594070136,
 1.4985773779142335,
 0.71270342687169241,
 1.6737730256573713,
 2.1989561102286102,
 1.4616780862067875,
 0.66361400557580985,
 1.8014019702624497,
 10.049045449550666,
 0.67061718507126689,
 1.3067451986614336,
 0.66617845998778347,
 0.31812615440605435,
 0.4243060381334835,
 1.1579095766178564,
 1.3835883556018338,
 0.81829016187837988,
 0.14381346356811719,
 1.7286516074732565,
 0.56301161428180935,
 1.8449370489501051,
 1.0770271913902865,
 0.5099945938660545,
 0.64185305186362385,
 0.29181431642815203,
 1.1112337857224186,
 1.446815528842591,
 0.24807342694643189,
 0.65863278300595818,
 1.2317518684506736,
 0.80100528662000081,
 0.81978804449240106,
 0.88775911282724684,
 0.3878667831689015,
 1.4922938941276229,
 1.7991165486023033,
 0.55845410275326013,
 1.1438894528935901,
 2.0164419825808992,
 0.4017460855636702,
 0.77250947271330084,
 1.6497634027031953,
 0.43746623677512869,
 0.75044274001667477,
 0.13786440496895735,
 0.20004787940785307,
 5.0476574250823365,
 0.79630695433706133,
 1.2819450225103519,
 1.6587918028031736,
 0.15742632393335868,
 0.53057177736304739,
 0.50677622350810614,
 5.4830144677050212,
 0.50542445661026947,
 1.8096715757496897,
 0.78927859391499555,
 0.45141528019048704,
 1.2910510892034393,
 2.1634436647705733,
 1.2106659358308762,
 0.38870375146441943,
 0.32089070767894057,
 0.20435711944436383,
 4.0887287314854337,
 0.92820918796553409,
 1.9400625044761683,
 1.3997230504563882,
 0.31582378453009835,
 1.748006607184428,
 1.06012034895833,
 0.71016059851104874,
 1.1292441148678909,
 1.2501755609998042,
 0.56843169244932523,
 1.8331452124565248,
 1.2733391072469513,
 1.8291454268048843,
 3.32108428976162,
 2.2071022214995506,
 0.038520986326191664,
 5.4202149463257268,
 1.681509140606319,
 0.76996300319430866,
 0.58532263132693996,
 0.48280978742124075,
 0.67687993414905656,
 0.61693146297161072,
 0.55942726182067759,
 1.4630771073484277,
 0.46378351382443234,
 0.45519445080215126,
 1.0163071459777269,
 0.38373267968399755,
 0.81655901319647561,
 1.1773995388013787,
 0.61227616062486923,
 1.1216388516700533,
 1.1498765883971045,
 1.12299309658514,
 1.2751794830013792,
 0.81576270527986627,
 0.41197741457519205,
 0.068239923042728731,
 3.1036973288723395,
 2.2494008822975573,
 0.36436648691230694,
 1.0747911593128352,
 1.7771534231948467,
 0.39619953581470074,
 1.2401389914048837,
 0.091993108136547402,
 1.0441943958021378,
 0.50278030581042654,
 0.34362506285676703,
 1.4330795557029679,
 0.4972966145130906,
 0.69819765819896384,
 0.46032815416221912,
 0.63844488998132631,
 0.9764551058479346,
 0.87527229894009073,
 0.34543661320544816,
 1.1561218783984615,
 0.42722572091505412,
 0.84130040132024286,
 0.58219466912531836,
 1.4048981705027779,
 0.29601920772843321,
 0.60657116345102513,
 1.1976357195440974,
 0.87782796380124473,
 0.0046652228046828031,
 1.0010466733441106,
 0.35760275742745118,
 1.2320374246239676,
 0.91562725099339526,
 0.040829182478581326,
 3.4561664343901728,
 0.50225134569311181,
 1.63588203229366,
 1.3811691178197527,
 0.98102681231681288,
 0.98334363336366226,
 1.8607170218999305,
 2.0192655745910111,
 0.0089092629786225192,
 1.676508373311687,
 0.87112308064081811,
 0.76374042096792927,
 0.54078340977609207,
 0.53630849336907882,
 0.66518701892470966,
 0.60400049957236801,
 0.80880509169363268,
 0.17223886037493016,
 0.58600144352429584,
 0.56701587189666469,
 0.72769353865847108,
 0.26867588042889101,
 1.2360691460822062,
 0.77496736875065864,
 0.14835676659847957,
 1.5440330095083299,
 0.0024389427536848172,
 0.022554610701042501,
 0.64506940700048576,
 4.6926196249807459,
 0.74026950383887802,
 0.85739717859931541]

In [239]:
results.summary()


Out[239]:
OLS Regression Results
Dep. Variable: vshift R-squared: 0.134
Model: OLS Adj. R-squared: -0.082
Method: Least Squares F-statistic: 0.6206
Date: Tue, 07 Feb 2017 Prob (F-statistic): 0.475
Time: 15:50:35 Log-Likelihood: -35.927
No. Observations: 5 AIC: 73.85
Df Residuals: 4 BIC: 73.46
Df Model: 1
Covariance Type: nonrobust
coef std err t P>|t| [95.0% Conf. Int.]
wavelength -0.0211 0.027 -0.788 0.475 -0.095 0.053
Omnibus: nan Durbin-Watson: 1.479
Prob(Omnibus): nan Jarque-Bera (JB): 0.634
Skew: 0.736 Prob(JB): 0.728
Kurtosis: 2.064 Cond. No. 1.00

In [232]:
alpha = []
distortion = []
for (index, g) in df2.groupby('system'):
    model = sm.OLS(g.vshift, g.x, )
    model.weights = g.sigma
    results = model.fit()
    alpha.append(results.rsquared_adj)
    
    model = sm.OLS(g.vshift, g.wavelength, )
    model.weights = g.sigma
    results = model.fit()
    distortion.append(results.rsquared_adj)

fig, ax = plt.subplots(figsize=(12, 8))
ax.hist(alpha, alpha=0.5, label='Alpha hypothesis')
ax.hist(distortion, alpha=0.5, label='Distortion hypothesis')
ax.legend()
fig.tight_layout()



In [ ]:
plt.hi

In [250]:
def sigmas(vshifts):
    return np.random.rand(len(vshifts)) * 3.0 + 5.0
 >>       4.2546172000SZ  0.0000000000     0.17171      0.14118   1.000000SN  0.000000   0.0000E+00QA  0.0000E+00  0 !
 >>       2.5595850000SZ  0.0000000000     0.14700      0.10265   1.000000SN  0.000000   0.0000E+00QA  0.0000E+00  0 !
 >>       2.6974513000SZ  0.0000000000    -0.17570      0.05530   1.000000SN  0.000000   0.0000E+00QA  0.0000E+00  0 !
 >>       3.1044038000SZ  0.0000000000    -1.02231      0.31933   1.000000SN  0.000000   0.0000E+00QA  0.0000E+00  0 !
 >>       3.1223774000SZ  0.0000000000    -0.62144      0.44735   1.000000SN  0.000000   0.0000E+00QA  0.0000E+00  0 !

In [ ]:


In [ ]:


In [ ]:

Alpha measurements as proxies for wavelength distortions

We have many measurements of $\alpha$ on both the Keck and VLT telescopes. Measurements with spectrographs both telescopes reveal statistically significant (though small in absolute terms) differences between the expected positions of absorption lines and the measured positions.

Hypothesis tests or statistical inference

There are many possible hypotheses that could explain these differences. To list just two:

  • the value of the fine-structure constant ($\alpha$) has changed
  • there are velocity distortions in the instrument.

We can consider any velocity shift measurements as a hypothesis test. Previous studies started with the assumption that any shift in absorption lines would come as a result of a change in $\alpha$. We have the reported $\frac{\Delta \alpha}{\alpha}$ values for both the Keck and VLT samples. If we invert the hypothesis: use the measured alpha values as a system of velocity shifts, we can effectively discriminate which hypothesis best fits the data.

Simulations

The goal will be to simulate what the velocity shifts look like if the dipole model is correct, and compare that to what we currently see.

Discussion

The end goal of this analysis is ultimately a change in how $\frac{\Delta \alpha}{\alpha}$ is measured. The proposed recipe is:

  1. Create the best fit velocity structure as usual.
  2. Fit for relative velocity shifts between all lines (vpfit already allows for this).
  3. Combine all velocity shifts for all measured systems with a particular telescope.
  4. Use standard hypothesis statistical tests to discriminate the most likely hypothesis: a wavelength distortion model, or a change in the fine-structure constant.

This will likely have rather low power for any single absorption system, but for ensembles, I think that it's likely the only case

References


In [1]:
%matplotlib inline
%config InlineBackend.figure_format='retina'
from __future__ import absolute_import, division, print_function
import matplotlib as mpl
from matplotlib import pyplot as plt
from matplotlib.pyplot import GridSpec
from itertools import combinations, islice, takewhile
import seaborn as sns
import mpld3
import numpy as np
import pandas as pd
import scipy.sparse
from scipy.constants import c
import os, sys
import warnings
from astropy import units as u
from astropy.coordinates import SkyCoord

sns.set_context('poster', font_scale=1.3)

In [2]:
from ipywidgets import interact, FloatSlider, SelectMultiple, interactive

In [4]:
vltqs = pd.read_csv('../data/vlt-transitions-new.tsv', sep='\t')
vltqs['trans'] = vltqs['transition'] + vltqs.four.astype(str)

keckqs = pd.read_csv("../data/keck-transitions-new.tsv", sep='\t')
keckqs['trans'] = keckqs.transition + keckqs.four.astype(str)

qvals = pd.read_csv("../data/qvalues.txt", sep=' ', index_col=0)

codes = pd.concat([keckqs.set_index('code')[['trans', 'wavelength']],
                   vltqs.set_index('code')[['trans', 'wavelength']]])

full_parse = pd.read_csv("../data/full-parse.tsv", sep='\t')

In [3]:
def shifted_velocity(del_alpha, q, lamb):
#     TODO: have I flipped the negative sign?
    # vj =v0 + ∆α xj, xj =−2cqjλ0j,
    x = -2 * c * q * lamb
    return del_alpha * x * 1e-8 * 1e-6

def observed_shifts(telescope='VLT'):
    waves = []
    shifts = []
    for index, row in full_parse[full_parse.source.eq(telescope)].iterrows():
        for tran in row['transition'].split():
            rest_wave = qvals.loc[codes.loc[tran].trans].wave
            measured_wave = rest_wave * (1 + row.zabs)
            qval = qvals.loc[codes.loc[tran].trans].qval
            waves.append(measured_wave)
            shifts.append(shifted_velocity(row.da, qval, rest_wave))
    return np.array(waves), np.array(shifts)

In [5]:
species = set([spec[0] for spec in qvals.index.str.split('I')])
colors = sns.color_palette(n_colors=len(species))
plot_colors = {}
for index, specie in enumerate(sorted(species)):
    plot_colors[specie] = colors[index]

In [11]:
def plot_absorption(specie=['Al', 'Mg'],
                    z=1.3,
                    daa=0.05,
                   ):
    fig, ax = plt.subplots(figsize=(12, 6))
    ax.hlines(1, 0, 10000)
    for tran, row in qvals[qvals.index.str.startswith(tuple(specie))].iterrows():
        ax.vlines((1.0 + z) * row.wave + row.qval * ((daa + 1)**2.0-1.0), 
                      0, 1, color=plot_colors[tran[:2]])
        ax.vlines((1.0 + z) * row.wave, 
                      0, 1, color=plot_colors[tran[:2]], alpha=0.2)
        
    ax.set_xlim(3000, 8e3)
    ax.set_ylim(0, 1.1)
    ax.set_ylabel("Normalized Flux")
    ax.set_xlabel(r"Wavelength $\AA$")
    
    for spec in specie:
        ax.vlines(-1, -1, 0, color=plot_colors[spec], label=spec)
    box = ax.get_position()
    ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])
    ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
    fig.tight_layout()

plot_absorption(specie=['Al', 'Mg', 'Fe', 'Ni'], )



In [51]:
def plot_shift(specie=['Al', 'Mg'],
                    z=1.3,
                    daa=0.05,
                   ):
    fig, ax = plt.subplots(figsize=(12, 6))
    ax.hlines(0, 0, 10000)
    for tran, row in qvals[qvals.index.str.startswith(tuple(specie))].iterrows():
        ax.scatter((1.0 + z) * row.wave, 
#                    row.qval * ((daa + 1)**2.0-1.0),
                   shifted_velocity(daa, row.qval, (1.0 + z) * row.wave),
                   color=plot_colors[tran[:2]], zorder=3)
        
    ax.set_xlim(3000, 8e3)
    ax.set_ylim(-200, 200)
    ax.set_ylabel("Velocity Shift [m/s]")
    ax.set_xlabel(r"Observed Wavelength $\AA$")
    
    for spec in specie:
        ax.vlines(-1, -1, 0, color=plot_colors[spec], label=spec, zorder=-3)
    box = ax.get_position()
    ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])
    ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
    fig.tight_layout()

plot_shift(specie=['Al', 'Mg', 'Fe', 'Ni'], )



In [52]:
redshift_slider = FloatSlider(value=2.1, min=0.0, max=3.5)
alpha_slider = FloatSlider(value=0.0, min=-5.0, max=5.0, step=0.1)
species_select = SelectMultiple(options=['Al', 'Cr', 'Fe', 'Mg', 'Mn', 'Ni', 'Si'],
                                description="Species",
                                value=['Al', 'Fe']
                               )
# w = interactive(plot_absorption, 
#                 specie=species_select,
#                 z=redshift_slider,
#                 daa=alpha_slider,
#                )
shift_interactive = interactive(plot_shift, 
                specie=species_select,
                z=redshift_slider,
                daa=alpha_slider,
               )

In [53]:
shift_interactive



In [42]:
full_parse.head()


Out[42]:
#J2000 zem zabs da eda sample source sigflag imrotator transition
0 J000149-015940 2.31 2.09510 0.34 7.27 B1 Keck 2 0 d g h i j k l s t u v w
1 J000149-015940 2.31 2.15390 36.05 39.54 B1 Keck 1 0 d f g l
2 J000322-260316 4.11 1.43420 -12.53 11.67 C Keck 1 1 b c p r
3 J000322-260316 4.11 3.38970 -78.43 35.48 C Keck 1 1 d g l m
4 J000520+052410 1.90 0.59137 -31.05 24.33 C Keck 1 0 b c n p q r

In [15]:
def plot_shifts(telescope='VLT'):
    fig, ax = plt.subplots(figsize=(12, 6))
    w, s = observed_shifts(telescope=telescope)
    sns.regplot(w, s, lowess=True, scatter_kws={'alpha':0.2}, ax=ax)
    ax.set_ylim(-200, 200)
    ax.set_xlabel(r"Observed Wavelength [$\AA$]")
    ax.set_ylabel("Velocity [m/s]")
    ax.set_title(telescope)
    fig.tight_layout()

In [16]:
plot_shifts(telescope='VLT')



In [17]:
plot_shifts(telescope='Keck')



In [59]:
def plot_all(title='Keck + VLT'):
    fig, ax = plt.subplots(figsize=(12, 8))
    telescope='VLT'
    w, s = observed_shifts(telescope=telescope)
    sns.regplot(w, s, lowess=True, scatter_kws={'alpha':0.5}, ax=ax, label=telescope)
    telescope='Keck'
    w, s = observed_shifts(telescope=telescope)
    sns.regplot(w, s, lowess=True, scatter_kws={'alpha':0.5}, ax=ax, label=telescope)
    ax.set_xlim(2000, 10000)
    ax.set_ylim(-200, 200)
    ax.legend()
    ax.set_xlabel(r"Observed Wavelength [$\AA$]")
    ax.set_ylabel("Velocity [m/s]")
    ax.set_title(title)
    fig.tight_layout()

In [60]:
plot_all()



In [61]:
def observed_shifts_by_sample(sample='A'):
    waves = []
    shifts = []
    for index, row in full_parse[full_parse['sample'] == sample].iterrows():
        for tran in row['transition'].split():
            rest_wave = qvals.loc[codes.loc[tran].trans].wave
            measured_wave = rest_wave * (1 + row.zabs)
            qval = qvals.loc[codes.loc[tran].trans].qval
            waves.append(measured_wave)
            shifts.append(shifted_velocity(row.da, qval, rest_wave))
    return np.array(waves), np.array(shifts)

def plot_samples():
    fig, ax = plt.subplots(figsize=(12, 8))
    telescope='VLT'
    for sample in sorted(full_parse['sample'].unique()):
        w, s = observed_shifts_by_sample(sample=sample)
        sns.regplot(w, s, lowess=True, scatter_kws={'alpha':0.5}, ax=ax, label=sample)
    ax.set_ylim(-400, 400)
    ax.set_xlim(2000, 10000)
    ax.legend()
#     frame = ax.get_frame()
#     frame.set_alpha(1.0)
    ax.set_xlabel(r"Observed Wavelength [$\AA$]")
    ax.set_ylabel("Velocity [m/s]")
    ax.set_title('Samples')
    fig.tight_layout()

In [62]:
plot_samples()


Simulations

Generate velocity shifts for the dipole hypothesis.


In [54]:
w



In [74]:
def plot_sim(telescope='VLT', use_dipole=True):
    fig, ax = plt.subplots(figsize=(12, 6))
    w, s = observed_shifts(telescope=telescope)
    sns.regplot(w, s, lowess=True, scatter_kws={'alpha':0.05}, ax=ax, label='Implied by Measured VLT')
    w, s = simulated_shifts(telescope=telescope, use_dipole=use_dipole)
    sns.regplot(w, s, lowess=True, scatter_kws={'alpha':0.05}, ax=ax, label='Implied by Dipole VLT')
    ax.set_ylim(-200, 200)
    ax.legend()
    ax.set_xlabel(r"Observed Wavelength [$\AA$]")
    ax.set_ylabel("Velocity [m/s]")
    ax.set_title('Simulated velocity shifts for ' + telescope)
    fig.tight_layout()

In [75]:
plot_sim()



In [ ]:


In [ ]:


In [ ]:


In [ ]:


In [ ]:


In [ ]:


In [ ]:


In [ ]:


In [69]:
def simulated_shifts(telescope='VLT', use_dipole=True):
    waves = []
    shifts = []
    for index, row in full_parse[full_parse.source.eq(telescope)].iterrows():
        for tran in row['transition'].split():
            rest_wave = qvals.loc[codes.loc[tran].trans].wave
            measured_wave = rest_wave * (1 + row.zabs)
            qval = qvals.loc[codes.loc[tran].trans].qval
            waves.append(measured_wave)
#             da = row.da
            if use_dipole:
                da = dipole_alpha(row['#J2000'])
            else:
                da = -3.0
            shifts.append(shifted_velocity(da, qval, rest_wave))
    return np.array(waves), np.array(shifts)

plot_sim(use_dipole=False)



In [ ]:


In [ ]:

Hypothesis test

I think that a non-parametric test for $\alpha$ would be to fit for all velocity shifts as independent shifts.

The general procedure would be thus:

  • fit velocity shifts per wavelength region (with errors).

In [80]:



Out[80]:
#J2000 zem zabs da eda sample source sigflag imrotator transition
152 J004131-493611 3.24 2.2485 -12.3 6.72 D VLT 3 0 j1 j2 j3 j6 j7 j8 c1 d1 d2 e2 h1 h2 h3 l1 l2 k...

In [88]:
row['transition'].values


Out[88]:
array(['j1 j2 j3 j6 j7 j8 c1 d1 d2 e2 h1 h2 h3 l1 l2 k1 i1 i2'], dtype=object)

In [99]:
telescope = 'VLT'
row = full_parse[full_parse.source.eq(telescope)].sample(1,
                                                   random_state=2
                                                  ).iloc[0]
waves = []
shifts = []
for tran in row['transition'].split():
    rest_wave = qvals.loc[codes.loc[tran].trans].wave
    measured_wave = rest_wave * (1 + row.zabs)
    qval = qvals.loc[codes.loc[tran].trans].qval
    waves.append(measured_wave)
#             da = row.da
#     if use_dipole:
    da = dipole_alpha(row['#J2000'])
#     else:
#         da = -3.0
    shifts.append(shifted_velocity(da, qval, rest_wave))
wav, shift = np.array(waves), np.array(shifts)



fig, ax = plt.subplots(figsize=(12, 6))
ax.scatter(wav, shift)
ax.set_ylim(-200, 200)
# ax.legend()
ax.set_xlabel(r"Observed Wavelength [$\AA$]")
ax.set_ylabel("Velocity [m/s]")
ax.set_title('Simulated velocity shifts for ' + telescope)
fig.tight_layout()



In [ ]:
telescope = 'VLT'
row = full_parse[full_parse.source.eq(telescope)].sample(1,
                                                   random_state=2
                                                  ).iloc[0]
waves = []
shifts = []
for tran in row['transition'].split():
    rest_wave = qvals.loc[codes.loc[tran].trans].wave
    measured_wave = rest_wave * (1 + row.zabs)
    qval = qvals.loc[codes.loc[tran].trans].qval
    waves.append(measured_wave)
#             da = row.da
#     if use_dipole:
    da = dipole_alpha(row['#J2000'])
#     else:
#         da = -3.0
    shifts.append(shifted_velocity(da, qval, rest_wave))
wav, shift = np.array(waves), np.array(shifts)



fig, ax = plt.subplots(figsize=(12, 6))
ax.scatter(wav, shift)
ax.set_ylim(-200, 200)
# ax.legend()
ax.set_xlabel(r"Observed Wavelength [$\AA$]")
ax.set_ylabel("Velocity [m/s]")
ax.set_title('Simulated velocity shifts for ' + telescope + " " + str(np.round(da, 2)))
fig.tight_layout()

In [113]:
telescope = 'VLT'
row = full_parse[full_parse.source.eq(telescope)].sample(1,
                                                   random_state=3
                                                  ).iloc[0]
waves = []
shifts = []
for tran in row['transition'].split():
    rest_wave = qvals.loc[codes.loc[tran].trans].wave
    measured_wave = rest_wave * (1 + row.zabs)
    qval = qvals.loc[codes.loc[tran].trans].qval
    waves.append(measured_wave)
#             da = row.da
#     if use_dipole:
    da = dipole_alpha(row['#J2000'])
#     else:
#         da = -3.0
    shifts.append(shifted_velocity(da, qval, rest_wave))
wav, shift = np.array(waves), np.array(shifts)



fig, ax = plt.subplots(figsize=(12, 6))
ax.scatter(wav, shift)
ax.set_ylim(-200, 200)
# ax.legend()
ax.set_xlabel(r"Observed Wavelength [$\AA$]")
ax.set_ylabel("Velocity [m/s]")
ax.set_title('Simulated velocity shifts for ' + telescope + " " + str(da))
fig.tight_layout()



In [112]:
telescope = 'VLT'
row = full_parse[full_parse.source.eq(telescope)].sample(1,
                                                   random_state=8
                                                  ).iloc[0]
waves = []
shifts = []
for tran in row['transition'].split():
    rest_wave = qvals.loc[codes.loc[tran].trans].wave
    measured_wave = rest_wave * (1 + row.zabs)
    qval = qvals.loc[codes.loc[tran].trans].qval
    waves.append(measured_wave)
    da = dipole_alpha(row['#J2000'])
    shifts.append(shifted_velocity(da, qval, rest_wave))
wav, shift = np.array(waves), np.array(shifts)


fig, ax = plt.subplots(figsize=(12, 6))
ax.scatter(wav, shift)

waves = []
shifts = []
for tran in row['transition'].split():
    rest_wave = qvals.loc[codes.loc[tran].trans].wave
    measured_wave = rest_wave * (1 + row.zabs)
    qval = qvals.loc[codes.loc[tran].trans].qval
    waves.append(measured_wave)
    da = row.da
    shifts.append(shifted_velocity(da, qval, rest_wave))
wav, shift = np.array(waves), np.array(shifts)

ax.scatter(wav, shift)
ax.set_xlabel(r"Observed Wavelength [$\AA$]")
ax.set_ylabel("Velocity [m/s]")
ax.set_title('Simulated velocity shifts for ' + telescope + " " + str(da))
fig.tight_layout()



In [ ]:


In [ ]:


In [ ]: