plotastrodata 1.9.10__tar.gz → 1.9.11__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.9.10/plotastrodata.egg-info → plotastrodata-1.9.11}/PKG-INFO +1 -1
- {plotastrodata-1.9.10 → plotastrodata-1.9.11}/plotastrodata/__init__.py +1 -1
- {plotastrodata-1.9.10 → plotastrodata-1.9.11}/plotastrodata/analysis_utils.py +113 -101
- {plotastrodata-1.9.10 → plotastrodata-1.9.11}/plotastrodata/coord_utils.py +1 -1
- {plotastrodata-1.9.10 → plotastrodata-1.9.11}/plotastrodata/fits_utils.py +1 -1
- {plotastrodata-1.9.10 → plotastrodata-1.9.11}/plotastrodata/fitting_utils.py +99 -75
- {plotastrodata-1.9.10 → plotastrodata-1.9.11}/plotastrodata/noise_utils.py +1 -1
- {plotastrodata-1.9.10 → plotastrodata-1.9.11/plotastrodata.egg-info}/PKG-INFO +1 -1
- {plotastrodata-1.9.10 → plotastrodata-1.9.11}/LICENSE +0 -0
- {plotastrodata-1.9.10 → plotastrodata-1.9.11}/MANIFEST.in +0 -0
- {plotastrodata-1.9.10 → plotastrodata-1.9.11}/README.md +0 -0
- {plotastrodata-1.9.10 → plotastrodata-1.9.11}/plotastrodata/const_utils.py +0 -0
- {plotastrodata-1.9.10 → plotastrodata-1.9.11}/plotastrodata/ext_utils.py +0 -0
- {plotastrodata-1.9.10 → plotastrodata-1.9.11}/plotastrodata/fft_utils.py +0 -0
- {plotastrodata-1.9.10 → plotastrodata-1.9.11}/plotastrodata/los_utils.py +0 -0
- {plotastrodata-1.9.10 → plotastrodata-1.9.11}/plotastrodata/matrix_utils.py +0 -0
- {plotastrodata-1.9.10 → plotastrodata-1.9.11}/plotastrodata/other_utils.py +0 -0
- {plotastrodata-1.9.10 → plotastrodata-1.9.11}/plotastrodata/plot_utils.py +0 -0
- {plotastrodata-1.9.10 → plotastrodata-1.9.11}/plotastrodata.egg-info/SOURCES.txt +0 -0
- {plotastrodata-1.9.10 → plotastrodata-1.9.11}/plotastrodata.egg-info/dependency_links.txt +0 -0
- {plotastrodata-1.9.10 → plotastrodata-1.9.11}/plotastrodata.egg-info/not-zip-safe +0 -0
- {plotastrodata-1.9.10 → plotastrodata-1.9.11}/plotastrodata.egg-info/requires.txt +0 -0
- {plotastrodata-1.9.10 → plotastrodata-1.9.11}/plotastrodata.egg-info/top_level.txt +0 -0
- {plotastrodata-1.9.10 → plotastrodata-1.9.11}/setup.cfg +0 -0
- {plotastrodata-1.9.10 → plotastrodata-1.9.11}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plotastrodata
|
|
3
|
-
Version: 1.9.
|
|
3
|
+
Version: 1.9.11
|
|
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
|
|
@@ -33,18 +33,19 @@ def quadrantmean(data: np.ndarray, x: np.ndarray, y: np.ndarray,
|
|
|
33
33
|
print('data must be 2D.')
|
|
34
34
|
return
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
else
|
|
47
|
-
|
|
36
|
+
if quadrants not in ['13', '24']:
|
|
37
|
+
print("quadrants must be '13' or '24'.")
|
|
38
|
+
return
|
|
39
|
+
|
|
40
|
+
dx = x[1] - x[0]
|
|
41
|
+
dy = y[1] - y[0]
|
|
42
|
+
nx = int(np.ceil(np.max(np.abs(x)) / dx))
|
|
43
|
+
ny = int(np.ceil(np.max(np.abs(y)) / dy))
|
|
44
|
+
xnew = np.linspace(-nx, nx, 2 * nx + 1) * dx
|
|
45
|
+
ynew = np.linspace(-ny, ny, 2 * ny + 1) * dy
|
|
46
|
+
s = 1 if quadrants == '13' else -1
|
|
47
|
+
f = RGI((y, s * x), data, bounds_error=False, fill_value=np.nan)
|
|
48
|
+
datanew = f(np.meshgrid(ynew, xnew, indexing='ij'))
|
|
48
49
|
datanew = (datanew + datanew[::-1, ::-1]) / 2.
|
|
49
50
|
return datanew[ny:, nx:], xnew[nx:], ynew[ny:]
|
|
50
51
|
|
|
@@ -70,12 +71,12 @@ def filled2d(data: np.ndarray, x: np.ndarray, y: np.ndarray, n: int = 1,
|
|
|
70
71
|
|
|
71
72
|
|
|
72
73
|
def _need_multipixels(method):
|
|
73
|
-
def wrapper(
|
|
74
|
-
singlepixel =
|
|
74
|
+
def wrapper(cls, *args, **kwargs):
|
|
75
|
+
singlepixel = cls.dx is None or cls.dy is None
|
|
75
76
|
if singlepixel:
|
|
76
77
|
print('No pixel size.')
|
|
77
78
|
return
|
|
78
|
-
return method(
|
|
79
|
+
return method(cls, *args, **kwargs)
|
|
79
80
|
return wrapper
|
|
80
81
|
|
|
81
82
|
|
|
@@ -140,57 +141,60 @@ class AstroData():
|
|
|
140
141
|
self.beam_org = None
|
|
141
142
|
self.fitsheader = None
|
|
142
143
|
|
|
143
|
-
def
|
|
144
|
+
def _binning_one(self, t: str, width: float):
|
|
145
|
+
grid = getattr(self, t)
|
|
146
|
+
if width == 1 or grid is None:
|
|
147
|
+
return
|
|
148
|
+
|
|
149
|
+
dt = f'd{t}'
|
|
150
|
+
sep = getattr(self, dt)
|
|
151
|
+
if sep is None:
|
|
152
|
+
s = f'Skip binning in the {t}-axis because {dt} is None.'
|
|
153
|
+
warnings.warn(s, UserWarning)
|
|
154
|
+
return
|
|
155
|
+
|
|
156
|
+
i = {'v': 1, 'y': 2, 'x': 3}[t]
|
|
157
|
+
sizenew = self.size[i] // width
|
|
158
|
+
self.size[i] = sizenew
|
|
159
|
+
data = np.moveaxis(self.data, i, 0)
|
|
160
|
+
datanew = np.moveaxis(np.zeros(self.size), i, 0)
|
|
161
|
+
gridnew = np.zeros(sizenew)
|
|
162
|
+
for start in range(width):
|
|
163
|
+
stop = start + sizenew * width
|
|
164
|
+
datanew += data[start:stop:width]
|
|
165
|
+
gridnew += grid[start:stop:width]
|
|
166
|
+
self.data = np.moveaxis(datanew, 0, i) / width
|
|
167
|
+
setattr(self, t, gridnew / width)
|
|
168
|
+
setattr(self, dt, sep * width)
|
|
169
|
+
|
|
170
|
+
def binning(self, width: list[int] = [1, 1, 1]):
|
|
144
171
|
"""Binning up neighboring pixels in the v, y, and x domain.
|
|
145
172
|
|
|
146
173
|
Args:
|
|
147
174
|
width (list, optional): Number of channels, y-pixels, and x-pixels for binning. Defaults to [1, 1, 1].
|
|
148
175
|
"""
|
|
149
|
-
w = [1] * (
|
|
176
|
+
w = np.array([1] * (3 - len(width)) + list(width), dtype=int)
|
|
150
177
|
if self.pv:
|
|
151
|
-
w[
|
|
152
|
-
w[
|
|
153
|
-
|
|
154
|
-
size = np.array(np.shape(
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
ws = ', '.join([f'{s:d}' for s in w[1:]])
|
|
178
|
+
w[1] = max(w[0], w[1])
|
|
179
|
+
w[2] = 1
|
|
180
|
+
self.data = to4dim(self.data)
|
|
181
|
+
size = np.array(np.shape(self.data))
|
|
182
|
+
if np.any(w > size[1:]):
|
|
183
|
+
w = np.minimum(w, size[1:])
|
|
184
|
+
ws = ', '.join([f'{s:d}' for s in w])
|
|
159
185
|
print(f'width was changed to [{ws}].')
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
width_v = w[2] if self.pv else w[1]
|
|
186
|
+
if (not self.pv and w[0] > 1) or (self.pv and w[1] > 1):
|
|
187
|
+
width_v = w[1] if self.pv else w[0]
|
|
163
188
|
print(f'sigma has been divided by sqrt({width_v:d})'
|
|
164
189
|
+ ' because of binning in the v-axis.')
|
|
165
190
|
self.sigma = self.sigma / np.sqrt(width_v)
|
|
166
|
-
if (not self.pv and w[
|
|
191
|
+
if (not self.pv and w[1] > 1) or w[2] > 1:
|
|
167
192
|
print('Binning in the x- or y-axis does not update sigma.')
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if dgrid[n] is None:
|
|
174
|
-
t = ['v', 'y', 'x'][n - 1]
|
|
175
|
-
s = f'Skip binning in the {t}-axis' \
|
|
176
|
-
+ f' because d{t} is None.'
|
|
177
|
-
warnings.warn(s, UserWarning)
|
|
178
|
-
continue
|
|
179
|
-
size[n] = newsize[n]
|
|
180
|
-
olddata = np.moveaxis(d, n, 0)
|
|
181
|
-
newdata = np.moveaxis(np.zeros(size), n, 0)
|
|
182
|
-
t = np.zeros(newsize[n])
|
|
183
|
-
for i in range(w[n]):
|
|
184
|
-
i_stop = i + newsize[n] * w[n]
|
|
185
|
-
i_step = w[n]
|
|
186
|
-
t += grid[n][i:i_stop:i_step]
|
|
187
|
-
newdata += olddata[i:i_stop:i_step]
|
|
188
|
-
grid[n] = t / w[n]
|
|
189
|
-
dgrid[n] = dgrid[n] * w[n]
|
|
190
|
-
d = np.moveaxis(newdata, 0, n) / w[n]
|
|
191
|
-
self.data = np.squeeze(d)
|
|
192
|
-
_, self.v, self.y, self.x = grid
|
|
193
|
-
_, self.dv, self.dy, self.dx = dgrid
|
|
193
|
+
self.size = size
|
|
194
|
+
for t, ww in zip(['v', 'y', 'x'], w):
|
|
195
|
+
self._binning_one(t, ww)
|
|
196
|
+
self.data = np.squeeze(self.data)
|
|
197
|
+
del self.size
|
|
194
198
|
if self.pv:
|
|
195
199
|
self.v = self.y
|
|
196
200
|
self.dv = self.dy
|
|
@@ -245,16 +249,14 @@ class AstroData():
|
|
|
245
249
|
nx = len(self.x) if len(self.x) % 2 == 1 else len(self.x) - 1
|
|
246
250
|
ny = len(self.y) if len(self.y) % 2 == 1 else len(self.y) - 1
|
|
247
251
|
y = np.linspace(-(ny-1) / 2, (ny-1) / 2, ny) * np.abs(self.dy)
|
|
248
|
-
g1 = np.exp(-4*np.log(2) * y**2 / (bmaj**2 - bmin**2))
|
|
252
|
+
g1 = np.exp(-4 * np.log(2) * y**2 / (bmaj**2 - bmin**2))
|
|
249
253
|
e = np.sqrt(1 - bmin**2 / bmaj**2)
|
|
250
254
|
g1 /= np.sqrt(np.pi / 4 / np.log(2) * bmin * e)
|
|
251
255
|
g = np.zeros((ny, nx))
|
|
252
256
|
g[:, (nx - 1) // 2] = g1
|
|
253
257
|
d = self.data.copy()
|
|
254
258
|
d[np.isnan(d)] = 0
|
|
255
|
-
|
|
256
|
-
d = [[convolve(c, g, mode='same') for c in cc] for cc in d]
|
|
257
|
-
self.data = np.squeeze(d)
|
|
259
|
+
self.data = np.squeeze(convolve(to4dim(d), [[g]], mode='same'))
|
|
258
260
|
self.rotate(bpa)
|
|
259
261
|
self.beam[1] = self.beam[0]
|
|
260
262
|
self.beam[2] = 0
|
|
@@ -272,8 +274,10 @@ class AstroData():
|
|
|
272
274
|
self.data = RGIxy(self.y, self.x, self.data, yxnew, **kwargs)
|
|
273
275
|
if None not in self.beam:
|
|
274
276
|
bmaj, bmin, bpa = self.beam
|
|
275
|
-
a, b = np.linalg.multi_dot([Mfac(1/bmaj, 1/bmin),
|
|
276
|
-
|
|
277
|
+
a, b = np.linalg.multi_dot([Mfac(1 / bmaj, 1 / bmin),
|
|
278
|
+
Mrot(pa - bpa),
|
|
279
|
+
Mfac(1, ci),
|
|
280
|
+
Mrot(-pa)]).T
|
|
277
281
|
alpha = (np.dot(a, a) + np.dot(b, b)) / 2
|
|
278
282
|
beta = np.dot(a, b)
|
|
279
283
|
gamma = (np.dot(a, a) - np.dot(b, b)) / 2
|
|
@@ -406,6 +410,21 @@ class AstroData():
|
|
|
406
410
|
if len(excludepix) == 2:
|
|
407
411
|
self.data[(excludepix[0] < mask) * (mask < excludepix[1])] = np.nan
|
|
408
412
|
|
|
413
|
+
def _gfit_profile(self, prof: list, gaussfit: bool):
|
|
414
|
+
if not gaussfit:
|
|
415
|
+
return {}
|
|
416
|
+
|
|
417
|
+
gfitres = {}
|
|
418
|
+
nprof = len(prof)
|
|
419
|
+
res = [None] * nprof
|
|
420
|
+
for i in range(nprof):
|
|
421
|
+
res[i] = gaussfit1d(xdata=self.v, ydata=prof[i],
|
|
422
|
+
sigma=None, show=True,
|
|
423
|
+
nwalkersperdim=8)
|
|
424
|
+
gfitres['best'] = [a['popt'][:3] for a in res]
|
|
425
|
+
gfitres['error'] = [a['perr'][:3] for a in res]
|
|
426
|
+
return gfitres
|
|
427
|
+
|
|
409
428
|
def profile(self, coords: list[str] = [],
|
|
410
429
|
xlist: list[float] = [], ylist: list[float] = [],
|
|
411
430
|
ellipse: list[float, float, float] | None = None,
|
|
@@ -430,13 +449,12 @@ class AstroData():
|
|
|
430
449
|
print('Data must be 3D with the v, y, and x axes.')
|
|
431
450
|
return
|
|
432
451
|
|
|
433
|
-
v = self.v
|
|
434
452
|
data, xf, yf = filled2d(self.data, self.x, self.y, ninterp)
|
|
435
453
|
x, y = np.meshgrid(xf, yf)
|
|
436
454
|
if len(coords) > 0:
|
|
437
455
|
xlist, ylist = coord2xy(coords, self.center) * 3600.
|
|
438
456
|
nprof = len(xlist)
|
|
439
|
-
prof = np.empty((nprof, len(v)))
|
|
457
|
+
prof = np.empty((nprof, len(self.v)))
|
|
440
458
|
ellipse = ellipse or [[0, 0, 0]] * nprof
|
|
441
459
|
calc = np.sum if flux else np.mean
|
|
442
460
|
for i, (xc, yc, e) in enumerate(zip(xlist, ylist, ellipse)):
|
|
@@ -455,16 +473,8 @@ class AstroData():
|
|
|
455
473
|
else:
|
|
456
474
|
Omega = np.pi * self.beam[0] * self.beam[1] / 4. / np.log(2.)
|
|
457
475
|
prof *= np.abs(self.dx * self.dy) / Omega
|
|
458
|
-
gfitres =
|
|
459
|
-
|
|
460
|
-
res = [None] * nprof
|
|
461
|
-
for i in range(nprof):
|
|
462
|
-
res[i] = gaussfit1d(xdata=v, ydata=prof[i],
|
|
463
|
-
sigma=None, show=True,
|
|
464
|
-
nwalkersperdim=8)
|
|
465
|
-
gfitres['best'] = [a['popt'][:3] for a in res]
|
|
466
|
-
gfitres['error'] = [a['perr'][:3] for a in res]
|
|
467
|
-
return v, prof, gfitres
|
|
476
|
+
gfitres = self._gfit_profile(prof, gaussfit)
|
|
477
|
+
return self.v, prof, gfitres
|
|
468
478
|
|
|
469
479
|
def rotate(self, pa: float = 0, **kwargs):
|
|
470
480
|
"""Counter clockwise rotation with respect to the center.
|
|
@@ -515,6 +525,31 @@ class AstroData():
|
|
|
515
525
|
'bunit': self.bunit}
|
|
516
526
|
return d
|
|
517
527
|
|
|
528
|
+
def _put_header(self, h: dict, t: str, crpix: int, crval: float,
|
|
529
|
+
cdelt: float | None = None) -> None:
|
|
530
|
+
fhd = self.fitsheader
|
|
531
|
+
axis = {'x': 1, 'y': 2, 'v': 2 if self.pv else 3}[t]
|
|
532
|
+
u = f'CUNIT{axis}'
|
|
533
|
+
indeg = fhd is None or u not in fhd or isdeg(fhd[u])
|
|
534
|
+
h[f'NAXIS{axis}'] = len(getattr(self, t))
|
|
535
|
+
h[f'CRPIX{axis}'] = int(crpix)
|
|
536
|
+
h[f'CRVAL{axis}'] = float(crval)
|
|
537
|
+
if cdelt is None:
|
|
538
|
+
cdelt = getattr(self, f'd{t}') / (3600 if indeg else 1)
|
|
539
|
+
h[f'CDELT{axis}'] = float(cdelt)
|
|
540
|
+
|
|
541
|
+
def _get_cvdv_in_freq(self, ck: int) -> tuple[float, float]:
|
|
542
|
+
cv = self.v[ck]
|
|
543
|
+
dv = self.dv
|
|
544
|
+
if self.restfreq is None or self.restfreq == 0:
|
|
545
|
+
s = 'No valid restfreq. The velocity axis is saved as is.'
|
|
546
|
+
warnings.warn(s, UserWarning)
|
|
547
|
+
return cv, dv
|
|
548
|
+
|
|
549
|
+
cv = (1 - cv / cu.c_kms) * self.restfreq
|
|
550
|
+
dv = -dv / cu.c_kms * self.restfreq
|
|
551
|
+
return cv, dv
|
|
552
|
+
|
|
518
553
|
@_need_multipixels
|
|
519
554
|
def writetofits(self, fitsimage: str = 'out.fits',
|
|
520
555
|
header: dict = {}) -> None:
|
|
@@ -524,7 +559,6 @@ class AstroData():
|
|
|
524
559
|
fitsimage (str, optional): Output FITS file name. Defaults to 'out.fits'.
|
|
525
560
|
header (dict, optional): Header dictionary. Defaults to {}.
|
|
526
561
|
"""
|
|
527
|
-
fhd = self.fitsheader
|
|
528
562
|
h = {}
|
|
529
563
|
ci = nearest_index(self.x)
|
|
530
564
|
cx = 0
|
|
@@ -536,35 +570,13 @@ class AstroData():
|
|
|
536
570
|
xy = [self.x[ci] / 3600, self.y[cj] / 3600]
|
|
537
571
|
cx, cy = coord2xy(xy2coord(xy, self.center))
|
|
538
572
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
h['NAXIS1'] = len(self.x)
|
|
544
|
-
h['CRPIX1'] = int(ci + 1)
|
|
545
|
-
h['CRVAL1'] = float(cx)
|
|
546
|
-
h['CDELT1'] = float(self.dx / (3600 if indeg('1') else 1))
|
|
573
|
+
self._put_header(h, 'x', crpix=ci + 1, crval=cx)
|
|
574
|
+
if not self.pv:
|
|
575
|
+
self._put_header(h, 'y', crpix=cj + 1, crval=cy)
|
|
547
576
|
if self.dv is not None:
|
|
548
|
-
vaxis = '2' if self.pv else '3'
|
|
549
577
|
ck = nearest_index(self.v)
|
|
550
|
-
cv = self.
|
|
551
|
-
|
|
552
|
-
if self.restfreq is None or self.restfreq == 0:
|
|
553
|
-
s = 'No valid restfreq.' \
|
|
554
|
-
+ f' Axis {vaxis} is saved as is.'
|
|
555
|
-
warnings.warn(s, UserWarning)
|
|
556
|
-
else:
|
|
557
|
-
cv = (1 - cv / cu.c_kms) * self.restfreq
|
|
558
|
-
dv = -dv / cu.c_kms * self.restfreq
|
|
559
|
-
h[f'NAXIS{vaxis}'] = len(self.v)
|
|
560
|
-
h[f'CRPIX{vaxis}'] = int(ck + 1)
|
|
561
|
-
h[f'CRVAL{vaxis}'] = float(cv)
|
|
562
|
-
h[f'CDELT{vaxis}'] = float(dv)
|
|
563
|
-
if not self.pv:
|
|
564
|
-
h['NAXIS2'] = len(self.y)
|
|
565
|
-
h['CRPIX2'] = int(cj + 1)
|
|
566
|
-
h['CRVAL2'] = float(cy)
|
|
567
|
-
h['CDELT2'] = float(self.dy / (3600 if indeg('2') else 1))
|
|
578
|
+
cv, dv = self._get_cvdv_in_freq(ck)
|
|
579
|
+
self._put_header(h, 'v', crpix=ck + 1, crval=cv, cdelt=dv)
|
|
568
580
|
if None not in self.beam:
|
|
569
581
|
beam = self.beam_org if self.pv else self.beam
|
|
570
582
|
h['BMAJ'] = float(beam[0] / 3600)
|
|
@@ -376,7 +376,7 @@ def fits2data(fitsimage: str, Tb: bool = False, log: bool = False,
|
|
|
376
376
|
restfreq: float | None = None, center: str | None = None,
|
|
377
377
|
vsys: float = 0., pv: bool = False, **kwargs
|
|
378
378
|
) -> tuple[np.ndarray, tuple[np.ndarray, np.ndarray, np.ndarray],
|
|
379
|
-
|
|
379
|
+
np.ndarray, str, float]:
|
|
380
380
|
"""Extract data from a fits file. kwargs are arguments of FitsData.trim().
|
|
381
381
|
|
|
382
382
|
Args:
|
|
@@ -281,6 +281,29 @@ class EmceeCorner():
|
|
|
281
281
|
ax.set_xlabel('Step')
|
|
282
282
|
close_figure(fig, savefig, show)
|
|
283
283
|
|
|
284
|
+
def _set_results_posteriorongrid(self) -> None:
|
|
285
|
+
p = self.p
|
|
286
|
+
p1d = self.p1d
|
|
287
|
+
pargrid = self.pargrid
|
|
288
|
+
if np.all(p == 0):
|
|
289
|
+
print('All posterior is below pcut.')
|
|
290
|
+
self.popt = np.full(self.dim, np.nan)
|
|
291
|
+
self.plow = np.full(self.dim, np.nan)
|
|
292
|
+
self.pmid = np.full(self.dim, np.nan)
|
|
293
|
+
self.phigh = np.full(self.dim, np.nan)
|
|
294
|
+
else:
|
|
295
|
+
i_max = np.unravel_index(np.argmax(p), np.shape(p))[::-1]
|
|
296
|
+
self.popt = np.array([p[i] for p, i in zip(pargrid, i_max)])
|
|
297
|
+
|
|
298
|
+
def getpercentile(percent: float):
|
|
299
|
+
a = [np.percentile(g, percent, method='inverted_cdf', weights=p)
|
|
300
|
+
for g, p in zip(pargrid, p1d)]
|
|
301
|
+
return np.array(a)
|
|
302
|
+
|
|
303
|
+
self.plow = getpercentile(self.percent[0])
|
|
304
|
+
self.pmid = getpercentile(50)
|
|
305
|
+
self.phigh = getpercentile(self.percent[1])
|
|
306
|
+
|
|
284
307
|
def posteriorongrid(self, ngrid: list[int] | int = 100,
|
|
285
308
|
log: list[bool] | bool = False, pcut: float = 0):
|
|
286
309
|
"""Calculate the posterior on a grid of ngrid x ngrid x ... x ngrid.
|
|
@@ -297,8 +320,8 @@ class EmceeCorner():
|
|
|
297
320
|
pargrid = []
|
|
298
321
|
inzip = [self.bounds[:, 0], self.bounds[:, 1], ngrid, log]
|
|
299
322
|
for start, stop, num, uselog in zip(*inzip):
|
|
300
|
-
|
|
301
|
-
pargrid.append(
|
|
323
|
+
spacer = np.geomspace if uselog else np.linspace
|
|
324
|
+
pargrid.append(spacer(start, stop, num))
|
|
302
325
|
p = np.exp(self.logl(np.meshgrid(*pargrid[::-1], indexing='ij')[::-1]))
|
|
303
326
|
p[p < pcut] = 0
|
|
304
327
|
dpar = []
|
|
@@ -309,33 +332,74 @@ class EmceeCorner():
|
|
|
309
332
|
else:
|
|
310
333
|
dpar.append(pg * 0 + pg[1] - pg[0])
|
|
311
334
|
vol = np.prod(np.meshgrid(*dpar[::-1], indexing='ij')[::-1], axis=0)
|
|
312
|
-
|
|
313
|
-
|
|
335
|
+
arrdim = np.arange(self.dim)
|
|
336
|
+
arrdim_r = arrdim[::-1]
|
|
337
|
+
# arrdim[::-1] is becuase the 0th parameter is the innermost axis.
|
|
338
|
+
axlist = [tuple(np.delete(arrdim, i)) for i in arrdim_r]
|
|
314
339
|
p1d = [np.sum(p * vol, axis=a) / np.sum(vol, axis=a) for a in axlist]
|
|
315
340
|
evidence = np.sum(p * vol) / np.sum(vol)
|
|
316
|
-
if np.all(p == 0):
|
|
317
|
-
print('All posterior is below pcut.')
|
|
318
|
-
self.popt = np.full(self.dim, np.nan)
|
|
319
|
-
self.plow = np.full(self.dim, np.nan)
|
|
320
|
-
self.pmid = np.full(self.dim, np.nan)
|
|
321
|
-
self.phigh = np.full(self.dim, np.nan)
|
|
322
|
-
else:
|
|
323
|
-
i_max = np.unravel_index(np.argmax(p), np.shape(p))[::-1]
|
|
324
|
-
self.popt = np.array([p[i] for p, i in zip(pargrid, i_max)])
|
|
325
|
-
|
|
326
|
-
def getpercentile(percent: float):
|
|
327
|
-
a = [np.percentile(g, percent, method='inverted_cdf', weights=p)
|
|
328
|
-
for g, p in zip(pargrid, p1d)]
|
|
329
|
-
return np.array(a)
|
|
330
|
-
|
|
331
|
-
self.plow = getpercentile(self.percent[0])
|
|
332
|
-
self.pmid = getpercentile(50)
|
|
333
|
-
self.phigh = getpercentile(self.percent[1])
|
|
334
341
|
self.p = p
|
|
335
342
|
self.p1d = p1d
|
|
336
343
|
self.pargrid = pargrid
|
|
337
344
|
self.vol = vol
|
|
338
345
|
self.evidence = evidence
|
|
346
|
+
self.arrdim = arrdim
|
|
347
|
+
self.arrdim_r = arrdim_r
|
|
348
|
+
self._set_results_posteriorongrid()
|
|
349
|
+
|
|
350
|
+
def _i_eq_j(self, fig, ax, i: int, k: int) -> None:
|
|
351
|
+
x = self.pargrid
|
|
352
|
+
y = self.p1d
|
|
353
|
+
s0 = self.pmid[i]
|
|
354
|
+
s1 = self.phigh[i] - self.pmid[i]
|
|
355
|
+
s2 = self.pmid[i] - self.plow[i]
|
|
356
|
+
s0 = f'{s0:.2f}'
|
|
357
|
+
s1 = '^{+' + f'{s1:.2f}' + '}'
|
|
358
|
+
s2 = '_{-' + f'{s2:.2f}' + '}'
|
|
359
|
+
s0 = s0 + s1 + s2
|
|
360
|
+
ax[k] = fig.add_subplot(self.dim, self.dim, k + 1)
|
|
361
|
+
ax[k].plot(x[i], y[i], 'k-', drawstyle='steps-mid')
|
|
362
|
+
ax[k].axvline(self.popt[i])
|
|
363
|
+
ax[k].axvline(self.plow[i], linestyle='--', color='k')
|
|
364
|
+
ax[k].axvline(self.pmid[i], linestyle='--', color='k')
|
|
365
|
+
ax[k].axvline(self.phigh[i], linestyle='--', color='k')
|
|
366
|
+
ax[k].set_title(rf'{self.labels[i]}=${s0}$')
|
|
367
|
+
ax[k].set_xlim(self.cornerrange[i])
|
|
368
|
+
ax[k].set_ylim([0, np.max(y[i]) * 1.2])
|
|
369
|
+
ax[k].set_yticks([])
|
|
370
|
+
if i == self.dim - 1:
|
|
371
|
+
plt.setp(ax[k].get_xticklabels(), rotation=45)
|
|
372
|
+
ax[k].set_xlabel(self.labels[i])
|
|
373
|
+
else:
|
|
374
|
+
plt.setp(ax[k].get_xticklabels(), visible=False)
|
|
375
|
+
|
|
376
|
+
def _i_neq_j(self, fig, ax, i: int, j: int, k: int) -> None:
|
|
377
|
+
x = self.pargrid
|
|
378
|
+
sharex = ax[self.dim * (i - 1) + j]
|
|
379
|
+
sharey = ax[self.dim * i + (j - 1)] if j > 1 else None
|
|
380
|
+
ax[k] = fig.add_subplot(self.dim, self.dim, k + 1,
|
|
381
|
+
sharex=sharex, sharey=sharey)
|
|
382
|
+
axis = tuple(np.delete(self.arrdim_r, [i, j]))
|
|
383
|
+
yy = np.sum(self.p * self.vol, axis=axis) \
|
|
384
|
+
/ np.sum(self.vol, axis=axis)
|
|
385
|
+
ax[k].pcolormesh(x[j], x[i], yy, cmap=self.cmap)
|
|
386
|
+
ax[k].contour(x[j], x[i], yy, colors='k',
|
|
387
|
+
levels=self.levels * np.nanmax(yy))
|
|
388
|
+
ax[k].plot(self.popt[j], self.popt[i], 'o')
|
|
389
|
+
ax[k].axvline(self.popt[j])
|
|
390
|
+
ax[k].axhline(self.popt[i])
|
|
391
|
+
ax[k].set_xlim(self.cornerrange[j])
|
|
392
|
+
ax[k].set_ylim(self.cornerrange[i])
|
|
393
|
+
if j == 0:
|
|
394
|
+
ax[k].set_ylabel(self.labels[i])
|
|
395
|
+
plt.setp(ax[k].get_yticklabels(), rotation=45)
|
|
396
|
+
else:
|
|
397
|
+
plt.setp(ax[k].get_yticklabels(), visible=False)
|
|
398
|
+
if i == self.dim - 1:
|
|
399
|
+
ax[k].set_xlabel(self.labels[j])
|
|
400
|
+
plt.setp(ax[k].get_xticklabels(), rotation=45)
|
|
401
|
+
else:
|
|
402
|
+
plt.setp(ax[k].get_xticklabels(), visible=False)
|
|
339
403
|
|
|
340
404
|
def plotongrid(self, show: bool = False, savefig: str | None = None,
|
|
341
405
|
labels: list[str] = None,
|
|
@@ -352,70 +416,30 @@ class EmceeCorner():
|
|
|
352
416
|
cmap: (str, optional): cmap for matplotlib.pyplot.plt.pcolormesh(). Defaults to 'binary'.
|
|
353
417
|
levels: (list, optional): levels for matplotlib.pyplot.plt.contour() relative to the peak. Defaults to [exp(-0.5*3^2), exp(-0.5*2^2), exp(-0.5*1^2)].
|
|
354
418
|
"""
|
|
355
|
-
adim = np.arange(self.dim)
|
|
356
419
|
if labels is None:
|
|
357
|
-
labels = [f'Par {i:d}' for i in
|
|
420
|
+
labels = [f'Par {i:d}' for i in self.arrdim]
|
|
358
421
|
if cornerrange is None:
|
|
359
422
|
cornerrange = self.bounds
|
|
360
|
-
|
|
361
|
-
|
|
423
|
+
self.labels = labels
|
|
424
|
+
self.cornerrange = cornerrange
|
|
425
|
+
self.cmap = cmap
|
|
426
|
+
self.levels = np.array(levels)
|
|
362
427
|
fig = plt.figure(figsize=(2 * self.dim * 1.2, 2 * self.dim * 1.2))
|
|
363
428
|
fig.subplots_adjust(hspace=0.05, wspace=0.05, top=0.87, right=0.87)
|
|
364
429
|
ax = np.empty(self.dim * self.dim, dtype='object')
|
|
365
|
-
for i in
|
|
366
|
-
for j in
|
|
430
|
+
for i in self.arrdim:
|
|
431
|
+
for j in self.arrdim:
|
|
367
432
|
if i < j:
|
|
368
433
|
continue
|
|
369
434
|
k = self.dim * i + j
|
|
370
435
|
if i == j:
|
|
371
|
-
|
|
372
|
-
s1 = self.phigh[i] - self.pmid[i]
|
|
373
|
-
s2 = self.pmid[i] - self.plow[i]
|
|
374
|
-
s0 = f'{s0:.2f}'
|
|
375
|
-
s1 = '^{+' + f'{s1:.2f}' + '}'
|
|
376
|
-
s2 = '_{-' + f'{s2:.2f}' + '}'
|
|
377
|
-
s0 = s0 + s1 + s2
|
|
378
|
-
ax[k] = fig.add_subplot(self.dim, self.dim, k + 1)
|
|
379
|
-
ax[k].plot(x[i], y[i], 'k-', drawstyle='steps-mid')
|
|
380
|
-
ax[k].axvline(self.popt[i])
|
|
381
|
-
ax[k].axvline(self.plow[i], linestyle='--', color='k')
|
|
382
|
-
ax[k].axvline(self.pmid[i], linestyle='--', color='k')
|
|
383
|
-
ax[k].axvline(self.phigh[i], linestyle='--', color='k')
|
|
384
|
-
ax[k].set_title(rf'{labels[i]}=${s0}$')
|
|
385
|
-
ax[k].set_xlim(cornerrange[i])
|
|
386
|
-
ax[k].set_ylim([0, np.max(y[i]) * 1.2])
|
|
387
|
-
ax[k].set_yticks([])
|
|
388
|
-
if i == self.dim - 1:
|
|
389
|
-
plt.setp(ax[k].get_xticklabels(), rotation=45)
|
|
390
|
-
ax[k].set_xlabel(labels[i])
|
|
391
|
-
else:
|
|
392
|
-
plt.setp(ax[k].get_xticklabels(), visible=False)
|
|
436
|
+
self._i_eq_j(fig, ax, i, k)
|
|
393
437
|
else:
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
yy = np.sum(self.p * self.vol, axis=axis) \
|
|
400
|
-
/ np.sum(self.vol, axis=axis)
|
|
401
|
-
ax[k].pcolormesh(x[j], x[i], yy, cmap=cmap)
|
|
402
|
-
ax[k].contour(x[j], x[i], yy, colors='k',
|
|
403
|
-
levels=np.array(levels) * np.nanmax(yy))
|
|
404
|
-
ax[k].plot(self.popt[j], self.popt[i], 'o')
|
|
405
|
-
ax[k].axvline(self.popt[j])
|
|
406
|
-
ax[k].axhline(self.popt[i])
|
|
407
|
-
ax[k].set_xlim(cornerrange[j])
|
|
408
|
-
ax[k].set_ylim(cornerrange[i])
|
|
409
|
-
if j == 0:
|
|
410
|
-
ax[k].set_ylabel(labels[i])
|
|
411
|
-
plt.setp(ax[k].get_yticklabels(), rotation=45)
|
|
412
|
-
else:
|
|
413
|
-
plt.setp(ax[k].get_yticklabels(), visible=False)
|
|
414
|
-
if i == self.dim - 1:
|
|
415
|
-
ax[k].set_xlabel(labels[j])
|
|
416
|
-
plt.setp(ax[k].get_xticklabels(), rotation=45)
|
|
417
|
-
else:
|
|
418
|
-
plt.setp(ax[k].get_xticklabels(), visible=False)
|
|
438
|
+
self._i_neq_j(fig, ax, i, j, k)
|
|
439
|
+
del self.labels
|
|
440
|
+
del self.cornerrange
|
|
441
|
+
del self.cmap
|
|
442
|
+
del self.levels
|
|
419
443
|
close_figure(fig, savefig, show, tight=False)
|
|
420
444
|
|
|
421
445
|
def getDNSevidence(self, **kwargs):
|
|
@@ -74,7 +74,7 @@ def select_noise(data: np.ndarray, sigma: str) -> np.ndarray:
|
|
|
74
74
|
Returns:
|
|
75
75
|
np.ndarray: 1D array that includes only the selected pixels.
|
|
76
76
|
"""
|
|
77
|
-
n = np.array(data)
|
|
77
|
+
n = np.array(data).copy()
|
|
78
78
|
if 'edge' in sigma:
|
|
79
79
|
if np.ndim(n) <= 2:
|
|
80
80
|
print('\'edge\' is ignored because ndim <= 2.')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plotastrodata
|
|
3
|
-
Version: 1.9.
|
|
3
|
+
Version: 1.9.11
|
|
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
|