Find graph motifs using intuitive notation

Overview

d o t m o t i f

Find graph motifs using intuitive notation

PyPI Codecov


DotMotif is a library that identifies subgraphs or motifs in a large graph. It looks like this:

# Look for all motifs of the form,

# Neuron A excites B:
A -> B [type = "excitatory"]
# ...and B inhibits C:
B -> C [type = "inhibitory"]

Or like this:

TwitterInfluencer(person) {
    # An influencer has more than a million
    # followers and is verified.
    person.followers > 1000000
    person.verified = true
}

InfluencerAwkward(person1, person2) {
    # Two people who are both influencers...
    TwitterInfluencer(person1)
    TwitterInfluencer(person2)
    # ...where one follows the other, but...
    person1 -> person2
    # ...the other doesn't follow back
    person2 !> person1
}

# Search for all awkward twitter influencer
# relationships in the dataset:
InfluencerAwkward(X, Y)

Get Started

To follow along in an interactive Binder without installing anything, launch a Jupyter Notebook here:

Binder

If you have DotMotif, a NetworkX graph, and a curious mind, you already have everything you need to start using DotMotif:

from dotmotif import Motif, GrandIsoExecutor

executor = GrandIsoExecutor(graph=my_networkx_graph)

triangle = Motif("""
A -> B
B -> C
C -> A
""")

results = executor.find(triangle)

Parameters

You can also pass optional parameters into the constructor for the dotmotif object. Those arguments are:

Argument Type, Default Behavior
ignore_direction bool: False Whether to disregard direction when generating the database query
limit int: None A limit (if any) to impose on the query results
enforce_inequality bool: False Whether to enforce inequality; in other words, whether two nodes should be permitted to be aliases for the same node. For example, in A->B->C; if A!=C, then set to True
exclude_automorphisms bool: False Whether to return only a single example for each detected automorphism. See more in the documentation

For more details on how to write a query, see Getting Started.


Citing

If this tool is helpful to your research, please consider citing it with:

# https://doi.org/10.1038/s41598-021-91025-5
@article{Matelsky_Motifs_2021, 
    title={{DotMotif: an open-source tool for connectome subgraph isomorphism search and graph queries}},
    volume={11}, 
    ISSN={2045-2322}, 
    url={http://dx.doi.org/10.1038/s41598-021-91025-5}, 
    DOI={10.1038/s41598-021-91025-5}, 
    number={1}, 
    journal={Scientific Reports}, 
    publisher={Springer Science and Business Media LLC}, 
    author={Matelsky, Jordan K. and Reilly, Elizabeth P. and Johnson, Erik C. and Stiso, Jennifer and Bassett, Danielle S. and Wester, Brock A. and Gray-Roncal, William},
    year={2021}, 
    month={Jun}
}
Comments
  • Neuprint Executor - Labeling Edges by ROI

    Neuprint Executor - Labeling Edges by ROI

    Hi Jordan,

    Do you see an easy way to assign ROI labels to edges in the neuprint executor? Let's say I want to query something like this:

    A -> B [weight > 20, ROI == "CX"]
    A -> B [weight > 30, ROI == "CRE(L)"] 
    

    So basically, there are two things here—multigraphs, which you address already in the docs, and encoding edge ROIs. I wonder if that's rather a hard thing to do or not. The data should be there as neuprint-python fetch_synapse_connections returns something like this

        bodyId_pre  bodyId_post roi_pre roi_post  x_pre  y_pre  z_pre  x_post  y_post  z_post  confidence_pre  confidence_post
    0    792368888    754547386  PED(R)   PED(R)  14013  27747  19307   13992   27720   19313           0.996         0.401035
    1    792368888    612742248  PED(R)   PED(R)  14049  27681  19417   14044   27662   19408           0.921         0.881487
    2    792368888   5901225361  PED(R)   PED(R)  14049  27681  19417   14055   27653   19420
    ...
    

    According to this issue it looks like it's possible. My observation is that the physical location of a connection between two neurons is an important feature of a motif. Looking forward to hearing what you say.

    EDIT: Maybe an indirect way to support multiple edges between two nodes is by grouping edge attributes. Does something like this seem plausible. You are doing smth similar in the multigraph docs already: A -> B [synapse_count > 2]. But what exactly is synapse_count?

    A -> B [[weight >= 20, ROI == "CX"], [weight > 30, ROI == "CRE(L)"]]
    

    Best, Jakob

    enhancement cypher Neo4jExecutor NeuPrintExecutor 
    opened by jakobtroidl 9
  • Error on first query

    Error on first query

    Tried to run the query from the tutorial:

    motif = Motif("""
    # My Awesome Motif
    
    Nose_Cell -> Brain_Cell
    Brain_Cell -> Arm_Cell
    """)
    

    But got this error:

    FileNotFoundError                         Traceback (most recent call last)
    <ipython-input-1-3a88159c0a0c> in <module>
    ----> 1 import dotmotif
          2 import networkx
          3 
          4 motif = Motif("""
          5 # My Awesome Motif
    
    ~\anaconda3\lib\site-packages\dotmotif\__init__.py in <module>
         24 from networkx.algorithms import isomorphism
         25 
    ---> 26 from .parsers.v2 import ParserV2
         27 from .validators import DisagreeingEdgesValidator
         28 
    
    ~\anaconda3\lib\site-packages\dotmotif\parsers\v2\__init__.py in <module>
         11 
         12 
    ---> 13 dm_parser = Lark(open(os.path.join(os.path.dirname(__file__), "grammar.lark"), "r"))
         14 
         15 
    
    FileNotFoundError: [Errno 2] No such file or directory: 'C:\\Users\\xxxx\\anaconda3\\lib\\site-packages\\dotmotif\\parsers\\v2\\grammar.lark'
    
    bug parser install 
    opened by lix2k3 9
  • Filtering By Properties w/ Invalid Characters in the Name

    Filtering By Properties w/ Invalid Characters in the Name

    Hey There, I'm using dotmotif to query the neuPrint dataset and have found some of the neurons have properties that aren't accepted in the query string format e.g. 'AVLP(R)': True,

    Is there a way to still query w/ these params? I tried adding directly to the _node_constraints but that doesn't seem to work either e.g.

    motif._node_constraints['A']['AVLP(R)'] = {}
    motif._node_constraints['A']['AVLP(R)']['='] = [True]
    
    Variable `R` not defined (line 2, column 83 (offset: 156))
    "    WHERE B.status = "Traced" AND A.status = "Orphan" AND A.INP = True AND A.AVLP(R) = True"
    
    parser cypher 
    opened by simonwarchol 7
  • fix: weight edge attribute doesn't throw errors anymore (#127)

    fix: weight edge attribute doesn't throw errors anymore (#127)

    The edge attribute in the neuprint executor threw an error with the new JSON feature implementation. I also made the neuprint executor tests more rigorous.

    opened by jakobtroidl 3
  • Upgrade grandiso version to use limits and iterable

    Upgrade grandiso version to use limits and iterable

    In grandiso v1.1.0 and above, there is an optional limit argument to the find_motifs call which short-circuits motif counting if a certain number of valid mappings are found.

    Right now, NetworkX and GrandIso executors implement the dotmotif limit parameter by finding all motifs and then downselecting, which is super inefficient and lame. We could pretty substantially improve performance by supporting the GrandIso limit arg.

    A notable challenge: We perform an additional downselect after running grandiso (to double-check attribute filters). So we may need to store a list of mappings temporarily in order to backfill the results list if candidate mappings are filtered out.

    enhancement GrandIsoExecutor 
    opened by j6k4m8 2
  • Non-string ids not supported by Neo4jExecutor

    Non-string ids not supported by Neo4jExecutor

    Ingesting a NetworkX graph with integer ids results in an error: ValueError: Could not export graph: unsupported operand type(s) for +: 'int' and 'str'. It should be straightforward to handle integers, though A node can be any hashable Python object except None. Maybe just cast with repr.

    question Neo4jExecutor 
    opened by jtpdowns 2
  • Support n constraints on each edge value-operator pair

    Support n constraints on each edge value-operator pair

    Currently, the parser overwrites previous operators if it's redefined:

    A -> B [value<=5, value<=2]
    

    ...will yield a constraint operator of

    { "value": { "lte": 2.0 } }
    

    (i.e. overwriting the first rule).

    bug parser 
    opened by j6k4m8 2
  • Node- and edge-attribute support in DSL

    Node- and edge-attribute support in DSL

    Proposed syntax concepts:

    Nodes

    Inline maplike:

    Node1 { type="GABA", z<12 } -> Node2
    

    Pros:

    • Succinct

    Cons:

    • Possible duplication or conflicting attributes if map is included on multiple lines for the same node

    Postfix where-like:

    Node1 -> Node2 | Node1.type = "GABA", Node1.z < 12
    

    Pros:

    • Succinct

    Cons:

    • Possible duplication or conflicting attributes if attrs are included on multiple lines for the same node

    Footnote constraints

    Node1 -> Node2
    
    Node1.type = "GABA"
    Node1.z < 12
    

    Pros:

    • Reduces possibility of conflicting constraints
    • Clear syntax; can be standalone in its own macro

    Cons:

    • Linecount verbose
    • Decouples attributes from connectivity clauses

    Edges

    Inline maplike:

    A ->{type: "excitatory", neurotransmitter: "ACh"} B
    

    Pros:

    • Inline

    Cons:

    • Reduces clarity of language

    Postfix where-like:

    A -> B | [type: "excitatory", neurotransmitter: "ACh"]
    

    Pros:

    • Inline

    Cons:

    • Reduces clarity of language

    Infix maplike:

    A -[type: "excitatory", neurotransmitter: "ACh"]> B
    

    Pros:

    • Inline

    Cons:

    • Reduces clarity of language
    enhancement DSL 
    opened by j6k4m8 2
  • Add macro edge aliases

    Add macro edge aliases

    This adds support for complex edge constraints in macros:

    decreasing_edge_weights(a, b, c) {
        a -> b as ab
        b -> c as bc
        ab.weight > bc.weight
    }
    
    ...
    

    In increasing levels of challengingness:

    • [x] Add support for simple (edge-value) edge constraints in macros
    • [x] Add support for dynamic (edge-edge) edge constraints in macros
    • [x] Extend support for recursive calls to macros with simple constraints
    • [x] Extend support for recursive calls to macros with dynamic constraints
    • [x] Update documentation

    This fixes #110 and finishes work started in #119.

    enhancement DSL parser 
    opened by j6k4m8 1
  • Add edge aliasing and edge constraints

    Add edge aliasing and edge constraints

    This PR adds support for edge aliases (first described in #110) and comparisons between edge attributes with values and with other edges.

    This enables syntax like this:

    A -> B as ab
    B -> A as ba
    
    ab.weight > ba.weight
    
    • [x] Add support in the DSL
    • [x] Add support in the parser + transformer
    • [x] Add support in the executors:
      • [x] GrandIso
      • [x] NetworkX
      • [x] NeuPrint
      • [x] Neo4j

    I am going to push macro support in a separate PR, since this one is getting pretty lengthy already!

    enhancement DSL parser cypher Neo4jExecutor NetworkXExecutor NeuPrintExecutor GrandIsoExecutor 
    opened by j6k4m8 1
  • Add node attribute bracket syntax

    Add node attribute bracket syntax

    Adds support for "bracket" syntax for node attributes. An attribute like XYZ(ABC) or ABC DEF used to be disallowed because of illegal characters in the attribute name, particularly when using the "dot-attribute" notation:

    # broken:
    A -> B
    A.ABC DEF > 10
    

    The new syntax uses bracket-attribute notation to "escape" these names:

    # working:
    import dotmotif
    from dotmotif.executors.NeuPrintExecutor import NeuPrintExecutor
    
    HOSTNAME = "neuprint.janelia.org"
    DATASET = "hemibrain:v1.2.1"
    TOKEN = "[YOUR TOKEN HERE]"
    
    motif = dotmotif.Motif("""
    A -> B
    A['AVLP(R)'] = True
    """)
    
    E = NeuPrintExecutor(HOSTNAME, DATASET, TOKEN)
    
    E.find(motif, limit=2)
    

    Fixes #111.

    parser cypher Neo4jExecutor NeuPrintExecutor 
    opened by j6k4m8 1
  • Add Impossible Constraints validator

    Add Impossible Constraints validator

    We should be able to automatically catch things like this:

    A.type = 4
    A.type != 4
    

    Right now, we'll catch them in certain instances, but not when constraints are inherited from automorphisms (see #118). Getting smarter about this will likely improve runtime considerably.

    enhancement validator 
    opened by j6k4m8 0
  • Anonymous motif participants

    Anonymous motif participants

    Anonymous motif participants:

    A -> _hidden
    _hidden -> B
    

    Anonymous node participants in macros:

    two_hop(A, B) {
        A -> _i
        _i -> B
    }
    
    two_hop(neuron1, neuron2)
    
    
    
    enhancement DSL parser 
    opened by j6k4m8 0
Releases(v0.13.0)
dask-sql is a distributed SQL query engine in python using Dask

dask-sql is a distributed SQL query engine in Python. It allows you to query and transform your data using a mixture of common SQL operations and Python code and also scale up the calculation easily if you need it.

Nils Braun 271 Dec 30, 2022
A simple password manager I typed with python using MongoDB .

Python with MongoDB A simple python code example using MongoDB. How do i run this code • First of all you need to have a python on your computer. If y

null 31 Dec 6, 2022
Implementing basic MySQL CRUD (Create, Read, Update, Delete) queries, using Python.

MySQL with Python Implementing basic MySQL CRUD (Create, Read, Update, Delete) queries, using Python. We can connect to a MySQL database hosted locall

MousamSingh 5 Dec 1, 2021
Implementing basic MongoDB CRUD (Create, Read, Update, Delete) queries, using Python.

MongoDB with Python Implementing basic MongoDB CRUD (Create, Read, Update, Delete) queries, using Python. We can connect to a MongoDB database hosted

MousamSingh 4 Dec 1, 2021
A tutorial designed to introduce you to SQlite 3 database using python

SQLite3-python-tutorial A tutorial designed to introduce you to SQlite 3 database using python What is SQLite? SQLite is an in-process library that im

null 0 Dec 28, 2021
MariaDB connector using python and flask

MariaDB connector using python and flask This should work with flask and to be deployed on docker. Setting up stuff 1. Docker build and run docker bui

Bayangmbe Mounmo 1 Jan 11, 2022
ianZiPu is a way to write notation for Guqin (古琴) music.

JianZiPu Font JianZiPu is a way to write notation for Guqin (古琴) music. This document will cover how to use this font, and how to contribute to its de

Nancy Yi Liang 8 Nov 25, 2022
Small python-gtk application, which helps the user to merge or split pdf documents and rotate, crop and rearrange their pages using an interactive and intuitive graphical interface

Small python-gtk application, which helps the user to merge or split pdf documents and rotate, crop and rearrange their pages using an interactive and intuitive graphical interface

null 1.8k Dec 29, 2022
An intuitive library to add plotting functionality to scikit-learn objects.

Welcome to Scikit-plot Single line functions for detailed visualizations The quickest and easiest way to go from analysis... ...to this. Scikit-plot i

Reiichiro Nakano 2.3k Dec 31, 2022
Graphing communities on Twitch.tv in a visually intuitive way

VisualizingTwitchCommunities This project maps communities of streamers on Twitch.tv based on shared viewership. The data is collected from the Twitch

Kiran Gershenfeld 312 Jan 7, 2023
An intuitive library to add plotting functionality to scikit-learn objects.

Welcome to Scikit-plot Single line functions for detailed visualizations The quickest and easiest way to go from analysis... ...to this. Scikit-plot i

Reiichiro Nakano 2.3k Dec 31, 2022
Greykite: A flexible, intuitive and fast forecasting library

The Greykite library provides flexible, intuitive and fast forecasts through its flagship algorithm, Silverkite.

LinkedIn 1.7k Jan 4, 2023
Bit is Python's fastest Bitcoin library and was designed from the beginning to feel intuitive, be effortless to use, and have readable source code.

Bit is Python's fastest Bitcoin library and was designed from the beginning to feel intuitive, be effortless to use, and have readable source code.

Ofek Lev 1.1k Jan 2, 2023
A drop-in replacement for django's ImageField that provides a flexible, intuitive and easily-extensible interface for quickly creating new images from the one assigned to the field.

django-versatileimagefield A drop-in replacement for django's ImageField that provides a flexible, intuitive and easily-extensible interface for creat

Jonathan Ellenberger 490 Dec 13, 2022
A drop-in replacement for django's ImageField that provides a flexible, intuitive and easily-extensible interface for quickly creating new images from the one assigned to the field.

django-versatileimagefield A drop-in replacement for django's ImageField that provides a flexible, intuitive and easily-extensible interface for creat

Jonathan Ellenberger 490 Dec 13, 2022
An extremely simple, intuitive, hardware-friendly, and well-performing network structure for LiDAR semantic segmentation on 2D range image. IROS21

FIDNet_SemanticKITTI Motivation Implementing complicated network modules with only one or two points improvement on hardware is tedious. So here we pr

YimingZhao 54 Dec 12, 2022
Python based framework providing a simple and intuitive framework for algorithmic trading

Harvest is a Python based framework providing a simple and intuitive framework for algorithmic trading. Visit Harvest's website for details, tutorials

null 100 Jan 3, 2023