# How to Create a Knowledge Graph from Your Research Notes

By the end of this article you will be able to turn a folder of reading notes into a queryable knowledge graph: a structured network of entities (things like genes, drugs, and diseases) connected by labeled relationships. You will design a small schema, write your notes as subject-predicate-object triples, load them into Python with NetworkX, run queries such as "what connects turmeric to chronic inflammation," and export the result for visualization. To follow the walkthrough you need Python 3.10 or newer, the libraries `networkx`, `pandas`, and `matplotlib`, and a text editor. Everything runs on a laptop with no internet connection required after installation.

## What a Knowledge Graph Is

A knowledge graph stores information as a network instead of as documents. It has two parts:

- **Nodes** (also called entities or vertices) are the things you care about. In [molecular biology](/blog/careers/molecular-biology) these might be proteins, pathways, or diseases.
- **Edges** are the relationships between nodes, and each edge carries a label such as "inhibits," "encodes," or "is a risk factor for."

A collection of one node, one labeled edge, and a second node is called a **triple**: subject, predicate, object. "Curcumin modulates NF-kappa B signaling" is one triple.

Biology already depends on this representation. The Gene Ontology, the most widely used controlled vocabulary for annotating proteins, organizes its concepts as a **directed acyclic graph**, a graph in which each node is a biological concept and each directed edge is the parent-child relationship between two terms. Protein annotations then link proteins to those functional terms, producing a combined graph of concepts and proteins [1]. When you build a graph from your own notes, you are constructing a miniature version of the same idea, scoped to the literature you actually read.

The payoff is that questions which are slow to answer by rereading notes become one-line queries. Instead of searching "which papers mention the NF-kappa B pathway," you ask the graph directly which nodes connect to it and how.

## Before You Start: What to Have on Hand

1. A folder of research notes in plain text or Markdown, one note per claim or per paper.
2. Python installed, with three packages added from the command line:

```bash
python -m pip install networkx pandas matplotlib
```

3. A spreadsheet program or plain-text editor for a small table of relationships.
4. Roughly one hour for a first pass over 20 to 50 notes.

You do not need a graph database, a server, or paid software to start. The workflow below keeps the data in files you can back up, version with Git, and migrate later.

## Build Your Literature Notes System First

A knowledge graph is only as good as the notes underneath it, so your research note taking needs two properties before any code runs: **atomicity** and **traceability**.

**Atomicity** means one note records one claim. "Curcumin modulates NF-kappa B signaling" is a note. "Curcumin, and also resveratrol, plus some thoughts on study design" is not. Atomic claims are what become triples. When you read a paper, extract each claim into its own literature note with a stable identifier, the source, and any qualifiers.

**Traceability** means every note records where the claim came from, so every edge in the final graph can point back to a note and every note to a paper.

A practical [literature notes system](/blog/research-skills/the-ultimate-guide-to-building-a-literature-notes-system-for-research) for this purpose looks like this, one file per claim:

```markdown
---
id: li2015_curcumin_nfkb
source: "Li et al., review on curcumin and inflammatory signaling"
date_read: 2026-11-02
tags: [curcumin, nfkb, inflammation]
---

Curcumin modulates NF-kappa B signaling, which regulates inflammatory
cytokine expression.
```

Three habits pay off here:

- Write claims in the form "X does Y to Z" whenever the science allows it. Sentences with a clear actor and a clear target convert into triples almost mechanically.
- Record qualifiers in the note ("in cell culture," "in mice," "at 10 micromolar") even though they will not all become edges. You will need them when you check the graph later.
- Use one canonical name per entity across all notes. Pick "NF-kappa B signaling" once and use it every time, rather than alternating with "NF-κB" and "NFKB."

## Design the Schema Before You Add Data

A **schema** is your decision about which entity types and relationship types exist in the graph. Skipping this step is the single most common reason personal graphs collapse into tangles.

Real projects show how restrictive schemas work. The Aliment to Bodily Condition knowledgebase (ABCkb) connects plants to human health outcomes through a fixed stepwise path: plant, then plant product, then human gene, then pathway, then indication, and it stores the result in a graph database so the connections can be queried [2]. The path has exactly five node types and a small set of connecting relationships. That restraint is what makes it usable.

For a pharmacology or nutrition reading project, a workable starter schema is:

| Entity type | Examples from this project |
|--|--|
| compound | curcumin, resveratrol |
| plant or food source | turmeric |
| pathway | NF-kappa B signaling |
| biological process | inflammatory cytokine expression |
| disease or phenotype | chronic inflammation |
| study or note | li2015_curcumin_nfkb |

And a starter set of relationship types:

| Predicate | Meaning | Direction |
|--|--|--|
| contains | a source contains a compound | source to compound |
| modulates | a compound changes a pathway | compound to pathway |
| regulates | a pathway changes a process | pathway to process |
| contributes to | a process contributes to a phenotype | process to phenotype |
| reported in | any claim traces to a note | any node to note |

Keep the predicate list short. If two predicates differ only in nuance, merge them. You can always split later, but merging a thousand inconsistent edges later is painful.

## Create a Knowledge Graph from Research Notes Step by Step

This walkthrough builds the graph shown in the tables above. Follow it with your own notes once the mechanics are clear.

### Step 1. Write your triples as a CSV file

Create a file called `triples.csv`. Each row is one claim from one note.

```csv
subject,subject_type,predicate,object,object_type,note
turmeric,plant source,contains,curcumin,compound,note044
curcumin,compound,modulates,NF-kappa B signaling,pathway,li2015_curcumin_nfkb
NF-kappa B signaling,pathway,regulates,inflammatory cytokine expression,biological process,li2015_curcumin_nfkb
inflammatory cytokine expression,biological process,contributes to,chronic inflammation,disease or phenotype,note043
```

The `note` column is your traceability guarantee. Every edge in the graph must have one.

### Step 2. Load the triples and build the graph

```python
import pandas as pd
import networkx as nx

triples = pd.read_csv("triples.csv")

G = nx.MultiDiGraph()

for row in triples.itertuples(index=False):
    G.add_node(row.subject, entity_type=row.subject_type)
    G.add_node(row.object, entity_type=row.object_type)
    G.add_edge(
        row.subject,
        row.object,
        predicate=row.predicate,
        note=row.note,
    )

print(G.number_of_nodes(), "nodes")
print(G.number_of_edges(), "edges")
```

Expected output:

```text
5 nodes
4 edges
```

`MultiDiGraph` is a directed graph (edges have direction) that allows multiple edges between the same pair of nodes. That matters because curcumin might modulate a pathway by two different mechanisms, and both should be kept as separate edges.

### Step 3. Canonicalize entity names

Before querying, collapse naming variants. Add a normalization function and rebuild, or normalize the CSV before loading:

```python
def canonical(name):
    return name.strip().lower().replace(" ", "_")

triples["subject"] = triples["subject"].apply(canonical)
triples["object"] = triples["object"].apply(canonical)
```

Now "NF-kappa B signaling," "nf-kb signaling," and "NFκB" all resolve to one node if your CSV maps them there. Build an explicit synonym table rather than relying on guesswork:

```python
synonyms = {
    "nfkb_signaling": "nf-kappa_b_signaling",
    "nf-kb": "nf-kappa_b_signaling",
}
triples["subject"] = triples["subject"].replace(synonyms)
triples["object"] = triples["object"].replace(synonyms)
```

### Step 4. Run your first queries

The neighbors of a node show everything your notes connect to it:

```python
print("Outgoing:", list(G.successors("curcumin")))
print("Incoming:", list(G.predecessors("curcumin")))
```

Expected output:

```text
Outgoing: ['nf-kappa_b_signaling']
Incoming: ['turmeric']
```

The question that justifies the whole project is the path query. What route does the evidence take from a food to a health outcome?

```python
paths = nx.all_simple_paths(
    G, source="turmeric", target="chronic_inflammation"
)
for p in paths:
    print(" -> ".join(p))
```

Expected output:

```text
turmeric -> curcumin -> nf-kappa_b_signaling -> inflammatory_cytokine_expression -> chronic_inflammation
```

This is the same stepwise chain from source to indication that ABCkb uses to connect plants to health outcomes for generating hypotheses [2]. Reconstructing it from your own reading tells you exactly where your evidence is thin, because a missing edge breaks every path that needs it.

### Step 5. Visualize and export

```python
import matplotlib.pyplot as plt

pos = nx.spring_layout(G, seed=42, k=1.2)
nx.draw(G, pos, with_labels=True, node_size=2500,
        node_color="lightblue", font_size=8)
plt.savefig("notes_graph.png", dpi=300, bbox_inches="tight")

nx.write_graphml(G, "notes_graph.graphml")
```

The `.graphml` file opens directly in Gephi or yEd, both free, where you can drag nodes into readable layouts and color them by `entity_type`. Keep `seed=42` so repeated runs produce the same layout and you can compare versions.

## Extracting Entities and Relationships at Scale

Hand-writing triples is the right method for a first graph of 50 to 200 edges. Past that, you will want help from text processing, and published work shows the two directions this can take.

The first direction is **entity and relation extraction**: software reads free text and outputs the entities it contains plus the relationships between them. This is a mature research problem in biomedical text. A study of discharge summaries from the MIMIC-III intensive care database extracted drugs, drug attributes such as strength and dosage, adverse drug events, and the relations between them from unstructured clinical notes, using neural models that jointly model entities and relations rather than treating them as separate steps [3]. The lesson for your notes is structural: entities and their relations are easier to extract together, in context, than one at a time. When you skim a note, read for the pair and the verb at the same time.

The second direction is **mapping to controlled vocabularies**. Real-world systems must reconcile free text with fixed code systems, and one recent approach matches unstructured medical text to ICD-10 disease codes using a machine learning model backed by a knowledge base of relationships between symptoms and diseases, reaching an accuracy of 81.07 percent with a fast [logistic regression](/blog/data-analysis/logistic-regression-definition-formula) model [4]. In your own project the equivalent step is deciding that "T2D," "diabetes mellitus type 2," and "NIDDM" are all the node `type_2_diabetes`, and, when you are ready, tying your nodes to external identifiers such as Gene Ontology terms or MeSH headings. Even a hand-maintained synonym table, like the one in Step 3, is this idea in miniature.

For a scientist without a machine learning background, the practical middle path is to draft triples manually while using simple string matching to enforce your synonym table, and to treat automated extraction as a later upgrade once your schema has stabilized.

## Storage and Export Options

![Comparison of a triples CSV, a NetworkX graph in memory, and a GraphML file across what each holds, its best use, and how to reach it.](/article-images/illustrations/create-knowledge-graph-from-research-notes-storage-options.webp)

| Option | What it is | Best for | How to reach it from this workflow |
|--|--|--|--|
| CSV of triples | A plain table of subject, predicate, object | Reviewing, editing, version control in Git | The `triples.csv` file itself |
| NetworkX graph | Python objects in memory | Analysis and queries on small to medium graphs | The code in Steps 2 and 4 |
| GraphML | An XML-based graph file format | Visualization in Gephi or yEd | `nx.write_graphml()` in Step 5 |
| RDF with SPARQL | The W3C standard for linked data, queried with SPARQL | Publishing data, joining with public datasets | The `rdflib` Python package |
| Property graph database | A database such as Neo4j queried with Cypher | Large graphs, interactive exploration, sharing | Export the CSV and load it with the database's CSV import |

Start with the first three. Move to RDF or a graph database only when a collaborator, a journal, or a dataset size makes it necessary.

## How to Check That the Graph Is Right

![Decision path for checking a knowledge graph: orphans, degree spikes, broken paths, and near-duplicate predicates each lead to a different fix.](/article-images/illustrations/create-knowledge-graph-from-research-notes-graph-check-triage.webp)

Run these checks each time you add a batch of edges.

1. **Spot-check edges against sources.** Pick ten edges at random, follow each `note` field back to the literature note, and confirm the predicate matches what the paper actually claimed, including qualifiers like "in vitro."
2. **Look for orphan nodes.** `nx.isolates(G)` lists nodes with no edges. Orphans usually mean a typo created a duplicate.
3. **Check degree sanity.** `G.degree()` per node should look plausible. A node with fifty edges after fifty notes is usually a canonicalization failure, not deep scholarship.
4. **Test the path queries you care about.** If the food-to-outcome chain exists in your reading but not in the graph, an edge is missing or misdirected.
5. **Review the predicate list.** If you have accumulated near-duplicates such as "inhibits" and "suppresses," merge them or define precisely when each applies.

## Common Mistakes and Limitations

These are the failure modes that show up repeatedly when researchers create a knowledge graph from research notes.

- **Skipping the schema.** Adding nodes first and inventing relationships as you go produces a graph where "affects," "modulates," and "influences" all mean whatever you felt that day. Write the entity and predicate tables first.
- **Turning notes into tag clouds.** A node connected to everything by an unlabeled or vague relation carries no information. The edge label is where the science lives. "Curcumin associated with NF-kappa B" is weak. "Curcumin modulates NF-kappa B signaling" is usable.
- **Recording claims without qualifiers.** A finding observed in cell culture and one observed in randomized trials should not be indistinguishable in the graph. Keep qualifiers in the source notes and, once the graph grows, consider adding them as edge attributes.
- **Inconsistent entity names.** Every synonym pair you miss splits one concept into two nodes and silently breaks paths. Maintain the synonym table from Step 3 from day one.
- **Confusing an edge for causation.** A triple asserts what a source reported, not that the relationship is true or causal. The graph is a map of the literature, and conflicting edges from different papers can and should coexist.
- **Copying paragraphs instead of claims.** Long notes resist decomposition into triples. Rewrite as atomic literature notes before graphing.
- **Adopting heavy tools too early.** A graph database adds setup and a query language you do not need for your first few hundred edges. CSV plus NetworkX plus GraphML covers a surprising range.
- **Expecting automation to think for you.** Automated extraction systems, even the well-engineered ones applied to clinical text, remain imperfect, which is why that field still calls for improvement [3]. Your judgment about which claims deserve an edge is the quality control.

A final limitation worth stating plainly: a personal knowledge graph reflects your reading, not the totality of evidence. Use it to organize and generate hypotheses, and treat any decision that affects health, safety, or experimental design as requiring expert consultation in its own right.

## Next Steps

Once the workflow above feels routine, three upgrades add the most value per hour. First, attach the note identifier and source citation as attributes on every edge automatically, so traceability becomes impossible to skip. Second, add provenance-aware queries, for example counting how many independent notes support each edge. Third, align your nodes with public resources, following the pattern of curated knowledgebases that integrate multiple sources into one queryable graph [2], so your private reading connects to community resources such as the Gene Ontology graph structure you already understand from [1].

## Frequently Asked Questions

### What is a knowledge graph in simple terms?

It is a map of facts as things and labeled relationships between them. Instead of pages of notes, you store statements like "curcumin modulates NF-kappa B signaling" as connected nodes that a computer can query.

### Do I need to know how to code to build one from my notes?

You can build one on paper or in a spreadsheet, since the core is just a table of subject-predicate-object rows. Code becomes worth learning when you want automatic queries, path finding, and clean visualizations, which is what the NetworkX walkthrough in this article provides.

### How is a knowledge graph different from a mind map?

A mind map is a free-form hierarchy for brainstorming, with unlabeled branches. A knowledge graph uses defined entity types, named relationship types, and directions, so the same question always gets the same answer.

### What software should I use to start?

Use a CSV file, the Python library NetworkX, and GraphML export viewed in Gephi or yEd. This stack is free, runs locally, and upgrades later to RDF or a graph database without changing your underlying triples.

### How do I choose the relationship types for my graph?

Start with five to ten predicates that mirror the verbs in your field's claims, such as "contains," "modulates," and "contributes to." Write their definitions down, and merge any pair you cannot clearly distinguish.

### How big does my graph need to be before it is useful?

Usefulness comes from coverage of the questions you actually ask, not from node count. A graph of 100 atomic, well-labeled claims that answers your five most common literature questions beats a graph of 5,000 vague ones.

### Can I use AI tools to extract triples from my notes automatically?

You can, and it speeds up drafting, but every machine-generated triple needs your review against the source note. Published biomedical extraction systems still require significant improvement even with specialized models, so treat automation as a draft generator rather than a curator.

### How do I keep the graph current as I read new papers?

Keep the triples CSV as the master file, append rows with each new literature note, rebuild the graph with the same script, and run the verification checks on each batch. The rebuild takes seconds, so there is no reason to batch up months of reading.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "What is a knowledge graph in simple terms?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "It is a map of facts as things and labeled relationships between them. Instead of pages of notes, you store statements like 'curcumin modulates NF-kappa B signaling' as connected nodes that a computer can query."
      }
    },
    {
      "@type": "Question",
      "name": "Do I need to know how to code to build one from my notes?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "You can build one on paper or in a spreadsheet, since the core is just a table of subject-predicate-object rows. Code becomes worth learning when you want automatic queries, path finding, and clean visualizations."
      }
    },
    {
      "@type": "Question",
      "name": "How is a knowledge graph different from a mind map?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "A mind map is a free-form hierarchy for brainstorming, with unlabeled branches. A knowledge graph uses defined entity types, named relationship types, and directions, so the same question always gets the same answer."
      }
    },
    {
      "@type": "Question",
      "name": "What software should I use to start?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Use a CSV file, the Python library NetworkX, and GraphML export viewed in Gephi or yEd. This stack is free, runs locally, and upgrades later to RDF or a graph database without changing your underlying triples."
      }
    },
    {
      "@type": "Question",
      "name": "How do I choose the relationship types for my graph?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Start with five to ten predicates that mirror the verbs in your field's claims, such as 'contains,' 'modulates,' and 'contributes to.' Write their definitions down, and merge any pair you cannot clearly distinguish."
      }
    },
    {
      "@type": "Question",
      "name": "How big does my graph need to be before it is useful?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Usefulness comes from coverage of the questions you actually ask, not from node count. A graph of 100 atomic, well-labeled claims that answers your most common literature questions beats a graph of 5,000 vague ones."
      }
    },
    {
      "@type": "Question",
      "name": "Can I use AI tools to extract triples from my notes automatically?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "You can, and it speeds up drafting, but every machine-generated triple needs your review against the source note. Treat automation as a draft generator rather than a curator."
      }
    },
    {
      "@type": "Question",
      "name": "How do I keep the graph current as I read new papers?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Keep the triples CSV as the master file, append rows with each new literature note, rebuild the graph with the same script, and run the verification checks on each batch."
      }
    }
  ]
}
</script>

## Sources

1. [GOGrapher: A Python library for GO graph representation and analysis.](https://pubmed.ncbi.nlm.nih.gov/19583843/)
2. [The Aliment to Bodily Condition knowledgebase (ABCkb): a database connecting plants and human health.](https://pubmed.ncbi.nlm.nih.gov/34838100/)
3. [Extraction of Information Related to Drug Safety Surveillance From Electronic Health Record Notes: Joint Modeling of Entities and Relations Using Knowledge-Aware Neural Attentive Models.](https://pubmed.ncbi.nlm.nih.gov/32459650/)
4. [Development of a Method for Automatic Matching of Unstructured Medical Data to ICD-10 Codes.](https://pubmed.ncbi.nlm.nih.gov/38785010/)