tree wide: way too many changes, im sorry

This commit is contained in:
2024-08-05 13:00:54 +02:00
parent 51e0d81c28
commit 7f014938a9
8 changed files with 1604 additions and 87 deletions
+4
View File
@@ -62,6 +62,7 @@ class AstrodataGUI(QtWidgets.QMainWindow):
self.btOpenSavedFlareCountFile.clicked.connect(self.btOpenSavedFlareCountFileClicked)
self.flaredetectorPreview.periodsCalculated.connect(self.updatePeriods)
self.flaredetectorPreview.epochCalculated.connect(self.updateEpochPeriod)
self.setupCustomSimbadQueries()
self.show()
@@ -205,6 +206,9 @@ class AstrodataGUI(QtWidgets.QMainWindow):
def updatePeriods(self, periods: list):
self.edPlotFoldPeriod.setText(str(periods[0].value))
def updateEpochPeriod(self, epoch: float):
self.edPlotFoldEpochTime.setText(str(epoch))
def btCombineSeqClicked(self):
#self.gbPlotOptionsNormalize.setEnabled(False)
mainName = self.listDownloaded.currentItem().text()
+27 -5
View File
@@ -4,32 +4,54 @@ import concurrent.futures
from ..util.MinimalLightCurve import read
import pandas as pd
from ..flaredetector.flaredetector import calculateFlareFitsForLightcurve
from ..flaredetector.util import getTotalValidDataInSeconds
from ..flaredetector.util import *
def getFlareCount(filesDict):
lc = read(filesDict["FilePath"])
lc.flux = lc["sap_flux"]
lc.flux_err = lc["sap_flux_err"]
sapPeaks, sapFits = calculateFlareFitsForLightcurve(lc.flatten(), minimalLC=True)
sapPeaks, sapFits = calculateFlareFitsForLightcurve(lc.flatten())
sapValSec, sapTds = getTotalValidDataInSeconds(lc, "sap_flux")
sapPeriodogram = lc.to_periodogram()
sapPeakPeriod = sapPeriodogram.period[findMaxIndices(sapPeriodogram, num=4, distance=100, sortByHighest=True)[0]]
sapEpochTime = getEpochTime(lc)
sapFoldedLC = lc.fold(period=sapPeakPeriod, epoch_time=sapEpochTime)
sapPhase, sapSineFit, sapFitType = getFoldedBestFit(sapFoldedLC)
sapMinima, sapMaxima = getFoldedFitPeakValley(sapSineFit)
sapminPhasesBoundsIndices, sapmaxPhasesBoundsIndices = getPhaseRangesNearPeak((sapMinima, sapMaxima), sapPhase)
lc.flux = lc["pdcsap_flux"]
lc.flux_err = lc["pdcsap_flux_err"]
pdcsapPeaks, pdcsapFits = calculateFlareFitsForLightcurve(lc.flatten(), minimalLC=True)
pdcsapPeaks, pdcsapFits = calculateFlareFitsForLightcurve(lc.flatten())
pdcsapValSec, pdcsapTds = getTotalValidDataInSeconds(lc, "pdcsap_flux")
pdcsapPeriodogram = lc.to_periodogram()
pdcsapPeakPeriod = pdcsapPeriodogram.period[findMaxIndices(pdcsapPeriodogram, num=4, distance=100, sortByHighest=True)[0]]
pdcsapEpochTime = getEpochTime(lc)
pdcsapFoldedLC = lc.fold(period=pdcsapPeakPeriod, epoch_time=pdcsapEpochTime)
pdcsapPhase, pdcsapSineFit, pdcsapFitType = getFoldedBestFit(pdcsapFoldedLC)
pdcsapMinima, pdcsapMaxima = getFoldedFitPeakValley(pdcsapSineFit)
pdcsapminPhasesBoundsIndices, pdcsapmaxPhasesBoundsIndices = getPhaseRangesNearPeak((pdcsapMinima, pdcsapMaxima), pdcsapPhase)
filesDict["sapPeaks"] = sapPeaks
filesDict["sapPeaksCount"] = len(sapPeaks)
filesDict["sapFits"] = sapFits
filesDict["sapValidTimespans"] = sapTds
filesDict["sapValidSeconds"] = sapValSec
filesDict["sapPeriod"] = sapPeakPeriod.value
filesDict["sapPeriodMinima"] = sapMinima
filesDict["sapPeriodMinimaBoundaries"] = sapminPhasesBoundsIndices
filesDict["sapPeriodMaxima"] = sapMaxima
filesDict["sapPeriodMaximaBoundaries"] = sapmaxPhasesBoundsIndices
filesDict["pdcsapPeaks"] = pdcsapPeaks
filesDict["pdcsapPeaksCount"] = len(pdcsapPeaks)
filesDict["pdcsapFits"] = pdcsapFits
filesDict["pdcsapValidTimespans"] = pdcsapTds
filesDict["pdcsapValidSeconds"] = pdcsapValSec
filesDict["pdcsapPeriod"] = pdcsapPeakPeriod.value
filesDict["pdcsapPeriodMinima"] = pdcsapMinima
filesDict["pdcsapPeriodMinimaBoundaries"] = pdcsapminPhasesBoundsIndices
filesDict["pdcsapPeriodMaxima"] = pdcsapMaxima
filesDict["pdcsapPeriodMaximaBoundaries"] = pdcsapmaxPhasesBoundsIndices
del lc
if(filesDict["StarName"] == "CD-51 13128" and filesDict["Sequence"] == 1):
print(filesDict)
return filesDict
class CalcAllFlaresThread(QThread):
+145 -2
View File
@@ -43,6 +43,8 @@ class FlareSummaryPlotGUI(QWidget):
self.btShowFlaresPerStar.clicked.connect(self.btShowFlaresPerStarClicked)
self.btShowFlaresPerStarNormalized = QPushButton("Show Flares/Star Normalized")
self.btShowFlaresPerStarNormalized.clicked.connect(self.btShowFlaresPerStarNormalizedClicked)
self.btShowPeriods = QPushButton("Show Mean Periods")
self.btShowPeriods.clicked.connect(self.btShowPeriodsClicked)
self.cbKepler = QCheckBox("Kepler")
self.cbKepler.setChecked(True)
@@ -68,6 +70,7 @@ class FlareSummaryPlotGUI(QWidget):
self.buttonGridLayout.addWidget(self.btShowFlaresPerFile, 0, 1)
self.buttonGridLayout.addWidget(self.btShowFlaresPerStar, 0, 2)
self.buttonGridLayout.addWidget(self.btShowFlaresPerStarNormalized, 0, 3)
self.buttonGridLayout.addWidget(self.btShowPeriods, 0, 4)
self.buttonGridLayout.addWidget(QLabel("Sources: "), 1, 0)
self.buttonGridLayout.addWidget(self.cbKepler, 1, 1)
@@ -169,7 +172,18 @@ class FlareSummaryPlotGUI(QWidget):
'pdcsapFits',
'pdcsapPeaks',
'sapFits',
'sapPeaks'])
'sapPeaks',
'sapPeriod',
'sapPeriodMinima',
'sapPeriodMinimaBoundaries',
'sapPeriodMaxima',
'sapPeriodMaximaBoundaries',
'pdcsapValidTimespans',
'pdcsapPeriod',
'pdcsapPeriodMinima',
'pdcsapPeriodMinimaBoundaries',
'pdcsapPeriodMaxima',
'pdcsapPeriodMaximaBoundaries'])
showSourceFilter = np.full(len(data), False)
@@ -248,7 +262,19 @@ class FlareSummaryPlotGUI(QWidget):
'pdcsapFits',
'pdcsapPeaks',
'sapFits',
'sapPeaks'])
'sapValidTimespans',
'sapPeaks',
'sapPeriod',
'sapPeriodMinima',
'sapPeriodMinimaBoundaries',
'sapPeriodMaxima',
'sapPeriodMaximaBoundaries',
'pdcsapValidTimespans',
'pdcsapPeriod',
'pdcsapPeriodMinima',
'pdcsapPeriodMinimaBoundaries',
'pdcsapPeriodMaxima',
'pdcsapPeriodMaximaBoundaries'])
showSourceFilter = np.full(len(data), False)
@@ -330,3 +356,120 @@ class FlareSummaryPlotGUI(QWidget):
self.figureAxis.set_xlabel("Star Number")
self.figureAxis.legend()
self.figure.canvas.draw_idle()
def btShowPeriodsClicked(self):
data = self.starFLareDictList.drop(columns=['Distance',
'DistanceUnit',
'FilePath',
'RotVel',
'RotVelUnit',
'Sequence',
'pdcsapFits',
'pdcsapPeaks',
'sapFits',
'sapValidTimespans',
'sapPeaks',
'sapPeriodMinima',
'sapPeriodMinimaBoundaries',
'sapPeriodMaxima',
'sapPeriodMaximaBoundaries',
'pdcsapValidTimespans',
'pdcsapPeriodMinima',
'pdcsapPeriodMinimaBoundaries',
'pdcsapPeriodMaxima',
'pdcsapPeriodMaximaBoundaries',
'sapPeaksCount',
'pdcsapPeaksCount',
'sapValidSeconds',
'pdcsapValidSeconds'])
showSourceFilter = np.full(len(data), False)
if(self.cbKepler.isChecked()):
showKepler = data["Source"] == "Kepler"
showSourceFilter |= showKepler
if(self.cbK2.isChecked()):
showK2 = data["Source"] == "K2"
showSourceFilter |= showK2
if(self.cbTESS.isChecked()):
showTESS = data["Source"] == "TESS"
showSourceFilter |= showTESS
print(data["sapPeriod"])
data = data[showSourceFilter].groupby(["StarName", "SpType"],
as_index=False).agg({"sapPeriod": "mean",
"pdcsapPeriod": "mean"})
x = np.arange(start=1, stop=len(data)+1, step=1, dtype=int)
if(self.cbSpTypeL.isChecked()):
Lfilter = data["SpType"].str.startswith("L")
Lfilter &= showSourceFilter
if(self.cbSpTypeM.isChecked()):
Mfilter = data["SpType"].str.startswith("M")
Mfilter &= showSourceFilter
if(self.cbSpTypeK.isChecked()):
Kfilter = data["SpType"].str.startswith("K")
Kfilter &= showSourceFilter
if(self.cbSpTypeG.isChecked()):
Gfilter = data["SpType"].str.startswith("G")
Gfilter &= showSourceFilter
if(self.cbSpTypeF.isChecked()):
Ffilter = data["SpType"].str.startswith("F")
Ffilter &= showSourceFilter
if(self.cbSpTypeUnknown.isChecked()):
Unknownfilter = data["SpType"].str.startswith("-")
Unknownfilter &= showSourceFilter
self.figureAxis.clear()
if(self.cbSpTypeL.isChecked()):
if(np.any(Lfilter)):
self.figureAxis.scatter(x[Lfilter], data[Lfilter]["sapPeriod"],
marker="o", color="brown", label="L SAP Period")
self.figureAxis.scatter(x[Lfilter], data[Lfilter]["pdcsapPeriod"],
marker="x", color="brown", label="L PDCSAP Period")
if(self.cbSpTypeM.isChecked()):
if(np.any(Mfilter)):
self.figureAxis.scatter(x[Mfilter], data[Mfilter]["sapPeriod"],
marker="o", color="red", label="M SAP Period")
self.figureAxis.scatter(x[Mfilter], data[Mfilter]["pdcsapPeriod"],
marker="x", color="red", label="M PDCSAP Period")
if(self.cbSpTypeK.isChecked()):
if(np.any(Kfilter)):
self.figureAxis.scatter(x[Kfilter], data[Kfilter]["sapPeriod"],
marker="o", color="orange", label="K SAP Period")
self.figureAxis.scatter(x[Kfilter], data[Kfilter]["pdcsapPeriod"],
marker="x", color="orange", label="K PDCSAP Period")
if(self.cbSpTypeG.isChecked()):
if(np.any(Gfilter)):
self.figureAxis.scatter(x[Gfilter], data[Gfilter]["sapPeriod"],
marker="o", color="yellow", label="G SAP Period")
self.figureAxis.scatter(x[Gfilter], data[Gfilter]["pdcsapPeriod"],
marker="x", color="yellow", label="G PDCSAP Period")
if(self.cbSpTypeF.isChecked()):
if(np.any(Ffilter)):
self.figureAxis.scatter(x[Ffilter], data[Ffilter]["sapPeriod"],
marker="o", color="greenyellow", label="F SAP Period")
self.figureAxis.scatter(x[Ffilter], data[Ffilter]["pdcsapPeriod"],
marker="x", color="greenyellow", label="F PDCSAP Period")
if(self.cbSpTypeUnknown.isChecked()):
if(np.any(Unknownfilter)):
self.figureAxis.scatter(x[Unknownfilter], data[Unknownfilter]["sapPeriod"],
marker="o", color="gray", label="Unknown SAP Period")
self.figureAxis.scatter(x[Unknownfilter], data[Unknownfilter]["pdcsapPeriod"],
marker="x", color="gray", label="Unknown PDCSAP Period")
self.figureAxis.set_ylabel("Period / days")
self.figureAxis.set_xlabel("Star Number")
self.figureAxis.legend()
self.figure.canvas.draw_idle()
pass
def btShowNumMinimaMaximaClicked(self):
pass
def btShowFlaresInMinimaMaximaClicked(self):
pass
def btShowFlaresInMinimaMaximaPerMinimaMaximaClicked(self):
pass
+1 -1
View File
@@ -254,7 +254,7 @@ class FlaredetectorWidget(QtWidgets.QWidget):
cycle, foldedIndex = convertStarndardIndexToFoldedIndex(lc, peak["StandardIndex"])
self.figureAxis.plot(lc.phase[lc.cycle == cycle][foldedIndex].value,
lc.flux[lc.cycle == cycle][foldedIndex], "x", color="red")
phase, sineFit = getFoldedBestFit(lc)
phase, sineFit, _ = getFoldedBestFit(lc)
self.figureAxis.plot(phase, sineFit, color="red")
minPhasesBoundsIndices, maxPhasesBoundsIndices = getPhaseRangesNearPeak(getFoldedFitPeakValley(sineFit), phase)
plotPhaseRangesNearPeak((minPhasesBoundsIndices, maxPhasesBoundsIndices), phase, ax=self.figureAxis)
+9 -2
View File
@@ -37,12 +37,19 @@ def calculateFitForFlare(lightcurve, peakIndex):
popt, pcov = curve_fit(lambda x, l, w: GaussExpo(x, Peak, l, w), flareFitDataPoints, flareData)
pcovDiag = np.diag(pcov)
fit = GaussExpo(flareFitDataPoints, Peak, *popt)
rss = compureRSS(fit, flareData)
aic = computeAIC(rss, 2, len(flareData))
bic = computeBIC(rss, 2, len(flareData))
r2 = 1 - rss/computeTotalSoS(flareData)
#print("Fit r2: ", r2)
if(r2 < 0.8):
raise Exception("Fit varies too much")
flareFitDataPoints = flareFitDataPoints + peakIndex
return np.array(Peak+1), flareFitDataPoints, np.array(fit+1)
def calculateFlareFitsForLightcurve(lightcurve, num=100, distance=1, height=0.05, minimalLC=False):
def calculateFlareFitsForLightcurve(lightcurve, num=100, distance=1, height=0.05):
maxIndices = findMaxIndices(lightcurve, num, distance=distance, height=height,
sortByHighest=True, minimalLC=minimalLC)
sortByHighest=True)
peaks = []
fits = []
for index in maxIndices:
+111 -76
View File
@@ -1,21 +1,15 @@
import numpy as np
from numpy.polynomial.polynomial import Polynomial
from scipy.signal import find_peaks
from scipy.signal import find_peaks, argrelextrema
from scipy.optimize import curve_fit
def findMaxIndices(lc, num=3, distance=100, height=(None, None), sortByHighest=False,
minimalLC=False):
if(minimalLC):
def findMaxIndices(lc, num=3, distance=100, height=(None, None), sortByHighest=False):
if(hasattr(lc, "power")):
data = lc.power
elif(hasattr(lc, "flux")):
data = lc.flux
else:
from lightkurve.periodogram import Periodogram as OrigPG
from lightkurve.lightcurve import LightCurve as OrigLC
if(isinstance(lc, OrigPG)):
data = lc.power
elif(isinstance(lc, OrigLC)):
data = lc.flux
else:
return np.zeros(num)-1
return np.zeros(num)-1
peak_indices, peak_dict = find_peaks(data, height=height,
distance=distance)
peak_heights = peak_dict["peak_heights"]
@@ -88,16 +82,22 @@ def singleSine(t, A, w, p, c):
def fitSingleSine(phase, flux):
initGuess = [np.ptp(flux)/2, 2 * np.pi / (np.max(phase) - np.min(phase)), 0, np.mean(flux)]
popt, _ = curve_fit(singleSine, phase, flux, p0=initGuess, maxfev=100000)
popt, _ = curve_fit(singleSine, phase, flux, p0=initGuess, maxfev=300)
return singleSine(phase, *popt)
def polynomial(phase, flux, degree):
return Polynomial.fit(phase, flux, degree).convert().coef
def fitPolynomial(phase, flux, degree):
return sum(p * phase**i for i, p in enumerate(Polynomial.fit(phase, flux, degree).convert().coef))
return sum(p * phase**i for i, p in enumerate(polynomial(phase, flux, degree)))
def compureRSS(fit, flux):
residuals = flux - fit
return np.sum(residuals**2)
def computeTotalSoS(flux):
return np.sum((flux - np.mean(flux))**2)
def computeAIC(rss, numParams, numDataPoints):
return 2 * numParams + numDataPoints * np.log(rss/numDataPoints)
@@ -113,30 +113,46 @@ def getFoldedBestFit(foldedLc):
flux = foldedLc.normalize().flux
phase = foldedLc.phase[filt].value
flux = flux[filt]
#popt, pcov = curve_fit(sine, phase, flux[filt], maxfev=100000)
singleFit = fitSingleSine(phase, flux)
polyFit = fitPolynomial(phase, flux, 10)
polyDegree = 7
fitThreshold = 0.0
smoothed_flux = np.convolve(flux, np.ones(len(flux)//100)/(len(flux)//100), mode="valid")
peaks, _ = find_peaks(smoothed_flux, height=np.mean(smoothed_flux))
#print("Num peaks: ", peaks)
singleRSS = compureRSS(singleFit, flux)
polyRSS = compureRSS(polyFit, flux)
try:
retFit = fitSingleSine(phase, flux)
r2 = 1 - (compureRSS(retFit, flux)/computeTotalSoS(flux))
fitType = "sine"
#print("R2: ", r2)
if(r2 < fitThreshold):
raise Exception("Sine fit is suboptimal")
#print("Single sine preferred")
except:
retFit = fitPolynomial(phase, flux, polyDegree)
fitType = "poly"
#print("Polynomial preferred")
singleAIC = computeAIC(singleRSS, 4, len(flux))
singleBIC = computeBIC(singleRSS, 4, len(flux))
polyAIC = computeAIC(polyRSS, 10, len(flux))
polyBIC = computeBIC(polyRSS, 10, len(flux))
if (singleAIC < polyAIC and singleBIC < polyBIC):
print("Single sine preferred")
sineFit = singleFit
else:
print("Polynomial preferred")
sineFit = polyFit
sineFit *= multi
return phase, sineFit
retFit *= multi
return phase, retFit, fitType
def getFoldedFitPeakValley(sineFit):
return np.argmin(sineFit), np.argmax(sineFit)
minima = argrelextrema(sineFit, np.less)[0]
maxima = argrelextrema(sineFit, np.greater)[0]
#print(minima)
#print(maxima)
if(len(minima) > 0 and len(maxima) > 0):
if(minima[0] < maxima[0] and minima[-1] > maxima[-1]):
if(sineFit[minima[0]] > sineFit[minima[-1]]):
minima = np.delete(minima, 0)
else:
minima = np.delete(minima, -1)
elif(maxima[0] < minima[0] and maxima[-1] > minima[-1]):
if(sineFit[maxima[0]] > sineFit[maxima[-1]]):
maxima = np.delete(maxima, -1)
else:
maxima = np.delete(maxima, 0)
return minima, maxima
def findNearestIndexOfValue(array, value):
array = np.asarray(array)
@@ -144,57 +160,76 @@ def findNearestIndexOfValue(array, value):
return idx
def getPhaseRangesNearPeak(maxArgs, phase):
minPhase = phase[maxArgs[0]]
maxPhase = phase[maxArgs[1]]
minPhases = phase[maxArgs[0]]
maxPhases = phase[maxArgs[1]]
totalPhase = abs(phase[0]) + abs(phase[-1])
phasePart = totalPhase * 0.3 / 2
minPhaseBounds = []
if(minPhase - phasePart < phase[0]):
minPhaseBounds.append((phase[0], minPhase + phasePart))
minPhaseBounds.append((phase[-1] + (minPhase - phasePart - phase[0]), phase[-1]))
elif(minPhase + phasePart > phase[-1]):
minPhaseBounds.append((minPhase - phasePart, phase[-1]))
minPhaseBounds.append((phase[0], phase[0] + (minPhase + phasePart - phase[-1])))
else:
minPhaseBounds.append((minPhase - phasePart, minPhase + phasePart))
phasePart = totalPhase * 0.3 / (len(minPhases) + len(maxPhases))
minPhasesBounds = []
for minPhase in minPhases:
minPhaseBounds = []
if(minPhase - phasePart < phase[0]):
minPhaseBounds.append((phase[0], minPhase + phasePart))
minPhaseBounds.append((phase[-1] + (minPhase - phasePart - phase[0]), phase[-1]))
elif(minPhase + phasePart > phase[-1]):
minPhaseBounds.append((minPhase - phasePart, phase[-1]))
minPhaseBounds.append((phase[0], phase[0] + (minPhase + phasePart - phase[-1])))
else:
minPhaseBounds.append((minPhase - phasePart, minPhase + phasePart))
minPhasesBounds.append(minPhaseBounds)
maxPhaseBounds = []
if(maxPhase - phasePart < phase[0]):
maxPhaseBounds.append((phase[0], maxPhase + phasePart))
maxPhaseBounds.append((phase[-1] + (maxPhase - phasePart - phase[0]), phase[-1]))
elif(maxPhase + phasePart > phase[-1]):
maxPhaseBounds.append((maxPhase - phasePart, phase[-1]))
maxPhaseBounds.append((phase[0], phase[0] + (maxPhase + phasePart - phase[-1])))
else:
maxPhaseBounds.append((maxPhase - phasePart, maxPhase + phasePart))
maxPhasesBounds = []
for maxPhase in maxPhases:
maxPhaseBounds = []
if(maxPhase - phasePart < phase[0]):
maxPhaseBounds.append((phase[0], maxPhase + phasePart))
maxPhaseBounds.append((phase[-1] + (maxPhase - phasePart - phase[0]), phase[-1]))
elif(maxPhase + phasePart > phase[-1]):
maxPhaseBounds.append((maxPhase - phasePart, phase[-1]))
maxPhaseBounds.append((phase[0], phase[0] + (maxPhase + phasePart - phase[-1])))
else:
maxPhaseBounds.append((maxPhase - phasePart, maxPhase + phasePart))
maxPhasesBounds.append(maxPhaseBounds)
minPhaseBoundsIndices = []
for l in minPhaseBounds:
minPhaseBoundsIndices.append([findNearestIndexOfValue(phase, lv) for lv in l])
minPhasesBoundsIndices = []
for minPhaseBounds in minPhasesBounds:
minPhaseBoundsIndices = []
for l in minPhaseBounds:
minPhaseBoundsIndices.append([findNearestIndexOfValue(phase, lv) for lv in l])
minPhasesBoundsIndices.append(minPhaseBoundsIndices)
maxPhaseBoundsIndices = []
for l in maxPhaseBounds:
maxPhaseBoundsIndices.append([findNearestIndexOfValue(phase, lv) for lv in l])
maxPhasesBoundsIndices = []
for maxPhaseBounds in maxPhasesBounds:
maxPhaseBoundsIndices = []
for l in maxPhaseBounds:
maxPhaseBoundsIndices.append([findNearestIndexOfValue(phase, lv) for lv in l])
maxPhasesBoundsIndices.append(maxPhaseBoundsIndices)
return minPhaseBoundsIndices, maxPhaseBoundsIndices
return minPhasesBoundsIndices, maxPhasesBoundsIndices
def plotPhaseRangesNearPeak(indices, phase, ax=None):
minPhaseBoundsIndices = indices[0]
maxPhaseBoundsIndices = indices[1]
minPhasesBoundsIndices = indices[0]
maxPhasesBoundsIndices = indices[1]
if(ax is None):
import matplotlib.pyplot as plt
for pair in minPhaseBoundsIndices:
for l in pair:
plt.axes.axvline(phase[l], color="violet")
for pair in maxPhaseBoundsIndices:
for l in pair:
plt.axes.axvline(phase[l], color="orange")
for minPhaseBoundsIndices in minPhasesBoundsIndices:
for pair in minPhaseBoundsIndices:
for l in pair:
plt.axes.axvline(phase[l], color="violet")
for maxPhaseBoundsIndices in maxPhasesBoundsIndices:
for pair in maxPhaseBoundsIndices:
for l in pair:
plt.axes.axvline(phase[l], color="orange")
else:
for pair in minPhaseBoundsIndices:
for l in pair:
ax.axvline(phase[l], color="violet")
for pair in maxPhaseBoundsIndices:
for l in pair:
ax.axvline(phase[l], color="orange")
for minPhaseBoundsIndices in minPhasesBoundsIndices:
for pair in minPhaseBoundsIndices:
for l in pair:
ax.axvline(phase[l], color="violet")
for maxPhaseBoundsIndices in maxPhasesBoundsIndices:
for pair in maxPhaseBoundsIndices:
for l in pair:
ax.axvline(phase[l], color="orange")
def getEpochTime(lc):
return lc.time.value[argrelextrema(np.asarray(lc.flux.value), np.less, order=500)[0]][0]
+203
View File
@@ -17,6 +17,10 @@ from astropy.time.formats import TimeFromEpoch
log = logging.getLogger(__name__)
class LightkurveWarning(Warning):
"""Class for all Lightkurve warnings."""
pass
class TimeBKJD(TimeFromEpoch):
name = 'bkjd'
unit = 1.0
@@ -402,6 +406,14 @@ class TessQualityFlags(QualityFlags):
32768: "Insufficient Targets for Error Correction Exclude",
}
def validate_method(method, supported_methods):
method = method.lower()
if method in supported_methods:
return method
raise ValueError(
"method '{}' is not supported; "
"must be one of {}".format(method, supported_methods)
)
def _is_dict_like(data1):
return hasattr(data1, "keys") and callable(getattr(data1, "keys"))
@@ -879,6 +891,195 @@ class LightCurve(TimeSeries):
return flatten_lc, trend_lc
return flatten_lc
def to_periodogram(self, method="lombscargle", **kwargs):
supported_methods = ["ls", "bls", "lombscargle", "boxleastsquares"]
method = validate_method(method.replace(" ", ""), supported_methods)
if method in ["bls", "boxleastsquares"]:
from .MinimalPeriodogram import BoxLeastSquaresPeriodogram
return BoxLeastSquaresPeriodogram.from_lightcurve(lc=self, **kwargs)
else:
from .MinimalPeriodogram import LombScarglePeriodogram
return LombScarglePeriodogram.from_lightcurve(lc=self, **kwargs)
def fold(
self,
period=None,
epoch_time=None,
epoch_phase=0,
wrap_phase=None,
normalize_phase=False,
):
# Lightkurve v1.x assumed that `period` was given in days if no unit
# was specified. We maintain this behavior for backwards-compatibility.
if period is not None and not isinstance(period, Quantity):
period *= u.day
if epoch_time is not None and not isinstance(epoch_time, Time):
epoch_time = Time(
epoch_time, format=self.time.format, scale=self.time.scale
)
if (
epoch_phase is not None
and not isinstance(epoch_phase, Quantity)
and not normalize_phase
):
epoch_phase *= u.day
if wrap_phase is not None and not isinstance(wrap_phase, Quantity):
wrap_phase *= u.day
# Warn if `epoch_time` appears to use the wrong format
if epoch_time is not None and epoch_time.value > 2450000:
if self.time.format == "bkjd":
warnings.warn(
"`epoch_time` appears to be given in JD, "
"however the light curve time uses BKJD "
"(i.e. JD - 2454833).",
LightkurveWarning,
)
elif self.time.format == "btjd":
warnings.warn(
"`epoch_time` appears to be given in JD, "
"however the light curve time uses BTJD "
"(i.e. JD - 2457000).",
LightkurveWarning,
)
ts = super().fold(
period=period,
epoch_time=epoch_time,
epoch_phase=epoch_phase,
wrap_phase=wrap_phase,
normalize_phase=normalize_phase,
)
# The folded time would pass the `TimeSeries` validation check if
# `normalize_phase=True`, so creating a `FoldedLightCurve` object
# requires the following three-step workaround:
# 1. Give the folded light curve a valid time column again
with ts._delay_required_column_checks():
folded_time = ts.time.copy()
ts.remove_column("time")
ts.add_column(self.time, name="time", index=0)
# 2. Create the folded object
lc = FoldedLightCurve(data=ts)
# 3. Restore the folded time
with lc._delay_required_column_checks():
lc.remove_column("time")
lc.add_column(folded_time, name="time", index=0)
# Add extra column and meta data specific to FoldedLightCurve
lc.add_column(
self.time.copy(), name="time_original", index=len(self._required_columns)
)
lc.meta["PERIOD"] = period
lc.meta["EPOCH_TIME"] = epoch_time
lc.meta["EPOCH_PHASE"] = epoch_phase
lc.meta["WRAP_PHASE"] = wrap_phase
lc.meta["NORMALIZE_PHASE"] = normalize_phase
lc.sort("time")
return lc
def normalize(self, unit="unscaled"):
validate_method(unit, ["unscaled", "percent", "ppt", "ppm"])
median_flux = np.nanmedian(self.flux)
std_flux = np.nanstd(self.flux)
# If the median flux is within half a standard deviation from zero, the
# light curve is likely zero-centered and normalization makes no sense.
if (median_flux == 0) or (
np.isfinite(std_flux) and (np.abs(median_flux) < 0.5 * std_flux)
):
warnings.warn(
"The light curve appears to be zero-centered "
"(median={:.2e} +/- {:.2e}); `normalize()` will divide "
"the light curve by a value close to zero, which is "
"probably not what you want."
"".format(median_flux, std_flux),
LightkurveWarning,
)
# If the median flux is negative, normalization will invert the light
# curve and makes no sense.
if median_flux < 0:
warnings.warn(
"The light curve has a negative median flux ({:.2e});"
" `normalize()` will therefore divide by a negative "
"number and invert the light curve, which is probably"
"not what you want".format(median_flux),
LightkurveWarning,
)
# Create a new light curve instance and normalize its values
lc = self.copy()
lc.flux = lc.flux / median_flux
lc.flux_err = lc.flux_err / median_flux
if not lc.flux.unit:
lc.flux *= u.dimensionless_unscaled
if not lc.flux_err.unit:
lc.flux_err *= u.dimensionless_unscaled
# Set the desired relative (dimensionless) units
if unit == "percent":
lc.flux = lc.flux.to(u.percent)
lc.flux_err = lc.flux_err.to(u.percent)
elif unit in ("ppt", "ppm"):
lc.flux = lc.flux.to(unit)
lc.flux_err = lc.flux_err.to(unit)
lc.meta["NORMALIZED"] = True
return lc
def remove_nans(self, column: str = "flux"):
return self[~np.isnan(self[column])] # This will return a sliced copy
class FoldedLightCurve(LightCurve):
@property
def phase(self):
"""Alias for `LightCurve.time`."""
return self.time
@property
def cycle(self):
cycle_epoch_start = self.epoch_time - self.period / 2
result = np.asarray(np.floor(((self.time_original - cycle_epoch_start) / self.period).value), dtype=int)
result = result - result.min()
return result
@property
def odd_mask(self):
return self.cycle % 2 == 1
@property
def even_mask(self):
return ~self.odd_mask
def _set_xlabel(self, kwargs):
if "xlabel" not in kwargs:
kwargs["xlabel"] = "Phase"
if isinstance(self.time, TimeDelta):
kwargs["xlabel"] += f" [{self.time.format.upper()}]"
return kwargs
def plot(self, **kwargs):
kwargs = self._set_xlabel(kwargs)
return super(FoldedLightCurve, self).plot(**kwargs)
def scatter(self, **kwargs):
kwargs = self._set_xlabel(kwargs)
return super(FoldedLightCurve, self).scatter(**kwargs)
def errorbar(self, **kwargs):
kwargs = self._set_xlabel(kwargs)
return super(FoldedLightCurve, self).errorbar(**kwargs)
def plot_river(self, **kwargs):
ax = super(FoldedLightCurve, self).plot_river(
period=self.period, epoch_time=self.epoch_time, **kwargs
)
return ax
class KeplerLightCurve(LightCurve):
"""Subclass of :class:`LightCurve <lightkurve.lightcurve.LightCurve>`
to represent data from NASA's Kepler and K2 mission."""
@@ -1418,3 +1619,5 @@ try:
except registry.IORegistryError as exc:
print(exc)
pass # necessary to enable autoreload during debugging
File diff suppressed because it is too large Load Diff