Creating a reusable, extensible knowledge graph is one of the most powerful ways to unify data, explore relationships, and support advanced analytics. In this article, we construct the baseline financial-domain knowledge graph that will underpin all future experiments. We use the Banking Industry Knowledge Graph (US/UK/EU) as our seed ontology and dataset, convert its CSV data into RDF/Turtle, import everything into GraphDB, and run initial SPARQL sanity checks.
This article sets the stage for deeper explorations into SPARQL, reasoning, graph patterns, and cross-domain integration in future pieces.
Why Start with the Financial Domain?
Finance is a domain that aligns closely with my professional experience and research interests. Much of my work has focused on knowledge graph technologies and their enterprise applications, allowing me to draw upon first-hand experience, practical insights, and real-world challenges throughout this project. This combination of domain relevance, research potential, and personal expertise makes finance a compelling choice for developing and evaluating a banking industry knowledge graph.
Finance is a rich, relationship-heavy domain where customers, accounts, transactions, branches, products, regulations, and risk indicators naturally form interconnected graph structures. Starting with finance offers several advantages:
A mature ontology with well-defined entities and relationships that has evolved over years.
A realistic dataset that closely mirrors real-world industry structures and interactions that is available free in the public domain that helps fast-track the experimentation process.
A strong foundation for extending experiments into other domains such as healthcare, retail, and logistics.
The Banking Industry Knowledge Graph provides exactly these characteristics, making it an ideal starting point for knowledge graph research and development. Additionally, the financial domain offers a vast range of potential use cases, including fraud detection, risk assessment, customer 360 analysis, regulatory compliance, anti-money laundering (AML), and personalized financial services. This breadth of applications enables the exploration of both theoretical and practical aspects of knowledge graph technologies.
Exploring the Banking Industry Knowledge Graph Repository
We use the GitHub repository Banking-Industry-Knowledge-Graph-for-US-UK-EU-main, which contains:
- Ontology:
ontology/banking-ontology.ttl - Data:
data/nodes.csvanddata/edges.csv - Supporting domain folders: additional datasets for later exploration
The ontology defines core classes such as:
- Bank
- Customer
- Account
- Transaction
- Branch
- Product

And relationships such as:
hasAccountperformsTransactionlocatedInoffersProduct
This ontology becomes the conceptual backbone of our knowledge graph.
3. RDF & Turtle: The Data Model Behind the Graph
GraphDB stores data as RDF triples:
- Subject — the entity
- Predicate — the relationship
- Object — the target entity or literal
Turtle (.ttl) is a compact, human-readable syntax for RDF. Since GraphDB’s open-source edition cannot import CSV directly, we convert the CSVs into Turtle using R.
Converting CSV Nodes & Edges to Turtle Using R
The repository provides nodes and edges as CSV files:
nodes.csv— entitiesedges.csv— relationships
Because GraphDB OSS lacks CSV import, we write an R script that:
- Reads the CSVs
- Constructs URIs for each node
- Maps edges to subject–predicate–object triples
- Writes
.ttlfiles with proper namespaces
This conversion step is crucial: it transforms tabular data into a graph-native format.
If you want the full R script, you can generate it here:
4. Creating the GraphDB Repository
With the ontology and Turtle data ready, we create a new GraphDB repository:
a. Open GraphDB Workbench
b. Create a repository (e.g., financial-kg)

c. Choose a reasoning level (RDFS or OWL-Horst)
d. Import banking-ontology.ttl
e. Import the generated .ttl instance data
f. Verify that no errors or warnings appear
GraphDB’s visual explorer helps confirm that entities and relationships are connected correctly. Refer to Figure 4. This is a good mechanism also to understand the ontology definitions that we loaded to the repository.

5. Initial SPARQL Sanity Checks
Once the graph is loaded, we run basic SPARQL queries to validate the import.
Count all banks/ Financial Institutions

The validation script has returned 64 Financial Institutions. Note that this ontology refers to Bank as Financial Institution.
Summary
This article established the foundational financial-domain knowledge graph that will support all future experiments. Using the Banking Industry Knowledge Graph (US/UK/EU) as the starting point, we explored the ontology, created the node and edge data, and converted the CSV files into RDF/Turtle so they could be imported into the open‑source edition of GraphDB. We then created a new repository, loaded the ontology and instance data, and validated the import with basic SPARQL queries and a minimalist graph exploration.
This initial setup now serves as the backbone for deeper work: advanced SPARQL patterns, reasoning, ontology extension, graph analytics, and cross‑domain integration. Future articles will build on this foundation as we continue exploring theoretical concepts and practical graph‑based experiments.
Appendix
Given below is the R-code that I have used to convert the .csv into .ttl files. It has gone through multiple iterations resolving some of the mapping issues.
GraphDB also provides API endpoints through which data can be uploaded dynamically. We will be exploring those options in future.
R Code
library(rdflib)# -----------------------------# CONFIG# -----------------------------data_folder <- "D:/KGraph/Banking-Industry-Knowledge-Graph-for-US-UK-EU-main/data"ttl_folder <- "D:/KGraph/Banking-Industry-Knowledge-Graph-for-US-UK-EU-main/data/ttl"base <- "https://banking-ontology.example.org/def/"rdf_type <- "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"# -----------------------------# ID COLUMN MAP (same as main loader)# -----------------------------id_map <- list( accounts = "accountId", applications = "applicationId", branches = "branchId", cards = "cardId", collateral = "collateralId", communications = "communicationId", compliance_alerts = "alertId", customers = "customerId", digital_assets = "assetId", esg_metrics = "esgId", exchange_rates = "rateId", fees = "feeId", financial_institutions = "institutionId", jurisdictions = "jurisdictionId", loan_agreements = "loanId", model_risks = "modelId", open_banking_apis = "apiId", parties = "partyId", products = "productId", regulations = "regulationId", risks = "riskId", transactions = "transactionId", vendors = "vendorId")# -----------------------------# CLASS MAP (same as main loader)# -----------------------------class_map <- list( customers = "Customer", accounts = "Account", transactions = "Transaction", cards = "Card", loan_agreements = "LoanAgreement", products = "Product", vendors = "Vendor", financial_institutions = "FinancialInstitution", jurisdictions = "Jurisdiction", esg_metrics = "ESG_Metric", risks = "Risk", regulations = "Regulation", communications = "Communication", collateral = "Collateral", branches = "Branch", applications = "Application", fees = "Fee", digital_assets = "DigitalAsset", parties = "Party", model_risks = "ModelRisk", open_banking_apis = "OpenBankingAPI", exchange_rates = "ExchangeRate", compliance_alerts = "ComplianceAlert")# -----------------------------# PURE CSV → TTL CONVERTER# -----------------------------convert_csv_to_ttl <- function(csv_path, ttl_path, class_name) { df <- read.csv(csv_path, stringsAsFactors = FALSE) id_column <- id_map[[class_name]] class_name_onto <- class_map[[class_name]] g <- rdf() for (i in 1:nrow(df)) { row <- df[i, ] raw_id <- row[[id_column]] if (is.na(raw_id) || raw_id == "" || raw_id == "NA") next clean_id <- gsub("[^A-Za-z0-9_-]", "_", raw_id) subj <- paste0(base, clean_id) # Type triple rdf_add(g, subj, rdf_type, paste0(base, class_name_onto)) # Add ALL columns as properties (including ID) for (col in names(df)) { value <- row[[col]] if (!is.na(value) && value != "" && value != "NA") { rdf_add(g, subj, paste0(base, col), as.character(value)) } } } rdf_serialize(g, ttl_path, format = "turtle")}# -----------------------------# PROCESS ALL CSV FILES# -----------------------------csv_files <- list.files(data_folder, pattern = "\\.csv$", full.names = TRUE)for (file in csv_files) { class_name <- tools::file_path_sans_ext(basename(file)) ttl_path <- paste0(ttl_folder, "/", class_name, ".ttl") # Only convert entity CSVs (edges are handled separately) if (class_name %in% names(class_map)) { convert_csv_to_ttl(file, ttl_path, class_name) }}
Leave a Reply