Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change radius API for RadialProfile and CurveOfGrowth #1540

Merged
merged 5 commits into from May 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 7 additions & 5 deletions CHANGES.rst
Expand Up @@ -17,11 +17,6 @@ General
New Features
^^^^^^^^^^^^

- ``photutils.profiles``

- Added ``EdgeRadialProfile`` class defined using radial bin edges.
[#1538]

Bug Fixes
^^^^^^^^^

Expand All @@ -40,6 +35,13 @@ API Changes

- Removed the ``ApertureStats`` ``unpack_nddata`` method. [#1537]

- ``photutils.profiles``

- The API for defining the radial bins for the ``RadialProfile`` and
``CurveOfGrowth`` classes was changed. While the new API allows for
more flexibility, unfortunately, it is not backwards-compatible.
[#1540]

- ``photutils.segmentation``

- Removed the deprecated ``kernel`` keyword from ``detect_sources``
Expand Down
183 changes: 61 additions & 122 deletions docs/profiles.rst
Expand Up @@ -7,7 +7,7 @@ Introduction
------------

`photutils.profiles` provides tools to calculate radial profiles and
curves of growth using concentric apertures.
curves of growth using concentric circular apertures.


Preliminaries
Expand Down Expand Up @@ -50,14 +50,10 @@ data before creating a radial profile or curve of growth.
Creating a Radial Profile
-------------------------

Photutils provides two classes for computing radial profiles. The
classes have the same functionality, but differ in how the radial bins
are input. The `~photutils.profiles.RadialProfile` radial bins are
computed using inputs (minimum, maximum, and step size) defined as
the radial bin centers. The `~photutils.profiles.EdgeRadialProfile`
class allows the user to directly input the radial bin edges.
Also, the radial spacing does not need to be constant for
`~photutils.profiles.EdgeRadialProfile` class.
Photutils provides the :class:`~photutils.profiles.RadialProfile` class
for computing radial profiles. The radial bins are defined by inputting
a 1D array of radial edges. The radial spacing does not need to be
constant.

First, we'll use the `~photutils.centroids.centroid_quadratic` function
to find the source centroid::
Expand All @@ -67,53 +63,35 @@ to find the source centroid::
>>> print(xycen) # doctest: +FLOAT_CMP
[47.61226319 52.04668132]

Now, let's create radial profiles using both classes. The radial
profiles will be centered at our centroid position computed above.

For the `~photutils.profiles.RadialProfile` class the profile will be
computed over the radial range from ``min_radius`` to ``max_radius``
with steps of ``radius_step`` (note these are the centers of the radial
bins)::
Now, let's create a radial profile. The radial profile will be centered
at our centroid position computed above.

>>> from photutils.profiles import RadialProfile
>>> min_radius = 0.0
>>> max_radius = 25.0
>>> radius_step = 1.0
>>> rp1 = RadialProfile(data, xycen, min_radius, max_radius, radius_step,
... error=error, mask=None)

For the `~photutils.profiles.EdgeRadialProfile` class the profile will be
computed using the input edge radii::

>>> from photutils.profiles import EdgeRadialProfile
>>> edge_radii = np.arange(26)
>>> rp2 = EdgeRadialProfile(data, xycen, edge_radii, error=error,
... mask=None)
>>> edge_radii = np.arange(25)
>>> rp = RadialProfile(data, xycen, edge_radii, error=error, mask=None)

The `~photutils.profiles.RadialProfile.radius`,
The `~photutils.profiles.RadialProfile.radius` (radial bin centers),
`~photutils.profiles.RadialProfile.profile`, and
`~photutils.profiles.RadialProfile.profile_error` attributes contain the
output 1D `~numpy.ndarray` objects::

>>> print(rp1.radius) # doctest: +FLOAT_CMP
[ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17.
18. 19. 20. 21. 22. 23. 24. 25.]

>>> print(rp1.profile) # doctest: +FLOAT_CMP
[ 4.27430150e+01 4.02150658e+01 3.81601146e+01 3.38116846e+01
2.89343205e+01 2.34250297e+01 1.84368533e+01 1.44310461e+01
9.55543388e+00 6.55415896e+00 4.49693014e+00 2.56010523e+00
1.50362911e+00 7.35389056e-01 6.04663625e-01 8.08820954e-01
2.31751912e-01 -1.39063329e-01 1.25181410e-01 4.84601845e-01
1.94567871e-01 4.49109676e-01 -2.00995374e-01 -7.74387397e-02
5.70302749e-02 -3.27578439e-02]

>>> print(rp1.profile_error) # doctest: +FLOAT_CMP
[2.95008692 1.17855895 0.6610777 0.51902503 0.47524302 0.43072819
0.39770113 0.37667594 0.33909996 0.35356048 0.30377721 0.29455808
0.25670656 0.26599511 0.27354232 0.2430305 0.22910334 0.22204777
0.22327174 0.23816561 0.2343794 0.2232632 0.19893783 0.17888776
0.18228289 0.19680823]
>>> print(rp.radius) # doctest: +FLOAT_CMP
[ 0.5 1.5 2.5 3.5 4.5 5.5 6.5 7.5 8.5 9.5 10.5 11.5 12.5 13.5
14.5 15.5 16.5 17.5 18.5 19.5 20.5 21.5 22.5 23.5]

>>> print(rp.profile) # doctest: +FLOAT_CMP
[ 4.15632243e+01 3.93402079e+01 3.59845746e+01 3.15540506e+01
2.62300757e+01 2.07297033e+01 1.65106801e+01 1.19376723e+01
7.75743772e+00 5.56759777e+00 3.44112671e+00 1.91350281e+00
1.17092981e+00 4.22261078e-01 9.70256904e-01 4.16355795e-01
1.52328707e-02 -6.69985111e-02 4.15522650e-01 2.48494731e-01
4.03348112e-01 1.43482678e-01 -2.62777461e-01 7.30653622e-02]

>>> print(rp.profile_error) # doctest: +FLOAT_CMP
[1.69588246 0.81797694 0.61132694 0.44670831 0.49499835 0.38025361
0.40844702 0.32906672 0.36466713 0.33059274 0.29661894 0.27314739
0.25551933 0.27675376 0.25553986 0.23421017 0.22966813 0.21747036
0.23654884 0.22760386 0.23941711 0.20661313 0.18999134 0.17469024]

If desired, the radial profiles can be normalized using the
:meth:`~photutils.profiles.RadialProfile.normalize` method.
Expand All @@ -123,10 +101,8 @@ error:

.. doctest-skip::

>>> rp1.plot(label='RadialProfile')
>>> rp1.plot_error()
>>> rp2.plot(label='EdgeRadialProfile')
>>> rp2.plot_error()
>>> rp.plot(label='Radial Profile')
>>> rp.plot_error()

.. plot::

Expand All @@ -135,7 +111,7 @@ error:

from photutils.centroids import centroid_quadratic
from photutils.datasets import make_noise_image
from photutils.profiles import EdgeRadialProfile, RadialProfile
from photutils.profiles import RadialProfile

# create an artificial single source
gmodel = Gaussian2D(42.1, 47.8, 52.4, 4.7, 4.7, 0)
Expand All @@ -148,20 +124,12 @@ error:
xycen = centroid_quadratic(data, xpeak=47, ypeak=52)

# create the radial profile
min_radius = 0.0
max_radius = 25.0
radius_step = 1.0
rp1 = RadialProfile(data, xycen, min_radius, max_radius, radius_step,
error=error, mask=None)

edge_radii = np.arange(26)
rp2 = EdgeRadialProfile(data, xycen, edge_radii, error=error, mask=None)
rp = RadialProfile(data, xycen, edge_radii, error=error, mask=None)

# plot the radial profile
rp1.plot(label='RadialProfile')
rp1.plot_error()
rp2.plot(label='EdgeRadialProfile')
rp2.plot_error()
rp.plot(label='Radial Profile')
rp.plot_error()
plt.legend()

The `~photutils.profiles.RadialProfile.apertures` attribute contains a
Expand Down Expand Up @@ -190,11 +158,8 @@ list of the apertures. Let's plot two of the annulus apertures for the
xycen = centroid_quadratic(data, xpeak=47, ypeak=52)

# create the radial profile
min_radius = 0.0
max_radius = 25.0
radius_step = 1.0
rp = RadialProfile(data, xycen, min_radius, max_radius, radius_step,
error=error, mask=None)
edge_radii = np.arange(26)
rp = RadialProfile(data, xycen, edge_radii, error=error, mask=None)

norm = simple_norm(data, 'sqrt')
plt.figure(figsize=(5, 5))
Expand All @@ -209,25 +174,15 @@ profile and return the Gaussian model using the

.. doctest-requires:: scipy

>>> rp1.gaussian_fit # doctest: +FLOAT_CMP
<Gaussian1D(amplitude=41.80620963, mean=0., stddev=4.69126969)>

.. doctest-requires:: scipy

>>> rp2.gaussian_fit # doctest: +FLOAT_CMP
>>> rp.gaussian_fit # doctest: +FLOAT_CMP
<Gaussian1D(amplitude=41.54880743, mean=0., stddev=4.71059406)>

The FWHM of the fitted 1D Gaussian model is stored in the
`~photutils.profiles.RadialProfile.gaussian_fwhm` attribute:

.. doctest-requires:: scipy

>>> print(rp1.gaussian_fwhm) # doctest: +FLOAT_CMP
11.04709589620093

.. doctest-requires:: scipy

>>> print(rp2.gaussian_fwhm) # doctest: +FLOAT_CMP
>>> print(rp.gaussian_fwhm) # doctest: +FLOAT_CMP
11.09260130738712

Finally, let's plot the fitted 1D Gaussian model for the
Expand All @@ -254,16 +209,13 @@ class:`~photutils.profiles.RadialProfile` radial profile:
xycen = centroid_quadratic(data, xpeak=48, ypeak=52)

# create the radial profile
min_radius = 0.0
max_radius = 25.0
radius_step = 1.0
rp1 = RadialProfile(data, xycen, min_radius, max_radius, radius_step,
error=error, mask=None)
edge_radii = np.arange(26)
rp = RadialProfile(data, xycen, edge_radii, error=error, mask=None)

# plot the radial profile
rp1.plot(label='Radial Profile')
rp1.plot_error()
plt.plot(rp1.radius, rp1.gaussian_profile, label='Gaussian Fit')
rp.plot(label='Radial Profile')
rp.plot_error()
plt.plot(rp.radius, rp.gaussian_profile, label='Gaussian Fit')

plt.legend()

Expand All @@ -276,16 +228,11 @@ Now let's create a curve of growth using the
defined above and the same source centroid.

The curve of growth will be centered at our centroid position. It will
be computed over the radial range from ``min_radius`` to ``max_radius``
with steps of ``radius_step``::
be computed over the radial range given by the input ``radii`` array::

>>> from photutils.profiles import CurveOfGrowth
>>> min_radius = 0.0
>>> max_radius = 25.0
>>> radius_step = 1.0
>>> cog = CurveOfGrowth(data, xycen, min_radius, max_radius, radius_step,
... error=error, mask=None)

>>> radii = np.arange(1, 26)
>>> cog = CurveOfGrowth(data, xycen, radii, error=error, mask=None)

The `~photutils.profiles.CurveOfGrowth` instance
has `~photutils.profiles.CurveOfGrowth.radius`,
Expand All @@ -294,24 +241,22 @@ has `~photutils.profiles.CurveOfGrowth.radius`,
contain 1D `~numpy.ndarray` objects::

>>> print(cog.radius) # doctest: +FLOAT_CMP
[ 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17.
18. 19. 20. 21. 22. 23. 24. 25.]
[ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
25]

>>> print(cog.profile) # doctest: +FLOAT_CMP
[ 0. 130.57472018 501.34744442 1066.59182074 1760.50163608
2502.13955554 3218.50667597 3892.81448231 4455.36403436 4869.66609313
5201.99745378 5429.02043984 5567.28370644 5659.24831854 5695.06577065
5783.46217755 5824.01080702 5825.59003768 5818.22316662 5866.52307412
5896.96917375 5948.92254787 5968.30540534 5931.15611704 5941.94457249
5942.06535486]
[ 130.57472018 501.34744442 1066.59182074 1760.50163608 2502.13955554
3218.50667597 3892.81448231 4455.36403436 4869.66609313 5201.99745378
5429.02043984 5567.28370644 5659.24831854 5695.06577065 5783.46217755
5824.01080702 5825.59003768 5818.22316662 5866.52307412 5896.96917375
5948.92254787 5968.30540534 5931.15611704 5941.94457249 5942.06535486]

>>> print(cog.profile_error) # doctest: +FLOAT_CMP
[ 0. 5.32777186 9.37111012 13.41750992 16.62928904
21.7350922 25.39862532 30.3867526 34.11478867 39.28263973
43.96047829 48.11931395 52.00967328 55.7471834 60.48824739
64.81392778 68.71042311 72.71899201 76.54959872 81.33806741
85.98568713 91.34841248 95.5173253 99.22190499 102.51980185
106.83601366]
[ 5.32777186 9.37111012 13.41750992 16.62928904 21.7350922
25.39862532 30.3867526 34.11478867 39.28263973 43.96047829
48.11931395 52.00967328 55.7471834 60.48824739 64.81392778
68.71042311 72.71899201 76.54959872 81.33806741 85.98568713
91.34841248 95.5173253 99.22190499 102.51980185 106.83601366]

If desired, the curve of growth profile can be normalized using the
:meth:`~photutils.profiles.RadialProfile.normalize` method.
Expand Down Expand Up @@ -344,11 +289,8 @@ error:
xycen = centroid_quadratic(data, xpeak=47, ypeak=52)

# create the radial profile
min_radius = 0.0
max_radius = 25.0
radius_step = 1.0
cog = CurveOfGrowth(data, xycen, min_radius, max_radius, radius_step,
error=error, mask=None)
radii = np.arange(1, 26)
cog = CurveOfGrowth(data, xycen, radii, error=error, mask=None)

# plot the radial profile
cog.plot()
Expand Down Expand Up @@ -379,11 +321,8 @@ list of the apertures. Let's plot a couple of the apertures on the data:
xycen = centroid_quadratic(data, xpeak=47, ypeak=52)

# create the radial profile
min_radius = 0.0
max_radius = 25.0
radius_step = 1.0
cog = CurveOfGrowth(data, xycen, min_radius, max_radius, radius_step,
error=error, mask=None)
radii = np.arange(1, 26)
cog = CurveOfGrowth(data, xycen, radii, error=error, mask=None)

norm = simple_norm(data, 'sqrt')
plt.figure(figsize=(5, 5))
Expand Down