Custom composable apps

You can make a simple customised app using the user_function app. This is a wrapper class that takes a reference to your function and the input, output and data types. The resulting app can then become part of a composed function.

Defining a user_function requires you consider four things.

func

A function you have written. This is required.

input_types

A type, or collection of type that your function can handle. This setting dictates what other apps have an output that is a compatable input for your function.

output_types

A type, or collection of type that your function produces. This setting dictates what other apps can have yours as input.

data_types

The data class names, as strings, that your function can handle. Not required, but useful.

A simple example

We make a very simple function first4, that returns the first 4 elements of an alignment.

def first4(val):
    return val[:4]

Now we define a user_function instance that takes and returns an ALIGNED_TYPE.

from cogent3.app.composable import user_function, ALIGNED_TYPE

just4 = user_function(
    first4,
    input_types=ALIGNED_TYPE,
    output_types=ALIGNED_TYPE,
    data_types="Alignment",
)

The repr() of your user_function instance indicates the wrapped function and the module it’s in.

just4
user_function(name='first4', module='__main__')

You use it like all composable apps which we demonstrate using a small sample alignment.

from cogent3 import make_aligned_seqs

aln = make_aligned_seqs(
    data=dict(a="GCAAGCGTTTAT", b="GCTTTTGTCAAT"), array_align=False
)
result = just4(aln)
result
0
aGCAA
b..TT

2 x 4 text alignment

Renaming sequences

This time we wrap a method call on a SequenceCollection (and the alignment sub-classes) for renaming sequences. We also illustrate here that to support both aligned and unaligned data types as input/output, we have to include these in the construction of the custom function.

Note

The SERIALISABLE_TYPE indicates the data has the ability to be converted to json.

from cogent3.app.composable import (
    user_function,
    ALIGNED_TYPE,
    SEQUENCE_TYPE,
    SERIALISABLE_TYPE,
)

def renamer(aln):
    """upper case names"""
    return aln.rename_seqs(lambda x: x.upper())

rename_seqs = user_function(
    renamer,
    input_types=(ALIGNED_TYPE, SEQUENCE_TYPE),
    output_types=SERIALISABLE_TYPE,
    data_types=("SequenceCollection", "Alignment", "ArrayAlignment"),
)
result = rename_seqs(aln)
result.names
['A', 'B']

A user function for with a different output type

In this example, we make an function that returns DistanceMatrix of an alignment.

from cogent3.app.composable import (
    user_function,
    ALIGNED_TYPE,
    PAIRWISE_DISTANCE_TYPE,
)

def _get_dist(aln):
    return aln.distance_matrix(calc="hamming", show_progress=False)

get_dist = user_function(
    _get_dist,
    input_types=ALIGNED_TYPE,
    output_types=PAIRWISE_DISTANCE_TYPE,
    data_types=("Alignment", "ArrayAlignment"),
)
result = get_dist(aln)
result
namesab
a0.00006.0000
b6.00000.0000