BPT (Baldwin, Phillips & Terlevich) plot with galamo¶

Welcome to the tutorial on using galamo, a Python package designed for comprehensive galaxy analysis.

Abstract: In this tutorial, we'll explore the bpt module that helps in classifying galaxy types on the basis of initial spectral data of the galaxies. bpt works on mathematical formulation given by Kewley and Kauffman.

This module is especially useful for astrophysics researchers and students working with AGNs (Active Galactic Nuclei) host galaxies from surveys like SDSS.

Keywords: pandas, astroquery, astropy, flux, matplotlib, bpt

Author: Jashanpreet Singh Dingra

References:

  • https://ned.ipac.caltech.edu/level5/Glossary/Essay_bpt.html
  • https://ui.adsabs.harvard.edu/abs/2001ApJ...556..121K/abstract (Kewley et al. (2001))
  • https://ui.adsabs.harvard.edu/abs/2003MNRAS.346.1055K/abstract (Kauffmann et al. (2003))

Let’s get started!


!pip install galamo

In [1]:
!pip show galamo #check requirements
Name: galamo
Version: 1.1.2
Summary: An open source Python package for comprehensive galaxy analysis, integrating machine learning and statistical methods. It provides automated tools for morphology classification, kinematics, photometry, and spectral analysis to aid astrophysical research.
Home-page: https://www.galamo.org
Author: Jashanpreet Singh Dingra
Author-email: astrodingra@gmail.com
License: 
Location: /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages
Requires: huggingface_hub, joblib, matplotlib, numpy, opencv-python, pandas, requests, scipy, tensorflow, termcolor, tqdm
Required-by: 

Imports¶

In [9]:
from astroquery.sdss import SDSS
from astropy.table import Table
import pandas as pd 
from galamo import bpt

Data Query using astroquery¶

In [3]:
# Define SQL query
query = """
SELECT TOP 1000
    i.specobjid,
    i.plateid,
    i.mjd,
    i.fiberid,
    i.z,
    i.ra,
    i.dec,

    -- Emission Line Fluxes
    l.h_alpha_flux,
    l.h_beta_flux,
    l.oiii_5007_flux,
    l.nii_6584_flux,
    l.oi_6300_flux,
    l.sii_6717_flux,
    l.sii_6731_flux,

    -- Equivalent Widths
    l.h_alpha_eqw,
    l.nii_6584_eqw,

    -- Flux Errors
    l.h_alpha_flux_err,
    l.h_beta_flux_err,
    l.oiii_5007_flux_err,
    l.nii_6584_flux_err,
    l.oi_6300_flux_err,
    l.sii_6717_flux_err,
    l.sii_6731_flux_err

FROM
    galSpecInfo AS i
JOIN
    galSpecLine AS l ON i.specobjid = l.specobjid

WHERE
    i.z BETWEEN 0 AND 0.3
    AND l.h_alpha_flux_err > 0 AND l.h_alpha_flux / l.h_alpha_flux_err > 5
    AND l.h_beta_flux_err > 0 AND l.h_beta_flux / l.h_beta_flux_err > 5
    AND l.oiii_5007_flux_err > 0 AND l.oiii_5007_flux / l.oiii_5007_flux_err > 5
    AND l.nii_6584_flux_err > 0 AND l.nii_6584_flux / l.nii_6584_flux_err > 5
    AND l.sii_6717_flux_err > 0 AND l.sii_6717_flux / l.sii_6717_flux_err > 5
    AND l.sii_6731_flux_err > 0 AND l.sii_6731_flux / l.sii_6731_flux_err > 5
    AND l.oi_6300_flux_err > 0 AND l.oi_6300_flux / l.oi_6300_flux_err > 5
"""

# Run query
result = SDSS.query_sql(query)

# Convert to Astropy Table and optionally save as CSV
if result:
    table = Table(result)
    table.write("AGN_data.csv", format="csv", overwrite=True)
    print("Query successful. Data saved to AGN_data.csv")
else:
    print("No results returned.")
Query successful. Data saved to AGN_data.csv

Plotting BPT Diagrams¶

Galamo's bpt module provides tools for generating BPT (Baldwin, Phillips & Terlevich) diagnostic diagrams, which are essential for classifying emission-line galaxies based on their ionization mechanisms.

Available Map Styles¶

The module currently supports the following visualization styles:

  • default
  • bubble
  • soft

Export Options¶

All BPT diagrams can be exported in a wide range of formats, including:

  • PNG
  • PDF
  • SVG
  • EPS

This allows for seamless integration into publications, presentations, or reports.

In [4]:
bpt.draw("AGN_data.csv") # default map
✅ Columns matched
No description has been provided for this image

Applying maps¶

  1. Bubble
In [5]:
bpt.draw("AGN_data.csv", map="bubble") # bubble map
✅ Columns matched
No description has been provided for this image
  1. Soft
In [6]:
bpt.draw("AGN_data.csv", map="soft") # bubble map
✅ Columns matched
No description has been provided for this image

Saving the diagram¶

In [7]:
bpt.draw("AGN_data.csv", map="bubble", save_figure=True, output_filename="BPT_diagram.pdf") # bubble map and saved as pdf
✅ Columns matched
No description has been provided for this image

Exported File:
BPT Diagram


Error Detection in data¶

If the queried data is missing any critical parameters required for generating the BPT diagram, the module will raise an error and provide a clear message indicating the missing parameter and its expected location in the data structure. This ensures data integrity and helps guide users to correct their input before proceeding with the analysis.

In [8]:
bpt.draw("wrong.csv")
---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
Cell In[8], line 1
----> 1 bpt.draw("wrong.csv")

File /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/galamo/bpt.py:23, in draw(input_file, map, save_figure, output_filename)
     19 required_columns = ['h_alpha_flux', 'h_beta_flux', 'oiii_5007_flux', 'nii_6584_flux',
     20                     'oi_6300_flux', 'sii_6717_flux', 'sii_6731_flux']
     22 # Read the data
---> 23 df = pd.read_csv(input_file)
     24 actual_columns = df.columns.tolist()
     26 # Check if all required columns are present

File /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/pandas/io/parsers/readers.py:1026, in read_csv(filepath_or_buffer, sep, delimiter, header, names, index_col, usecols, dtype, engine, converters, true_values, false_values, skipinitialspace, skiprows, skipfooter, nrows, na_values, keep_default_na, na_filter, verbose, skip_blank_lines, parse_dates, infer_datetime_format, keep_date_col, date_parser, date_format, dayfirst, cache_dates, iterator, chunksize, compression, thousands, decimal, lineterminator, quotechar, quoting, doublequote, escapechar, comment, encoding, encoding_errors, dialect, on_bad_lines, delim_whitespace, low_memory, memory_map, float_precision, storage_options, dtype_backend)
   1013 kwds_defaults = _refine_defaults_read(
   1014     dialect,
   1015     delimiter,
   (...)
   1022     dtype_backend=dtype_backend,
   1023 )
   1024 kwds.update(kwds_defaults)
-> 1026 return _read(filepath_or_buffer, kwds)

File /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/pandas/io/parsers/readers.py:620, in _read(filepath_or_buffer, kwds)
    617 _validate_names(kwds.get("names", None))
    619 # Create the parser.
--> 620 parser = TextFileReader(filepath_or_buffer, **kwds)
    622 if chunksize or iterator:
    623     return parser

File /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/pandas/io/parsers/readers.py:1620, in TextFileReader.__init__(self, f, engine, **kwds)
   1617     self.options["has_index_names"] = kwds["has_index_names"]
   1619 self.handles: IOHandles | None = None
-> 1620 self._engine = self._make_engine(f, self.engine)

File /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/pandas/io/parsers/readers.py:1880, in TextFileReader._make_engine(self, f, engine)
   1878     if "b" not in mode:
   1879         mode += "b"
-> 1880 self.handles = get_handle(
   1881     f,
   1882     mode,
   1883     encoding=self.options.get("encoding", None),
   1884     compression=self.options.get("compression", None),
   1885     memory_map=self.options.get("memory_map", False),
   1886     is_text=is_text,
   1887     errors=self.options.get("encoding_errors", "strict"),
   1888     storage_options=self.options.get("storage_options", None),
   1889 )
   1890 assert self.handles is not None
   1891 f = self.handles.handle

File /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/pandas/io/common.py:873, in get_handle(path_or_buf, mode, encoding, compression, memory_map, is_text, errors, storage_options)
    868 elif isinstance(handle, str):
    869     # Check whether the filename is to be opened in binary mode.
    870     # Binary mode does not support 'encoding' and 'newline'.
    871     if ioargs.encoding and "b" not in ioargs.mode:
    872         # Encoding
--> 873         handle = open(
    874             handle,
    875             ioargs.mode,
    876             encoding=ioargs.encoding,
    877             errors=errors,
    878             newline="",
    879         )
    880     else:
    881         # Binary mode
    882         handle = open(handle, ioargs.mode)

FileNotFoundError: [Errno 2] No such file or directory: 'wrong.csv'

For any issue in the module raise @ https://github.com/galamo-org/galamo/issues

In [10]:
from astroquery.sdss import SDSS
from astropy.table import Table

query = """
SELECT TOP 1000
    s.specObjID,
    s.ra, s.dec, s.z,
    
    l.h_alpha_flux,      l.h_alpha_flux_err,
    l.h_beta_flux,       l.h_beta_flux_err,
    l.oiii_5007_flux,    l.oiii_5007_flux_err,
    l.nii_6584_flux,     l.nii_6584_flux_err,
    l.oi_6300_flux,      l.oi_6300_flux_err,
    l.sii_6717_flux,     l.sii_6717_flux_err,
    l.sii_6731_flux,     l.sii_6731_flux_err

FROM SpecObj AS s
JOIN galSpecLine AS l ON s.specObjID = l.specObjID

WHERE
    s.class = 'GALAXY' AND
    l.h_alpha_flux > 0 AND
    l.h_beta_flux > 0 AND
    l.oiii_5007_flux > 0 AND
    l.nii_6584_flux > 0 AND
    l.oi_6300_flux > 0 AND
    l.sii_6717_flux > 0 AND
    l.sii_6731_flux > 0
"""

# Run the query using astroquery
results = SDSS.query_sql(query)

results.write('bpt_data.csv', format='csv', overwrite=True)

print("Saved to bpt_data.csv")
Saved to bpt_data.csv