Slices¶
A slice is a named subset of a graph. Think of it as a condition,
cohort, or sample label — the network analog of an obs group in
AnnData.
A slice membership is two things:
- a set of vertex ids
- a set of edge ids
The same vertex or edge can live in many slices.
from annnet import AnnNet
G = AnnNet(directed=True)
G.add_vertices(['A', 'B', 'C', 'D'])
G.add_edges('A', 'B', edge_id='e1', weight=1.0)
G.add_edges('B', 'C', edge_id='e2', weight=2.0)
G.add_edges('C', 'D', edge_id='e3', weight=3.0)
G
AnnNet object with n_vertices × n_edges = 4 × 3
directed: True
slices: ['default']
Create slices¶
G.slices.add(name, **attrs) registers a new slice. Slice-level
attributes (e.g. cohort label) go in the kwargs.
G.slices.add('control', cohort='wt')
G.slices.add('treated', cohort='ko')
print('slices:', G.slices.list())
print('control attrs:', G.attrs.get_slice_attr('control', 'cohort'))
slices: ['default', 'control', 'treated'] control attrs: wt
Attach edges and vertices¶
Two ways:
- Pass
slice='name'when callingadd_edges(the edge lands in that slice immediately). - Call
G.slices.add_edges('name', [edge_ids])after the fact.
Either way, the slice's vertex set grows to include the endpoints.
# Path A: declare at edge creation time
G.add_edges('A', 'D', edge_id='e_short', slice='treated', weight=5.0)
# Path B: assign existing edges to a slice
G.slices.add_edges('control', ['e1', 'e2'])
G.slices.add_edges('treated', ['e2', 'e3', 'e_short'])
print('control edges:', sorted(G.slices.edges('control')))
print('treated edges:', sorted(G.slices.edges('treated')))
print('treated vertices:', sorted(G.slices.vertices('treated')))
control edges: ['e1', 'e2'] treated edges: ['e2', 'e3', 'e_short'] treated vertices: ['A', 'B', 'C', 'D']
Slice algebra¶
Combine slices with union, intersect, difference. Each returns a
dict of {'vertices': set, 'edges': set}.
both = G.slices.intersect(['control', 'treated'])
print('common edges:', sorted(both['edges']))
either = G.slices.union(['control', 'treated'])
print('union edges:', sorted(either['edges']))
control_only = G.slices.difference('control', 'treated')
print('control-only edges:', sorted(control_only['edges']))
common edges: ['e2'] union edges: ['e1', 'e2', 'e3', 'e_short'] control-only edges: ['e1']
Per-slice weight overrides¶
A single edge can have different weights in different slices. The base
weight lives on the edge; per-slice overrides live on the
(slice, edge) pair.
G.attrs.set_edge_slice_attrs('treated', 'e2', weight=99.0)
print('base weight (e2): ', G.attrs.get_effective_edge_weight('e2'))
print('weight in slice control: ', G.attrs.get_effective_edge_weight('e2', slice='control'))
print('weight in slice treated: ', G.attrs.get_effective_edge_weight('e2', slice='treated'))
base weight (e2): 2.0 weight in slice control: 2.0 weight in slice treated: 99.0
!!! tip "Active slice"
G.slices.active returns the currently active slice (default
'default'). When you call get_effective_edge_weight(eid)
without a slice=, the active slice is used as fallback.
Next: 05 — Hyperedges.