In [15]:
import numpy as np
import scipy as sp
import matplotlib.pyplot as plt
from astropy.io import fits
import astropy.table as table
import matplotlib as mpl
import matplotlib.image as mpimg
import os

sys.path.insert(0, '../../ppxf')

objname = 'UGC04329'

im=fits.open(objname + '.msobj.fits')
#plt.imshow(im[0].data, cmap='bone')

l = table.Table.read('../arc_disp.txt', format='ascii')
fibers = table.Table.read('../../fibers.txt', format='ascii')

#get the target coordinates and print them

f = open(objname + '.list')
targ_raw_im_name = f.readline()
h = fits.open(targ_raw_im_name)[0].header

#print h['TARGRA']
#print h['TARGDEC']

ralist = [float(i) for i in str(h['TARGRA']).split(':')]
declist = [float(i) for i in str(h['TARGDEC']).split(':')]
for i in range(0, 3):
    ralist[i] = ralist[i] * [360./24., 360./24./60., 360./24./60./60.][i]
    declist[i] = declist[i] * [1., 1./60., 1./60./60.][i]

print 'coords:', sum(ralist), sum(declist)


coords: 124.757875 21.1858583333

In [16]:
def circles(x, y, s, c='b', ax=None, vmin=None, vmax=None, **kwargs):
    """
    Make a scatter of circles plot of x vs y, where x and y are sequence 
    like objects of the same lengths. The size of circles are in data scale.

    Parameters
    ----------
    x,y : scalar or array_like, shape (n, )
        Input data
    s : scalar or array_like, shape (n, ) 
        Radius of circle in data scale (ie. in data unit)
    c : color or sequence of color, optional, default : 'b'
        `c` can be a single color format string, or a sequence of color
        specifications of length `N`, or a sequence of `N` numbers to be
        mapped to colors using the `cmap` and `norm` specified via kwargs.
        Note that `c` should not be a single numeric RGB or
        RGBA sequence because that is indistinguishable from an array of
        values to be colormapped.  `c` can be a 2-D array in which the
        rows are RGB or RGBA, however.
    ax : Axes object, optional, default: None
        Parent axes of the plot. It uses gca() if not specified.
    vmin, vmax : scalar, optional, default: None
        `vmin` and `vmax` are used in conjunction with `norm` to normalize
        luminance data.  If either are `None`, the min and max of the
        color array is used.  (Note if you pass a `norm` instance, your
        settings for `vmin` and `vmax` will be ignored.)

    Returns
    -------
    paths : `~matplotlib.collections.PathCollection`

    Other parameters
    ----------------
    kwargs : `~matplotlib.collections.Collection` properties
        eg. alpha, edgecolors, facecolors, linewidths, linestyles, norm, cmap

    Examples
    --------
    a = np.arange(11)
    circles(a, a, a*0.2, c=a, alpha=0.5, edgecolor='none')
    """
    from matplotlib.patches import Circle
    from matplotlib.collections import PatchCollection
    import pylab as plt
    #import matplotlib.colors as colors

    if ax is None:
        ax = plt.gca()    

    if isinstance(c,basestring):
        color = c     # ie. use colors.colorConverter.to_rgba_array(c)
    else:
        color = None  # use cmap, norm after collection is created
    kwargs.update(color=color)

    if isinstance(x, (int, long, float)):
        patches = [Circle((x, y), s),]
    elif isinstance(s, (int, long, float)):
        patches = [Circle((x_,y_), s) for x_,y_ in zip(x,y)]
    else:
        patches = [Circle((x_,y_), s_) for x_,y_,s_ in zip(x,y,s)]
    collection = PatchCollection(patches, **kwargs)

    if color is None:
        collection.set_array(np.asarray(c))
        if vmin is not None or vmax is not None:
            collection.set_clim(vmin, vmax)

    ax.add_collection(collection)
    return collection

Fun with Fiber Layout


In [17]:
#test fiber map
fibersize=4.687

fig = plt.figure(figsize=(6,6))
for row in fibers:
    plt.scatter(row['ra'], row['dec'], s=200, c='y')
    plt.text(row['ra']-2, row['dec']-1.5, s=row['fiber'], color='k')
    
plt.xlabel(r'RA (arcsec)')
plt.ylabel(r'dec (arcsec)')
plt.title('SparsePak fiber spectrograph rows')


Out[17]:
<matplotlib.text.Text at 0x4eb15d0>

In [18]:
fig = plt.figure(figsize=(6,6), dpi=200)
for row in fibers:
    if row['sky']!=1:
        plt.scatter(row['ra'], row['dec'], s=500, c='y')
        plt.text(row['ra']-.85, row['dec']-.85, s=row['row'], color='k')
    
plt.xlabel(r'RA offset from center (arcsec)')
plt.ylabel(r'Dec offset from center (arcsec)')
plt.title('SparsePak fiber data rows')


Out[18]:
<matplotlib.text.Text at 0x5964e10>

In [19]:
#now put the fibers into radius bins
fibers = table.Table.read('../../fibers.txt', format='ascii')
radius = table.Column(name = 'r', data = np.sqrt( (np.asarray(fibers['ra']))**2. + (np.asarray(fibers['dec']))**2. ) )
#print 'radius:', len(radius)
#print 'fibers:', len(fibers)

if 'radius' not in fibers.colnames:
    fibers.add_column(radius)

fig = plt.figure(figsize=(6,6), dpi=200)
for row in fibers:
    if row['sky']!=1:
        plt.scatter(row['ra'], row['dec'], s=500, c='y')
        plt.text(row['ra']-.85, row['dec']-.85, s=row['fiber'], color='k')
        #print row['ra'], row['dec'], row['row']
        
circles(np.zeros(len(np.unique(radius))), np.zeros(len(np.unique(radius))), np.unique(radius)[::-1], c='w', edgecolor = 'k', alpha = 0.9, linestyle = '--', lw = 1, zorder = 0)

plt.xlabel(r'RA offset from center (arcsec)')
plt.ylabel(r'Dec offset from center (arcsec)')
plt.title('SparsePak fiber rows')

fibers.sort(['r'])
#fibers.pprint(max_lines=-1, max_width=-1)



In [20]:
#now figure out radius groups

fig = plt.figure(figsize=(16,16), dpi=200)

from sklearn.cluster import MeanShift, estimate_bandwidth

R = np.array(zip(fibers['r'][fibers['sky']!=1], np.zeros(len(fibers['r'][fibers['sky']!=1]))))
bandwidth = estimate_bandwidth(R, quantile=.08)

ms = MeanShift(bandwidth=bandwidth, bin_seeding=True)
ms.fit(R)
labels = ms.labels_
cluster_centers = ms.cluster_centers_

labels_unique = np.unique(labels)
n_clusters_ = len(labels_unique)

#now match fibers to a radius group by iterating
group = -9999*np.ones(len(fibers['r'])).astype(int)
r_group_dict = {}

for k in range(n_clusters_):
    my_members = labels == k
    #print "cluster {0}: {1}".format(k, R[my_members, 0])
    plt.axhline(np.mean(R[my_members, 0]))
    #print k, my_members
    for i, fiber in enumerate(my_members):
        if fiber==True:
            group[i] = k
    r_group_dict[k] = np.mean(R[my_members, 0])
#print group
print r_group_dict

r_group = table.Column(name = 'r group', data = group)
if 'r group' not in fibers.colnames:
    fibers.add_column(r_group)

for i, row in enumerate(fibers):
    if row['sky']!=1:
        plt.scatter(i, row['r'])
        plt.annotate(str(row['fiber']) + '\n' + str(row['row']), (i, row['r']), xytext=(i, row['r']+4*(i%2)-2.), arrowprops=dict(arrowstyle='-|>'))
        #print row['ra'], row['dec'], row['row']
#plt.yscale('log')
#plt.ylim([-5., 60.])
plt.title('radial bins')
plt.annotate('fiber \n data', (8, 35), xytext = (10, 40), fontsize = 16, arrowprops=dict(arrowstyle='-|>'))

print fibers
#fibers now includes a radius-group designation for each fiber, so we can radially bin data. This method should actually be fairly extensible to other IFUs
#we also have a dictionary r_group_dict, which associates each group number with a mean radius


{0: 25.924020397058879, 1: 35.340783724952374, 2: 33.953369315093624, 3: 9.7995865171465244, 4: 5.6597283309216335, 5: 19.604903571224114, 6: 16.976698002306907, 7: 29.404489025232696, 8: 44.879911987435982, 9: 42.792160496988231, 10: 39.101112516142052, 11: 11.26, 12: 0.0}
fiber   ra    dec   sky  row        r       r group
----- ------ ------ --- ----- ------------- -------
   52    0.0    0.0   0    47           0.0      12
   31    0.0  -5.63   0    27          5.63       4
   47    0.0   5.63   0    42          5.63       4
   26   4.93   2.81   0    22 5.67459249638       4
   43  -4.93   2.81   0    38 5.67459249638       4
   46   4.93  -2.81   0    41 5.67459249638       4
   66  -4.93  -2.81   0    60 5.67459249638       4
   25   4.93  -8.44   0    21 9.77437977572       3
   41   4.93   8.44   0    36 9.77437977572       3
   51  -4.93  -8.44   0    46 9.77437977572       3
   62  -4.93   8.44   0    56 9.77437977572       3
  ...    ...    ... ...   ...           ...     ...
    6  29.56  33.77   0     4 44.8799119874       8
   12  29.56 -33.77   0    10 44.8799119874       8
   57 -29.56  33.77   0    51 44.8799119874       8
   72 -29.56 -33.77   0    65 44.8799119874       8
   16    0.0  61.92   1 -9999         61.92   -9999
   80 -64.05   2.81   1 -9999  64.111610493   -9999
    2 -29.56  61.92   1 -9999  68.613992742   -9999
   22  29.56  61.92   1 -9999  68.613992742   -9999
   70 -64.05 -30.96   1 -9999 71.1401721955   -9999
   54 -64.05  36.59   1 -9999 73.7646975185   -9999
   37 -59.12  61.92   1 -9999 85.6111020838   -9999

Test Spectra


In [21]:
fig = plt.figure(figsize=(12,6), dpi=200)

im=fits.open(objname + '.msobj.fits')
l = table.Table.read('../arc_disp.txt', format='ascii')

ifu=im[0].data
for i in range(0, len(ifu)):
    plt.plot(l['Wavelength'], ifu[i])

plt.xlabel('observed wavelength ($\AA$)')
plt.ylabel('counts')
plt.title('All fibers')
plt.show()



In [26]:
fig = plt.figure(figsize=(12,12), dpi=200)
ax = fig.add_subplot(111)

im=fits.open(objname + '.msobj.fits')
l = table.Table.read('../arc_disp.txt', format='ascii')

dist = 60.
z_sdss = .014
l_corr = 1 + z_sdss

fiberlist = [47, 42, 22, 41, 27, 60, 38, 56, 39, 36, 0, 21, 43, 46, 40, 56, 37, 18, 19, 24, 20, 29, 72, 64, 58, 74, 57, 50]
fiberlist = [42, 22, 41, 27, 60, 38, 56, 39, 36, 0, 21, 43, 46, 40, 56, 37, 18, 19, 24, 20, 29, 72, 64, 58, 74, 57, 50]
fiberlist = [42, 22, 41, 27, 60, 38, 56, 39, 36, 0, 21, 43, 46, 40]

labely = np.linspace(.1, .9, len(fiberlist))
#print labely

maxes = [ifu[i].max() for i in fiberlist]
maxarray = np.array(zip(fiberlist, maxes))
maxarray = maxarray[maxarray[:,1].argsort()]

#print maxarray

ifu=im[0].data

ax.plot(l['Wavelength'] / l_corr, ifu[i])
#.8*maxes[fiberlist.index(i)]/np.max(maxes)
for i in range(0, len(ifu)):
    if i in fiberlist:
        spectrum, = ax.plot(l['Wavelength'] / l_corr, ifu[i])
        ax.annotate(str(i), (l['Wavelength'][np.argmax(ifu[i])] / l_corr, ifu[i][np.argmax(ifu[i])]), xytext=(.92, labely[np.where(maxarray[:,0]==i)] ), textcoords = 'figure fraction', fontsize = 12, arrowprops=dict(facecolor = 'black', arrowstyle='-|>'), color = spectrum.get_color())

ax.set_xlim(ax.get_xlim()[0], ax.get_xlim()[1] + 100)

ax.grid(linestyle = '--')

ax.set_xlabel('emitted wavelength ($\AA$)')
ax.set_ylabel('counts')
#ax.set_title('Fiber spectra (labeled by data row)')

ax2 = ax.twiny()
ax2.set_xlim(ax.get_xlim()[0] * l_corr, (ax.get_xlim()[1] + 100) * l_corr)
ax2.set_xlabel('observed wavelength ($\AA$)')
ax2.grid(color = 'b', linestyle = ':')

plt.subplots_adjust(hspace=0.1)



In [34]:
#try radial binning
#first plot total flux against by radius

def galprof(r, i0, rd):
    return i0*np.exp(-r/rd)

total_light = np.sum(ifu, axis=1)
fibers.sort(['row'])

rlist = []
ilist = []

for row in fibers:
    if row['sky']!=1:
        rlist.append(r_group_dict[row['r group']])
        ilist.append(total_light[row['row']])
        plt.errorbar(r_group_dict[row['r group']], total_light[row['row']], marker = 'x', c = 'k')
plt.xlabel('r (arcsec)')
plt.ylabel('counts')
plt.title('light gathered by radius')

rlist = np.asarray(rlist)
ilist = np.asarray(ilist)

popt, pcov = sp.optimize.curve_fit(galprof, rlist, ilist, p0 = [3.7e7, 5])
scale_angle = popt[1]

scale_length = scale_angle * 4.848e-6 * dist
scale_length_unc = pcov[1,1] * 4.848e-6 * dist
print 'scale length is', str(np.round(scale_length * 1000, 2)), 'kpc +/-', str(np.round(scale_length_unc * 1000, 2)), 'kpc'

plt.plot(np.linspace(0., 50., 100), galprof(np.linspace(0., 50., 100), popt[0], popt[1]))
plt.axvline(scale_angle, c = 'b')
plt.axvline(scale_angle - pcov[1,1], linestyle = '--', c = 'b')
plt.axvline(scale_angle + pcov[1,1], linestyle = '--', c = 'b')
plt.show()


scale length is 3.95 kpc +/- 0.72 kpc

In [25]:
#try mapping the log of total light onto an IFU fiber diagram
import matplotlib
fig= plt.figure(figsize=(12,12), dpi=400)

#you can get custom-sized images at http://skyserver.sdss3.org/dr10/en/tools/chart/image.aspx
#explore at http://skyserver.sdss3.org/dr10/en/tools/explore/default.aspx

im=mpimg.imread(objname + '.png')
x = y = np.linspace(-40., 40., shape(im)[0])
X, Y = np.meshgrid(x, y)
galim = plt.imshow(im, extent = [-40, 40, -40, 40], origin = 'lower', interpolation = 'nearest', zorder = 0)

total_light = np.sum(ifu, axis=1)
fibers.sort(['row'])

plt.contour(X, Y, im.sum(axis = -1), zorder = 1, cmap = 'Greens')

#print fibers

cir = circles(fibers['ra'][fibers['sky'] != 1], fibers['dec'][fibers['sky'] != 1], s = fibersize/2, c = total_light, edgecolor = 'w', alpha = 0.8, norm=matplotlib.colors.LogNorm(), cmap = 'hot', zorder = 2)
for row in fibers:
    if row['sky']!=1:
        plt.text(row['ra']-.85, row['dec']-1.25, s=str(row['fiber']) + '\n' + str(row['row']), color = 'g', size = 14, zorder = 3)
fig.colorbar(cir, shrink=0.8)

plt.xlim([-40, 40])
plt.ylim([-40, 40])
plt.xlabel('RA offset (arcsec)', fontsize = 16)
plt.ylabel('Dec offset (arcsec)', fontsize = 16)
plt.title(objname, fontsize=18)

#this is the correct way to do this!


Out[25]:
<matplotlib.text.Text at 0x74d5e50>