The same query, the same frame¶
Connectomic datasets each speak their own dialect. CAVE has materialization
versions, root IDs that change under you, and per-datastack table names. neuPrint
has immutable body IDs, Cypher, and dataset strings like hemibrain:v1.2.1. And
every dataset has its own idea of what "type" or "side" means.
connecto is one interface over all of it.
import connecto as cn
fw = cn.FlyWire() # neuPrint (backend="cave" for the other door)
hb = cn.Hemibrain() # neuPrint
fw.connectivity.edges("DA1_lPN") # pre, post, weight
hb.connectivity.edges("DA1_lPN") # the same frame - different backend, different species conventions
Both calls return pre, post, weight as int64, int64, int32. Positions are
always nanometres. Side is always left / right / center. Skeletons and
meshes are always navis neurons.
Two promises¶
It is the same frame, and that is tested¶
BANC is served by both backends at the same snapshot — CAVE materialization 888
is neuPrint banc:v888, and its neuPrint body IDs are valid CAVE root IDs. So the
promise is checkable rather than aspirational:
cave = cn.BANC(backend="cave").connectivity.edges(ids)
np_ = cn.BANC(backend="neuprint").connectivity.edges(ids)
pd.testing.assert_frame_equal(cave, np_) # passes
That test has already caught two real divergences. How →
Nothing degrades silently¶
If a dataset cannot do what you asked, it says so. It does not quietly hand you an unfiltered result.
fw.connectivity.synapses(x, min_score=100) # fine
mic.connectivity.synapses(x, min_score=100) # CapabilityError
Defaults never raise; only an explicit request for something the dataset lacks does. Whole namespaces are absent rather than broken, so feature detection works:
One mini-language for selecting neurons¶
ids() takes anything¶
ds.ids(720575940604407468) # a root/body ID
ds.ids("DA1_lPN") # a type
ds.ids("/^AOTU00.*") # a regex
ds.ids("super_class:visual_projection")
ds.ids("DA1_lPN", side="left")
Anything you can pass to ids() you can pass to any query. It is a pure function:
it returns IDs and mutates nothing.
More →
Datasets¶
| dataset | species | backend | annot | conn | syn | scores | NT | roi-conn | rois | skel | mesh | seg | cgraph | proof | soma | live |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
flywire |
fly | neuprint | ✅ | ✅ | ✅ | ✅ | · | ✅ | ✅ | ✅ | ✅ | ✅ | · | · | ✅ | · |
| cave | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | · | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | · | ||
flywire-production |
fly | cave | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | · | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
banc |
fly | neuprint | ✅ | ✅ | ✅ | · | · | ✅ | ✅ | · | · | · | · | · | ✅ | · |
| cave | ✅ | ✅ | ✅ | · | · | ✅ | · | ✅ | ✅ | ✅ | ✅ | · | ✅ | · | ||
fanc |
fly | cave | ✅ | ✅ | ✅ | ✅ | · | · | · | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
hemibrain |
fly | neuprint | ✅ | ✅ | ✅ | ✅ | · | ✅ | ✅ | ✅ | ✅ | ✅ | · | · | ✅ | · |
malecns |
fly | neuprint | ✅ | ✅ | ✅ | ✅ | · | ✅ | ✅ | ✅ | ✅ | ✅ | · | · | ✅ | · |
manc |
fly | neuprint | ✅ | ✅ | ✅ | ✅ | · | ✅ | ✅ | ✅ | ✅ | ✅ | · | · | ✅ | · |
microns |
mouse | cave | ✅ | ✅ | ✅ | · | · | · | · | ✅ | ✅ | ✅ | ✅ | · | ✅ | · |
aedes |
mosquito | cave | ✅ | ✅ | ✅ | · | · | · | · | ✅ | ✅ | ✅ | ✅ | · | ✅ | ✅ |
fish2 |
zebrafish | neuprint | ✅ | ✅ | ✅ | ✅ | · | ✅ | ✅ | ✅ | ✅ | · | · | · | ✅ | · |
A · means the call raises, not that it returns something subtly wrong. The
table is generated by cn.capability_matrix(), so it cannot drift from the code.
aedes's CAVE datastack ships no annotation table — so its cell typing comes from
FlyTable (the lab's SeaTable) instead, keyed by root ID. It is lab-internal, so it
needs a SEATABLE_TOKEN: without one you get a clear missing-token error, not a
silently empty frame. What aedes still has no · for is a per-synapse score — its
synapse table records size, not a confidence — so min_score= raises.
Anything not listed works too, without writing a class:
Get going¶
Install¶
Then point it at your existing CAVE and neuPrint tokens — connecto reads from wherever they already live.
Learn¶
Six short tutorials, from a first query to running one script unchanged across two species.
Look things up¶
The API reference is generated from the docstrings, so there is no second copy of the documentation to drift out of sync.
What it doesn't do¶
Data fetching only. No clustering, no matching, no connectivity vectors, no
curation policy — connecto will backfill a type from the columns your dataset
declares, but it will never decide that some types are "bad" and null them out.
That is analysis, and it belongs upstream (see
cocoa).