/DiscreteSignals

A domain specific language for modeling and manipulating discrete time signals.

Primary LanguagePythonMIT LicenseMIT

py-aiger logo

A domain specific language for modeling and manipulating discrete time signals.

Build Status codecov Updates

PyPI version License: MIT

About

This library aims to provide a domain specific language for modeling and manipulating discrete time signals. Intuitively, most of the time, the discrete time signal's value is undefined.

If discrete-signals isn't for you, I recommend checking out traces (which this library took design inspiration from). Both libraries offer a convenient way to model unevenly-spaced discrete time signals.

Installation

$ pip install discrete-signals

Usage

from discrete_signals import signal

x = signal([(0, 1), (1, 2), (2, 3)], start=0, end=10, tag='x')
y = signal([(0.5, 'a'), (1, 'b'), (2, 'c')], start=0, end=3, tag='y')

x
# start, end: [0, 10)
# data: [(0, {'x': 1}), (1, {'x': 2}), (2, {'x': 3})]

y
# start, end: [0, 3)
# data: [(0.5, {'y': 'a'}), (1, {'y': 'b'}), (2, {'y': 'c'})]

Parallel Composition

x | y
# start, end: [0, 10)
# data: [(0, {'x': 1}), (0.5, {'y': 'a'}), (1, {'x': 2, 'y': 'b'}), (2, {'x': 3, 'y': 'c'})]

Concatenation

x @ y
# start, end: [0, 13)
# data: [(0, {'x': 1}), (1, {'x': 2}), (2, {'x': 3}), (10.5, {'y': 'a'}), (11, {'y': 'b'}), (12, {'y': 'c'})]

Retagging/Relabeling

x.retag({'x': 'z'})
# start, end: [0, 10)
# data: [(0, {'z': 1}), (1, {'z': 2}), (2, {'z': 3})]

Time shifting

x >> 1.1
# start, end: [1.1, 11.1)
# data: [(1.1, {'x': 1}), (2.1, {'x': 2}), (3.1, {'x': 3})]

x << 1
# start, end: [-1, 9)
# data: [(-1, {'x': 1}), (0, {'x': 2}), (1, {'x': 3})]

Slicing

x[1:]
# start, end: [1, 10)
# data: [(1, {'x': 2}), (2, {'x': 3})]

x[:1]
# start, end: [0, 1)
# data: [(0, {'x': 1})]

Rolling Window

x.rolling(1, 3)
# start, end: [-1, 7)
# data: [(-1, {'x': (1, 2)}), (0, {'x': (2, 3)}), (1, {'x': (3,)})]

Mapping a Function

One perform a point wise transform of the signal. For example, the following is equivalent to retagging the signal and adding 1.

x.transform(lambda val: {'y': val['x'] + 1})
# start, end: [0, 10)
# data: [(0, {'y': 2}), (1, {'y': 3}), (2, {'y': 4})]

Alternatively, DiscreteSignals support mapping the dictionary of values to a single value (and optionally tag it):

x.map(lambda val: str(val['x']), tag='z')
# start, end: [0, 10)
# data: [(0, {'z': '1'}), (1, {'z': '2'}), (2, {'z': '3'})]

Filter a signal

x.filter(lambda val: val['x'] > 2)
# start, end: [0, 10)
# data: [(2, {'x': 3})]

Projecting onto a subset of the tags.

(x | y).project({'x'})
# start, end: [0, 10)
# data: [(0, {'x': 1}), (1, {'x': 2}), (2, {'x': 3})]

Attributes

(x | y).tags
# {'x', 'y'}

x.values()
# SortedDict_values([defaultdict(None, {'x': 1}), defaultdict(None, {'x': 2}), defaultdict(None, {'x': 3})])

list(x.times())
# [0, 1, 2]