plotastrodata 1.8.15__tar.gz → 1.8.16__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {plotastrodata-1.8.15/plotastrodata.egg-info → plotastrodata-1.8.16}/PKG-INFO +1 -1
- {plotastrodata-1.8.15 → plotastrodata-1.8.16}/plotastrodata/__init__.py +1 -1
- {plotastrodata-1.8.15 → plotastrodata-1.8.16}/plotastrodata/analysis_utils.py +18 -33
- {plotastrodata-1.8.15 → plotastrodata-1.8.16}/plotastrodata/fitting_utils.py +116 -27
- {plotastrodata-1.8.15 → plotastrodata-1.8.16}/plotastrodata/other_utils.py +0 -25
- {plotastrodata-1.8.15 → plotastrodata-1.8.16/plotastrodata.egg-info}/PKG-INFO +1 -1
- {plotastrodata-1.8.15 → plotastrodata-1.8.16}/LICENSE +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.16}/MANIFEST.in +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.16}/README.md +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.16}/plotastrodata/const_utils.py +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.16}/plotastrodata/coord_utils.py +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.16}/plotastrodata/ext_utils.py +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.16}/plotastrodata/fft_utils.py +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.16}/plotastrodata/fits_utils.py +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.16}/plotastrodata/los_utils.py +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.16}/plotastrodata/matrix_utils.py +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.16}/plotastrodata/noise_utils.py +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.16}/plotastrodata/plot_utils.py +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.16}/plotastrodata.egg-info/SOURCES.txt +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.16}/plotastrodata.egg-info/dependency_links.txt +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.16}/plotastrodata.egg-info/not-zip-safe +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.16}/plotastrodata.egg-info/requires.txt +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.16}/plotastrodata.egg-info/top_level.txt +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.16}/setup.cfg +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.16}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plotastrodata
|
|
3
|
-
Version: 1.8.
|
|
3
|
+
Version: 1.8.16
|
|
4
4
|
Summary: plotastrodata is a tool for astronomers to create figures from FITS files and perform fundamental data analyses with ease.
|
|
5
5
|
Home-page: https://github.com/yusukeaso-astron/plotastrodata
|
|
6
6
|
Download-URL: https://github.com/yusukeaso-astron/plotastrodata
|
|
@@ -2,17 +2,16 @@ import numpy as np
|
|
|
2
2
|
import warnings
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
from scipy.interpolate import RegularGridInterpolator as RGI
|
|
5
|
-
from scipy.optimize import curve_fit
|
|
6
5
|
from scipy.signal import convolve
|
|
7
6
|
|
|
8
7
|
from plotastrodata import const_utils as cu
|
|
9
8
|
from plotastrodata.coord_utils import coord2xy, rel2abs, xy2coord
|
|
10
9
|
from plotastrodata.fits_utils import data2fits, FitsData, Jy2K
|
|
11
|
-
from plotastrodata.fitting_utils import EmceeCorner,
|
|
10
|
+
from plotastrodata.fitting_utils import (EmceeCorner, gaussian2d,
|
|
11
|
+
gaussfit1d, gaussfit2d)
|
|
12
12
|
from plotastrodata.matrix_utils import dot2d, Mfac, Mrot
|
|
13
13
|
from plotastrodata.noise_utils import estimate_rms
|
|
14
|
-
from plotastrodata.other_utils import
|
|
15
|
-
RGIxy, RGIxyv, to4dim, trim)
|
|
14
|
+
from plotastrodata.other_utils import isdeg, RGIxy, RGIxyv, to4dim, trim
|
|
16
15
|
|
|
17
16
|
|
|
18
17
|
def quadrantmean(data: np.ndarray, x: np.ndarray, y: np.ndarray,
|
|
@@ -106,7 +105,7 @@ class AstroData():
|
|
|
106
105
|
beam: np.ndarray | tuple[None] = (None, None, None)
|
|
107
106
|
fitsimage: str | None = None
|
|
108
107
|
Tb: bool = False
|
|
109
|
-
sigma: str = 'hist'
|
|
108
|
+
sigma: str | float | None = 'hist'
|
|
110
109
|
center: str = 'common'
|
|
111
110
|
restfreq: float | None = None
|
|
112
111
|
cfactor: float = 1
|
|
@@ -333,47 +332,32 @@ class AstroData():
|
|
|
333
332
|
|
|
334
333
|
@_need_multipixels
|
|
335
334
|
def gaussfit2d(self, chan: int | None = None) -> dict:
|
|
336
|
-
"""Fit a 2D Gaussian function to self.data.
|
|
335
|
+
"""Fit a 2D Gaussian function to self.data using fitting_utils.gaussfit2d().
|
|
337
336
|
|
|
338
337
|
Args:
|
|
339
338
|
chan (int): The channel number where the 2D Gaussian is fitted. Defaults to None.
|
|
340
339
|
|
|
341
340
|
Returns:
|
|
342
|
-
dict: The best parameter set (popt), the
|
|
341
|
+
dict: The best parameter set (popt), the error set (perr), the best 2D Gaussian array (model), the residual from the model (residual), and the coordinates of the best-fit center (center).
|
|
343
342
|
"""
|
|
344
|
-
|
|
345
|
-
x = self.x
|
|
346
|
-
y = self.y
|
|
347
|
-
ds = np.min([np.abs(self.dx), np.abs(self.dy)])
|
|
348
|
-
p0 = (np.max(d), np.median(x), np.median(y), 5 * ds, 5 * ds, 0)
|
|
349
|
-
amax = np.max(np.abs(d))
|
|
350
|
-
xmin = np.min(x)
|
|
351
|
-
xmax = np.max(x)
|
|
352
|
-
ymin = np.min(y)
|
|
353
|
-
ymax = np.max(y)
|
|
354
|
-
smax = np.max([xmax - xmin, ymax - ymin])
|
|
355
|
-
bounds = [[-amax, xmin, ymin, ds, ds, -90],
|
|
356
|
-
[amax, xmax, ymax, smax, smax, 90]]
|
|
357
|
-
x, y = np.meshgrid(x, y)
|
|
343
|
+
z = self.data if chan is None else self.data[chan]
|
|
358
344
|
Omega = np.pi * self.beam[0] * self.beam[1] / 4 / np.log(2)
|
|
359
345
|
pixelperbeam = Omega / np.abs(self.dx * self.dy)
|
|
360
346
|
s = 'sigma is multiplied by sqrt(pixel-per-beam)' \
|
|
361
347
|
+ ' to consider the noise correlation in a beam.' \
|
|
362
348
|
+ ' This correction is relatively conservative.'
|
|
363
349
|
warnings.warn(s, UserWarning)
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
xy = popt[1:3] / 3600
|
|
373
|
-
newcenter = xy2coord(xy, coordorg=center)
|
|
350
|
+
res = gaussfit2d(xdata=self.x, ydata=self.y, zdata=z,
|
|
351
|
+
sigma=self.sigma * np.sqrt(pixelperbeam),
|
|
352
|
+
show=False, nwalkersperdim=4)
|
|
353
|
+
popt, perr = res['popt'], res['perr']
|
|
354
|
+
model = gaussian2d(np.meshgrid(self.x, self.y), *popt)
|
|
355
|
+
residual = z - model
|
|
356
|
+
if self.center is not None:
|
|
357
|
+
newcenter = xy2coord(popt[1:3] / 3600, coordorg=self.center)
|
|
374
358
|
else:
|
|
375
359
|
newcenter = None
|
|
376
|
-
return {'popt': popt, '
|
|
360
|
+
return {'popt': popt, 'perr': perr,
|
|
377
361
|
'model': model, 'residual': residual,
|
|
378
362
|
'center': newcenter}
|
|
379
363
|
|
|
@@ -468,7 +452,8 @@ class AstroData():
|
|
|
468
452
|
res = [None] * nprof
|
|
469
453
|
for i in range(nprof):
|
|
470
454
|
res[i] = gaussfit1d(xdata=v, ydata=prof[i],
|
|
471
|
-
sigma=None, show=True
|
|
455
|
+
sigma=None, show=True,
|
|
456
|
+
nwalkersperdim=8)
|
|
472
457
|
gfitres['best'] = [a['popt'][:3] for a in res]
|
|
473
458
|
gfitres['error'] = [a['perr'][:3] for a in res]
|
|
474
459
|
return v, prof, gfitres
|
|
@@ -8,6 +8,7 @@ from dynesty import DynamicNestedSampler as DNS
|
|
|
8
8
|
from multiprocessing import Pool
|
|
9
9
|
from tqdm import tqdm
|
|
10
10
|
|
|
11
|
+
from plotastrodata.matrix_utils import Mrot, dot2d
|
|
11
12
|
from plotastrodata.other_utils import close_figure
|
|
12
13
|
|
|
13
14
|
|
|
@@ -395,6 +396,47 @@ class EmceeCorner():
|
|
|
395
396
|
return {'evidence': evidence, 'error': error}
|
|
396
397
|
|
|
397
398
|
|
|
399
|
+
def gaussian1d(x: np.ndarray | float,
|
|
400
|
+
amplitude: float, xo: float, fwhm: float,
|
|
401
|
+
) -> np.ndarray:
|
|
402
|
+
"""One dimensional Gaussian function.
|
|
403
|
+
|
|
404
|
+
Args:
|
|
405
|
+
x (np.ndarray): Variable of the Gaussian function.
|
|
406
|
+
amplitude (float): Peak value.
|
|
407
|
+
xo (float): Offset in the x direction.
|
|
408
|
+
fwhm (float): Full width at half maximum.
|
|
409
|
+
|
|
410
|
+
Returns:
|
|
411
|
+
g (np.ndarray): 1D numpy array.
|
|
412
|
+
"""
|
|
413
|
+
g = amplitude * np.exp2(-4 * (((x - xo) / fwhm)**2))
|
|
414
|
+
return g
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
def gaussian2d(xy: np.ndarray,
|
|
418
|
+
amplitude: float, xo: float, yo: float,
|
|
419
|
+
fwhm_major: float, fwhm_minor: float, pa: float
|
|
420
|
+
) -> np.ndarray:
|
|
421
|
+
"""Two dimensional Gaussian function.
|
|
422
|
+
|
|
423
|
+
Args:
|
|
424
|
+
xy (np.ndarray): A pair of (x, y).
|
|
425
|
+
amplitude (float): Peak value.
|
|
426
|
+
xo (float): Offset in the x direction.
|
|
427
|
+
yo (float): Offset in the y direction.
|
|
428
|
+
fwhm_major (float): Full width at half maximum in the major axis (but can be shorter than the minor axis).
|
|
429
|
+
fwhm_minor (float): Full width at half maximum in the minor axis (but can be longer then the major axis).
|
|
430
|
+
pa (float): Position angle of the major axis from the +y axis to the +x axis in the unit of degree.
|
|
431
|
+
|
|
432
|
+
Returns:
|
|
433
|
+
g (np.ndarray): Output array in the same shape as xy.
|
|
434
|
+
"""
|
|
435
|
+
s, t = dot2d(Mrot(-pa), [xy[1] - yo, xy[0] - xo])
|
|
436
|
+
g = amplitude * np.exp2(-4 * ((s / fwhm_major)**2 + (t / fwhm_minor)**2))
|
|
437
|
+
return g
|
|
438
|
+
|
|
439
|
+
|
|
398
440
|
def gaussfit1d(xdata: np.ndarray, ydata: np.ndarray,
|
|
399
441
|
sigma: float | np.ndarray | None,
|
|
400
442
|
show: bool = False, **kwargs) -> dict:
|
|
@@ -403,43 +445,90 @@ def gaussfit1d(xdata: np.ndarray, ydata: np.ndarray,
|
|
|
403
445
|
Args:
|
|
404
446
|
xdata (np.ndarray): ydata is compared with Gauss(xdata).
|
|
405
447
|
ydata (np.ndarray): ydata is compared with Gauss(xdata).
|
|
406
|
-
sigma (float | np.ndarray | None): Noise level of ydata. If None is given, sigma is
|
|
448
|
+
sigma (float | np.ndarray | None): Noise level of ydata. If None is given, sigma is estimated by a temporary fitting. Defaults to None.
|
|
407
449
|
show (bool, optional): True means to show the best-fit parameters and uncertainties. Defaults to False.
|
|
408
450
|
|
|
409
451
|
Returns:
|
|
410
|
-
dict:
|
|
452
|
+
dict: The keys are popt, perr, and sigma.
|
|
411
453
|
"""
|
|
412
|
-
if sigma is not None and np.shape(sigma) == ():
|
|
413
|
-
sigma = [sigma] * len(xdata)
|
|
414
454
|
xmin, xmax = np.min(xdata), np.max(xdata)
|
|
415
455
|
ymin, ymax = np.min(ydata), np.max(ydata)
|
|
456
|
+
xw = xmax - xmin
|
|
457
|
+
yw = ymax - ymin
|
|
416
458
|
dx = np.abs(xdata[1] - xdata[0])
|
|
417
|
-
bounds = [[ymin, ymax], [xmin, xmax], [dx,
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
if sigma is None:
|
|
426
|
-
def logl(p):
|
|
427
|
-
sigmdl = np.exp(p[3])
|
|
428
|
-
chi2 = np.sum(((ydata - g(xdata, p[:3])) / sigmdl)**2)
|
|
429
|
-
return -0.5 * chi2 - p[3]
|
|
430
|
-
else:
|
|
431
|
-
def logl(p):
|
|
432
|
-
chi2 = np.sum(((ydata - g(xdata, p)) / sigma)**2)
|
|
433
|
-
return -0.5 * chi2
|
|
434
|
-
|
|
435
|
-
fitter = EmceeCorner(bounds=bounds, logl=logl)
|
|
436
|
-
fitter.fit(**kwargs)
|
|
459
|
+
bounds = [[ymin - yw * 10, ymax + yw * 10], [xmin, xmax], [dx, xw]]
|
|
460
|
+
sigtmp = sigma or max(np.abs(ymin), np.abs(ymax)) * 0.01
|
|
461
|
+
for i in range(2 if sigma is None else 1):
|
|
462
|
+
fitter = EmceeCorner(bounds=bounds, model=gaussian1d,
|
|
463
|
+
sigma=sigtmp, xdata=xdata, ydata=ydata)
|
|
464
|
+
fitter.fit(**kwargs)
|
|
465
|
+
if i == 0:
|
|
466
|
+
sigtmp = np.std(ydata - gaussian1d(xdata, *fitter.popt))
|
|
437
467
|
popt = fitter.popt
|
|
438
468
|
plow = fitter.plow
|
|
439
469
|
phigh = fitter.phigh
|
|
440
470
|
perr = (phigh - plow) / 2
|
|
441
|
-
pars = 'peak, center, FWHM' + (', lnsigma' if sigma is None else '')
|
|
442
471
|
if show:
|
|
443
|
-
print(
|
|
472
|
+
print('Gauss (peak, center, FWHM):', popt)
|
|
473
|
+
print('Gauss uncertainties:', perr)
|
|
474
|
+
if sigma is None:
|
|
475
|
+
print('Estimated sigma: ', sigtmp)
|
|
476
|
+
return {'popt': popt, 'perr': perr, 'sigma': sigtmp}
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
def gaussfit2d(xdata: np.ndarray, ydata: np.ndarray, zdata: np.ndarray,
|
|
480
|
+
sigma: float | np.ndarray | None,
|
|
481
|
+
show: bool = False, **kwargs) -> dict:
|
|
482
|
+
"""Gaussian fitting to a pair of 1D arrays.
|
|
483
|
+
|
|
484
|
+
Args:
|
|
485
|
+
xdata (np.ndarray): zdata is compared with Gauss(xdata, ydata).
|
|
486
|
+
ydata (np.ndarray): zdata is compared with Gauss(xdata, ydata).
|
|
487
|
+
zdata (np.ndarray): zdata is compared with Gauss(xdata, ydata).
|
|
488
|
+
sigma (float | np.ndarray | None): Noise level of ydata. If None is given, sigma is estimated by a temporary fitting. Defaults to None.
|
|
489
|
+
show (bool, optional): True means to show the best-fit parameters and uncertainties. Defaults to False.
|
|
490
|
+
|
|
491
|
+
Returns:
|
|
492
|
+
dict: The keys are popt, perr, and sigma.
|
|
493
|
+
"""
|
|
494
|
+
xmin, xmax = np.min(xdata), np.max(xdata)
|
|
495
|
+
ymin, ymax = np.min(ydata), np.max(ydata)
|
|
496
|
+
zmin, zmax = np.min(zdata), np.max(zdata)
|
|
497
|
+
xw = xmax - xmin
|
|
498
|
+
yw = ymax - ymin
|
|
499
|
+
zw = zmax - zmin
|
|
500
|
+
dx = min(np.abs(xdata[1] - xdata[0]), np.abs(ydata[1] - ydata[0]))
|
|
501
|
+
xw = max(xw, yw)
|
|
502
|
+
xy = np.meshgrid(xdata, ydata)
|
|
503
|
+
sigtmp = sigma or max(np.abs(zmin), np.abs(zmax)) * 0.01
|
|
504
|
+
|
|
505
|
+
def model(xy, a, cx, cy, wmaj, wmin, pa):
|
|
506
|
+
if wmaj < wmin:
|
|
507
|
+
return np.inf
|
|
508
|
+
else:
|
|
509
|
+
return gaussian2d(xy, a, cx, cy, wmaj, wmin, pa)
|
|
510
|
+
|
|
511
|
+
bounds = [[zmin - zw * 10, zmax + zw * 10],
|
|
512
|
+
[xmin, xmax], [ymin, ymax],
|
|
513
|
+
[dx, xw], [dx, xw], [-90, 90]]
|
|
514
|
+
for i in range(2):
|
|
515
|
+
fitter = EmceeCorner(bounds=bounds, model=model,
|
|
516
|
+
sigma=sigtmp, xdata=xy, ydata=zdata)
|
|
517
|
+
fitter.fit(**kwargs)
|
|
518
|
+
a, cx, cy, wmaj, wmin, pa = fitter.popt
|
|
519
|
+
wmax = max(wmaj, wmin)
|
|
520
|
+
bounds = [sorted([a / 2, a * 2]),
|
|
521
|
+
[cx - wmax, cx + wmax], [cy - wmax, cy + wmax],
|
|
522
|
+
[wmaj / 2, wmaj * 2], [wmin / 2, wmin * 2],
|
|
523
|
+
[pa - 45, pa + 45]]
|
|
524
|
+
if i == 0 and sigma is None:
|
|
525
|
+
sigtmp = np.std(ydata - gaussian2d(xy, *fitter.popt))
|
|
526
|
+
popt = fitter.popt
|
|
527
|
+
popt[-1] = (popt[-1] + 90) % 180 - 90
|
|
528
|
+
perr = (fitter.phigh - fitter.plow) / 2
|
|
529
|
+
if show:
|
|
530
|
+
print('Gauss (peak, center, FWHM):', popt)
|
|
444
531
|
print('Gauss uncertainties:', perr)
|
|
445
|
-
|
|
532
|
+
if sigma is None:
|
|
533
|
+
print('Estimated sigma: ', sigtmp)
|
|
534
|
+
return {'popt': popt, 'perr': perr, 'sigma': sigtmp}
|
|
@@ -2,8 +2,6 @@ import matplotlib.pyplot as plt
|
|
|
2
2
|
import numpy as np
|
|
3
3
|
from scipy.interpolate import RegularGridInterpolator as RGI
|
|
4
4
|
|
|
5
|
-
from plotastrodata.matrix_utils import Mrot, dot2d
|
|
6
|
-
|
|
7
5
|
|
|
8
6
|
def listing(*args) -> list:
|
|
9
7
|
"""Output a list of the input when the input is string or number.
|
|
@@ -176,29 +174,6 @@ def RGIxyv(v: np.ndarray, y: np.ndarray, x: np.ndarray, data: np.ndarray,
|
|
|
176
174
|
return np.squeeze([f3d(tuple(vyxnew)) for f3d in f])
|
|
177
175
|
|
|
178
176
|
|
|
179
|
-
def gaussian2d(xy: np.ndarray,
|
|
180
|
-
amplitude: float, xo: float, yo: float,
|
|
181
|
-
fwhm_major: float, fwhm_minor: float, pa: float
|
|
182
|
-
) -> np.ndarray:
|
|
183
|
-
"""Two dimensional Gaussian function.
|
|
184
|
-
|
|
185
|
-
Args:
|
|
186
|
-
xy (np.ndarray): A pair of (x, y).
|
|
187
|
-
amplitude (float): Peak value.
|
|
188
|
-
xo (float): Offset in the x direction.
|
|
189
|
-
yo (float): Offset in the y direction.
|
|
190
|
-
fwhm_major (float): Full width at half maximum in the major axis (but can be shorter than the minor axis).
|
|
191
|
-
fwhm_minor (float): Full width at half maximum in the minor axis (but can be longer then the major axis).
|
|
192
|
-
pa (float): Position angle of the major axis from the +y axis to the +x axis in the unit of degree.
|
|
193
|
-
|
|
194
|
-
Returns:
|
|
195
|
-
g (np.ndarray): 2D numpy array.
|
|
196
|
-
"""
|
|
197
|
-
s, t = dot2d(Mrot(-pa), [xy[1] - yo, xy[0] - xo])
|
|
198
|
-
g = amplitude * np.exp2(-4 * ((s / fwhm_major)**2 + (t / fwhm_minor)**2))
|
|
199
|
-
return g
|
|
200
|
-
|
|
201
|
-
|
|
202
177
|
def close_figure(fig: object, savefig: dict | str | None = None,
|
|
203
178
|
show: bool = False, tight: bool = True) -> None:
|
|
204
179
|
"""Save, show, and close the figure.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plotastrodata
|
|
3
|
-
Version: 1.8.
|
|
3
|
+
Version: 1.8.16
|
|
4
4
|
Summary: plotastrodata is a tool for astronomers to create figures from FITS files and perform fundamental data analyses with ease.
|
|
5
5
|
Home-page: https://github.com/yusukeaso-astron/plotastrodata
|
|
6
6
|
Download-URL: https://github.com/yusukeaso-astron/plotastrodata
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|