Creating a metabolic pathway in Neo4j

前端 未结 3 1821
忘了有多久
忘了有多久 2020-12-18 07:42

I am attempting to create the glycolytic pathway shown in the image at the bottom of this question, in Neo4j, using these data:

glycolysis_bioentities.csv

相关标签:
3条回答
  • 2020-12-18 08:14

    [UPDATED]

    There are multiple issues and possible improvements:

    1. The second MERGE should be deleted, since it creates orphaned nodes. A relationship type should not be tuned into a Glycolysis node, and such nodes would never be connected to any other nodes.
    2. The 1st and 3rd MERGE clauses must use the same property name (say, name) for source and target nodes, or else the same chemical can end up with 2 nodes (with different property keys). This is why you ended up with nodes that did not have all the expected connections.
    3. The APOC procedure apoc.cypher.doIt can be used to simplify somewhat the MERGE of relationships with dynamic names.
    4. The glycolysis_bioentities.csv is not needed for this use case.

    With the above changes, you end up with something like this, which will generate a connected graph that matches your input data:

    LOAD CSV WITH HEADERS FROM "file:/glycolysis_relations.csv" AS row
    MERGE (s:Glycolysis {name: row.source})
    MERGE (t:Glycolysis {name: row.target})
    WITH s, t, row
    CALL apoc.cypher.doIt(
      'MERGE (s)-[r:' + row.relation + ']->(t)',
      {s:s, t:t}) YIELD value
    RETURN 1;
    
    0 讨论(0)
  • 2020-12-18 08:34

    If permitted, I'd like to post one more follow-on answer -- my reason being that currently there is very little out there on recreating metabolic pathways in Neo4j, and the following will provide a complete summary under this StackOverflow title/subject, "Creating a metabolic pathway in Neo4j".

    Like my Glycolysis pathway, above, I recreated in Neo4j the TCA (citric acid cycle | Kreb's cycle) pathway:

    [TCA cycle image source: https://metabolicpathways.stanford.edu/]

    An issue that arose during the creation of my TCA pathway graph was that the one of the nodes (the enzyme, "aconitase") was used twice, so during the graph creation MERGE merged the common node aconitase as a single entity, resulting in this layout,

    ... not this one, as desired,

    My solution to that issue was to create the "TCA graph" using node properties, to temporarily differentially-tag the affected source and target nodes (later removing those tags, after the graph was properly created).

    I also added a :Metabolism label, so that I could select the individual pathways (:Glycolysis | :TCA) or the complete metabolic network (:Metabolism), as desired.

    Lastly, I needed to connect the two pathways (:Glycolysis | :TCA) through their common node, pyruvate, which I was able to do through an APOC procedure (here, appended to the end of my glycolysis.cql (Cypher) script.

    Here are my CSV data files, *.cql Cypher scripts, script execution, and the resultant graph.

    glycolysis.csv:

    source,relation,target
    α-D-glucose,substrate_of,hexokinase
    hexokinase,yields,glucose 6-phosphate
    glucose 6-phosphate,substrate_of,glucose-6-phosphatase
    glucose-6-phosphatase,yields,α-D-glucose
    glucose 6-phosphate,substrate_of,phosphoglucose isomerase
    phosphoglucose isomerase,yields,fructose 6-phosphate
    fructose 6-phosphate,substrate_of,phosphofructokinase
    phosphofructokinase,yields,"fructose 1,6-bisphosphate"
    "fructose 1,6-bisphosphate",substrate_of,"fructose-bisphosphate aldolase, class I"
    "fructose-bisphosphate aldolase, class I",yields,D-glyceraldehyde 3-phosphate
    D-glyceraldehyde 3-phosphate,substrate_of,glyceraldehyde-3-phosphate dehydrogenase
    D-glyceraldehyde 3-phosphate,substrate_of,triosephosphate isomerase (TIM)
    triosephosphate isomerase (TIM),yields,dihydroxyacetone phosphate
    glyceraldehyde-3-phosphate dehydrogenase,yields,"1,3-bisphosphoglycerate"
    "1,3-bisphosphoglycerate",substrate_of,phosphoglycerate kinase
    phosphoglycerate kinase,yields,3-phosphoglycerate
    3-phosphoglycerate,substrate_of,phosphoglycerate mutase
    phosphoglycerate mutase,yields,2-phosphoglycerate
    2-phosphoglycerate,substrate_of,enolase
    enolase,yields,phosphoenolpyruvate
    phosphoenolpyruvate,substrate_of,pyruvate kinase
    pyruvate kinase,yields,pyruvate
    

    tca.csv:

    source,relation,target,tag1,tag2
    pyruvate,substrate_of,pyruvate dehydrogenase,,
    pyruvate dehydrogenase,yields,acetyl CoA,,
    acetyl CoA,substrate_of,citrate synthase,,
    oxaloacetate,substrate_of,citrate synthase,,
    citrate synthase,yields,citrate,,
    citrate,substrate_of,aconitase,,1
    aconitase,yields,cis-aconitate,1,
    cis-aconitate,substrate_of,aconitase,,2
    aconitase,yields,isocitrate,2,
    isocitrate,substrate_of,isocitrate dehydrogenase,,
    isocitrate dehydrogenase,yields,α-ketoglutarate,,
    α-ketoglutarate,substrate_of,α-ketoglutarate dehydrogenase,,
    α-ketoglutarate dehydrogenase,yields,succinyl-CoA,,
    succinyl-CoA,substrate_of,succinyl-CoA synthetase,,
    succinyl-CoA synthetase,yields,succinate,,
    succinate,substrate_of,succinate dehydrogenase,,
    succinate dehydrogenase,yields,fumarate,,
    fumarate,substrate_of,fumarase,,
    fumarase,yields,S-malate,,
    S-malate,substrate_of,malate dehydrogenase,,
    malate dehydrogenase,yields,oxaloacetate,,
    

    "tag1" and "tag"2 in "tsv.csv" are used to uniquely those source and target nodes, when they are created via the "tca.cql" script:

    tca.cql:

    // CREATE INDICES:
    CREATE INDEX ON :Metabolism(name);
    CREATE INDEX ON :TCA(name);
    
    // CREATE GRAPH:
    // USING PERIODIC COMMIT 5000
    LOAD CSV WITH HEADERS FROM "file:/mnt/Vancouver/Programming/data/metabolism/tca.csv" AS row
    MERGE (s:Metabolism:TCA {name: row.source, tag:COALESCE(row.tag1, '')})
    MERGE (t:Metabolism:TCA {name: row.target, tag:COALESCE(row.tag2, '')})
    WITH s, t, row
      CALL apoc.merge.relationship(s, row.relation, {}, {}, t) YIELD rel
      REMOVE s.tag, t.tag
    RETURN COUNT(*);
    

    glycolysis.cql:

    // CREATE INDICES:
    CREATE INDEX ON :Metabolism(name);
    CREATE INDEX ON :Glycolysis(name);
    
    // CREATE GRAPH:
    //USING PERIODIC COMMIT 5000
    LOAD CSV WITH HEADERS FROM "file:/mnt/Vancouver/Programming/data/metabolism/glycolysis.csv" AS row
    MERGE (s:Metabolism:Glycolysis {name: row.source})
    MERGE (t:Metabolism:Glycolysis {name: row.target})
    WITH s, t, row
      CALL apoc.merge.relationship(s, row.relation, {}, {}, t) YIELD rel
    RETURN COUNT(*);
    
    // MERGE COMMON NODE (GLYCOLYSIS: PYRUVATE; TCA: PYRUVATE):
    // As presented, run "tca.cql" first, then "glycolysis.cql"
    
    MATCH (g:Glycolysis), (t:TCA) WHERE g.name = t.name
    CALL apoc.refactor.mergeNodes([g,t]) YIELD node
      RETURN node;
    

    Script execution:

    $ cat tca.cql |  cypher-shell -u *** -p ***
      COUNT(*)
      21
    
    $ cat glycolysis.cql |  cypher-shell -u *** -p ***
      COUNT(*)
      22
      node
      (:Metabolism:TCA:Glycolysis {name: "pyruvate"})
    
    $ 
    

    Neo4j graph (:Metabolism view):

    0 讨论(0)
  • 2020-12-18 08:36

    @cybersam's answer is excellent, providing the most elegant solution (once again: thank you!) -- please upvote that accepted answer.

    Since this question/answer/topic is likely to be of interest to others, I wanted to mention that my code (based on this SO thread, How to specify relationship type in CSV?, and modified per the hints provided by @cybersam) now works, and show the result:

    Solution 1 (my original post, updated):

    LOAD CSV WITH HEADERS FROM "file:/glycolysis_relations.csv" AS row
    MERGE (s:Glycolysis {name:row.source})
    MERGE (t:Glycolysis {name:row.target})
    FOREACH (x in case row.relation when "substrate_of" then [1] else [] end |
      MERGE (s)-[r:substrate_of]->(t)
    )
    FOREACH (x in case row.relation when "yields" then [1] else [] end |
      MERGE (s)-[r:yields]->(t)
      );
    

    Solution 2 (cybersam's, updated):

    LOAD CSV WITH HEADERS FROM "file:/glycolysis_relations.csv" AS row
    MERGE (s:Metabolism:Glycolysis {name: row.source})
    MERGE (t:Metabolism:Glycolysis {name: row.target})
    WITH s, t, row
      // "Bug" -- additional duplicate relations with each iteration of this statement/script:
      // CALL apoc.create.relationship(s, row.relation, {}, t) YIELD rel
      // Solution: 
      // https://github.com/neo4j-contrib/neo4j-apoc-procedures/issues/271
      // https://stackoverflow.com/questions/47808421/neo4j-load-csv-to-create-dynamic-relationship-types
      CALL apoc.merge.relationship(s, row.relation, {}, {}, t) YIELD rel
    RETURN COUNT(*);
    

    Both solutions generate the identical graph, below. :-D

    0 讨论(0)
提交回复
热议问题