Ticket #304 (closed enhancement: worksforme)
detrending by fitting a linear function
| Reported by: | driel | Owned by: | |
|---|---|---|---|
| Priority: | minor | Component: | ObsPy library |
| Keywords: | Cc: |
Description
Before adding this to obspy, I decided put it here for discussion, maybe there is some problem I did not anticipate:
For now, detrending is done using the first and last and last value of the trace in obspy.signal.invsim.detrend. This fails with a lot of noise or when the time window of the trace contains only part of the signal. Wouldn't it be better to fit a linear function to the trace and remove that from the data? E.g. like this:
for tr in st:
dt = tr.stats.delta
t = np.linspace(0., (tr.stats.npts - 1) * dt, tr.stats.npts)
A = np.vstack([t, np.ones(len(t))]).T
m, c = np.linalg.lstsq(A, tr.data)[0]
tr.data = tr.data - m*t - c
Works fine for me.
Cheers, Martin
Attachments
Change History
comment:2 Changed 4 months ago by barsch
oh most important part is missing in the last paragraph above: "as long you have a proper documentation for the new method it in the docstring" ;)
comment:3 Changed 4 months ago by driel
Sure, would keep the old version for compatibility reasons. Also, for very long data, the fitting might be slow and memory intensive.
comment:4 Changed 4 months ago by anonymous
Accidently answered with an email that does not show up here... So once again:
I have always used scipy's detrend routine which, like your solution, just subtracts a linear least square fit:
http://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.detrend.html
It also has on optional 'method' argument so one could also just remove the mean value.
Maybe just wrap that routine? I also agree that simply removing a line through the first and last sample makes little sense.
comment:5 Changed 4 months ago by megies
I think using the scipy routine is the way to go. I would suggest implementing a detrend() method on Trace/Stream to make this easier to handle for users. We can leave the current detrend function in obspy.signal.invsim untouched and use the scipy routine directly at Trace/Stream. probably also with an kwarg switch like:
trace.detrend(method="LS")
method == "LS": use scipy least squares
method == "simple": use method from `signal.invsim` with first/last sample
With the least squares linear regression approach it might be a good idea to also include tapering (controlled with another kwarg maybe) for a few samples at the start. Otherwise it is not guaranteed that the data starts at 0 which is important for any filtering coming after detrending.
comment:6 Changed 4 months ago by driel
I didn't know the function proposed by Lion, thanks for that. I agree with Tobias as for adding the detrend to Stream / Trace and I suggest when someone starts with wrapping functions in Stream and Trace, it wouldn't be much more then copy and paste to also do sth. about #265 (also using a method argument and using simple diff/cumptrapz as a start).
I'll have a look at it, but might need some help with the tests at some point.
comment:7 Changed 4 months ago by krischer
I would also suggest to add some routines to Trace/Stream. I think its cleaner to add seperate detrend and taper convenience methods and maybe even give the optional possibility to detrend and taper right in the filter method, e.g.
stream.filter('lowpass', freq=1.0, corners=2, zerophase=True,
detrend='LS', taper='hanning')
comment:8 Changed 4 months ago by driel
In [2967/obspy]:
comment:9 Changed 4 months ago by driel
In [2968/obspy]:
comment:10 Changed 4 months ago by driel
In [2969/obspy]:
comment:11 Changed 4 months ago by driel
In [2970/obspy]:
comment:12 Changed 4 months ago by driel
Okay, I implemented part of what we discussed above. I would ask one of the core developers to quickly double check. How can I check the docstrings format? docs.obspy.org is not updated after svn commit I guess...?
comment:13 Changed 4 months ago by driel
Right, thanks Robert for cleaning up. The only thing I dislike is the detrend(type='constant'), I'd rather suggest to call it demean(). As it does not remove any 'trend', the name detrend is not intuitive to me.
comment:14 Changed 4 months ago by barsch
Its taken from http://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.detrend.html - change it if you don't like it or just add another keyword 'demean' doing exactly the same thing ;)
comment:15 Changed 4 months ago by megies
During filtering, data arrays get converted to float.. should we do this for detrend (tr.data = tr.data.astype('float') before detrending), too, what do you think?
comment:16 Changed 4 months ago by driel
I don't see any reason not to, but a couple of good ones to do so...
comment:17 Changed 4 months ago by krischer
They definitely have to be converted to floats. The detrending array should be a float array anyway and therefore the resulting detrended data array should also be float.
Assuming tr.data is an integer array and detrend a float array, then
tr.data -= detrend
results in an integer array which is wrong.
tr.data = tr.data - detrend
on the other hand would result in a float array but I would prefer the following:
tr.data = np.require(tr.data, 'float32') tr.data -= detrend
comment:18 Changed 4 months ago by megies
.astype()/require() is the only option since the rest is done inside the scipy routine that is called (and no type conversion done there).
comment:19 Changed 3 months ago by driel
I kind of lost track, what's the status here? Can this ticket be closed or is there some issue with the float stuff discussed above?
comment:20 Changed 10 days ago by barsch
- Status changed from new to closed
- Resolution set to worksforme

I can't answer from a scientific view as I'm not really into in signal processing. However as far as I know its always going down to the question what you actually want to to with your data. Some prefer method x to do something while some prefer something else.
Therefore I would suggest to include "your" detrending function as an addition into obspy.signal - either by introducing a new keyword "method=..."to the current detrend function or giving it a completely new name - pretty much the same we did with xcorr, merge etc.
just my 2 cents Robert