Defining Features in Ascelpias
For more explanation on the theory behind features, see Design of the Features module. |
Find the last event that occurs within a time window of other events
This example demonstrates:
-
the
formMeetingSequence
function frominterval-algebra
-
handling a failure case
-
writing a function generic over both the concept and interval types
In this example, the goal is to write a function that, given a list of concepts, converts a list of events into a list of interval durations such that:
-
the events with any of the given concepts are combined into a "meeting sequence";
-
durations of events of the resulting sequence which have all of the given concepts are returned;
-
but an empty result is treated as a failure.
A function like this could be useful if you wanted to find the durations of time when a subject was both hospitalized and on some medication. |
durationsOf
:: forall n m c a b
. (KnownSymbol n, Eventable c m a, IntervalSizeable a b)
=> [c]
-> [Event c m a]
-> Feature n [b]
durationsOf cpts =
filter (`hasAnyConcepts` cpts) (1)
.> fmap (into @(ConceptsInterval c a)) (2) (3)
.> formMeetingSequence (4)
.> filter (`hasAllConcepts` cpts) (5)
.> \x -> if null x (6)
then makeFeature $ featureDataL $ Other "no cases"
else makeFeature $ featureDataR (durations x)
Take the case that a subject has the following events, and we want to know the duration that a subject was both hospitalized and on antibiotics. Below, we walk through the function step-by-by using this case.
-- <- [Non-medication]
---- <- [Hospitalized]
-- <- [Antibiotics]
---- <- [Antibiotics]
------------------------------
1 | Filter events to those that contain at least one
of the given concepts.
|
2 | Cast each event into a ConceptsInterval c a ,
which is a synonym for PairedInterval (Concepts c) a . |
3 | This step is important for the formMeetingSequence function,
as it requires the "data" part of the paired interval to be a Monoid .
Concepts are a Monoid by unioning the elements of two values. |
4 | Form a sequence of intervals where one meets the next.
The data of the running example would look like:
|
5 | Filter to those intervals that have both of the given concepts.
Note that hasAllConcepts works here because
PairedInterval (Concepts c) a is defined as an instance of the HasConcepts
typeclass in event-data-theory .
|
6 | Lastly, if the result of the previous step is empty,
we return a failure, i.e. a Left value of FeatureData .
Otherwise, we return the durations of any intervals,
as a successful Right value of FeatureData . |
The durationsOf
function can be lifted into a Definition
using defineA
:
def
:: (KnownSymbol n1, KnownSymbol n2, Eventable c m a, IntervalSizeable a b)
=> [c] (1)
-> Def (F n1 [Event c m a] -> F n2 [b]) (2)
def cpts = defineA (durationsOf cpts)
1 | Create a function which takes a list of concepts and |
2 | Returns a Definition |
Find durations of time that satisfy multiple conditions
This example demonstrates
-
reasoning with the interval algebra
-
manipulating intervals
-
using concepts to group events
In this example, the goal is to write a function that, given a pair of lists of concepts and an interval of time:
-
filters an input list of events to those that concur with the given interval
-
splits the events into those with the first concepts and those with the second
-
returns the start of the last event of the first setof concepts where it occurs within +/- 3 time units of an event of the second set of concepts.
A function like this could be useful for defining an index event where the index needs to occur with a time window of other events. |
examplePairComparison
:: (Eventable c m a, IntervalSizeable a b)
=> ([c], [c])
-> Interval a
-> [Event c m a]
-> Maybe a
examplePairComparison (c1, c2) i =
filterConcur i -- (1)
.> splitByConcepts c1 c2 (2)
.> uncurry allPairs (3)
.> filter (\pr -> fst pr `concur` expand 3 3 (snd pr)) (4)
.> lastMay (5)
.> fmap (begin . fst) (6)
Take the case that a subject has the following events,
and we want to know the first time when a diagnosis
occurred within +/- 3 days of a procedure.
Our given interval, called Baseline
here,
is (6, 15).
Below, we walk through the function step-by-by using this case.
--------- <- Baseline - <- [pr] - <- [pr] - <- [dx] - <- [pr] ---- <- [foo] ------------------------------
1 | Filter events to those concurring
with the given interval.
--------- <- Baseline - <- [pr] - <- [dx] - <- [pr] ---- <- [foo] ------------------------------ |
2 | Form a pair of lists where the first element
has c1 (dx in our example) events and
the second has c2 (pr in our example) events.
Any events without c1 or c2 concepts are dropped.
In the running example,
the intervals of the events would make the
following pair:
( [(10,11)] -- the dx event , [(6,7), (12,13)] -- the pr events ) |
3 | Form a list of all pairs of events
from the previous step.
[ ( (10,11), (6,7) ) , ( (10,11), (12,13) ) ] |
4 | Filter this list of pairs to cases where a c1 event concurs
with a c2 event after
c2 event intervals have been expanded by +/- 3 units of time.
The list from the last step gets converted to:
[ ( (10,11), (3,10) ) , ( (10,11), (9,16) ) ] And then filtered to: [ ( (10,11), (9,16) ) ] |
5 | Take Just the last element of the list, if it exists.
Otherwise, Nothing . |
6 | If it exists, take the begin of the last c1 interval.
In our example, this is Just 10 . |
Lastly, the example function can be lifted into a Definition
using
the define
function:
def
:: (Eventable c m a, IntervalSizeable a b)
=> ([c], [c])
-> Def (F n1 (Interval a) -> F n2 [Event c m a] -> F n3 (Maybe a))
def cpts = define (examplePairComparison cpts)