https://github.com/abetlen/star-chart-notebook
Science Score: 13.0%
This score indicates how likely this project is to be science-related based on various indicators:
-
○CITATION.cff file
-
✓codemeta.json file
Found codemeta.json file -
○.zenodo.json file
-
○DOI references
-
○Academic publication links
-
○Academic email domains
-
○Institutional organization owner
-
○JOSS paper metadata
-
○Scientific vocabulary similarity
Low similarity (8.6%) to scientific vocabulary
Repository
Basic Info
- Host: GitHub
- Owner: abetlen
- Language: Jupyter Notebook
- Default Branch: master
- Size: 845 KB
Statistics
- Stars: 6
- Watchers: 2
- Forks: 3
- Open Issues: 0
- Releases: 0
Metadata Files
README.md
```python %pylab inline
import re import pandas as pd from functools import partial from mpl_toolkits.basemap import Basemap ```
Populating the interactive namespace from numpy and matplotlib
Star cartography with Scipy and Pandas
The other day I decided I wanted to learn a little bit about astronomy. My goal was to get a better understanding of how we describe celestial objects, develop some general tools for working with celestial data, and draw some pretty pictures.

To get this going I compiled together a dataset from wikipedia, cleaned it up, and put it into a .csv file that you can use at your own risk. I used pandas to manipulate the dataset and the matplotlib library Basemap for plotting cartographic data.
So I'm gonna take you through that process and explain some of the concepts along the way. First though we have to load that dataset.
```python starsdf = pd.read_csv('./constellations.csv')
starsdf.head() ```
| Name | Right Ascension | Declination | Apparent Magnitude | Constellation | |
|---|---|---|---|---|---|
| 0 | Alpha Andromedae | 00h 08m 23.17s | +29° 05′ 27.0″ | 2.07 | Andromeda |
| 1 | Beta Andromedae | 01h 09m 43.80s | +35° 37′ 15.0″ | 2.07 | Andromeda |
| 2 | Gamma Andromedae | 02h 03m 53.92s | +42° 19′ 47.5″ | 2.10 | Andromeda |
| 3 | Delta Andromedae | 00h 39m 19.60s | +30° 51′ 40.4″ | 3.27 | Andromeda |
| 4 | Andromeda Galaxy | 00h 42m 44.31s | +41° 16′ 09.4″ | 3.44 | Andromeda |
Okay, let me also explain some of the columns and terms we'll be using:
- Constellation: These are just regional groupings of the stars in the sky; there are 88 modern constellations.
- Right Ascension: This relates to the geographic Longitude, how far to the East of the prime meridian something is, except instead of degrees it's measured in hours, minutes, and seconds.
- Declination: This relates to geographic Latitude, how far north or south; this time measured in degrees, arcminutes, and arcseconds.
- Epoch: An epoch is a point in time used as a reference for celestial coordinates. Usually given as midnight January 1st 2000.
- Apparent Magnitude: A funny way of measuring how bright a star looks. I say funny because it's an inverse logarithmic scale, meaning 6.5 is the limit of stars visible to the naked eye and the sun has an apparent magnitude of -27.
- Asterism: A pattern of stars, think big dipper.
Now the first thing I did was write some helper functions to convert from celestial to geographic coordinates.
```python from functools import wraps
def nan(f): '''Function decorator that returns a nan on Exception'''
@wraps(f)
def wrapped(*args, **kwargs):
try:
return f(*args, **kwargs)
except Exception:
return np.nan
return wrapped
@nan def ra2long(ra): '''Parse string representing right ascension into a longitudinal angle'''
h, m, s = (float(x) for x in re.match(r'(.*)h\s*(.*)m\s*(.*)s', ra.lower()).groups())
return 360 * (h / 24 + m / 24 / 60 + s / 24 / 60 / 60)
@nan def dec2lat(dec): '''Parse declination into a latitudinal angle'''
d, m, s = (float(x) for x in re.match(r'(.*)°\s*(.*)′\s*(.*)″', dec.lower()).groups())
return d + np.copysign(m / 60, d) + np.copysign(s / 3600, d)
Add these new columns to our DataFrame
starsdf['Latitude'] = starsdf['Declination'].apply(dec2lat) starsdf['Longitude'] = starsdf['Right Ascension'].apply(ra2long)
starsdf.head() ```
| Name | Right Ascension | Declination | Apparent Magnitude | Constellation | Latitude | Longitude | |
|---|---|---|---|---|---|---|---|
| 0 | Alpha Andromedae | 00h 08m 23.17s | +29° 05′ 27.0″ | 2.07 | Andromeda | 29.090833 | 2.096542 |
| 1 | Beta Andromedae | 01h 09m 43.80s | +35° 37′ 15.0″ | 2.07 | Andromeda | 35.620833 | 17.432500 |
| 2 | Gamma Andromedae | 02h 03m 53.92s | +42° 19′ 47.5″ | 2.10 | Andromeda | 42.329861 | 30.974667 |
| 3 | Delta Andromedae | 00h 39m 19.60s | +30° 51′ 40.4″ | 3.27 | Andromeda | 30.861222 | 9.831667 |
| 4 | Andromeda Galaxy | 00h 42m 44.31s | +41° 16′ 09.4″ | 3.44 | Andromeda | 41.269278 | 10.684625 |
Then I filtered out all stars that would not be visible to the naked eye.
```python min_am = 6.5
DataFrame of visible stars
visdf = starsdf[starsdf['Apparent Magnitude'] <= min_am]
long = visdf.Longitude lat = visdf.Latitude ams = ((visdf['Apparent Magnitude'].max() - visdf['Apparent Magnitude'])) / (visdf['Apparent Magnitude'].max() - visdf['Apparent Magnitude'].min())
visdf['Apparent Magnitude'].hist() plt.show() ```

Then I created a dataset of just the constellations. This is useful when you want to label them later.
```python
Pull out into data accquisition notebook
constellations = { 'Name': [], 'Latitude': [], 'Longitude': [] }
for c in starsdf['Constellation'].unique(): constellations['Name'].append(c) constellations['Latitude'].append(visdf[visdf['Constellation'] == c].Latitude.median()) constellations['Longitude'].append(visdf[visdf['Constellation'] == c].Longitude.median())
constellationsdf = pd.DataFrame(constellations)
print(constellationsdf.Name.unique()) ```
['Andromeda' 'Antlia' 'Apus' 'Aquarius' 'Aquila' 'Ara' 'Aries' 'Auriga'
'Boötes' 'Caelum' 'Camelopardalis' 'Cancer' 'Canes Venatici' 'Canis Major'
'Canis Minor' 'Capricornus' 'Carina' 'Cassiopeia' 'Centaurus' 'Cepheus'
'Cetus' 'Chamaeleon' 'Circinus' 'Columba' 'Coma Berenices'
'Corona Australis' 'Corona Borealis' 'Corvus' 'Crater' 'Crux' 'Cygnus'
'Delphinus' 'Dorado' 'Draco' 'Equuleus' 'Eridanus' 'Fornax' 'Gemini'
'Grus' 'Hercules' 'Horologium' 'Hydra' 'Hydrus' 'Indus' 'Lacerta' 'Leo'
'Leo Minor' 'Lepus' 'Libra' 'Lupus' 'Lynx' 'Lyra' 'Mensa' 'Microscopium'
'Monoceros' 'Musca' 'Norma' 'Octans' 'Ophiuchus' 'Orion' 'Pavo' 'Pegasus'
'Perseus' 'Phoenix' 'Pictor' 'Pisces' 'Piscis Austrinus' 'Puppis' 'Pyxis'
'Reticulum' 'Sagitta' 'Sagittarius' 'Scorpius' 'Sculptor' 'Scutum'
'Serpens' 'Sextans' 'Taurus' 'Telescopium' 'Triangulum'
'Triangulum Australe' 'Tucana' 'Ursa Major' 'Ursa Minor' 'Vela' 'Virgo'
'Volans' 'Vulpecula']
Some more helper to convert back to celestial coordinates. These are used in the Basemap drawmeridian and drawparallels functions for custom labels.
```python def long2ra(long, fmtstr='{:.0f}h {:.0f}m {:.2f}s'): '''Convert a longitude to a Right Ascension according to fmt'''
m, h = np.modf(long / 360 * 24)
s, m = np.modf(m * 60)
s *= 60
return fmtstr.format(h, m, s)
def lat2dec(lat, fmtstr='{:.0f}° {:.0f}\' {:.2f}\"'): '''Convert a Latitude to a Declination according to fmt'''
m, d = np.modf(lat)
s, m = np.modf(m * 60)
s *= 60
return fmtstr.format(d, m, s)
Useful for labelling parrallels and meridians
slong2ra = partial(long2ra, fmtstr='{:.0f}h') slat2dec = partial(lat2dec, fmtstr='{:.0f}°')
print((long2ra(95), slong2ra(95))) print((lat2dec(50), slong2ra(50))) ```
('6h 20m 0.00s', '6h')
('50° 0\' 0.00"', '3h')
And now we're ready to plot. A canonical example in astronomy is to plot the northern and southern hemispheres with the constellations labeled.
```python fig, (ax1, ax2) = plt.subplots(1, 2)
fig.setsizeinches((15, 15))
map1 = Basemap(projection='npstere', boundinglat=15, lon0=135, ax=ax1, celestial=True) map2 = Basemap(projection='spstere', boundinglat=-15, lon0=135, ax=ax2, celestial=True)
map1.drawmeridians(np.arange(0,360,15), labels=[1, 1, 1, 1],fmt=slong2ra) map2.drawmeridians(np.arange(0,360,15), labels=[1, 1, 1, 1], fmt=slong2ra)
map1.drawparallels(np.arange(-90,90,15)) map2.drawparallels(np.arange(-90,90,15))
Plot Stars
map1.scatter(long.values, lat.values, s=ams.values, c='k', latlon=True) map2.scatter(long.values, lat.values, s=ams.values, c='k', latlon=True)
ax1.settitle('Northern Hemisphere', y=1.05, size=18) ax2.settitle('Southern Hemisphere', y=1.05, size=18)
Label Constellations
for , i in constellationsdf.iterrows(): xpt, ypt = map1(i.Longitude, i.Latitude) ax1.text(xpt, ypt, i.Name, transform=ax1.transData, clipon=True, color='grey', weight='semibold')
xpt, ypt = map2(i.Longitude, i.Latitude)
ax2.text(xpt, ypt, i.Name, transform=ax2.transData, clip_on=True, color='grey', weight='semibold')
North Star
polarislong = starsdf[starsdf.Name == 'Polaris'].Longitude.values[0] polarislat = starsdf[starsdf.Name == 'Polaris'].Latitude.values[0]
pxpt, pypt = map1(polarislong, polarislat)
ax1.plot(pxpt, pypt, 'go') ax1.text(pxpt + 4e5, pypt + 4e5, 'Polaris\n{}\n{}'.format(long2ra(polarislong), lat2dec(polarislat)), size=10, fontdict={'weight':'semibold','color':'g'})
plt.show() ```

You might also want to plot a close-up of a specific constellation. So let's look at Ursa Minor, highlight the region of space it occupies, and present it's main asterism.
```python from scipy.spatial import ConvexHull from matplotlib.path import Path from matplotlib.patches import PathPatch
Ursa Minor Asterism created by the stars:
alpha umi, delta umi, epsilon umi, zeta umi, beta umi, gamma umi, eta umi, zeta umi
um = starsdf[starsdf.Constellation == 'Ursa Minor'].iloc[[0, 6, 3, 5, 1, 2, 9, 5]]
Plot Sky
plt.gcf().setsizeinches((6, 6))
map1 = Basemap(projection='stere', celestial=True, width=4e6, height=4e6, latts=50, lat0=um.Latitude.mean(), lon_0=135)
map1.drawmeridians(np.arange(0,360,15), labels=[1, 1, 0, 1], fmt=slong2ra, size=14) map1.drawparallels(np.arange(-90,90,15))
umxpt, umypt = map1(um.Longitude.mean(), um.Latitude.mean())
Compute convex hull of visible stars in the Ursa Minor Constellation
points = np.array(map1(starsdf[starsdf.Constellation == 'Ursa Minor'].Longitude.values, starsdf[starsdf.Constellation == 'Ursa Minor'].Latitude.values)).T hull = ConvexHull(points) points = hull.points[hull.vertices] path = Path(points, closed=True) patch = PathPatch(path, facecolor='white', edgecolor='w', lw=20, joinstyle='round')
Set bg color and highlight constellation
plt.gca().setaxisbgcolor((.95, .95, 1.)) plt.gca().add_patch(patch)
Plot Ursa Minor Asterism
map1.plot(um.Longitude.values, um.Latitude.values, 'g-', linewidth=2, latlon=True, zorder=3)
Plot Stars
map1.scatter(long.values, lat.values, s=20*ams.values, c='k', latlon=True, zorder=4)
Label Constellations
for , i in constellationsdf.iterrows(): xpt, ypt = map1(i.Longitude, i.Latitude) plt.gca().text(xpt, ypt, i.Name, clipon=True, size=24, family='serif')
plt.show() ```

Owner
- Name: Andrei
- Login: abetlen
- Kind: user
- Location: San Francisco, California
- Twitter: abetlen
- Repositories: 49
- Profile: https://github.com/abetlen
GitHub Events
Total
- Watch event: 3
Last Year
- Watch event: 3
Issues and Pull Requests
Last synced: about 1 year ago
All Time
- Total issues: 0
- Total pull requests: 0
- Average time to close issues: N/A
- Average time to close pull requests: N/A
- Total issue authors: 0
- Total pull request authors: 0
- Average comments per issue: 0
- Average comments per pull request: 0
- Merged pull requests: 0
- Bot issues: 0
- Bot pull requests: 0
Past Year
- Issues: 0
- Pull requests: 0
- Average time to close issues: N/A
- Average time to close pull requests: N/A
- Issue authors: 0
- Pull request authors: 0
- Average comments per issue: 0
- Average comments per pull request: 0
- Merged pull requests: 0
- Bot issues: 0
- Bot pull requests: 0