Exemple #1
0
def window(series, start=0, end=None):

	"""
	desc:
		Extracts a window from a signal.

	arguments:
		series:
			desc:	The signal to get a window from.
			type:	SeriesColumn

	keywords:
		start:
			desc:	The window start.
			type:	int
		end:
			desc:	The window end, or None to go to the signal end.
			type:	[int, None]

	returns:
		desc:	A window of the signal.
		type:	SeriesColumn
	"""

	if end is None:
		end = series.depth
	a = series[:,start:end]
	depth = a.shape[1]
	window_series = _SeriesColumn(series._datamatrix, depth)
	window_series[:] = a
	return window_series
Exemple #2
0
def _apply_fnc(series, fnc, **kwdict):

	"""
	visible: False

	desc:
		Applies a function to each cell.

	arguments:
		series:
			desc:	A signal to apply the function to.
			type:	SeriesColumn
		fnc:
			desc:	The function to apply.

	keyword-dict:
		kwdict:		A dict with keyword arguments for fnc.

	returns:
		desc:	A new signal.
		type:	SeriesColumn
	"""

	new_series = _SeriesColumn(series._datamatrix, depth=series.depth)
	for i, cell in enumerate(series):
		new_series[i] = fnc(cell, **kwdict)
	return new_series
Exemple #3
0
def endlock(series):
	
	"""
	desc:
		Locks a series to the end, so that any nan-values that were at the end
		are moved to the front.
		
	arguments:
		series:
			desc:	The signal to end-lock.
			type:	SeriesColumn
			
	returns:
		desc:	An end-locked signal.
		type:	SeriesColumn
	"""

	endlock_series = _SeriesColumn(series._datamatrix, series.depth)
	endlock_series[:] = np.nan
	for i in range(len(series)):
		for j in range(series.depth-1, -1, -1):
			if not np.isnan(series[i,j]):
				break
		endlock_series[i,-j-1:] = series[i,:j+1]
	return endlock_series
Exemple #4
0
def threshold(series, fnc, min_length=1):

	"""
	desc:
		Finds samples that satisfy some threshold criterion for a given period.

	arguments:
		series:
			desc:	A signal to threshold.
			type:	SeriesColumn
		fnc:
			desc:	A function that takes a single value and returns True if
					this value exceeds a threshold, and False otherwise.
			type:	FunctionType

	keywords:
		min_length:
			desc:	The minimum number of samples for which `fnc` must return
					True.
			type:	int

	returns:
		desc:	A series where 0 indicates below threshold, and 1 indicates
				above threshold.
		type:	SeriesColumn
	"""

	threshold_series = _SeriesColumn(series._datamatrix, series.depth)
	threshold_series[:] = 0
	# First walk through all rows
	for i, trace in enumerate(series):
		print()
		# Then walk through all samples within a row
		nhit = 0
		for j, val in enumerate(trace):
			hit = fnc(val)
			if hit:
				nhit += 1
				continue
			if nhit >= min_length:
				threshold_series[i,j-nhit:j] = 1
			nhit = 0
		if nhit >= min_length:
			threshold_series[i,j-nhit:j] = 1
	return threshold_series
Exemple #5
0
def concatenate(*series):

	"""
	desc: |
		Concatenates multiple series such that a new series is created with a
		depth that is equal to the sum of the depths of all input series.

		__Example:__

		%--
		python: |
		 from datamatrix import series as srs

		 dm = DataMatrix(length=1)
		 dm.s1 = SeriesColumn(depth=3)
		 dm.s1[:] = 1,2,3
		 dm.s2 = SeriesColumn(depth=3)
		 dm.s2[:] = 3,2,1
		 dm.s = srs.concatenate(dm.s1, dm.s2)
		 print(dm.s)
		--%

	argument-list:
		series: A list of series.

	returns:
		desc:	A new series.
		type:	SeriesColumn
	"""

	if not series or not all(isinstance(s, _SeriesColumn) for s in series):
		raise TypeError(u'Expecting one or more SeriesColumn objects')
	if not all(s.dm is series[0].dm for s in series):
		raise ValueError(
			u'SeriesColumn objects don\'t belong to the same DataMatrix')
	newseries = _SeriesColumn(series[0]._datamatrix,
		depth=sum(s.depth for s in series))
	i = 0
	for s in series:
		newseries[:,i:i+s.depth] = s
		i += s.depth
	return newseries
Exemple #6
0
def endlock(series):
    """
	desc: |
		Locks a series to the end, so that any nan-values that were at the end
		are moved to the start.

		__Example:__

		%--
		python: |
		 import numpy as np
		 from matplotlib import pyplot as plt
		 from datamatrix import DataMatrix, SeriesColumn, series

		 LENGTH = 5 # Number of rows
		 DEPTH = 10 # Depth (or length) of SeriesColumns

		 sinewave = np.sin(np.linspace(0, 2*np.pi, DEPTH))

		 dm = DataMatrix(length=LENGTH)
		 # First create five identical rows with a sinewave
		 dm.y = SeriesColumn(depth=DEPTH)
		 dm.y.setallrows(sinewave)
		 # Add a random offset to the Y values
		 dm.y += np.random.random(LENGTH)
		 # Set some observations at the end to nan
		 for i, row in enumerate(dm):
		 	row.y[-i:] = np.nan
		 # Lock the degraded traces to the end, so that all nans
		 # now come at the start of the trace
		 dm.y2 = series.endlock(dm.y)

		 plt.clf()
		 plt.subplot(121)
		 plt.title('Original (nans at end)')
		 plt.plot(dm.y.plottable)
		 plt.subplot(122)
		 plt.title('Endlocked (nans at start)')
		 plt.plot(dm.y2.plottable)
		 plt.savefig('content/pages/img/series/endlock.png')
		--%

		%--
		figure:
		 source: endlock.png
		 id: FigEndLock
		--%

	arguments:
		series:
			desc:	The signal to end-lock.
			type:	SeriesColumn

	returns:
		desc:	An end-locked signal.
		type:	SeriesColumn
	"""

    endlock_series = _SeriesColumn(series._datamatrix, series.depth)
    endlock_series[:] = np.nan
    src = series._seq
    dst = endlock_series._seq
    for rownr, row in enumerate(src):
        nancols = np.where(np.isnan(row))[0]
        for nancol in nancols:
            if np.any(~np.isnan(row[nancol:])):
                continue
            dst[rownr, -nancol:] = row[:nancol]
            break
        else:
            dst[rownr] = row
    return endlock_series
Exemple #7
0
def threshold(series, fnc, min_length=1):
    """
	desc: |
		Finds samples that satisfy some threshold criterion for a given period.

		__Example:__

		%--
		python: |
		 import numpy as np
		 from matplotlib import pyplot as plt
		 from datamatrix import DataMatrix, SeriesColumn, series

		 LENGTH = 1 # Number of rows
		 DEPTH = 100 # Depth (or length) of SeriesColumns

		 sinewave = np.sin(np.linspace(0, 2*np.pi, DEPTH))

		 dm = DataMatrix(length=LENGTH)
		 # First create five identical rows with a sinewave
		 dm.y = SeriesColumn(depth=DEPTH)
		 dm.y.setallrows(sinewave)
		 # And also a bit of random jitter
		 dm.y += np.random.random( (LENGTH, DEPTH) )
		 # Threshold the signal by > 0 for at least 10 samples
		 dm.t = series.threshold(dm.y, fnc=lambda y: y > 0, min_length=10)

		 plt.clf()
		 # Mark the thresholded signal
		 plt.fill_between(np.arange(DEPTH), dm.t[0], color='black', alpha=.25)
		 plt.plot(dm.y.plottable)
		 plt.savefig('content/pages/img/series/threshold.png')

		 print(dm)
		--%

		%--
		figure:
		 source: threshold.png
		 id: FigThreshold
		--%

	arguments:
		series:
			desc:	A signal to threshold.
			type:	SeriesColumn
		fnc:
			desc:	A function that takes a single value and returns True if
					this value exceeds a threshold, and False otherwise.
			type:	FunctionType

	keywords:
		min_length:
			desc:	The minimum number of samples for which `fnc` must return
					True.
			type:	int

	returns:
		desc:	A series where 0 indicates below threshold, and 1 indicates
				above threshold.
		type:	SeriesColumn
	"""

    threshold_series = _SeriesColumn(series._datamatrix, series.depth)
    threshold_series[:] = 0
    # First walk through all rows
    for i, trace in enumerate(series):
        # Then walk through all samples within a row
        nhit = 0
        for j, val in enumerate(trace):
            hit = fnc(val)
            if hit:
                nhit += 1
                continue
            if nhit >= min_length:
                threshold_series[i, j - nhit:j] = 1
            nhit = 0
        if nhit >= min_length:
            threshold_series[i, j - nhit + 1:j + 1] = 1
    return threshold_series
Exemple #8
0
def normalize_time(dataseries, timeseries):
    """
	desc: |
		*New in v0.7.0*

		Creates a new series in which a series of timestamps (`timeseries`) is
		used as the indices for a series of data point (`dataseries`). This is
		useful, for example, if you have a series of measurements and a
		separate series of timestamps, and you want to combine the two.

		The resulting series will generally contain a lot of `nan` values, which
		you can interpolate with `interpolate()`.

		__Example:__

		%--
		python: |
		 from matplotlib import pyplot as plt
		 from datamatrix import DataMatrix, SeriesColumn, series as srs, NAN

		 # Create a DataMatrix with one series column that contains samples
		 # and one series column that contains timestamps.
		 dm = DataMatrix(length=2)
		 dm.samples = SeriesColumn(depth=3)
		 dm.time = SeriesColumn(depth=3)
		 dm.samples[0] = 3, 1, 2
		 dm.time[0]    = 1, 2, 3
		 dm.samples[1] = 1, 3, 2
		 dm.time[1]    = 0, 5, 10
		 # Create a normalized column with samples spread out according to
		 # the timestamps, and also create an interpolate version of this
		 # column for smooth plotting.
		 dm.normalized = srs.normalize_time(
		 	dataseries=dm.samples,
		 	timeseries=dm.time
		 )
		 dm.interpolated = srs.interpolate(dm.normalized)
		 # And plot!
		 plt.clf()
		 plt.plot(dm.normalized.plottable, 'o')
		 plt.plot(dm.interpolated.plottable, ':')
		 plt.xlabel('Time')
		 plt.ylabel('Data')
		 plt.savefig('content/pages/img/series/normalize_time.png')
		--%

		%--
		figure:
		 source: normalize_time.png
		 id: FigNormalizeTime
		--%

	arguments:
		dataseries:
			desc:	A column with datapoints.
			type:	SeriesColumn
		timeseries:
			desc:	A column with timestamps. This should be an increasing list
					of the same depth as `dataseries`. NAN values are allowed,
					but only at the end.
			type:	SeriesColumn

	returns:
		desc:	A new series in which the data points are spread according to
				the timestamps.
		type:	SeriesColumn
	"""

    if (not isinstance(dataseries, _SeriesColumn)
            or not isinstance(timeseries, _SeriesColumn)):
        raise TypeError(
            'dataseries and timeseries should be SeriesColumn objects')
    if dataseries.dm is not timeseries.dm:
        raise ValueError(
            'dataseries and timeseries should belong to the same DataMatrix')
    if dataseries.depth != timeseries.depth:
        raise ValueError(
            'dataseries and timeseries should have the same depth')
    if max(timeseries.max) < 0 or min(timeseries.min) < 0:
        raise ValueError('timeseries should contain only positive values')
    series = _SeriesColumn(dataseries.dm, depth=int(max(timeseries.max)) + 1)
    haystack = np.arange(series.depth, dtype=int)
    for row in range(series._seq.shape[0]):
        needle = timeseries._seq[row]
        values = dataseries._seq[row]
        while len(needle) and np.isnan(needle)[-1]:
            needle = needle[:-1]
            values = values[:-1]
        if np.any(np.isnan(needle)):
            raise ValueError(
                'timeseries should not contain NAN values, except at the end')
        if not np.all(np.diff(needle) > 0):
            raise ValueError('timeseries should contain increasing values '
                             '(i.e. time should go forward)')
        indices = np.searchsorted(haystack, needle)
        series._seq[row, indices] = values
    return series
Exemple #9
0
def lock(series, lock):
    """
	desc: |
		Shifts each row from a series by a certain number of steps along its
		depth. This is useful to lock, or align, a series based on a sequence of
		values.

		__Example:__

		%--
		python: |
		 import numpy as np
		 from matplotlib import pyplot as plt
		 from datamatrix import DataMatrix, SeriesColumn, series as srs

	 	 LENGTH = 5 # Number of rows
		 DEPTH = 10 # Depth (or length) of SeriesColumns

	 	 dm = DataMatrix(length=LENGTH)
		 # First create five traces with a partial cosinewave. Each row is
		 # offset slightly on the x and y axes
		 dm.y = SeriesColumn(depth=DEPTH)
		 dm.x_offset = -1
		 dm.y_offset = -1
		 for row in dm:
		 	row.x_offset = np.random.randint(0, DEPTH)
		 	row.y_offset = np.random.random()
		 	row.y = np.roll(np.cos(np.linspace(0, np.pi, DEPTH)),
		 		row.x_offset)+row.y_offset
		 # Now use the x offset to lock the traces to the 0 point of the cosine,
		 # i.e. to their peaks.
		 dm.y2, zero_point = srs.lock(dm.y, lock=dm.x_offset)

		 plt.clf()
		 plt.subplot(121)
		 plt.title('Original')
		 plt.plot(dm.y.plottable)
		 plt.subplot(122)
		 plt.title('Locked to peak')
		 plt.plot(dm.y2.plottable)
		 plt.axvline(zero_point, color='black', linestyle=':')
		 plt.savefig('content/pages/img/series/lock.png')
		--%

		%--
		figure:
		 source: lock.png
		 id: FigLock
		--%

	arguments:
		series:
			desc:	The signal to lock.
			type:	SeriesColumn
		lock:
			desc:	A sequence of lock values with the same length as the
					Series. This can be a column, a list, a numpy array, etc.

	returns:
		desc:	A `(series, zero_point)` tuple, in which `series` is a
				`SeriesColumn` and `zero_point` is the zero point to which the
				signal has been locked.
	"""

    if not isinstance(series, _SeriesColumn):
        raise TypeError('series should be a SeriesColumn')
    if len(series) != len(lock):
        raise ValueError('lock and series should be the same length')
    try:
        zero_point = int(max(lock))
    except TypeError:
        raise TypeError('lock should be a sequence of integers')
    lpad = [int(zero_point - l) for l in lock]
    lock_series = _SeriesColumn(series.dm, series.depth + max(lpad))
    for lpad, lock_row, orig_row in zip(lpad, lock_series, series):
        lock_row[lpad:lpad + series.depth] = orig_row
    return lock_series, zero_point
Exemple #10
0
def fft(series, truncate=True):
    """
	desc: |
		*New in v0.9.2*

		Performs a fast-fourrier transform (FFT) for the signal. For more
		information, see [`numpy.fft`](https://docs.scipy.org/doc/numpy/reference/routines.fft.html#module-numpy.fft).

		__Example:__

		%--
		python: |
		 import numpy as np
		 from matplotlib import pyplot as plt
		 from datamatrix import DataMatrix, SeriesColumn, series

		 LENGTH = 3
		 DEPTH = 200

		 # Create one fast oscillation, and two combined fast and slow oscillations
		 dm = DataMatrix(length=LENGTH)
		 dm.s = SeriesColumn(depth=DEPTH)
		 dm.s[0] = np.sin(np.linspace(0, 150 * np.pi, DEPTH))
		 dm.s[1] = np.sin(np.linspace(0, 75 * np.pi, DEPTH))
		 dm.s[2] = np.sin(np.linspace(0, 10 * np.pi, DEPTH))
		 dm.f = series.fft(dm.s)

		 # Plot the original signal
		 plt.clf()
		 plt.subplot(121)
		 plt.title('Original')
		 plt.plot(dm.s[0])
		 plt.plot(dm.s[1])
		 plt.plot(dm.s[2])
		 plt.subplot(122)
		 # And the filtered signal!
		 plt.title('FFT')
		 plt.plot(dm.f[0])
		 plt.plot(dm.f[1])
		 plt.plot(dm.f[2])
		 plt.savefig('content/pages/img/series/fft.png')
		--%

		%--
		figure:
		 source: fft.png
		 id: FigFFT
		--%

	arguments:
		series:
			desc:	A signal to determine the FFT for.
			type:	SeriesColumn

	keywords:
		truncate:
			desc:	FFT series of real signals are symmetric. The `truncate`
					keyword indicates whether the last (symmetric) part of the
					FFT should be removed.
			type:	bool

	returns:
		desc:	The FFT of the signal.
		type:	SeriesColumn
	"""

    newseries = _SeriesColumn(series._datamatrix, depth=series.depth)
    newseries[:] = np.fft.fft(series, axis=1)
    if truncate:
        newseries.depth = newseries.depth // 2
    return newseries