diff --git a/pyleoclim/core/series.py b/pyleoclim/core/series.py index 6f72517b..071553b3 100644 --- a/pyleoclim/core/series.py +++ b/pyleoclim/core/series.py @@ -2368,6 +2368,27 @@ def clean(self, verbose=False, keep_log = False): new.log += ({len(new.log):'clean', 'verbose': verbose},) return new + def drop_na(self): + ''' Remove NaN values while preserving temporal order + + Unlike clean(), this method only removes NaN values without sorting the data. + + Returns + ------- + Series : Series + Series object with NaN values removed + + See Also + -------- + clean : Remove NaNs and sort by time + + ''' + new = self.copy() + valid_mask = ~(np.isnan(self.time) | np.isnan(self.value)) + new.time = self.time[valid_mask] + new.value = self.value[valid_mask] + return new + def sort(self, verbose=False, ascending = True, keep_log = False): ''' Ensure timeseries is set to a monotonically increasing axis. If the time axis is prograde to begin with, no transformation is applied. diff --git a/pyleoclim/tests/test_core_Series.py b/pyleoclim/tests/test_core_Series.py index 7d10e1aa..9344f78f 100644 --- a/pyleoclim/tests/test_core_Series.py +++ b/pyleoclim/tests/test_core_Series.py @@ -414,6 +414,30 @@ def test_clean(self, gen_normal): assert time[len(time) - 1] >= time[0] assert len(time) == len(value) + def test_drop_na(self, gen_normal): + """Test drop_na method""" + + # Generate data + ts = gen_normal() + + # Add NaN values to test drop_na functionality + v = ts.value.copy() + v[1] = np.nan + v[3] = np.nan + ts_with_nans = pyleo.Series(time=ts.time, value=v, dropna=False) + + # Call function for testing + ts_clean = ts_with_nans.drop_na() + + # Isolate time and value components + time = ts_clean.__dict__["time"] + value = ts_clean.__dict__["value"] + + assert len(time) == len(value) + assert len(time) == 98 # Original 100 - 2 NaNs = 98 + assert not np.any(np.isnan(time)) + assert not np.any(np.isnan(value)) + class TestSeriesAnnualize: # Written mostly by Claude AI