Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update documentation + minor fixes #33

Merged
merged 5 commits into from
Nov 14, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -90,3 +90,101 @@ For interactive development, you can serve the app in development mode
with `panel serve`:

rye run panel serve src/tseda --dev --show --args tests/data/test.trees

## Adding a new application page

Adding a new application page boils down to adding a new module to
`vpages` and registering that module in `vpages.__init__`.

Assuming your new page is called `MyAnalysis`, start by adding a file
named file `myanalysis.py` to `vpages`. At the very minimum, the file
should contain a class called `MyAnalysisPage` with the following
template:

```python
import panel as pn

from .core import View


class MyAnalysisPage(View):
key = "myanalysis"
title = "My Analysis"

def __panel__(self):
return pn.Column()
```

In `vpages.__init__.py` add `myanalysis` to the first `import`
statement, and append `myanalysis.MyAnalysisPage` to the `PAGES` list.
This barebones example should produce a new tab in the application.

## Adding a plot to the application page

To add a plot, you should add classes that consume the datastore
object and generate views of the data. The following example shows how
to add a plot of nucleotide diversity.

First we create a new `View`-derived class `NucDivPlot`. The
conversion of data requires some additional imports. The class defines
a `param` object for window size, which will show up as an input box
widget in the `sidebar()` function. The `__panel__()` function sets up
the actual plot.

```python
import numpy as np
import pandas as pd
import hvplot.pandas
import param

from .core import make_windows
from tseda import config

class NucDivPlot(View):
window_size = param.Integer(
default=10000, bounds=(1, None), doc="Size of window"
)

@param.depends("window_size")
def __panel__(self):
windows = make_windows(self.window_size, self.datastore.tsm.ts.sequence_length)
data = pd.DataFrame(self.datastore.tsm.ts.diversity(windows=windows))
data.columns = ["pi"]
return pn.panel(data.hvplot.line(y="pi"))

def sidebar(self):
return pn.Card(
self.param.window_size,
collapsed=True,
title="Nucleotide diversity plotting options",
header_background=config.SIDEBAR_BACKGROUND,
active_header_background=config.SIDEBAR_BACKGROUND,
styles=config.VCARD_STYLE,
)

```

Then we modify the `MyAnalysisPage` class as follows:

```python

class MyAnalysisPage(View):
key = "myanalysis"
title = "My Analysis"
nucdiv = param.ClassSelector(class_=NucDivPlot)

def __init__(self, **kwargs):
super().__init__(**kwargs)
self.nucdiv = NucDivPlot(datastore=self.datastore)

def __panel__(self):
return pn.Column(self.nucdiv)

def sidebar(self):
return pn.Column(self.nucdiv.sidebar)
```

Reload the app and hopefully you will see an added plot and sidebar.

For a more detailed example, see one of the actual modules, e.g.,
`tseda.vpages.stats`.
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -24,13 +24,25 @@ to produce the output `data.trees.tseda`. To launch the web app run

## Installation

Tseda is currently in development. To install the latest dev version
tseda is currently in development. To install the latest dev version
from github, run

```
python -m pip install git+https://github.com/tforest/tseda
```

## Features (WIP)

- sample set editor to customize sample set definitions, names and
colors
- summary statistics (Fst, genealogical nearest neighbours (GNN))
compared and averaged over sample sets
- genealogical nearest neighbour (GNN) analyses of samples
- plots of common summary statistics over sequence length (diversity,
Tajimas_D, divergence, Fst)
- marginal tree plots
- pairwise coalescence plots

## Tests

The package comes with test data files corresponding to an
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ authors = [
]
dependencies = [
"panel~=1.5.3",
"tskit~=0.5.8",
"tskit~=0.6.0",
"tszip~=0.2.5",
"click~=8.1.7",
"daiquiri~=3.2.5.1",
2 changes: 1 addition & 1 deletion requirements-dev.lock
Original file line number Diff line number Diff line change
@@ -539,7 +539,7 @@ trove-classifiers==2024.7.2
# via hatchling
tsbrowse @ git+https://github.com/tskit-dev/tsbrowse@2faab76ad3a1f4bb51e6a39ce1c244dc1ac8209b
# via tseda
tskit==0.5.8
tskit==0.6.0
# via msprime
# via pyslim
# via tsbrowse
2 changes: 1 addition & 1 deletion requirements.lock
Original file line number Diff line number Diff line change
@@ -239,7 +239,7 @@ tqdm==4.66.5
# via panel
tsbrowse @ git+https://github.com/tskit-dev/tsbrowse@2faab76ad3a1f4bb51e6a39ce1c244dc1ac8209b
# via tseda
tskit==0.5.8
tskit==0.6.0
# via tsbrowse
# via tseda
# via tszip
1 change: 0 additions & 1 deletion src/tseda/__main__.py
Original file line number Diff line number Diff line change
@@ -9,7 +9,6 @@
from . import app # noqa
from . import config # noqa
from . import model # noqa
from . import pages # noqa
from . import datastore # noqa
from .datastore import IndividualsTable # noqa
from tsbrowse import preprocess as preprocess_ # noqa
8 changes: 7 additions & 1 deletion src/tseda/datastore.py
Original file line number Diff line number Diff line change
@@ -41,7 +41,7 @@ def preprocess(tsm):

class IndividualsTable(Viewer):
"""Class to hold and view individuals and perform calculations to
change filter."""
change filters."""

columns = [
"name",
@@ -146,6 +146,12 @@ def sample2ind(self):
d[node] = index
return d

def samples(self):
"""Return all samples"""
for _, ind in self.data.rx.value.iterrows():
for node in ind.nodes:
yield node

def loc(self, i):
"""Return individual by index"""
return self.data.rx.value.loc[i]
16 changes: 10 additions & 6 deletions src/tseda/vpages/trees.py
Original file line number Diff line number Diff line change
@@ -47,7 +47,7 @@ class Tree(View):
lambda x: x.prev_tree(), doc="Previous tree", label="Previous tree"
)

symbol_size = param.Number(default=5, bounds=(0, None), doc="Symbol size")
symbol_size = param.Number(default=8, bounds=(0, None), doc="Symbol size")

def next_tree(self):
self.position = None
@@ -61,11 +61,13 @@ def prev_tree(self):
def default_css(self):
"""Default css styles for tree nodes"""
styles = []
for (
ssid,
ss,
) in self.datastore.sample_sets_table.data.rx.value.iterrows():
s = f".node.p{ssid} > .sym " + "{" + f"fill: {ss.color} " + "}"
sample_sets = self.datastore.sample_sets_table.data.rx.value
individuals = self.datastore.individuals_table.data.rx.value
sample2ind = self.datastore.individuals_table.sample2ind
for n in self.datastore.individuals_table.samples():
ssid = individuals.loc[sample2ind[n]].sample_set_id
ss = sample_sets.loc[ssid]
s = f".node.n{n} > .sym " + "{" + f"fill: {ss.color} " + "}"
styles.append(s)
css_string = " ".join(styles)
return css_string
@@ -126,6 +128,7 @@ class TreesPage(View):
def __init__(self, **params):
super().__init__(**params)
self.data = Tree(datastore=self.datastore)
self.sample_sets = self.datastore.sample_sets_table

def __panel__(self):
return pn.Column(
@@ -135,4 +138,5 @@ def __panel__(self):
def sidebar(self):
return pn.Column(
self.data.sidebar,
self.sample_sets.sidebar_table,
)
14 changes: 13 additions & 1 deletion tests/test_datastore.py
Original file line number Diff line number Diff line change
@@ -20,7 +20,19 @@ def test_datastore_preprocess(tsm):


def test_individuals_table(individuals_table):
print(individuals_table.loc(5))
ind = individuals_table.loc(5)
assert ind is not None
assert ind.sample_set_id == 1
assert ind.population == 1
assert ind["name"] == "tsk_6"
assert ind.name == 5
assert individuals_table.sample2ind[ind.nodes[0]] == 5
assert individuals_table.sample2ind[ind.nodes[1]] == 5
_, ss = individuals_table.sample_sets()
assert len(ss) == 6
assert len(ss[0]) == 12
samples = list(individuals_table.samples())
assert len(samples) == 42


def test_datastore(ds):
22 changes: 22 additions & 0 deletions tests/test_trees.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import pytest

from tseda.vpages import trees


@pytest.fixture
def treespage(ds):
return trees.TreesPage(datastore=ds)


@pytest.fixture
def tree(ds):
return trees.Tree(datastore=ds)


def test_treespage(treespage):
assert treespage.title == "Trees"
assert treespage.key == "trees"


def test_tree(tree):
assert ".node.n26 > .sym {fill: #e4ae38 }" in tree.default_css
Loading
Oops, something went wrong.