@@ -504,6 +504,9 @@ def read_response_file(self, response_fn=None, data_fn=None):
504504
505505 entries = []
506506
507+ # initialise at zero
508+ self .dataframe ["t_zy" ] = 0.0 + 0 * 1j
509+
507510 for line in data_list :
508511 res_log = False
509512 # try:
@@ -556,35 +559,70 @@ def read_response_file(self, response_fn=None, data_fn=None):
556559 self .dataframe [key + "_model_error" ] = 0.0
557560
558561 def _group_df (self ):
562+
559563 df = self .dataframe
560- for station in np .unique (df ["station" ]):
561- for period in np .unique (df ["period" ]):
562- filt = np .all (
563- [
564- df ["station" ] == station ,
565- df ["period" ] == period ,
566- ],
567- axis = 0 ,
568- )
569- if np .any (filt ):
570- for key in ["res_xy" , "res_yx" , "phase_xy" , "phase_yx" ]:
571- df .loc [filt , key ] = np .unique (df .loc [filt , key ])[0 ]
572- df .loc [filt , key + "_model_error" ] = np .unique (
573- df .loc [filt , key + "_model_error" ]
574- )[0 ]
575- tx_real_vals = np .unique (np .real (df .loc [filt , "t_zy" ]))
576- tx_imag_vals = np .unique (np .imag (df .loc [filt , "t_zy" ]))
577-
578- if np .any (tx_real_vals != 0 ) or np .any (tx_imag_vals != 0 ):
579- tx_real_val , tx_imag_val = 0.0 , 0.0
580- if len (tx_real_vals [tx_real_vals != 0 ]) > 0 :
581- tx_real_val = tx_real_vals [tx_real_vals != 0 ][0 ]
582- if len (tx_imag_vals [tx_imag_vals != 0 ]) > 0 :
583- tx_imag_val = tx_imag_vals [tx_imag_vals != 0 ][0 ]
584-
585- df .loc [filt , "t_zy" ] = tx_real_val + 1j * tx_imag_val
586-
587- df .drop (df .index [filt ][1 :], inplace = True )
564+
565+ # 1. Helper function for the complex number logic
566+ def process_t_zy (series ):
567+ # Drop NaNs and convert to a standard Python list
568+ vals = series .dropna ().tolist ()
569+
570+ reals = []
571+ imags = []
572+
573+ for v in vals :
574+ try :
575+ # Force the value into a standard complex type safely
576+ c = complex (v )
577+ if c .real != 0.0 :
578+ reals .append (c .real )
579+ if c .imag != 0.0 :
580+ imags .append (c .imag )
581+ except (TypeError , ValueError ):
582+ # Safely ignore any weird strings or corrupted data
583+ continue
584+
585+ # sort the values, so [0] grabs the minimum.
586+ r = sorted (set (reals ))[0 ] if reals else 0.0
587+ i = sorted (set (imags ))[0 ] if imags else 0.0
588+
589+ return complex (r , i )
590+
591+ # 2. Identify the specific columns
592+ keys = ["res_xy" , "res_yx" , "phase_xy" , "phase_yx" ]
593+ error_keys = [f"{ k } _model_error" for k in keys ]
594+ special_cols = keys + error_keys
595+
596+ # 3. Build the aggregation dictionary with Python Lambdas
597+ agg_funcs = {}
598+ for col in df .columns :
599+ if col in ["station" , "period" ]:
600+ continue
601+ elif col == "t_zy" :
602+ agg_funcs [col ] = process_t_zy
603+ elif col in special_cols :
604+ # Bypass Cython by using a lambda for minimum
605+ agg_funcs [col ] = lambda x : x .min ()
606+ else :
607+ # Bypass Cython by explicitly asking for the first index
608+ agg_funcs [col ] = lambda x : x .iloc [0 ]
609+
610+ # 4. Perform the GroupBy operation
611+ self .dataframe = df .groupby (["station" , "period" ], as_index = False ).agg (
612+ agg_funcs
613+ )
614+
615+ # def _group_df(self):
616+ # print("grouping df")
617+
618+ # # A custom Python function that does exactly what .first() does:
619+ # # It drops NaNs, and grabs the first available value.
620+ # def get_first_valid(series):
621+ # valid_data = series.dropna()
622+ # return valid_data.iloc[0] if not valid_data.empty else np.nan
623+
624+ # # .agg() with a custom lambda forces pandas to stay in Python space
625+ # self.dataframe = self.dataframe.groupby(['station', 'period'], as_index=False).agg(get_first_valid)
588626
589627 def _get_model_mode_from_data (self , res_log ):
590628 """Get inversion mode from the data.
0 commit comments