5151
5252@Author: Christian Palmroos <[email protected] > 5353
54- @Updated: 2024-11-25
54+ @Updated: 2024-12-04
5555
5656Known problems/bugs:
5757 > Does not work with SolO/STEP due to electron and proton channels not defined in all_channels() -method
@@ -1780,6 +1780,10 @@ def VDA(self, onset_times=None, Onset=None, energy:str='gmean', selection=None,
17801780 stopreason (ODR), fig and axes.
17811781 """
17821782
1783+ VDA_MARKERSIZE = 24
1784+ VDA_ELINEWIDTH = 2.2
1785+ VDA_CAPSIZE = 6.0
1786+
17831787 from scipy .stats import t as studentt
17841788
17851789 import numpy .ma as ma
@@ -1945,8 +1949,14 @@ def VDA(self, onset_times=None, Onset=None, energy:str='gmean', selection=None,
19451949 plus_err = pd .Timedelta (seconds = 1 )
19461950 minus_err = pd .Timedelta (seconds = 1 )
19471951
1948- plus_errs = np .append (plus_errs , plus_err ) if plus_err >= self .minimum_cadences [f"{ spacecraft } _{ instrument } " ]/ 2 else np .append (plus_errs , pd .Timedelta (self .minimum_cadences [f"{ spacecraft } _{ instrument } " ])/ 2 )
1949- minus_errs = np .append (minus_errs , minus_err ) if minus_err >= self .minimum_cadences [f"{ spacecraft } _{ instrument } " ]/ 2 else np .append (minus_errs , pd .Timedelta (self .minimum_cadences [f"{ spacecraft } _{ instrument } " ])/ 2 )
1952+ # Before a check was done here to make sure that the errors are not smaller than half of the minimum
1953+ # cadence of the respective instrument; this is now obsolete, as the errors are properly inspected
1954+ # while the weighting is performed.
1955+ # plus_errs = np.append(plus_errs, plus_err) if plus_err >= self.minimum_cadences[f"{spacecraft}_{instrument}"]/2 else np.append(plus_errs, pd.Timedelta(self.minimum_cadences[f"{spacecraft}_{instrument}"])/2)
1956+ # minus_errs = np.append(minus_errs, minus_err) if minus_err >= self.minimum_cadences[f"{spacecraft}_{instrument}"]/2 else np.append(minus_errs, pd.Timedelta(self.minimum_cadences[f"{spacecraft}_{instrument}"])/2)
1957+ plus_errs = np .append (plus_errs , plus_err )
1958+ minus_errs = np .append (minus_errs , minus_err )
1959+
19501960
19511961 for ch in channels1 :
19521962
@@ -1959,8 +1969,8 @@ def VDA(self, onset_times=None, Onset=None, energy:str='gmean', selection=None,
19591969 plus_err1 = pd .Timedelta (seconds = 1 )
19601970 minus_err1 = pd .Timedelta (seconds = 1 )
19611971
1962- plus_errs1 = np .append (plus_errs1 , plus_err1 ) if plus_err1 >= Onset . minimum_cadences [ f" { Onset . spacecraft } _ { Onset . sensor } " ] / 2 else np . append ( plus_errs1 , pd . Timedelta ( Onset . minimum_cadences [ f" { Onset . spacecraft } _ { Onset . sensor } " ]) / 2 )
1963- minus_errs1 = np .append (minus_errs1 , minus_err1 ) if minus_err1 >= Onset . minimum_cadences [ f" { Onset . spacecraft } _ { Onset . sensor } " ] / 2 else np . append ( minus_errs1 , pd . Timedelta ( Onset . minimum_cadences [ f" { Onset . spacecraft } _ { Onset . sensor } " ]) / 2 )
1972+ plus_errs1 = np .append (plus_errs1 , plus_err1 )
1973+ minus_errs1 = np .append (minus_errs1 , minus_err1 )
19641974
19651975 plus_errs_all = np .append (plus_errs , plus_errs1 )
19661976 minus_errs_all = np .append (minus_errs , minus_errs1 )
@@ -1978,33 +1988,58 @@ def VDA(self, onset_times=None, Onset=None, energy:str='gmean', selection=None,
19781988 y_errors_all = np .array ([minus_errs_secs_all , plus_errs_secs_all ])
19791989
19801990 else :
1981- # The y-directional error, which is the temporal uncertainty
1982- try :
1991+ # The y-directional error was given by the user
1992+ # 4 arrays, so asymmetric plus and minus errors
1993+ if len (yerrs )== 4 :
19831994
1984- reso_str = get_time_reso ( self . flux_series )
1985- reso_str1 = get_time_reso ( Onset . flux_series )
1995+ plus_errs , minus_errs = np . array ([]), np . array ([] )
1996+ plus_errs1 , minus_errs1 = np . array ([]), np . array ([] )
19861997
1987- time_error = [ pd . Timedelta ( reso_str ) for _ in range ( len ( x_errors )) ]
1988- time_error1 = [ pd . Timedelta ( reso_str1 ) for _ in range ( len ( x_errors1 )) ]
1998+ minus_timestamps , plus_timestamps = yerrs [ 0 ], yerrs [ 1 ]
1999+ minus_timestamps1 , plus_timestamps1 = yerrs [ 0 ], yerrs [ 1 ]
19892000
1990- except ValueError :
2001+ for i , ch in enumerate ( channels ) :
19912002
1992- # get_time_errors() attempts to manually extract the time errors of each data point
1993- time_error = get_time_errors (onset_times = onset_times , spacecraft = spacecraft )
1994- time_error1 = get_time_errors (onset_times = onset_times1 , spacecraft = Onset .spacecraft .lower ())
2003+ try :
2004+ minus_err = minus_timestamps [i ]
2005+ plus_err = plus_timestamps [i ]
2006+ except KeyError as e :
2007+ plus_err = pd .Timedelta (seconds = 1 )
2008+ minus_err = pd .Timedelta (seconds = 1 )
2009+
2010+ plus_errs = np .append (plus_errs , plus_err )
2011+ minus_errs = np .append (minus_errs , minus_err )
19952012
1996- try :
1997- _ = str (time_error [0 ].seconds )
1998- # Indexerror is caused by all NaTs -> no point in doing VDA
1999- except IndexError :
2000- return None
2013+ y_errors_all = np .array ([plus_errs ,minus_errs ])
20012014
2015+ # Convert errors in time to seconds
2016+ plus_errs_secs = [err .seconds for err in plus_errs ]
2017+ minus_errs_secs = [err .seconds for err in minus_errs ]
20022018
2003- # These are for the fitting function
2004- y_errors = [err .seconds for err in time_error ]
2005- y_errors1 = [err .seconds for err in time_error1 ]
2019+ y_errors_all_secs = np .array ([plus_errs_secs ,minus_errs_secs ])
20062020
2007- y_errors_all = np .concatenate ((y_errors , y_errors1 ))
2021+ # 2 arrays -> symmetric errors for both
2022+ elif len (yerrs )== 2 :
2023+
2024+ # Check the first entry of yerrs. If it's not a timedelta, then it's the reach of the error
2025+ if not isinstance (yerrs [0 ][0 ], (pd .Timedelta , datetime .timedelta )):
2026+ y_errors_all = np .array ([]) #= yerrs
2027+
2028+ for i , ch in enumerate (channels ):
2029+ y_err = yerrs [i ] - self .onset_statistics [ch ][ref_idx ]
2030+ y_errors_all = np .append (y_errors , y_err )
2031+
2032+ else :
2033+ # These are for the fitting function
2034+ y_errors = yerrs [0 ]
2035+ y_errors1 = yerrs [1 ]
2036+
2037+ y_errors_all = np .concatenate ((y_errors , y_errors1 ))
2038+ y_errors_all_secs = [err .seconds for err in y_errors_all ]
2039+
2040+ # 1 array -> wrong
2041+ else :
2042+ raise ValueError ("The y-errors for two-instrument VDA must be in form of 4 or 2 lists!" )
20082043
20092044 # Numpy masks work so that True values get masked as invalid, while False remains unaltered
20102045 mask = np .isnan (date_in_sec_all )
@@ -2016,17 +2051,18 @@ def VDA(self, onset_times=None, Onset=None, energy:str='gmean', selection=None,
20162051 inverse_beta_corrected = ma .array (inverse_beta_corrected , mask = mask )
20172052 x_errors_all = ma .array (x_errors_all , mask = mask )
20182053
2054+ # Asymmetric errors / errors not defined by the user
20192055 if len (y_errors_all )== 2 :
20202056 y_errors_plot = ma .array ([minus_errs , plus_errs ], mask = [np .isnan (date_in_sec ),np .isnan (date_in_sec )])
20212057 y_errors1_plot = ma .array ([minus_errs1 , plus_errs1 ], mask = [np .isnan (date_in_sec1 ),np .isnan (date_in_sec1 )])
20222058 y_errors_all_plot = ma .array ([minus_errs_all , plus_errs_all ], mask = [mask ,mask ])
20232059 y_errors_all_secs = ma .array ([minus_errs_secs_all , plus_errs_secs_all ], mask = [mask ,mask ])
20242060
20252061 else :
2026- y_errors_plot = ma .array (time_error , mask = np .isnan (date_in_sec ))
2027- y_errors1_plot = ma .array (time_error1 , mask = np .isnan (date_in_sec1 ))
2028- y_errors_all_plot = ma .array (y_errors_all , mask = [ mask , mask ] )
2029- y_errors_all_secs = ma .array (y_errors_all , mask = mask )
2062+ y_errors_plot = ma .array (y_errors , mask = np .isnan (date_in_sec ))
2063+ y_errors1_plot = ma .array (y_errors1 , mask = np .isnan (date_in_sec1 ))
2064+ y_errors_all_plot = ma .array (y_errors_all , mask = mask )
2065+ y_errors_all_secs = ma .array (y_errors_all_secs , mask = mask )
20302066
20312067 # After masking NaNs and Nats away, slice
20322068 # which datapoints to consider for the fit
@@ -2338,25 +2374,24 @@ def VDA(self, onset_times=None, Onset=None, energy:str='gmean', selection=None,
23382374 if Onset and len (inverse_beta1 )> 0 :
23392375 label1 = Onset .sensor .upper () if mass_energy == mass_energy1 else f"{ Onset .sensor .upper ()} { species_title1 } "
23402376 ax .errorbar (inverse_beta1 , onset_times1 , yerr = y_errors1_plot , xerr = [x_errors_upper1 , x_errors_lower1 ],
2341- fmt = 'o' , elinewidth = 1.5 , capsize = 4.5 , zorder = 1 , label = label1 )
2377+ fmt = 'o' , elinewidth = VDA_ELINEWIDTH , capsize = VDA_CAPSIZE , zorder = 1 , label = label1 )
23422378
2343- # The reason xerr seems to be wrong way is that 'upper' refers to the upper ENERGY boundary, which corresponds to the LOWER 1/beta boundary
23442379 if not Onset :
23452380 label = "onset times"
23462381 else :
23472382 label = self .sensor .upper () if mass_energy == mass_energy1 else f"{ self .sensor .upper ()} { species_title } "
23482383
23492384 if len (inverse_beta ) > 0 :
23502385 ax .errorbar (inverse_beta , onset_times , yerr = y_errors_plot , xerr = [x_errors_upper , x_errors_lower ],
2351- fmt = 'o' , elinewidth = 1.5 , capsize = 4.5 , zorder = 1 , label = label )
2386+ fmt = 'o' , elinewidth = VDA_ELINEWIDTH , capsize = VDA_CAPSIZE , zorder = 1 , label = label )
23522387
23532388 # Omitted datapoints, paint all points white and then those not omitted blue (+ red) again
23542389 if omitted_exists :
23552390 if Onset :
2356- ax .scatter (inverse_beta1 [selection1 ], onset_times1 [selection1 ], s = 11 , zorder = 3 )
2391+ ax .scatter (inverse_beta1 [selection1 ], onset_times1 [selection1 ], s = VDA_MARKERSIZE , zorder = 3 )
23572392
2358- ax .scatter (inverse_beta_all , onset_times_all , c = "white" , s = 10 , zorder = 2 )
2359- ax .scatter (inverse_beta [selection ], onset_times [selection ], s = 11 , zorder = 3 )
2393+ ax .scatter (inverse_beta_all , onset_times_all , c = "white" , s = VDA_MARKERSIZE - 5 , zorder = 2 )
2394+ ax .scatter (inverse_beta [selection ], onset_times [selection ], s = VDA_MARKERSIZE , zorder = 3 )
23602395
23612396 # The odr fit
23622397 # Here we need to first take the selection of i_beta_all and ONLY after that take the compressed form, which is the set of valid values
@@ -5256,8 +5291,15 @@ def get_figdate(dt_array):
52565291 figdate = date
52575292 break
52585293
5259-
5260- return figdate .date ().strftime ("%Y-%m-%d" )
5294+ try :
5295+ date = figdate .date ().strftime ("%Y-%m-%d" )
5296+
5297+ # An attributeerror is cause by figdate being numpy.datetime64 -object. handle
5298+ # it appropriately
5299+ except AttributeError :
5300+ date = np .datetime_as_string (figdate , unit = 'D' )
5301+
5302+ return date
52615303
52625304
52635305def _isnotebook ():
0 commit comments