Do LC16 and LC6 receive the same inputs?¶
Introduction¶
Question¶
Do LC16 and LC6 receive input from the same presynaptic cell types?
Background¶
Wu et al. 2016 find that the two types stratify in the same layers of the lobula, and expect the same inputs from it:
Consistent with their similar responses in the imaging experiments, LC6 and LC16 have very similar lobula layer patterns while LC11 has a different arbor stratification indicating that LC11 receives inputs from a different set of medulla cell types than LC6 and LC16.
— Wu et al. 2016, p. 29 · PDF p. 29 · 10.7554/eLife.21022
The same paper cautions that a shared layer does not settle it:
since all lobula layers contain terminals of many neurons, connectivity cannot be inferred based on layer patterns alone, as not all the neuronal types that arborize in a shared layer will be synaptic partners.
— Wu et al. 2016, p. 18 · PDF p. 18 · 10.7554/eLife.21022
The connectome gives the inputs themselves.
This notebook compares the input of the two types by the types of
their strong input partners
(lc_pathways defines input partners and
lc_output_strength strong ones),
with LC11 as a type that the paper says differs.
It uses the right hemisphere only,
the better-proofread side.
Expectations¶
- If the two types share their inputs, the Jaccard index of their sets of input partner types is higher than the index of either with LC11. If the layers say nothing about the inputs, the three indices are about equal.
- If they share their heaviest inputs, the 3 input types that carry the most synapses to each type overlap. If they do not, the heaviest types differ even where the sets overlap.
Load data¶
The analysis reads census, which gives each cell's type,
sides, which says which hemisphere a cell is in,
and proofread_ids, which lists the proofread neurons.
| root_id | primary_type |
|---|---|
| i64 | str |
| 720575940599457990 | "T4b" |
| 720575940599763910 | "T5b" |
| 720575940600623020 | "Dm3q" |
| 720575940602564320 | "KCg-m" |
| 720575940602720940 | "JO-CA" |
| … | … |
| 720575940661275009 | "KCab-p" |
| 720575940661281409 | "L4" |
| 720575940661285249 | "LPC2" |
| 720575940661304449 | "Lawf1" |
| 720575940661333889 | "CB2303" |
| root_id | side |
|---|---|
| i64 | str |
| 720575940596125868 | "right" |
| 720575940597856265 | "right" |
| 720575940597944841 | "right" |
| 720575940598267657 | "right" |
| 720575940599333574 | "right" |
| … | … |
| 720575940661323905 | "left" |
| 720575940661325697 | "left" |
| 720575940661327745 | "right" |
| 720575940661336193 | "left" |
| 720575940661339777 | "right" |
| root_id |
|---|
| i64 |
| 720575940599457990 |
| 720575940599763910 |
| 720575940600623020 |
| 720575940602309600 |
| 720575940602564320 |
| … |
| 720575940661275009 |
| 720575940661281409 |
| 720575940661285249 |
| 720575940661304449 |
| 720575940661333889 |
The types are restricted to the right hemisphere.
cells = cells_of_types(census, sides, TYPES)
pl.DataFrame(
{
"type": list(TYPES),
"right-hemisphere cells": [len(cells[_t]) for _t in TYPES],
}
)
| type | right-hemisphere cells |
|---|---|
| str | i64 |
| "LC16" | 74 |
| "LC6" | 60 |
| "LC11" | 61 |
inputs has one row per connection that ends at one of those
cells, with its weight, the type of the receiving cell
(post_type),
and the type of the sending cell (pre_type),
which is null for a cell the census does not name.
input_connections is the same table with the two ends swapped
(reversed_connections), as in lc_pathways,
so that a strength reads the same way for inputs as for outputs.
_scored = [_id for _ids in cells.values() for _id in _ids]
_raw = load_connections_of(_scored, end="post")
inputs = with_cell_columns(
with_post_type(_raw, cells),
census.rename({"primary_type": "type"}),
end="pre",
)
input_connections = with_pre_type(reversed_connections(_raw), cells)
inputs
| pre_pt_root_id | post_pt_root_id | weight | post_type | pre_type |
|---|---|---|---|---|
| i64 | i64 | i64 | str | str |
| 720575940379617287 | 720575940605242242 | 1 | "LC16" | null |
| 720575940379635975 | 720575940613168554 | 1 | "LC16" | null |
| 720575940379671303 | 720575940614712115 | 1 | "LC16" | null |
| 720575940379697671 | 720575940624123536 | 1 | "LC16" | null |
| 720575940379736406 | 720575940634144875 | 1 | "LC11" | null |
| … | … | … | … | … |
| 720575940661271937 | 720575940624983531 | 1 | "LC16" | "MLt1" |
| 720575940661316481 | 720575940621434789 | 1 | "LC6" | "Tm5c" |
| 720575940661316993 | 720575940622014758 | 1 | "LC16" | "LPLC2" |
| 720575940661316993 | 720575940627874832 | 1 | "LC16" | "LPLC2" |
| 720575940661318017 | 720575940636578014 | 1 | "LC11" | "LLPC3" |
Input by sender type¶
The synapses that a type receives from the cells of each sender type are
where \(w(i \to j)\) is the weight of the connection from \(i\) to \(j\).
The types have different numbers of cells,
so the share \(I_T(A) / \sum_{A'} I_T(A')\) compares them,
over the sender types the census names.
A cell without a type cannot be matched between the types,
so it is left out.
input_shares gives \(I_T(A)\) and the share for each type.
On three connections onto two types:
def input_shares(inputs: pl.DataFrame) -> pl.DataFrame:
"""The synapses each receiving type gets from each sender type, and
their share of the type's typed input.
inputs is DataFrame[post_type, pre_type, weight, ...]; a connection
with a null pre_type or post_type is left out.
DataFrame[type, sender_type, synapses, share].
"""
return (
inputs.drop_nulls(["pre_type", "post_type"])
.group_by("post_type", "pre_type")
.agg(synapses=pl.col("weight").sum())
.rename({"post_type": "type", "pre_type": "sender_type"})
.with_columns(
share=pl.col("synapses") / pl.col("synapses").sum().over("type")
)
.sort("type", "synapses", descending=[False, True])
)
input_shares(
pl.DataFrame(
{
"post_type": ["T", "T", "U"],
"pre_type": ["A", "B", None],
"weight": [3, 1, 9],
}
)
)
| type | sender_type | synapses | share |
|---|---|---|---|
| str | str | i64 | f64 |
| "T" | "A" | 3 | 0.75 |
| "T" | "B" | 1 | 0.25 |
| type | sender_type | synapses | share |
|---|---|---|---|
| str | str | i64 | f64 |
| "LC11" | "T3" | 27413 | 0.168868 |
| "LC11" | "T2" | 22156 | 0.136484 |
| "LC11" | "T2a" | 12899 | 0.07946 |
| "LC11" | "Tm21" | 10942 | 0.067404 |
| "LC11" | "Tm25" | 7562 | 0.046583 |
| … | … | … | … |
| "LC6" | "CB2316" | 1 | 0.000024 |
| "LC6" | "LatB" | 1 | 0.000024 |
| "LC6" | "LTe50" | 1 | 0.000024 |
| "LC6" | "SLP003" | 1 | 0.000024 |
| "LC6" | "PLP004" | 1 | 0.000024 |
The chart shows the sender types with the largest share in either of the two types, largest at the top.
_top = (
shares.filter(pl.col("type").is_in(PAIR))
.group_by("sender_type")
.agg(pl.col("share").max())
.sort("share", descending=True)
.head(SHARES_SHOWN)["sender_type"]
.to_list()
)
alt.Chart(
shares.filter(
pl.col("type").is_in(PAIR) & pl.col("sender_type").is_in(_top)
)
).mark_bar().encode(
y=alt.Y("sender_type:N", sort=_top, title=None),
yOffset=alt.YOffset("type:N", sort=list(PAIR)),
x=alt.X(
"share:Q",
axis=alt.Axis(format="%"),
title="% of the type's typed input synapses",
),
color=type_color(),
tooltip=[
"type:N",
"sender_type:N",
alt.Tooltip("share:Q", format=".1%"),
],
).properties(width=420, height=360)
Overlap of the input types¶
The strong input partners of a type are the proofread cells that
connect to it with a strength of at least STRONG_SYN,
without the LC cells among them (labeled_partners and
strong_set, as in lc_pathways).
Their types, from the census, make the set of input types of the
type.
A proofread neuron that the census does not name has no type to
compare, so it is left out.
The Jaccard index
\(J(A, B) = |A \cap B| / |A \cup B|\)
is 1 for identical sets and 0 for disjoint ones.
It counts a type shared at the weakest the same as one shared at
the strongest,
so the heaviest input types are compared as well.
strong_in = strong_set(
labeled_partners(input_connections, cells, census, proofread_ids)
)
input_types = sets_by_type(
strong_in.filter(pl.col("partner_type") != "untyped neuron"),
"partner_type",
TYPES,
)
overlap = pairwise_jaccard(input_types)
overlap
| a | b | jaccard | shared | union |
|---|---|---|---|---|
| str | str | f64 | i64 | i64 |
| "LC16" | "LC6" | 0.263158 | 10 | 38 |
| "LC16" | "LC11" | 0.133333 | 8 | 60 |
| "LC6" | "LC11" | 0.137931 | 8 | 58 |
The heaviest input types of each type,
by the synapses it receives from the strong partners of the type
(type_mass):
heaviest = {
_t: type_mass(strong_in)
.filter(pl.col("type") == _t)
.head(TOP_INPUTS)["partner_type"]
.to_list()
for _t in TYPES
}
pl.DataFrame(
{
"rank": list(range(1, TOP_INPUTS + 1)),
**{
_t: heaviest[_t] + [None] * (TOP_INPUTS - len(heaviest[_t]))
for _t in TYPES
},
}
)
| rank | LC16 | LC6 | LC11 |
|---|---|---|---|
| i64 | str | str | str |
| 1 | "PVLP007" | "Tm5b" | "T3" |
| 2 | "Tm20" | "PVLP008" | "T2" |
| 3 | "Tm37" | "Li33" | "Li15" |
Discussion¶
Expectation 1: the index of LC16 and LC6 is 0.26, higher than the index of the pairs with LC11 (LC16/LC11 0.13 and LC6/LC11 0.14). The two types have 25 and 23 input types, and 10 of the 38 types in either are in both (Overlap of the input types).
Expectation 2: 0 of the 3 heaviest input types are shared. The heaviest are PVLP007, Tm20, and Tm37 for LC16 and Tm5b, PVLP008, and Li33 for LC6.
Conclusion. The two types share more input types than either shares with LC11, and 0 of their 3 heaviest are common. The comparison is at the level of cell types, so it cannot say whether the two types receive a shared type in the same columns or from the same cells, and a shared type can still feed them different parts of their dendrites.
Limitations¶
The input types are those of the strong input partners that are proofread neurons with a type in the census, so a type whose members are mostly unproofread or unnamed is missing from both sets. The weights are the synapse counts of the Codex release. The analysis uses the right hemisphere only.
Open questions¶
- Do LC16 and LC6 share input cells where their dendrites cover
the same columns?
Test: the Jaccard index of the strong input partners of pairs of
LC16 and LC6 cells with nearby addresses
(
lc_output_clusters), against pairs with distant addresses. Refuted if nearby pairs share no more input cells than distant ones. - Do the two types receive their heaviest inputs at different depths of the lobula? Test: the depth of those synapses on each type's dendrite; it needs the coordinates of the synapses, which the connections table does not hold. Refuted if both types receive them at the same depth.
Terms¶
- connection: all the synapses from cell \(i\) to cell \(j\).
- input partner: a cell that sends a connection onto some cell of a type.
- input type: the type of an input partner.
- Jaccard index \(J(A, B) = |A \cap B| / |A \cup B|\): the share of the union of two sets that they have in common.
- proofread neuron: a segment that people have proofread and
joined into a neuron (
proofread_ids). - share \(I_T(A) / \sum_{A'} I_T(A')\): the fraction of the typed input synapses of a type that come from the sender type \(A\).
- strength \(s(T \to j)\): the weight of the heaviest single connection from a cell of the type \(T\) onto the partner \(j\); for an input partner, the heaviest connection from it onto a cell of the type.
- strong partner: a proofread partner that is not an LC cell
and has a strength of at least
STRONG_SYN. - weight \(w(i \to j)\): the number of synapses in the connection from \(i\) to \(j\).