diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..869f6ae
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,136 @@
+demo-Copy1.ipynb
+.vscode
+.ipynb_checkpoints
+build
+.idea
+.DS_Store
+
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+pip-wheel-metadata/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+.python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
diff --git a/README.md b/README.md
index 44d8d6c..d891e62 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,9 @@
-# GRAMS
\ No newline at end of file
+1. Install package using:
+
+```python
+poetry install
+```
+
+2. For usage, take a look at `demo.ipynb`.
+
+Note: this package has been modified heavily to support plain table (without linked entities). Use this [link](https://drive.google.com/drive/folders/14U6k1OBXL1e356xXYb3dk-j86wnk0EBR) to access to the code used in the paper before the official release is available.
\ No newline at end of file
diff --git a/data/.gitignore b/data/.gitignore
new file mode 100644
index 0000000..c96a04f
--- /dev/null
+++ b/data/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
\ No newline at end of file
diff --git a/demo.ipynb b/demo.ipynb
new file mode 100644
index 0000000..f7dab65
--- /dev/null
+++ b/demo.ipynb
@@ -0,0 +1,406 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Setup the application**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import glob, sys, threading\n",
+ "\n",
+ "from omegaconf import OmegaConf\n",
+ "from tqdm.auto import tqdm\n",
+ "from grams.prelude import GRAMS, ROOT_DIR, DATA_DIR, I, fetch_tables, WikidataSemanticModelHelper\n",
+ "\n",
+ "from sm_widgets.widgets.annotator import Annotator, BatchAnnotator\n",
+ "from sm_widgets_integration.wikidata import get_qnode_db, get_ontclass_db, get_ontprop_db\n",
+ "from sm_widgets_integration.with_grams import convert_table\n",
+ "from sm_widgets.widgets.slider import DisplayShell, Slider"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "cwd = ROOT_DIR / \"examples/misc\"\n",
+ "cfg = OmegaConf.load(ROOT_DIR / \"grams.yaml\")\n",
+ "grams = GRAMS(DATA_DIR, cfg)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Fetch tables from a webpage and select table that we want to annotate**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "tables = fetch_tables(\"https://en.wikipedia.org/wiki/List_of_largest_selling_pharmaceutical_products\")\n",
+ "# tables = fetch_tables(\"https://en.wikipedia.org/wiki/Novartis\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "c3943e96e3014e6ab808540101f66489",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "HTML(value='
')"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "f6cbd37618a64a5fb772fe3b4d1f14fb",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "Output()"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "application/javascript": [
+ "\n",
+ " if (window.IPyApps === undefined) {\n",
+ " window.IPyApps = new Map();\n",
+ " }\n",
+ " window.IPyApps.set('ffbc0f8c-820c-48b0-b6d9-68f75b552d71', window);\n",
+ " "
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "2e0dcba7de71442c99bf9de79a194ccc",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "SlowTunnelWidget(js_endpoint=(0, ''), py_endpoint=(0, ''), tunnel_id='a84666d8-62ed-4dd4-969d-4d2d63bea7d7')"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "62c1a41b78eb43bca17880e7b6af7cc8",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "Output()"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "application/javascript": [
+ "\n",
+ " \n",
+ " function repeatUntilSuccess(fn, timeout, maxTry) {\n",
+ " if (fn() === true) {\n",
+ " return;\n",
+ " }\n",
+ " if (maxTry === undefined) {\n",
+ " maxTry = 10;\n",
+ " }\n",
+ " if (maxTry === 0) {\n",
+ " console.error(\"Max retries error\");\n",
+ " alert(\"max retries error\");\n",
+ " throw new Error(\"Max retries error\");\n",
+ " } else {\n",
+ " setTimeout(function () {\n",
+ " repeatUntilSuccess(fn, timeout, maxTry - 1);\n",
+ " }, timeout);\n",
+ " }\n",
+ " }\n",
+ " \n",
+ " function setupApp0dda4470e08b48a3aa820ab69fabfefe() {\n",
+ " if (window.IPyCallback === undefined || window.IPyApps === undefined) {\n",
+ " return false;\n",
+ " }\n",
+ " let tunnel = window.IPyCallback.get('a84666d8-62ed-4dd4-969d-4d2d63bea7d7');\n",
+ " if (tunnel === undefined) {\n",
+ " return false;\n",
+ " }\n",
+ " \n",
+ " let appwin = window.IPyApps.get('ffbc0f8c-820c-48b0-b6d9-68f75b552d71');\n",
+ " if (appwin === undefined) {\n",
+ " return false;\n",
+ " }\n",
+ " \n",
+ " let container = appwin.document.getElementById('ffbc0f8c-820c-48b0-b6d9-68f75b552d71');\n",
+ " if (container === null) {\n",
+ " return false;\n",
+ " }\n",
+ " \n",
+ " let div = appwin.document.createElement(\"div\");\n",
+ " div.id = '0dda4470-e08b-48a3-aa82-0ab69fabfefe';\n",
+ " div.style = \"margin-bottom: 8px\";\n",
+ " container.parentElement.prepend(div);\n",
+ " \n",
+ " // use the tunnel first to send out the code, after the application is rendered, the listening function \n",
+ " // is going to be replaced by the listener in the application, so we don't have to worry.\n",
+ " tunnel.on_receive(function (version, msg) {\n",
+ " let payload = JSON.parse(msg);\n",
+ " if (payload.id !== '/init/download_code') {\n",
+ " alert('invalid calling order. you need to set the source code first');\n",
+ " console.error(\"invalid call order\", payload);\n",
+ " return;\n",
+ " }\n",
+ " appwin.eval(payload.response);\n",
+ " appwin.Slider.renderApp('0dda4470-e08b-48a3-aa82-0ab69fabfefe', tunnel);\n",
+ " window.IPyApps.set('0dda4470-e08b-48a3-aa82-0ab69fabfefe', appwin);\n",
+ " tunnel.send_msg(JSON.stringify({ url: '/init/done', params: null, id: '/init/done' }));\n",
+ " });\n",
+ " tunnel.send_msg(JSON.stringify({ url: '/init/download_code', params: null, id: '/init/download_code' }));\n",
+ " return true;\n",
+ " }\n",
+ " repeatUntilSuccess(setupApp0dda4470e08b48a3aa820ab69fabfefe, 50, 10);\n",
+ " "
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "slider = Slider(app=DisplayShell(render_fn=lambda df: display(df)), app_render_fn='set_data', embedded_error_log=True)\n",
+ "slider.render()\n",
+ "\n",
+ "threading.Thread(\n",
+ " target=lambda: slider.set_data([\n",
+ " {\"description\": \"
\" + \" > \".join([r.header for r in table.context]), \"args\": [table.as_df()]}\n",
+ " for table in tables\n",
+ " ])\n",
+ ").start()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Annotate the selected table**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "selected_table = 3\n",
+ "\n",
+ "table = tables[selected_table]\n",
+ "table = table.as_relational_linked_table(table_id=f\"{table.page_url}/{selected_table}\")\n",
+ "annotation = grams.annotate(table)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Show the annotation**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "16413b403f7c41568a5d00c4a5944617",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "SlowTunnelWidget(js_endpoint=(0, ''), py_endpoint=(0, ''), tunnel_id='053f0625-c56f-4144-82e7-0f94d938e150')"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "22165517ed8448b193976d47b2846037",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "HTML(value='')"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "application/javascript": [
+ "\n",
+ " \n",
+ " function repeatUntilSuccess(fn, timeout, maxTry) {\n",
+ " if (fn() === true) {\n",
+ " return;\n",
+ " }\n",
+ " if (maxTry === undefined) {\n",
+ " maxTry = 10;\n",
+ " }\n",
+ " if (maxTry === 0) {\n",
+ " console.error(\"Max retries error\");\n",
+ " alert(\"max retries error\");\n",
+ " throw new Error(\"Max retries error\");\n",
+ " } else {\n",
+ " setTimeout(function () {\n",
+ " repeatUntilSuccess(fn, timeout, maxTry - 1);\n",
+ " }, timeout);\n",
+ " }\n",
+ " }\n",
+ " \n",
+ " if (window.IPyApps === undefined) {\n",
+ " window.IPyApps = new Map();\n",
+ " }\n",
+ " function setupApp1d93062019d74f92ab9433d552c16659() {\n",
+ " // get tunnel\n",
+ " if (window.IPyCallback === undefined) {\n",
+ " return false;\n",
+ " }\n",
+ " let tunnel = window.IPyCallback.get('053f0625-c56f-4144-82e7-0f94d938e150');\n",
+ " if (tunnel === undefined) {\n",
+ " return false;\n",
+ " }\n",
+ " \n",
+ " // setup html container\n",
+ " let win = window;\n",
+ " \n",
+ " // use the tunnel first to send out the code, after the application is rendered, the listening function \n",
+ " // is going to be replaced by the listener in the application, so we don't have to worry.\n",
+ " tunnel.on_receive(function (version, msg) {\n",
+ " let payload = JSON.parse(msg);\n",
+ " if (payload.id !== '/init/download_code') {\n",
+ " alert('invalid calling order. you need to set the source code first');\n",
+ " console.error(\"invalid call order. waiting for source code but get:\", payload);\n",
+ " return;\n",
+ " }\n",
+ " win.eval(payload.response);\n",
+ " let shadowDOM = true;\n",
+ " win.Annotator.renderApp('1d930620-19d7-4f92-ab94-33d552c16659', tunnel, undefined, shadowDOM);\n",
+ " window.IPyApps.set('1d930620-19d7-4f92-ab94-33d552c16659', win);\n",
+ " tunnel.send_msg(JSON.stringify({ url: '/init/done', params: null, id: '/init/done' }));\n",
+ " });\n",
+ " tunnel.send_msg(JSON.stringify({ url: '/init/download_code', params: null, id: '/init/download_code' }));\n",
+ " return true;\n",
+ " }\n",
+ " repeatUntilSuccess(setupApp1d93062019d74f92ab9433d552c16659, 50, 10);\n",
+ " "
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 6,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "assistant = None\n",
+ "annotator = Annotator(\n",
+ " entities=get_qnode_db(grams.qnodes), \n",
+ " ontclasses=get_ontclass_db(grams.wdclasses), \n",
+ " ontprops=get_ontprop_db(grams.wdprops), \n",
+ " prop_type_uri='http://www.wikidata.org/prop/P31', \n",
+ " savedir=cwd / \"ground-truth\", \n",
+ " eshost='http://mira.isi.edu:9200', \n",
+ " dev=False, \n",
+ " assistant=assistant)\n",
+ "annotator.render(True)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "threading.Thread(\n",
+ " target=lambda: annotator.set_data(table.id, convert_table(table), sms=[annotation.sm])\n",
+ ").start()"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "grams",
+ "language": "python",
+ "name": "grams"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.8.5"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/examples/.gitignore b/examples/.gitignore
new file mode 100644
index 0000000..a9ec6ef
--- /dev/null
+++ b/examples/.gitignore
@@ -0,0 +1 @@
+t2dv2_tmp
diff --git a/examples/misc/ground-truth/list_of_largest_selling_pharmaceutical_products_1_5056ea45fa51e4aa85161f1d9f5ff171/version.01.json b/examples/misc/ground-truth/list_of_largest_selling_pharmaceutical_products_1_5056ea45fa51e4aa85161f1d9f5ff171/version.01.json
new file mode 100644
index 0000000..ffafccd
--- /dev/null
+++ b/examples/misc/ground-truth/list_of_largest_selling_pharmaceutical_products_1_5056ea45fa51e4aa85161f1d9f5ff171/version.01.json
@@ -0,0 +1,13 @@
+{
+ "version":2,
+ "table_id":"https://en.wikipedia.org/wiki/List_of_largest_selling_pharmaceutical_products/1",
+ "semantic_models":[
+ {"version":1,
+ "nodes":[
+ {"id":"col-1","col_index":1,"label":"Drug"},
+ {"id":"http://www.wikidata.org/entity/Q12140:0","abs_uri":"http://www.wikidata.org/entity/Q12140","rel_uri":"wd:Q12140","approximation":false,"readable_label":"medication (Q12140)"},
+ {"id":"col-2","col_index":2,"label":"Main indication"},
+ {"id":"http://www.wikidata.org/entity/Q42303753:0","abs_uri":"http://www.wikidata.org/entity/Q42303753","rel_uri":"wd:Q42303753","approximation":false,"readable_label":"designated intractable/rare diseases (Q42303753)"},{"id":"stmt:column-1-P683-val:72296","abs_uri":"http://wikiba.se/ontology#Statement","rel_uri":"wikibase:Statement","approximation":false,"readable_label":null},{"id":"col-0","col_index":0,"label":"Rank"},{"id":"val:72296","abs_uri":"http://wikiba.se/ontology#Statement","rel_uri":"wikibase:Statement","approximation":false,"readable_label":null},{"id":"col-3","col_index":3,"label":"Trade name"}],
+ "edges":[
+ {"source":"http://www.wikidata.org/entity/Q12140:0","target":"col-1","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q12140:0","target":"stmt:column-1-P683-val:72296","abs_uri":"http://www.wikidata.org/prop/P683","rel_uri":"p:P683","approximation":false,"readable_label":"ChEBI ID (P683)","id":5},{"source":"http://www.wikidata.org/entity/Q12140:0","target":"col-3","abs_uri":"http://www.wikidata.org/prop/P3780","rel_uri":"p:P3780","approximation":false,"readable_label":"active ingredient in (P3780)","id":11},{"source":"http://www.wikidata.org/entity/Q42303753:0","target":"col-2","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":2},{"source":"http://www.wikidata.org/entity/Q42303753:0","target":"http://www.wikidata.org/entity/Q12140:0","abs_uri":"http://www.wikidata.org/prop/P2176","rel_uri":"p:P2176","approximation":false,"readable_label":"drug used for treatment (P2176)","id":10},{"source":"stmt:column-1-P683-val:72296","target":"col-0","abs_uri":"http://www.wikidata.org/prop/P4390","rel_uri":"p:P4390","approximation":false,"readable_label":"mapping relation type (P4390)","id":7},
+ {"source":"stmt:column-1-P683-val:72296","target":"val:72296","abs_uri":"http://www.wikidata.org/prop/P683","rel_uri":"p:P683","approximation":false,"readable_label":"ChEBI ID (P683)","id":8}]}],"is_curated":false,"note":""}
\ No newline at end of file
diff --git a/examples/misc/ground-truth/list_of_largest_selling_pharmaceutical_products_2_0e75aebc11389ca6b9327bfd3de52abd/version.01.json b/examples/misc/ground-truth/list_of_largest_selling_pharmaceutical_products_2_0e75aebc11389ca6b9327bfd3de52abd/version.01.json
new file mode 100644
index 0000000..c789396
--- /dev/null
+++ b/examples/misc/ground-truth/list_of_largest_selling_pharmaceutical_products_2_0e75aebc11389ca6b9327bfd3de52abd/version.01.json
@@ -0,0 +1 @@
+{"version":2,"table_id":"https://en.wikipedia.org/wiki/List_of_largest_selling_pharmaceutical_products/2","semantic_models":[{"version":1,"nodes":[{"id":"col-1","col_index":1,"label":"Drug"},{"id":"http://www.wikidata.org/entity/Q12140:0","abs_uri":"http://www.wikidata.org/entity/Q12140","rel_uri":"wd:Q12140","approximation":false,"readable_label":"medication (Q12140)"},{"id":"col-2","col_index":2,"label":"Main indication"},{"id":"http://www.wikidata.org/entity/Q930752:0","abs_uri":"http://www.wikidata.org/entity/Q930752","rel_uri":"wd:Q930752","approximation":false,"readable_label":"medical specialty (Q930752)"}],"edges":[{"source":"http://www.wikidata.org/entity/Q12140:0","target":"col-1","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q12140:0","target":"http://www.wikidata.org/entity/Q930752:0","abs_uri":"http://www.wikidata.org/prop/P2175","rel_uri":"p:P2175","approximation":false,"readable_label":"medical condition treated (P2175)","id":5},{"source":"http://www.wikidata.org/entity/Q930752:0","target":"col-2","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":2}]}],"is_curated":false,"note":""}
\ No newline at end of file
diff --git a/examples/misc/tables/list_of_largest_selling_pharmaceutical_products_1_5056ea45fa51e4aa85161f1d9f5ff171.json b/examples/misc/tables/list_of_largest_selling_pharmaceutical_products_1_5056ea45fa51e4aa85161f1d9f5ff171.json
new file mode 100644
index 0000000..5462338
--- /dev/null
+++ b/examples/misc/tables/list_of_largest_selling_pharmaceutical_products_1_5056ea45fa51e4aa85161f1d9f5ff171.json
@@ -0,0 +1 @@
+{"table":{"columns":[{"index":0,"name":"Rank","values":["1","2","3","4","5"]},{"index":1,"name":"Drug","values":["adalimumab ","apixaban ","etanercept ","ustekinumab ","pembrolizumab "]},{"index":2,"name":"Main indication","values":["rheumatoid arthritis ","anticoagulant ","rheumatoid arthritis ","psoriasis ","oncology "]},{"index":3,"name":"Trade name","values":["Humira","Eliquis","Enbrel","Stelara","Keytruda"]},{"index":4,"name":"2015","values":["10.1","1.6","7.2","2.0","0.4"]},{"index":5,"name":"2016","values":["13.5","3.0","7.6","2.6","0.7"]},{"index":6,"name":"2017","values":["16.3","4.6","7.9","3.7","2.2"]},{"index":7,"name":"2018","values":["18.4","7.1","8.0","5.0","4.3"]},{"index":8,"name":"2019","values":["21.4","9.9","8.1","6.6","6.5"]}],"metadata":{"table_id":"https://en.wikipedia.org/wiki/List_of_largest_selling_pharmaceutical_products/1","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[],[{"start":0,"end":10,"url":"https://en.wikipedia.org/wiki/Adalimumab","qnode_id":"Q348260"}],[{"start":0,"end":20,"url":"https://en.wikipedia.org/wiki/Rheumatoid_arthritis","qnode_id":"Q187255"}],[],[],[],[],[],[]],[[],[{"start":0,"end":8,"url":"https://en.wikipedia.org/wiki/Apixaban","qnode_id":"Q414462"}],[{"start":0,"end":13,"url":"https://en.wikipedia.org/wiki/Anticoagulant","qnode_id":"Q215118"}],[],[],[],[],[],[]],[[],[{"start":0,"end":10,"url":"https://en.wikipedia.org/wiki/Etanercept","qnode_id":"Q415343"}],[{"start":0,"end":20,"url":"https://en.wikipedia.org/wiki/Rheumatoid_arthritis","qnode_id":"Q187255"}],[],[],[],[],[],[]],[[],[{"start":0,"end":11,"url":"https://en.wikipedia.org/wiki/Ustekinumab","qnode_id":"Q420305"}],[{"start":0,"end":9,"url":"https://en.wikipedia.org/wiki/Psoriasis","qnode_id":"Q179945"}],[],[],[],[],[],[]],[[],[{"start":0,"end":13,"url":"https://en.wikipedia.org/wiki/Pembrolizumab","qnode_id":"Q13896859"}],[{"start":0,"end":8,"url":"https://en.wikipedia.org/wiki/Oncology","qnode_id":"Q162555"}],[],[],[],[],[],[]]]}
\ No newline at end of file
diff --git a/examples/misc/tables/list_of_largest_selling_pharmaceutical_products_2_0e75aebc11389ca6b9327bfd3de52abd.json b/examples/misc/tables/list_of_largest_selling_pharmaceutical_products_2_0e75aebc11389ca6b9327bfd3de52abd.json
new file mode 100644
index 0000000..ed9ea10
--- /dev/null
+++ b/examples/misc/tables/list_of_largest_selling_pharmaceutical_products_2_0e75aebc11389ca6b9327bfd3de52abd.json
@@ -0,0 +1 @@
+{"table":{"columns":[{"index":0,"name":"Rank","values":["1","3","4","5","6","7","8","9","10","11","12","13","14","15","16"]},{"index":1,"name":"Drug","values":["adalimumab ","apixaban ","lenalidomide ","nivolumab ","pembrolizumab ","etanercept ","trastuzumab ","bevacizumab ","rituximab ","rivaroxaban ","aflibercept ","infliximab ","Pneumococcal conjugate vaccine ","ustekinumab ","pregabalin "]},{"index":2,"name":"Main indication","values":["rheumatoid arthritis ","anticoagulant ","multiple myeloma ","oncology ","oncology ","rheumatoid arthritis ","breast cancer ","colon cancer ","non-Hodgkin's lymphoma ","anticoagulant ","","","","","epilepsy "]},{"index":3,"name":"Trade name","values":["Humira","Eliquis","Revlimid","Opdivo","Keytruda","Enbrel","Herceptin","Avastin","Rituxan, MabThera ","Xarelto","Eylea","Remicade","Prevnar 13 Prevenar 13 ","Stelara","Lyrica"]},{"index":4,"name":"2018 sales (million USD)","values":["19 936","9872","9685","7570","7171","7126","6981","6847","6750","6589","6551","5908","5802","5156","4970"]},{"index":5,"name":"2017 sales (million USD)","values":["18427","7395","8187","5763","3809","7885","7013","6686","7298","6234","5830","7152","5601","4011","5065"]}],"metadata":{"table_id":"https://en.wikipedia.org/wiki/List_of_largest_selling_pharmaceutical_products/2","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[],[{"start":0,"end":10,"url":"https://en.wikipedia.org/wiki/Adalimumab","qnode_id":"Q348260"}],[{"start":0,"end":20,"url":"https://en.wikipedia.org/wiki/Rheumatoid_arthritis","qnode_id":"Q187255"}],[],[],[]],[[],[{"start":0,"end":8,"url":"https://en.wikipedia.org/wiki/Apixaban","qnode_id":"Q414462"}],[{"start":0,"end":13,"url":"https://en.wikipedia.org/wiki/Anticoagulant","qnode_id":"Q215118"}],[],[],[]],[[],[{"start":0,"end":12,"url":"https://en.wikipedia.org/wiki/Lenalidomide","qnode_id":"Q425681"}],[{"start":0,"end":16,"url":"https://en.wikipedia.org/wiki/Multiple_myeloma","qnode_id":"Q467635"}],[],[],[]],[[],[{"start":0,"end":9,"url":"https://en.wikipedia.org/wiki/Nivolumab","qnode_id":"Q7041828"}],[{"start":0,"end":8,"url":"https://en.wikipedia.org/wiki/Oncology","qnode_id":"Q162555"}],[],[],[]],[[],[{"start":0,"end":13,"url":"https://en.wikipedia.org/wiki/Pembrolizumab","qnode_id":"Q13896859"}],[{"start":0,"end":8,"url":"https://en.wikipedia.org/wiki/Oncology","qnode_id":"Q162555"}],[],[],[]],[[],[{"start":0,"end":10,"url":"https://en.wikipedia.org/wiki/Etanercept","qnode_id":"Q415343"}],[{"start":0,"end":20,"url":"https://en.wikipedia.org/wiki/Rheumatoid_arthritis","qnode_id":"Q187255"}],[],[],[]],[[],[{"start":0,"end":11,"url":"https://en.wikipedia.org/wiki/Trastuzumab","qnode_id":"Q412616"}],[{"start":0,"end":13,"url":"https://en.wikipedia.org/wiki/Breast_cancer","qnode_id":"Q128581"}],[],[],[]],[[],[{"start":0,"end":11,"url":"https://en.wikipedia.org/wiki/Bevacizumab","qnode_id":"Q413299"}],[{"start":0,"end":12,"url":"https://en.wikipedia.org/wiki/Colon_cancer","qnode_id":null}],[],[],[]],[[],[{"start":0,"end":9,"url":"https://en.wikipedia.org/wiki/Rituximab","qnode_id":"Q412323"}],[{"start":0,"end":22,"url":"https://en.wikipedia.org/wiki/Non-Hodgkin%27s_lymphoma","qnode_id":null}],[],[],[]],[[],[{"start":0,"end":11,"url":"https://en.wikipedia.org/wiki/Rivaroxaban","qnode_id":"Q420262"}],[{"start":0,"end":13,"url":"https://en.wikipedia.org/wiki/Anticoagulant","qnode_id":"Q215118"}],[],[],[]],[[],[{"start":0,"end":11,"url":"https://en.wikipedia.org/wiki/Aflibercept","qnode_id":"Q4689286"}],[],[],[],[]],[[],[{"start":0,"end":10,"url":"https://en.wikipedia.org/wiki/Infliximab","qnode_id":"Q415264"}],[],[],[],[]],[[],[{"start":0,"end":30,"url":"https://en.wikipedia.org/wiki/Pneumococcal_conjugate_vaccine","qnode_id":"Q7205991"}],[],[],[],[]],[[],[{"start":0,"end":11,"url":"https://en.wikipedia.org/wiki/Ustekinumab","qnode_id":"Q420305"}],[],[],[],[]],[[],[{"start":0,"end":10,"url":"https://en.wikipedia.org/wiki/Pregabalin","qnode_id":"Q412174"}],[{"start":0,"end":8,"url":"https://en.wikipedia.org/wiki/Epilepsy","qnode_id":"Q41571"}],[],[],[]]]}
\ No newline at end of file
diff --git a/examples/novartis/ground-truth/table_01/version.01.json b/examples/novartis/ground-truth/table_01/version.01.json
new file mode 100644
index 0000000..c16b742
--- /dev/null
+++ b/examples/novartis/ground-truth/table_01/version.01.json
@@ -0,0 +1,204 @@
+{
+ "version": 2,
+ "table_id": "table_01",
+ "semantic_models": [
+ {
+ "version": 1,
+ "nodes": [
+ {
+ "id": "col-2",
+ "col_index": 2,
+ "label": "Dise"
+ },
+ {
+ "id": "col-4",
+ "col_index": 4,
+ "label": "Tissue"
+ },
+ {
+ "id": "http://www.wikidata.org/entity/Q12136:0",
+ "abs_uri": "http://www.wikidata.org/entity/Q12136",
+ "rel_uri": "wd:Q12136",
+ "approximation": false,
+ "readable_label": "disease (Q12136)"
+ },
+ {
+ "id": "http://www.wikidata.org/entity/Q40397:0",
+ "abs_uri": "http://www.wikidata.org/entity/Q40397",
+ "rel_uri": "wd:Q40397",
+ "approximation": false,
+ "readable_label": "tissue (Q40397)"
+ },
+ {
+ "id": "col-0",
+ "col_index": 0,
+ "label": "Description"
+ },
+ {
+ "id": "http://www.wikidata.org/entity/Q13442814:0",
+ "abs_uri": "http://www.wikidata.org/entity/Q13442814",
+ "rel_uri": "wd:Q13442814",
+ "approximation": false,
+ "readable_label": "scholarly article (Q13442814)"
+ },
+ {
+ "id": "col-3",
+ "col_index": 3,
+ "label": "Gene"
+ },
+ {
+ "id": "http://www.wikidata.org/entity/Q7187:0",
+ "abs_uri": "http://www.wikidata.org/entity/Q7187",
+ "rel_uri": "wd:Q7187",
+ "approximation": false,
+ "readable_label": "gene (Q7187)"
+ },
+ {
+ "id": "stmt:column-0-P698-column-1",
+ "abs_uri": "http://wikiba.se/ontology#Statement",
+ "rel_uri": "wikibase:Statement",
+ "approximation": false,
+ "readable_label": null
+ },
+ {
+ "id": "stmt:column-0-P921-column-2",
+ "abs_uri": "http://wikiba.se/ontology#Statement",
+ "rel_uri": "wikibase:Statement",
+ "approximation": false,
+ "readable_label": null
+ },
+ {
+ "id": "stmt:column-3-P2293-column-2",
+ "abs_uri": "http://wikiba.se/ontology#Statement",
+ "rel_uri": "wikibase:Statement",
+ "approximation": false,
+ "readable_label": null
+ },
+ {
+ "id": "stmt:column-3-PExpressInTissue-column-4",
+ "abs_uri": "http://wikiba.se/ontology#Statement",
+ "rel_uri": "wikibase:Statement",
+ "approximation": false,
+ "readable_label": null
+ },
+ {
+ "id": "col-1",
+ "col_index": 1,
+ "label": "PUBMEDID"
+ }
+ ],
+ "edges": [
+ {
+ "source": "http://www.wikidata.org/entity/Q12136:0",
+ "target": "col-2",
+ "abs_uri": "http://www.w3.org/2000/01/rdf-schema#label",
+ "rel_uri": "rdfs:label",
+ "approximation": false,
+ "readable_label": null,
+ "id": 1
+ },
+ {
+ "source": "http://www.wikidata.org/entity/Q13442814:0",
+ "target": "col-0",
+ "abs_uri": "http://www.w3.org/2000/01/rdf-schema#label",
+ "rel_uri": "rdfs:label",
+ "approximation": false,
+ "readable_label": null,
+ "id": 2
+ },
+ {
+ "source": "http://www.wikidata.org/entity/Q13442814:0",
+ "target": "stmt:column-0-P698-column-1",
+ "abs_uri": "http://www.wikidata.org/prop/P698",
+ "rel_uri": "p:P698",
+ "approximation": false,
+ "readable_label": "PubMed ID (P698)",
+ "id": 4
+ },
+ {
+ "source": "http://www.wikidata.org/entity/Q13442814:0",
+ "target": "stmt:column-0-P921-column-2",
+ "abs_uri": "http://www.wikidata.org/prop/P921",
+ "rel_uri": "p:P921",
+ "approximation": false,
+ "readable_label": "main subject (P921)",
+ "id": 5
+ },
+ {
+ "source": "http://www.wikidata.org/entity/Q7187:0",
+ "target": "col-3",
+ "abs_uri": "http://www.w3.org/2000/01/rdf-schema#label",
+ "rel_uri": "rdfs:label",
+ "approximation": false,
+ "readable_label": null,
+ "id": 3
+ },
+ {
+ "source": "stmt:column-0-P698-column-1",
+ "target": "col-1",
+ "abs_uri": "http://www.wikidata.org/prop/P698",
+ "rel_uri": "p:P698",
+ "approximation": false,
+ "readable_label": "PubMed ID (P698)",
+ "id": 6
+ },
+ {
+ "source": "stmt:column-0-P921-column-2",
+ "target": "http://www.wikidata.org/entity/Q12136:0",
+ "abs_uri": "http://www.wikidata.org/prop/P921",
+ "rel_uri": "p:P921",
+ "approximation": false,
+ "readable_label": "main subject (P921)",
+ "id": 7
+ },
+ {
+ "source": "http://www.wikidata.org/entity/Q40397:0",
+ "target": "col-4",
+ "abs_uri": "http://www.wikidata.org/prop/P279",
+ "rel_uri": "p:P279",
+ "approximation": false,
+ "readable_label": null,
+ "id": 8
+ },
+ {
+ "source": "http://www.wikidata.org/entity/Q7187:0",
+ "target": "stmt:column-3-P2293-column-2",
+ "abs_uri": "http://www.wikidata.org/prop/P2293",
+ "rel_uri": "p:P2293",
+ "approximation": false,
+ "readable_label": null,
+ "id": 9
+ },
+ {
+ "source": "stmt:column-3-P2293-column-2",
+ "target": "http://www.wikidata.org/entity/Q12136:0",
+ "abs_uri": "http://www.wikidata.org/prop/P2293",
+ "rel_uri": "p:P2293",
+ "approximation": false,
+ "readable_label": null,
+ "id": 10
+ },
+ {
+ "source": "http://www.wikidata.org/entity/Q7187:0",
+ "target": "stmt:column-3-PExpressInTissue-column-4",
+ "abs_uri": "http://www.wikidata.org/prop/P2293",
+ "rel_uri": "p:P2293",
+ "approximation": false,
+ "readable_label": "expressed in tissue",
+ "id": 9
+ },
+ {
+ "source": "stmt:column-3-PExpressInTissue-column-4",
+ "target": "http://www.wikidata.org/entity/Q40397:0",
+ "abs_uri": "http://www.wikidata.org/prop/P2293",
+ "rel_uri": "p:P2293",
+ "approximation": false,
+ "readable_label": "expressed in tissue",
+ "id": 10
+ }
+ ]
+ }
+ ],
+ "is_curated": false,
+ "note": ""
+}
\ No newline at end of file
diff --git a/examples/novartis/ground-truth/table_01/version.02.json b/examples/novartis/ground-truth/table_01/version.02.json
new file mode 100644
index 0000000..07b98d3
--- /dev/null
+++ b/examples/novartis/ground-truth/table_01/version.02.json
@@ -0,0 +1,156 @@
+{
+ "version": 2,
+ "table_id": "table_01",
+ "semantic_models": [
+ {
+ "version": 1,
+ "nodes": [
+ {
+ "id": "col-2",
+ "col_index": 2,
+ "label": "Dise"
+ },
+ {
+ "id": "col-4",
+ "col_index": 4,
+ "label": "Tissue"
+ },
+ {
+ "id": "http://www.wikidata.org/entity/Q12136:0",
+ "abs_uri": "http://www.wikidata.org/entity/Q12136",
+ "rel_uri": "wd:Q12136",
+ "approximation": false,
+ "readable_label": null
+ },
+ {
+ "id": "http://www.wikidata.org/entity/Q40397:0",
+ "abs_uri": "http://www.wikidata.org/entity/Q40397",
+ "rel_uri": "wd:Q40397",
+ "approximation": false,
+ "readable_label": null
+ },
+ {
+ "id": "col-0",
+ "col_index": 0,
+ "label": "Description"
+ },
+ {
+ "id": "http://www.wikidata.org/entity/Q13442814:0",
+ "abs_uri": "http://www.wikidata.org/entity/Q13442814",
+ "rel_uri": "wd:Q13442814",
+ "approximation": false,
+ "readable_label": null
+ },
+ {
+ "id": "col-3",
+ "col_index": 3,
+ "label": "Gene"
+ },
+ {
+ "id": "http://www.wikidata.org/entity/Q7187:0",
+ "abs_uri": "http://www.wikidata.org/entity/Q7187",
+ "rel_uri": "wd:Q7187",
+ "approximation": false,
+ "readable_label": null
+ },
+ {
+ "id": "stmt:column-3-PExpressInTissue-column-4",
+ "abs_uri": "http://wikiba.se/ontology#Statement",
+ "rel_uri": "wikibase:Statement",
+ "approximation": false,
+ "readable_label": null
+ },
+ {
+ "id": "col-1",
+ "col_index": 1,
+ "label": "PUBMEDID"
+ }
+ ],
+ "edges": [
+ {
+ "source": "http://www.wikidata.org/entity/Q12136:0",
+ "target": "col-2",
+ "abs_uri": "http://www.w3.org/2000/01/rdf-schema#label",
+ "rel_uri": "rdfs:label",
+ "approximation": false,
+ "readable_label": null,
+ "id": 1
+ },
+ {
+ "source": "http://www.wikidata.org/entity/Q40397:0",
+ "target": "col-4",
+ "abs_uri": "http://www.wikidata.org/prop/P279",
+ "rel_uri": "p:P279",
+ "approximation": false,
+ "readable_label": null,
+ "id": 2
+ },
+ {
+ "source": "http://www.wikidata.org/entity/Q13442814:0",
+ "target": "col-0",
+ "abs_uri": "http://www.w3.org/2000/01/rdf-schema#label",
+ "rel_uri": "rdfs:label",
+ "approximation": false,
+ "readable_label": null,
+ "id": 3
+ },
+ {
+ "source": "http://www.wikidata.org/entity/Q13442814:0",
+ "target": "col-1",
+ "abs_uri": "http://www.wikidata.org/prop/P698",
+ "rel_uri": "p:P698",
+ "approximation": false,
+ "readable_label": null,
+ "id": 7
+ },
+ {
+ "source": "http://www.wikidata.org/entity/Q13442814:0",
+ "target": "http://www.wikidata.org/entity/Q12136:0",
+ "abs_uri": "http://www.wikidata.org/prop/P921",
+ "rel_uri": "p:P921",
+ "approximation": false,
+ "readable_label": null,
+ "id": 8
+ },
+ {
+ "source": "http://www.wikidata.org/entity/Q7187:0",
+ "target": "col-3",
+ "abs_uri": "http://www.w3.org/2000/01/rdf-schema#label",
+ "rel_uri": "rdfs:label",
+ "approximation": false,
+ "readable_label": null,
+ "id": 4
+ },
+ {
+ "source": "http://www.wikidata.org/entity/Q7187:0",
+ "target": "stmt:column-3-PExpressInTissue-column-4",
+ "abs_uri": "http://www.wikidata.org/prop/P2293",
+ "rel_uri": "p:P2293",
+ "approximation": false,
+ "readable_label": null,
+ "id": 5
+ },
+ {
+ "source": "http://www.wikidata.org/entity/Q7187:0",
+ "target": "http://www.wikidata.org/entity/Q12136:0",
+ "abs_uri": "http://www.wikidata.org/prop/P2293",
+ "rel_uri": "p:P2293",
+ "approximation": false,
+ "readable_label": null,
+ "id": 9
+ },
+ {
+ "source": "stmt:column-3-PExpressInTissue-column-4",
+ "target": "http://www.wikidata.org/entity/Q40397:0",
+ "abs_uri": "http://www.wikidata.org/prop/P2293",
+ "rel_uri": "p:P2293",
+ "approximation": false,
+ "readable_label": null,
+ "id": 6
+ }
+ ]
+ }
+ ],
+ "is_curated": false,
+ "note": ""
+}
\ No newline at end of file
diff --git a/examples/novartis/ground-truth/table_01/version.03.json b/examples/novartis/ground-truth/table_01/version.03.json
new file mode 100644
index 0000000..9fd4b52
--- /dev/null
+++ b/examples/novartis/ground-truth/table_01/version.03.json
@@ -0,0 +1,140 @@
+{
+ "version": 2,
+ "table_id": "table_01",
+ "semantic_models": [
+ {
+ "version": 1,
+ "nodes": [
+ {
+ "id": "col-2",
+ "col_index": 2,
+ "label": "Dise"
+ },
+ {
+ "id": "col-4",
+ "col_index": 4,
+ "label": "Tissue"
+ },
+ {
+ "id": "http://www.wikidata.org/entity/Q12136:0",
+ "abs_uri": "http://www.wikidata.org/entity/Q12136",
+ "rel_uri": "wd:Q12136",
+ "approximation": false,
+ "readable_label": "disease (Q12136)"
+ },
+ {
+ "id": "http://www.wikidata.org/entity/Q40397:0",
+ "abs_uri": "http://www.wikidata.org/entity/Q40397",
+ "rel_uri": "wd:Q40397",
+ "approximation": false,
+ "readable_label": "tissue (Q40397)"
+ },
+ {
+ "id": "col-0",
+ "col_index": 0,
+ "label": "Description"
+ },
+ {
+ "id": "http://www.wikidata.org/entity/Q13442814:0",
+ "abs_uri": "http://www.wikidata.org/entity/Q13442814",
+ "rel_uri": "wd:Q13442814",
+ "approximation": false,
+ "readable_label": "scholarly article (Q13442814)"
+ },
+ {
+ "id": "col-3",
+ "col_index": 3,
+ "label": "Gene"
+ },
+ {
+ "id": "http://www.wikidata.org/entity/Q7187:0",
+ "abs_uri": "http://www.wikidata.org/entity/Q7187",
+ "rel_uri": "wd:Q7187",
+ "approximation": false,
+ "readable_label": "gene (Q7187)"
+ },
+ {
+ "id": "col-1",
+ "col_index": 1,
+ "label": "PUBMEDID"
+ }
+ ],
+ "edges": [
+ {
+ "source": "http://www.wikidata.org/entity/Q12136:0",
+ "target": "col-2",
+ "abs_uri": "http://www.w3.org/2000/01/rdf-schema#label",
+ "rel_uri": "rdfs:label",
+ "approximation": false,
+ "readable_label": null,
+ "id": 1
+ },
+ {
+ "source": "http://www.wikidata.org/entity/Q40397:0",
+ "target": "col-4",
+ "abs_uri": "http://www.wikidata.org/prop/P279",
+ "rel_uri": "p:P279",
+ "approximation": false,
+ "readable_label": "subclass of (P279)",
+ "id": 2
+ },
+ {
+ "source": "http://www.wikidata.org/entity/Q13442814:0",
+ "target": "col-0",
+ "abs_uri": "http://www.w3.org/2000/01/rdf-schema#label",
+ "rel_uri": "rdfs:label",
+ "approximation": false,
+ "readable_label": null,
+ "id": 3
+ },
+ {
+ "source": "http://www.wikidata.org/entity/Q13442814:0",
+ "target": "col-1",
+ "abs_uri": "http://www.wikidata.org/prop/P698",
+ "rel_uri": "p:P698",
+ "approximation": false,
+ "readable_label": "PubMed ID (P698)",
+ "id": 5
+ },
+ {
+ "source": "http://www.wikidata.org/entity/Q13442814:0",
+ "target": "http://www.wikidata.org/entity/Q12136:0",
+ "abs_uri": "http://www.wikidata.org/prop/P921",
+ "rel_uri": "p:P921",
+ "approximation": false,
+ "readable_label": "main subject (P921)",
+ "id": 6
+ },
+ {
+ "source": "http://www.wikidata.org/entity/Q7187:0",
+ "target": "col-3",
+ "abs_uri": "http://www.w3.org/2000/01/rdf-schema#label",
+ "rel_uri": "rdfs:label",
+ "approximation": false,
+ "readable_label": null,
+ "id": 4
+ },
+ {
+ "source": "http://www.wikidata.org/entity/Q7187:0",
+ "target": "http://www.wikidata.org/entity/Q12136:0",
+ "abs_uri": "http://www.wikidata.org/prop/P2293",
+ "rel_uri": "p:P2293",
+ "approximation": false,
+ "readable_label": "genetic association (P2293)",
+ "id": 7
+ },
+ {
+ "source": "http://www.wikidata.org/entity/Q7187:0",
+ "target": "http://www.wikidata.org/entity/Q40397:0",
+ "abs_uri": "http://www.wikidata.org/prop/P5572",
+ "rel_uri": "p:P5572",
+ "approximation": false,
+ "readable_label": "expressed in (P5572)",
+ "id": 7
+ }
+ ]
+ }
+ ],
+ "is_curated": false,
+ "note": ""
+}
\ No newline at end of file
diff --git a/examples/novartis/ground-truth/table_02/version.01.json b/examples/novartis/ground-truth/table_02/version.01.json
new file mode 100644
index 0000000..fc363c0
--- /dev/null
+++ b/examples/novartis/ground-truth/table_02/version.01.json
@@ -0,0 +1,126 @@
+{
+ "version": 2,
+ "table_id": "table_02",
+ "semantic_models": [
+ {
+ "version": 1,
+ "nodes": [
+ {
+ "id": "col-1",
+ "col_index": 1,
+ "label": "Drug"
+ },
+ {
+ "id": "col-2",
+ "col_index": 2,
+ "label": "Trade name"
+ },
+ {
+ "id": "col-4",
+ "col_index": 4,
+ "label": "Main indications"
+ },
+ {
+ "id": "col-5",
+ "col_index": 5,
+ "label": "Company"
+ },
+ {
+ "id": "http://www.wikidata.org/entity/Q891723:0",
+ "abs_uri": "http://www.wikidata.org/entity/Q891723",
+ "rel_uri": "wd:Q891723",
+ "approximation": false,
+ "readable_label": "public company (Q891723)"
+ },
+ {
+ "id": "http://www.wikidata.org/entity/Q28885102:0",
+ "abs_uri": "http://www.wikidata.org/entity/Q28885102",
+ "rel_uri": "wd:Q28885102",
+ "approximation": false,
+ "readable_label": "pharmaceutical product (Q28885102)"
+ },
+ {
+ "id": "http://www.wikidata.org/entity/Q12140:0",
+ "abs_uri": "http://www.wikidata.org/entity/Q12140",
+ "rel_uri": "wd:Q12140",
+ "approximation": false,
+ "readable_label": "medication (Q12140)"
+ },
+ {
+ "id": "http://www.wikidata.org/entity/Q12136:0",
+ "abs_uri": "http://www.wikidata.org/entity/Q12136",
+ "rel_uri": "wd:Q12136",
+ "approximation": false,
+ "readable_label": "disease (Q12136)"
+ }
+ ],
+ "edges": [
+ {
+ "source": "http://www.wikidata.org/entity/Q891723:0",
+ "target": "col-5",
+ "abs_uri": "http://www.w3.org/2000/01/rdf-schema#label",
+ "rel_uri": "rdfs:label",
+ "approximation": false,
+ "readable_label": null,
+ "id": 1
+ },
+ {
+ "source": "http://www.wikidata.org/entity/Q28885102:0",
+ "target": "col-2",
+ "abs_uri": "http://www.w3.org/2000/01/rdf-schema#label",
+ "rel_uri": "rdfs:label",
+ "approximation": false,
+ "readable_label": null,
+ "id": 2
+ },
+ {
+ "source": "http://www.wikidata.org/entity/Q12140:0",
+ "target": "col-1",
+ "abs_uri": "http://www.w3.org/2000/01/rdf-schema#label",
+ "rel_uri": "rdfs:label",
+ "approximation": false,
+ "readable_label": null,
+ "id": 3
+ },
+ {
+ "source": "http://www.wikidata.org/entity/Q12136:0",
+ "target": "col-4",
+ "abs_uri": "http://www.w3.org/2000/01/rdf-schema#label",
+ "rel_uri": "rdfs:label",
+ "approximation": false,
+ "readable_label": null,
+ "id": 4
+ },
+ {
+ "source": "http://www.wikidata.org/entity/Q28885102:0",
+ "target": "http://www.wikidata.org/entity/Q12140:0",
+ "abs_uri": "https://www.wikidata.org/prop/P3781",
+ "rel_uri": "p:P3781",
+ "approximation": false,
+ "readable_label": "has active ingredient (P3781)",
+ "id": 5
+ },
+ {
+ "source": "http://www.wikidata.org/entity/Q12140:0",
+ "target": "http://www.wikidata.org/entity/Q12136:0",
+ "abs_uri": "https://www.wikidata.org/prop/P2175",
+ "rel_uri": "p:P2175",
+ "approximation": false,
+ "readable_label": "medical condition treated (P2175)",
+ "id": 6
+ },
+ {
+ "source": "http://www.wikidata.org/entity/Q891723:0",
+ "target": "http://www.wikidata.org/entity/Q28885102:0",
+ "abs_uri": "https://www.wikidata.org/prop/P1056",
+ "rel_uri": "p:P1056",
+ "approximation": false,
+ "readable_label": "product or material produced (P1056)",
+ "id": 7
+ }
+ ]
+ }
+ ],
+ "is_curated": false,
+ "note": ""
+}
\ No newline at end of file
diff --git a/examples/novartis/ground-truth/table_03/version.01.json b/examples/novartis/ground-truth/table_03/version.01.json
new file mode 100644
index 0000000..44654ed
--- /dev/null
+++ b/examples/novartis/ground-truth/table_03/version.01.json
@@ -0,0 +1,82 @@
+{
+ "version": 2,
+ "table_id": "table_03",
+ "semantic_models": [
+ {
+ "version": 1,
+ "nodes": [
+ {
+ "id": "col-0",
+ "col_index": 0,
+ "label": "Disease"
+ },
+ {
+ "id": "http://www.wikidata.org/entity/Q12136:0",
+ "abs_uri": "http://www.wikidata.org/entity/Q12136",
+ "rel_uri": "wd:Q12136",
+ "approximation": false,
+ "readable_label": "disease (Q12136)"
+ },
+ {
+ "id": "col-1",
+ "col_index": 1,
+ "label": "Gene"
+ },
+ {
+ "id": "http://www.wikidata.org/entity/Q7187:0",
+ "abs_uri": "http://www.wikidata.org/entity/Q7187",
+ "rel_uri": "wd:Q7187",
+ "approximation": false,
+ "readable_label": "gene (Q7187)"
+ },
+ {
+ "id": "stmt:column-0-P2293-column-1",
+ "abs_uri": "http://wikiba.se/ontology#Statement",
+ "rel_uri": "wikibase:Statement",
+ "approximation": false,
+ "readable_label": null
+ }
+ ],
+ "edges": [
+ {
+ "source": "http://www.wikidata.org/entity/Q12136:0",
+ "target": "col-0",
+ "abs_uri": "http://www.w3.org/2000/01/rdf-schema#label",
+ "rel_uri": "rdfs:label",
+ "approximation": false,
+ "readable_label": null,
+ "id": 1
+ },
+ {
+ "source": "http://www.wikidata.org/entity/Q12136:0",
+ "target": "stmt:column-0-P2293-column-1",
+ "abs_uri": "http://www.wikidata.org/prop/P2293",
+ "rel_uri": "p:P2293",
+ "approximation": false,
+ "readable_label": "genetic association (P2293)",
+ "id": 4
+ },
+ {
+ "source": "http://www.wikidata.org/entity/Q7187:0",
+ "target": "col-1",
+ "abs_uri": "http://www.w3.org/2000/01/rdf-schema#label",
+ "rel_uri": "rdfs:label",
+ "approximation": false,
+ "readable_label": null,
+ "id": 2
+ },
+ {
+ "source": "stmt:column-0-P2293-column-1",
+ "target": "http://www.wikidata.org/entity/Q7187:0",
+ "abs_uri": "http://www.wikidata.org/prop/P2293",
+ "rel_uri": "p:P2293",
+ "approximation": false,
+ "readable_label": "genetic association (P2293)",
+ "id": 3
+ }
+ ]
+ }
+ ],
+ "is_curated": false,
+ "note": ""
+}
\ No newline at end of file
diff --git a/examples/novartis/main.ipynb b/examples/novartis/main.ipynb
new file mode 100644
index 0000000..5b2e8d6
--- /dev/null
+++ b/examples/novartis/main.ipynb
@@ -0,0 +1,333 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import glob, sys\n",
+ "\n",
+ "from omegaconf import OmegaConf\n",
+ "from tqdm.auto import tqdm\n",
+ "from grams.prelude import GRAMS, ROOT_DIR, DATA_DIR, I, O, M, WikidataSemanticModelHelper\n",
+ "sys.path.append(str(ROOT_DIR / \"sm_annotator\"))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "cwd = ROOT_DIR / \"examples/novartis\"\n",
+ "cfg = OmegaConf.load(ROOT_DIR / \"grams.yaml\")\n",
+ "grams = GRAMS(DATA_DIR, cfg)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {
+ "pycharm": {
+ "name": "#%% Run Novartis related tables and show the results\n"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "tables = [I.W2WTable.from_csv_file(infile) for infile in glob.glob(str(cwd / \"tables/*.csv\"))]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "87de641791e14d1295ff5bbf5222da59",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ " 0%| | 0/1 [00:00, ?it/s]"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "annotations = [grams.annotate(table) for table in tqdm(tables)]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {
+ "pycharm": {
+ "name": "#%% To get the performance, use the\n"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "150e5e49ef9b4b33a75c3d1dd142b0c2",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "Output()"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "65acb8cbc08f4f4f875ffd0161c432b4",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "SlowTunnelWidget(js_endpoint=(0, ''), py_endpoint=(0, ''), tunnel_id='a41c030b-87ec-4907-a86a-0d5d1d21ebce')"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "8b235dc83650412ca65dde14173d9e85",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "HTML(value='')"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "application/javascript": [
+ "\n",
+ " \n",
+ " function repeatUntilSuccess(fn, timeout, maxTry) {\n",
+ " if (fn() === true) {\n",
+ " return;\n",
+ " }\n",
+ " if (maxTry === undefined) {\n",
+ " maxTry = 10;\n",
+ " }\n",
+ " if (maxTry === 0) {\n",
+ " console.error(\"Max retries error\");\n",
+ " alert(\"max retries error\");\n",
+ " throw new Error(\"Max retries error\");\n",
+ " } else {\n",
+ " setTimeout(function () {\n",
+ " repeatUntilSuccess(fn, timeout, maxTry - 1);\n",
+ " }, timeout);\n",
+ " }\n",
+ " }\n",
+ " \n",
+ " if (window.IPyApps === undefined) {\n",
+ " window.IPyApps = new Map();\n",
+ " }\n",
+ " \n",
+ " function setupAppfa3da3bff1224bd3aeb357e7e6325633() {\n",
+ " if (window.IPyCallback === undefined) {\n",
+ " return false;\n",
+ " }\n",
+ " let tunnel = window.IPyCallback.get('a41c030b-87ec-4907-a86a-0d5d1d21ebce');\n",
+ " if (tunnel === undefined) {\n",
+ " return false;\n",
+ " }\n",
+ " \n",
+ " // use the tunnel first to send out the code, after the application is rendered, the listening function \n",
+ " // is going to be replaced by the listener in the application, so we don't have to worry.\n",
+ " tunnel.on_receive(function (version, msg) {\n",
+ " let payload = JSON.parse(msg);\n",
+ " if (payload.id !== 'get_source_code') {\n",
+ " alert('invalid calling order. you need to set the source code first');\n",
+ " console.error(\"invalid call order. waiting for source code but get:\", payload);\n",
+ " return;\n",
+ " }\n",
+ " eval(payload.response);\n",
+ " let shadowDOM = true;\n",
+ " window.Annotator.renderApp('fa3da3bf-f122-4bd3-aeb3-57e7e6325633', tunnel, undefined, shadowDOM);\n",
+ " });\n",
+ " tunnel.send_msg(JSON.stringify({ url: '/get_source_code', params: null, id: 'get_source_code' }));\n",
+ " return true;\n",
+ " }\n",
+ " repeatUntilSuccess(setupAppfa3da3bff1224bd3aeb357e7e6325633, 50, 10);\n",
+ " "
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "3590e556e36c4eb491bbd3fa161e7ddb",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "SlowTunnelWidget(js_endpoint=(0, ''), py_endpoint=(0, ''), tunnel_id='75c72064-7b37-4590-ba03-eebecd958e78')"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "application/javascript": [
+ "\n",
+ " \n",
+ " function repeatUntilSuccess(fn, timeout, maxTry) {\n",
+ " if (fn() === true) {\n",
+ " return;\n",
+ " }\n",
+ " if (maxTry === undefined) {\n",
+ " maxTry = 10;\n",
+ " }\n",
+ " if (maxTry === 0) {\n",
+ " console.error(\"Max retries error\");\n",
+ " alert(\"max retries error\");\n",
+ " throw new Error(\"Max retries error\");\n",
+ " } else {\n",
+ " setTimeout(function () {\n",
+ " repeatUntilSuccess(fn, timeout, maxTry - 1);\n",
+ " }, timeout);\n",
+ " }\n",
+ " }\n",
+ " \n",
+ " \n",
+ " function setupSliderApp() {\n",
+ " let container = window.document.getElementById('fa3da3bf-f122-4bd3-aeb3-57e7e6325633');\n",
+ " if (container === null || window.IPyCallback === undefined) {\n",
+ " return false;\n",
+ " }\n",
+ " \n",
+ " let tunnel = window.IPyCallback.get('75c72064-7b37-4590-ba03-eebecd958e78');\n",
+ " if (tunnel === undefined) {\n",
+ " return false;\n",
+ " }\n",
+ " \n",
+ " let div = window.document.createElement(\"div\");\n",
+ " div.id = 'c9b35606-38ee-42aa-b140-f2dbc06961b1';\n",
+ " div.style = \"margin-bottom: 8px\";\n",
+ " container.parentElement.prepend(div);\n",
+ " \n",
+ " // use the tunnel first to send out the code, after the application is rendered, the listening function \n",
+ " // is going to be replaced by the listener in the application, so we don't have to worry.\n",
+ " tunnel.on_receive(function (version, msg) {\n",
+ " let payload = JSON.parse(msg);\n",
+ " if (payload.id !== 'get_source_code') {\n",
+ " alert('invalid calling order. you need to set the source code first');\n",
+ " return;\n",
+ " }\n",
+ " window.eval(payload.response);\n",
+ " window.Slider.renderApp('c9b35606-38ee-42aa-b140-f2dbc06961b1', tunnel);\n",
+ " });\n",
+ " tunnel.send_msg(JSON.stringify({ url: '/get_source_code', params: null, id: 'get_source_code' }));\n",
+ " return true;\n",
+ " }\n",
+ " repeatUntilSuccess(setupSliderApp, 50, 10);\n",
+ " "
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "from sm_annotator.prelude import Annotator, BatchAnnotator, widgets, GRAMSAnnotatorAssistant, SliderApp\n",
+ "\n",
+ "qnodes = grams.qnodes.cache_dict()\n",
+ "wdprops = grams.wdprops\n",
+ "wdclasses = grams.wdclasses.cache_dict()\n",
+ "\n",
+ "assistant = GRAMSAnnotatorAssistant([\n",
+ " dict(table=table, sg=annotation.sg, dg=annotation.dg)\n",
+ " for annotation, table in zip(annotations, tables)\n",
+ "], qnodes, wdprops)\n",
+ "annotator = Annotator(qnodes, wdclasses, wdprops, cwd / \"ground-truth\", \n",
+ " eshost='http://mira.isi.edu:9200', username='', password='', dev=False, assistant=assistant)\n",
+ "batch_annotator = BatchAnnotator(annotator)\n",
+ "batch_annotator.render(same_tab=True)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "batch_annotator.batch_annotate([\n",
+ " (table.get_friendly_fs_id(), \"\", table)\n",
+ " for table in tables\n",
+ "], start_index=0)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "grams",
+ "language": "python",
+ "name": "grams"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.8.5"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/examples/novartis/tables/table_01.csv b/examples/novartis/tables/table_01.csv
new file mode 100644
index 0000000..7047e92
--- /dev/null
+++ b/examples/novartis/tables/table_01.csv
@@ -0,0 +1,5 @@
+Description,PUBMEDID,Dise,Gene,Tissue
+Identification of 19 new risk loci and potential regulatory mechanisms influencing susceptibility to testicular germ cell tumor.,28604728,Testicular germ cell tumor,AIFM3,prostate epithelium
+A genetic variant near GATA3 implicated in inherited susceptibility and etiology of benign prostatic hyperplasia (BPH) and lower urinary tract symptoms (LUTS).,28656603,Benign prostatic hyperplasia,GATA3,prostate epithelium
+"Meta-analysis of GWAS of over 16,000 individuals with autism spectrum disorder highlights a novel locus at 10q24.32 and a significant overlap with schizophrenia.",28540026,Autism spectrum disorder,52164,brain
+GATA3 is a potential target for Benign prostatic hyperplasia,,Nodular hyperplasia of prostate gland,4172,prostate epithelium
diff --git a/examples/novartis/tables/table_01.links.tsv b/examples/novartis/tables/table_01.links.tsv
new file mode 100644
index 0000000..ca13252
--- /dev/null
+++ b/examples/novartis/tables/table_01.links.tsv
@@ -0,0 +1,9 @@
+0 0 Q39182862
+0 2 Q18556865
+0 3 Q18051929
+1 0 Q40143114
+1 2 Q506659
+1 3 Q18025464
+2 0 Q33717278
+2 2 Q1436063
+2 4 Q104519508
diff --git a/examples/novartis/tables/table_02.csv b/examples/novartis/tables/table_02.csv
new file mode 100644
index 0000000..9b57add
--- /dev/null
+++ b/examples/novartis/tables/table_02.csv
@@ -0,0 +1,14 @@
+Rank,Drug,Trade name,Type,Main indications,Company,Sales ( USD millions/year),∆ vs 2014
+1,Adalimumab ,Humira,Biologic,Rheumatoid arthritis ,AbbVie Inc. ,"14,012","1,469"
+2,Ledipasvir/sofosbuvir ,Harvoni,Small molecule,Hepatitis C ,Gilead Sciences ,"13,864","11,737"
+3,Etanercept ,Enbrel,Biologic,Rheumatoid arthritis ,Amgen Pfizer ,"8,697","4,009"
+4,Infliximab ,Remicade,Biologic,"Crohn's Disease
+Rheumatoid Arthritis ",Johnson & Johnson ,"8,355","1,487"
+5,Rituximab ,Mabthera Rituxan ,Biologic,Lymphoma Leukemia Autoimmune disorders ,Roche Genentech ,"7,115","1,456"
+6,Insulin glargine ,Lantus,Biologic,Diabetes mellitus ,Sanofi ,"7,029",51
+7,Bevacizumab ,Avastin,Biologic,Metastatic cancers ,Roche Genentech ,"6,751",270
+8,Trastuzumab ,Herceptin,Biologic,Breast cancer ,Roche Genentech ,"6,603",265
+9,Lenalidomide ,Revlimid,Small molecule,Multiple myeloma Myelodysplastic syndromes ,Celgene ,"5,801",821
+10,Sofosbuvir ,Sovaldi,Small molecule,Hepatitis C ,Gilead Sciences ,"5,276","(5,007)"
+11,Fluticasone propionate/salmeterol ,Seretide Advair ,Small molecule,Asthma Chronic obstructive pulmonary disease ,GlaxoSmithKline ,"5,227",(778)
+12,Rosuvastatin ,Crestor,Small molecule,Cardiovascular diseases ,AstraZeneca ,"5,017",(495)
diff --git a/examples/novartis/tables/table_02.links.tsv b/examples/novartis/tables/table_02.links.tsv
new file mode 100644
index 0000000..5322584
--- /dev/null
+++ b/examples/novartis/tables/table_02.links.tsv
@@ -0,0 +1,44 @@
+0 1 Q348260
+0 4 Q187255
+1 1 Q19597688
+1 4 Q154869
+1 5 Q663596
+2 1 Q415343
+2 4 Q187255
+2 5 Q470517 Q206921
+3 1 Q415264
+3 4 Q223591
+3 5 Q333718
+4 1 Q412323
+4 4 Q208414 Q29496 Q8084905
+4 5 Q212646 Q899140
+5 1 Q417317
+5 5 Q158205
+6 1 Q413299
+6 4 Q12078
+6 5 Q212646 Q899140
+7 1 Q412616
+7 4 Q128581
+7 5 Q212646 Q899140
+8 1 Q425681
+8 4 Q467635 Q954625
+8 5 Q842947
+9 1 Q2502747
+9 4 Q154869
+9 5 Q663596
+10 4 Q35869 Q199804
+10 5 Q212322
+11 1 Q415159
+11 4 Q389735
+11 5 Q731938
+0 2 Q29005938
+1 2 Q29005922
+2 2 Q29005794
+3 2 Q29006415
+4 2 Q29006155 Q412323
+5 2 Q29006109
+6 2 Q413299
+7 2 Q29005931
+8 2 Q29006434
+9 2 Q29006498
+11 2 Q47521418
\ No newline at end of file
diff --git a/examples/novartis/tables/table_03.csv b/examples/novartis/tables/table_03.csv
new file mode 100644
index 0000000..07a9994
--- /dev/null
+++ b/examples/novartis/tables/table_03.csv
@@ -0,0 +1,4 @@
+Disease,Gene,Inheritance,Clinical Features
+Achondroplasia,Fibroblast growth factor receptor 3 (FGR3),"Autosomal dominant (normal parents can have an affected child due to new mutation, and risk of recurrence in subsequent children is low)","Short limbs relative to trunk, prominent forehead, low nasal root, redundant skin folds on arms and legs"
+Cystic Fibrosis,Cystic fibrosis transmembrane regulator (CFTR),"Autosomal Recessive (most common genetic disorder among Caucasians in North America)","Pancreatic insufficiency due to fibrotic lesions, obstruction of lungs due to thick mucus, lung infections (Staph, aureus, Pseud. aeruginosa)"
+Duchenne Muscular Dystrophy,Dystrophin (DMD),"X-linked recessive","Gradual degeneration of skeletal muscle, impaired heart and respiratory musculature"
diff --git a/examples/novartis/tables/table_03.links.tsv b/examples/novartis/tables/table_03.links.tsv
new file mode 100644
index 0000000..b8e256a
--- /dev/null
+++ b/examples/novartis/tables/table_03.links.tsv
@@ -0,0 +1,6 @@
+0 0 Q340594
+1 0 Q178194
+2 0 Q1648484
+0 1 Q14914358
+1 1 Q14864712
+2 1 Q14864292
diff --git a/examples/semtab2020_novartis/CPA_Round4_gt.csv b/examples/semtab2020_novartis/CPA_Round4_gt.csv
new file mode 100644
index 0000000..7ebd766
--- /dev/null
+++ b/examples/semtab2020_novartis/CPA_Round4_gt.csv
@@ -0,0 +1,128 @@
+DTWAT6QK,0,1,http://www.wikidata.org/prop/direct/P361
+DTWAT6QK,0,2,http://www.wikidata.org/prop/direct/P702
+DTWAT6QK,0,3,http://www.wikidata.org/prop/direct/P129
+ZVXL71YI,0,1,http://www.wikidata.org/prop/direct/P361
+ZVXL71YI,0,2,http://www.wikidata.org/prop/direct/P702
+ZVXL71YI,0,3,http://www.wikidata.org/prop/direct/P129
+YLSE3F5H,0,1,http://www.wikidata.org/prop/direct/P1057
+YLSE3F5H,0,2,http://www.wikidata.org/prop/direct/P2548
+AQRRDDX4,0,1,http://www.wikidata.org/prop/direct/P1057
+AQRRDDX4,0,2,http://www.wikidata.org/prop/direct/P2548
+8GXR654P,0,1,http://www.wikidata.org/prop/direct/P1057
+8GXR654P,0,2,http://www.wikidata.org/prop/direct/P2548
+L2NFO494,0,1,http://www.wikidata.org/prop/direct/P1057
+L2NFO494,0,2,http://www.wikidata.org/prop/direct/P2548
+XI5424YZ,0,1,http://www.wikidata.org/prop/direct/P1057
+XI5424YZ,0,2,http://www.wikidata.org/prop/direct/P2548
+M8ZR2FPN,0,1,http://www.wikidata.org/prop/direct/P684
+M8ZR2FPN,0,2,http://www.wikidata.org/prop/direct/P2293
+M8ZR2FPN,0,3,http://www.wikidata.org/prop/direct/P279
+4B1GJLSN,0,1,http://www.wikidata.org/prop/direct/P684
+4B1GJLSN,0,2,http://www.wikidata.org/prop/direct/P2293
+4B1GJLSN,0,3,http://www.wikidata.org/prop/direct/P279
+E02EDD05,0,1,http://www.wikidata.org/prop/direct/P684
+E02EDD05,0,2,http://www.wikidata.org/prop/direct/P2293
+E02EDD05,0,3,http://www.wikidata.org/prop/direct/P279
+N6HGUVIJ,0,1,http://www.wikidata.org/prop/direct/P684
+N6HGUVIJ,0,2,http://www.wikidata.org/prop/direct/P2293
+N6HGUVIJ,0,3,http://www.wikidata.org/prop/direct/P279
+3MQ7IT3G,0,1,http://www.wikidata.org/prop/direct/P688
+3MQ7IT3G,0,2,http://www.wikidata.org/prop/direct/P682
+WKDRXIGC,0,1,http://www.wikidata.org/prop/direct/P681
+WKDRXIGC,0,2,http://www.wikidata.org/prop/direct/P703
+PNRJ3I2M,0,1,http://www.wikidata.org/prop/direct/P1995
+PNRJ3I2M,0,2,http://www.wikidata.org/prop/direct/P1889
+WEMTI4U7,0,1,http://www.wikidata.org/prop/direct/P1995
+WEMTI4U7,0,2,http://www.wikidata.org/prop/direct/P1889
+I24JCB76,0,1,http://www.wikidata.org/prop/direct/P1995
+I24JCB76,0,2,http://www.wikidata.org/prop/direct/P1889
+MQHMLU2B,0,1,http://www.wikidata.org/prop/direct/P279
+MQHMLU2B,0,3,http://www.wikidata.org/prop/direct/P828
+MQHMLU2B,0,4,http://www.wikidata.org/prop/direct/P780
+MQHMLU2B,0,2,http://www.wikidata.org/prop/direct/P3487
+GE958EIH,0,1,http://www.wikidata.org/prop/direct/P279
+GE958EIH,0,2,http://www.wikidata.org/prop/direct/P361
+GE958EIH,0,3,http://www.wikidata.org/prop/direct/P3489
+7XVEWDHG,0,1,http://www.wikidata.org/prop/direct/P1343
+7XVEWDHG,0,2,http://www.wikidata.org/prop/direct/P527
+OF3DIONL,0,1,http://www.wikidata.org/prop/direct/P1343
+OF3DIONL,0,2,http://www.wikidata.org/prop/direct/P527
+FJ6IZAGM,0,1,http://www.wikidata.org/prop/direct/P1343
+FJ6IZAGM,0,2,http://www.wikidata.org/prop/direct/P527
+FVRBAOU9,0,2,http://www.wikidata.org/prop/direct/P131
+FVRBAOU9,0,3,http://www.wikidata.org/prop/direct/P4844
+FVRBAOU9,0,1,http://www.wikidata.org/prop/direct/P1132
+FVRBAOU9,0,4,http://www.wikidata.org/prop/direct/P582
+4RBZR30S,0,1,http://www.wikidata.org/prop/direct/P6153
+4RBZR30S,0,2,http://www.wikidata.org/prop/direct/P1050
+4RBZR30S,0,3,http://www.wikidata.org/prop/direct/P859
+MS68S6OX,0,1,http://www.wikidata.org/prop/direct/P6153
+MS68S6OX,0,2,http://www.wikidata.org/prop/direct/P1050
+MS68S6OX,0,3,http://www.wikidata.org/prop/direct/P859
+61Q74GUH,0,1,http://www.wikidata.org/prop/direct/P6153
+61Q74GUH,0,2,http://www.wikidata.org/prop/direct/P1050
+61Q74GUH,0,3,http://www.wikidata.org/prop/direct/P859
+6UREB4EJ,0,1,http://www.wikidata.org/prop/direct/P6153
+6UREB4EJ,0,2,http://www.wikidata.org/prop/direct/P1050
+6UREB4EJ,0,3,http://www.wikidata.org/prop/direct/P859
+AABG0C8A,0,1,http://www.wikidata.org/prop/direct/P2868
+AABG0C8A,0,2,http://www.wikidata.org/prop/direct/P2175
+V1MLK9TP,0,1,http://www.wikidata.org/prop/direct/P279
+V1MLK9TP,0,2,http://www.wikidata.org/prop/direct/P2293
+XIL86R61,0,1,http://www.wikidata.org/prop/direct/P1057
+XIL86R61,0,2,http://www.wikidata.org/prop/direct/P2548
+XIL86R61,0,3,http://www.wikidata.org/prop/direct/P684
+XIL86R61,0,4,http://www.wikidata.org/prop/direct/P688
+XIL86R61,0,5,http://www.wikidata.org/prop/direct/P279
+XIL86R61,0,6,http://www.wikidata.org/prop/direct/P703
+L4HZ1ZOK,0,1,http://www.wikidata.org/prop/direct/P1995
+L4HZ1ZOK,0,2,http://www.wikidata.org/prop/direct/P279
+E9DCUMM2,0,1,http://www.wikidata.org/prop/direct/P1995
+E9DCUMM2,0,2,http://www.wikidata.org/prop/direct/P780
+F40WSTOZ,0,1,http://www.wikidata.org/prop/direct/P1995
+F40WSTOZ,0,2,http://www.wikidata.org/prop/direct/P780
+HFOA410A,0,1,http://www.wikidata.org/prop/direct/P2293
+HFOA410A,0,2,http://www.wikidata.org/prop/direct/P279
+3XS3Q318,0,1,http://www.wikidata.org/prop/direct/P2293
+3XS3Q318,0,2,http://www.wikidata.org/prop/direct/P279
+MT7GAOG6,0,1,http://www.wikidata.org/prop/direct/P527
+MT7GAOG6,0,2,http://www.wikidata.org/prop/direct/P1050
+6FJO0SHP,0,1,http://www.wikidata.org/prop/direct/P527
+6FJO0SHP,0,2,http://www.wikidata.org/prop/direct/P1050
+7QUE5XLF,0,1,http://www.wikidata.org/prop/direct/P3354
+7QUE5XLF,0,2,http://www.wikidata.org/prop/direct/P3433
+7QUE5XLF,0,3,http://www.wikidata.org/prop/direct/P1057
+7QUE5XLF,0,4,http://www.wikidata.org/prop/direct/P3355
+09JYR2PD,0,1,http://www.wikidata.org/prop/direct/P279
+09JYR2PD,0,2,http://www.wikidata.org/prop/direct/P780
+O54MRTYW,0,1,http://www.wikidata.org/prop/direct/P1995
+O54MRTYW,0,2,http://www.wikidata.org/prop/direct/P2176
+OIVY0LAW,0,1,http://www.wikidata.org/prop/direct/P1995
+OIVY0LAW,0,2,http://www.wikidata.org/prop/direct/P2176
+YY51Z3LY,0,1,http://www.wikidata.org/prop/direct/P1995
+YY51Z3LY,0,2,http://www.wikidata.org/prop/direct/P2176
+BVV82N5B,0,1,http://www.wikidata.org/prop/direct/P3355
+BVV82N5B,0,2,http://www.wikidata.org/prop/direct/P3433
+BVV82N5B,0,3,http://www.wikidata.org/prop/direct/P1057
+AV1MZ4ZK,0,1,http://www.wikidata.org/prop/direct/P3355
+AV1MZ4ZK,0,2,http://www.wikidata.org/prop/direct/P3433
+AV1MZ4ZK,0,3,http://www.wikidata.org/prop/direct/P1057
+779ATGSV,0,1,http://www.wikidata.org/prop/direct/P3354
+779ATGSV,0,2,http://www.wikidata.org/prop/direct/P3433
+779ATGSV,0,3,http://www.wikidata.org/prop/direct/P3355
+779ATGSV,0,4,http://www.wikidata.org/prop/direct/P1057
+5Y26A605,0,1,http://www.wikidata.org/prop/direct/P2293
+5Y26A605,0,2,http://www.wikidata.org/prop/direct/P138
+5Y26A605,0,3,http://www.wikidata.org/prop/direct/P279
+5Y26A605,0,4,http://www.wikidata.org/prop/direct/P1995
+YZPEH2QC,0,1,http://www.wikidata.org/prop/direct/P279
+YZPEH2QC,0,2,http://www.wikidata.org/prop/direct/P1995
+YZPEH2QC,0,3,http://www.wikidata.org/prop/direct/P2293
+P7AG5V6C,0,1,http://www.wikidata.org/prop/direct/P4250
+P7AG5V6C,0,2,http://www.wikidata.org/prop/direct/P2101
+HIFECYUR,0,1,http://www.wikidata.org/prop/direct/P3487
+HIFECYUR,0,2,http://www.wikidata.org/prop/direct/P3488
+IUVV79LP,0,1,http://www.wikidata.org/prop/direct/P4250
+IUVV79LP,0,2,http://www.wikidata.org/prop/direct/P2101
+3W4Z4171,0,1,http://www.wikidata.org/prop/direct/P3487
+3W4Z4171,0,2,http://www.wikidata.org/prop/direct/P3488
diff --git a/examples/semtab2020_novartis/CPA_Round4_targets.csv b/examples/semtab2020_novartis/CPA_Round4_targets.csv
new file mode 100644
index 0000000..50ef769
--- /dev/null
+++ b/examples/semtab2020_novartis/CPA_Round4_targets.csv
@@ -0,0 +1,128 @@
+DTWAT6QK,0,1
+DTWAT6QK,0,2
+DTWAT6QK,0,3
+ZVXL71YI,0,1
+ZVXL71YI,0,2
+ZVXL71YI,0,3
+YLSE3F5H,0,1
+YLSE3F5H,0,2
+AQRRDDX4,0,1
+AQRRDDX4,0,2
+8GXR654P,0,1
+8GXR654P,0,2
+L2NFO494,0,1
+L2NFO494,0,2
+XI5424YZ,0,1
+XI5424YZ,0,2
+M8ZR2FPN,0,1
+M8ZR2FPN,0,2
+M8ZR2FPN,0,3
+4B1GJLSN,0,1
+4B1GJLSN,0,2
+4B1GJLSN,0,3
+E02EDD05,0,1
+E02EDD05,0,2
+E02EDD05,0,3
+N6HGUVIJ,0,1
+N6HGUVIJ,0,2
+N6HGUVIJ,0,3
+3MQ7IT3G,0,1
+3MQ7IT3G,0,2
+WKDRXIGC,0,1
+WKDRXIGC,0,2
+PNRJ3I2M,0,1
+PNRJ3I2M,0,2
+WEMTI4U7,0,1
+WEMTI4U7,0,2
+I24JCB76,0,1
+I24JCB76,0,2
+MQHMLU2B,0,1
+MQHMLU2B,0,3
+MQHMLU2B,0,4
+MQHMLU2B,0,2
+GE958EIH,0,1
+GE958EIH,0,2
+GE958EIH,0,3
+7XVEWDHG,0,1
+7XVEWDHG,0,2
+OF3DIONL,0,1
+OF3DIONL,0,2
+FJ6IZAGM,0,1
+FJ6IZAGM,0,2
+FVRBAOU9,0,2
+FVRBAOU9,0,3
+FVRBAOU9,0,1
+FVRBAOU9,0,4
+4RBZR30S,0,1
+4RBZR30S,0,2
+4RBZR30S,0,3
+MS68S6OX,0,1
+MS68S6OX,0,2
+MS68S6OX,0,3
+61Q74GUH,0,1
+61Q74GUH,0,2
+61Q74GUH,0,3
+6UREB4EJ,0,1
+6UREB4EJ,0,2
+6UREB4EJ,0,3
+AABG0C8A,0,1
+AABG0C8A,0,2
+V1MLK9TP,0,1
+V1MLK9TP,0,2
+XIL86R61,0,1
+XIL86R61,0,2
+XIL86R61,0,3
+XIL86R61,0,4
+XIL86R61,0,5
+XIL86R61,0,6
+L4HZ1ZOK,0,1
+L4HZ1ZOK,0,2
+E9DCUMM2,0,1
+E9DCUMM2,0,2
+F40WSTOZ,0,1
+F40WSTOZ,0,2
+HFOA410A,0,1
+HFOA410A,0,2
+3XS3Q318,0,1
+3XS3Q318,0,2
+MT7GAOG6,0,1
+MT7GAOG6,0,2
+6FJO0SHP,0,1
+6FJO0SHP,0,2
+7QUE5XLF,0,1
+7QUE5XLF,0,2
+7QUE5XLF,0,3
+7QUE5XLF,0,4
+09JYR2PD,0,1
+09JYR2PD,0,2
+O54MRTYW,0,1
+O54MRTYW,0,2
+OIVY0LAW,0,1
+OIVY0LAW,0,2
+YY51Z3LY,0,1
+YY51Z3LY,0,2
+BVV82N5B,0,1
+BVV82N5B,0,2
+BVV82N5B,0,3
+AV1MZ4ZK,0,1
+AV1MZ4ZK,0,2
+AV1MZ4ZK,0,3
+779ATGSV,0,1
+779ATGSV,0,2
+779ATGSV,0,3
+779ATGSV,0,4
+5Y26A605,0,1
+5Y26A605,0,2
+5Y26A605,0,3
+5Y26A605,0,4
+YZPEH2QC,0,1
+YZPEH2QC,0,2
+YZPEH2QC,0,3
+P7AG5V6C,0,1
+P7AG5V6C,0,2
+HIFECYUR,0,1
+HIFECYUR,0,2
+IUVV79LP,0,1
+IUVV79LP,0,2
+3W4Z4171,0,1
+3W4Z4171,0,2
diff --git a/examples/semtab2020_novartis/CTA_Round4_gt.csv b/examples/semtab2020_novartis/CTA_Round4_gt.csv
new file mode 100644
index 0000000..e2fb55f
--- /dev/null
+++ b/examples/semtab2020_novartis/CTA_Round4_gt.csv
@@ -0,0 +1,103 @@
+DTWAT6QK,0,http://www.wikidata.org/entity/Q8054
+DTWAT6QK,2,http://www.wikidata.org/entity/Q7187
+ZVXL71YI,0,http://www.wikidata.org/entity/Q8054
+ZVXL71YI,2,http://www.wikidata.org/entity/Q7187
+ZVXL71YI,3,http://www.wikidata.org/entity/Q11173
+YLSE3F5H,0,http://www.wikidata.org/entity/Q7187
+YLSE3F5H,1,http://www.wikidata.org/entity/Q37748
+AQRRDDX4,0,http://www.wikidata.org/entity/Q7187
+AQRRDDX4,1,http://www.wikidata.org/entity/Q37748
+8GXR654P,0,http://www.wikidata.org/entity/Q7187
+8GXR654P,1,http://www.wikidata.org/entity/Q37748
+L2NFO494,0,http://www.wikidata.org/entity/Q7187
+L2NFO494,1,http://www.wikidata.org/entity/Q37748
+XI5424YZ,0,http://www.wikidata.org/entity/Q7187
+XI5424YZ,1,http://www.wikidata.org/entity/Q37748
+M8ZR2FPN,0,http://www.wikidata.org/entity/Q7187
+M8ZR2FPN,1,http://www.wikidata.org/entity/Q7187
+M8ZR2FPN,2,http://www.wikidata.org/entity/Q12136
+4B1GJLSN,0,http://www.wikidata.org/entity/Q7187
+4B1GJLSN,1,http://www.wikidata.org/entity/Q7187
+4B1GJLSN,2,http://www.wikidata.org/entity/Q12136
+E02EDD05,0,http://www.wikidata.org/entity/Q7187
+E02EDD05,1,http://www.wikidata.org/entity/Q7187
+E02EDD05,2,http://www.wikidata.org/entity/Q12136
+N6HGUVIJ,0,http://www.wikidata.org/entity/Q7187
+N6HGUVIJ,1,http://www.wikidata.org/entity/Q7187
+N6HGUVIJ,2,http://www.wikidata.org/entity/Q12136
+3MQ7IT3G,0,http://www.wikidata.org/entity/Q7187
+3MQ7IT3G,2,http://www.wikidata.org/entity/Q2996394
+WKDRXIGC,0,http://www.wikidata.org/entity/Q7187
+WKDRXIGC,1,http://www.wikidata.org/entity/Q5058355
+PNRJ3I2M,0,http://www.wikidata.org/entity/Q12136
+WEMTI4U7,0,http://www.wikidata.org/entity/Q12136
+I24JCB76,0,http://www.wikidata.org/entity/Q12136
+MQHMLU2B,0,http://www.wikidata.org/entity/Q12136
+GE958EIH,0,http://www.wikidata.org/entity/Q12140
+7XVEWDHG,0,http://www.wikidata.org/entity/Q12140
+OF3DIONL,0,http://www.wikidata.org/entity/Q12140
+FJ6IZAGM,0,http://www.wikidata.org/entity/Q12140
+FJ6IZAGM,2,http://www.wikidata.org/entity/Q11344
+FVRBAOU9,0,http://www.wikidata.org/entity/Q30612
+FVRBAOU9,3,http://www.wikidata.org/entity/Q12140
+4RBZR30S,0,http://www.wikidata.org/entity/Q30612
+4RBZR30S,2,http://www.wikidata.org/entity/Q12136
+MS68S6OX,0,http://www.wikidata.org/entity/Q30612
+MS68S6OX,2,http://www.wikidata.org/entity/Q12136
+61Q74GUH,0,http://www.wikidata.org/entity/Q30612
+61Q74GUH,2,http://www.wikidata.org/entity/Q12136
+6UREB4EJ,0,http://www.wikidata.org/entity/Q30612
+6UREB4EJ,2,http://www.wikidata.org/entity/Q12136
+AABG0C8A,0,http://www.wikidata.org/entity/Q134856
+AABG0C8A,2,http://www.wikidata.org/entity/Q12136
+V1MLK9TP,0,http://www.wikidata.org/entity/Q200779
+V1MLK9TP,2,http://www.wikidata.org/entity/Q7187
+XIL86R61,0,http://www.wikidata.org/entity/Q277338
+XIL86R61,1,http://www.wikidata.org/entity/Q37748
+XIL86R61,4,http://www.wikidata.org/entity/Q8054
+XIL86R61,6,http://www.wikidata.org/entity/Q16521
+L4HZ1ZOK,0,http://www.wikidata.org/entity/Q708176
+L4HZ1ZOK,1,http://www.wikidata.org/entity/Q930752
+E9DCUMM2,0,http://www.wikidata.org/entity/Q929833
+E9DCUMM2,1,http://www.wikidata.org/entity/Q930752
+F40WSTOZ,0,http://www.wikidata.org/entity/Q929833
+HFOA410A,0,http://www.wikidata.org/entity/Q3311537
+HFOA410A,1,http://www.wikidata.org/entity/Q7187
+3XS3Q318,0,http://www.wikidata.org/entity/Q3311537
+3XS3Q318,1,http://www.wikidata.org/entity/Q7187
+MT7GAOG6,0,http://www.wikidata.org/entity/Q4915012
+MT7GAOG6,2,http://www.wikidata.org/entity/Q12136
+6FJO0SHP,0,http://www.wikidata.org/entity/Q4915012
+6FJO0SHP,2,http://www.wikidata.org/entity/Q12136
+7QUE5XLF,0,http://www.wikidata.org/entity/Q15304597
+7QUE5XLF,2,http://www.wikidata.org/entity/Q7187
+7QUE5XLF,3,http://www.wikidata.org/entity/Q37748
+09JYR2PD,0,http://www.wikidata.org/entity/Q18123741
+O54MRTYW,0,http://www.wikidata.org/entity/Q18123741
+O54MRTYW,1,http://www.wikidata.org/entity/Q930752
+OIVY0LAW,0,http://www.wikidata.org/entity/Q18123741
+OIVY0LAW,1,http://www.wikidata.org/entity/Q930752
+YY51Z3LY,0,http://www.wikidata.org/entity/Q18123741
+YY51Z3LY,1,http://www.wikidata.org/entity/Q930752
+BVV82N5B,0,http://www.wikidata.org/entity/Q27429979
+BVV82N5B,2,http://www.wikidata.org/entity/Q7187
+BVV82N5B,3,http://www.wikidata.org/entity/Q37748
+AV1MZ4ZK,0,http://www.wikidata.org/entity/Q27429979
+AV1MZ4ZK,1,http://www.wikidata.org/entity/Q11173
+AV1MZ4ZK,2,http://www.wikidata.org/entity/Q7187
+AV1MZ4ZK,3,http://www.wikidata.org/entity/Q37748
+779ATGSV,0,http://www.wikidata.org/entity/Q28419128
+779ATGSV,1,http://www.wikidata.org/entity/Q11173
+779ATGSV,2,http://www.wikidata.org/entity/Q7187
+779ATGSV,3,http://www.wikidata.org/entity/Q11173
+779ATGSV,4,http://www.wikidata.org/entity/Q37748
+5Y26A605,0,http://www.wikidata.org/entity/Q42303753
+5Y26A605,1,http://www.wikidata.org/entity/Q7187
+5Y26A605,4,http://www.wikidata.org/entity/Q930752
+YZPEH2QC,0,http://www.wikidata.org/entity/Q55789477
+YZPEH2QC,2,http://www.wikidata.org/entity/Q930752
+YZPEH2QC,3,http://www.wikidata.org/entity/Q7187
+P7AG5V6C,0,http://www.wikidata.org/entity/Q12140
+HIFECYUR,0,http://www.wikidata.org/entity/Q18123741
+IUVV79LP,0,http://www.wikidata.org/entity/Q12140
+3W4Z4171,0,http://www.wikidata.org/entity/Q18123741
diff --git a/examples/semtab2020_novartis/CTA_Round4_targets.csv b/examples/semtab2020_novartis/CTA_Round4_targets.csv
new file mode 100644
index 0000000..a7aa0b0
--- /dev/null
+++ b/examples/semtab2020_novartis/CTA_Round4_targets.csv
@@ -0,0 +1,103 @@
+DTWAT6QK,0
+DTWAT6QK,2
+ZVXL71YI,0
+ZVXL71YI,2
+ZVXL71YI,3
+YLSE3F5H,0
+YLSE3F5H,1
+AQRRDDX4,0
+AQRRDDX4,1
+8GXR654P,0
+8GXR654P,1
+L2NFO494,0
+L2NFO494,1
+XI5424YZ,0
+XI5424YZ,1
+M8ZR2FPN,0
+M8ZR2FPN,1
+M8ZR2FPN,2
+4B1GJLSN,0
+4B1GJLSN,1
+4B1GJLSN,2
+E02EDD05,0
+E02EDD05,1
+E02EDD05,2
+N6HGUVIJ,0
+N6HGUVIJ,1
+N6HGUVIJ,2
+3MQ7IT3G,0
+3MQ7IT3G,2
+WKDRXIGC,0
+WKDRXIGC,1
+PNRJ3I2M,0
+WEMTI4U7,0
+I24JCB76,0
+MQHMLU2B,0
+GE958EIH,0
+7XVEWDHG,0
+OF3DIONL,0
+FJ6IZAGM,0
+FJ6IZAGM,2
+FVRBAOU9,0
+FVRBAOU9,3
+4RBZR30S,0
+4RBZR30S,2
+MS68S6OX,0
+MS68S6OX,2
+61Q74GUH,0
+61Q74GUH,2
+6UREB4EJ,0
+6UREB4EJ,2
+AABG0C8A,0
+AABG0C8A,2
+V1MLK9TP,0
+V1MLK9TP,2
+XIL86R61,0
+XIL86R61,1
+XIL86R61,4
+XIL86R61,6
+L4HZ1ZOK,0
+L4HZ1ZOK,1
+E9DCUMM2,0
+E9DCUMM2,1
+F40WSTOZ,0
+HFOA410A,0
+HFOA410A,1
+3XS3Q318,0
+3XS3Q318,1
+MT7GAOG6,0
+MT7GAOG6,2
+6FJO0SHP,0
+6FJO0SHP,2
+7QUE5XLF,0
+7QUE5XLF,2
+7QUE5XLF,3
+09JYR2PD,0
+O54MRTYW,0
+O54MRTYW,1
+OIVY0LAW,0
+OIVY0LAW,1
+YY51Z3LY,0
+YY51Z3LY,1
+BVV82N5B,0
+BVV82N5B,2
+BVV82N5B,3
+AV1MZ4ZK,0
+AV1MZ4ZK,1
+AV1MZ4ZK,2
+AV1MZ4ZK,3
+779ATGSV,0
+779ATGSV,1
+779ATGSV,2
+779ATGSV,3
+779ATGSV,4
+5Y26A605,0
+5Y26A605,1
+5Y26A605,4
+YZPEH2QC,0
+YZPEH2QC,2
+YZPEH2QC,3
+P7AG5V6C,0
+HIFECYUR,0
+IUVV79LP,0
+3W4Z4171,0
diff --git a/examples/semtab2020_novartis/main.ipynb b/examples/semtab2020_novartis/main.ipynb
new file mode 100644
index 0000000..9ce2f31
--- /dev/null
+++ b/examples/semtab2020_novartis/main.ipynb
@@ -0,0 +1,338 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import glob, sys\n",
+ "\n",
+ "from omegaconf import OmegaConf\n",
+ "from tqdm.auto import tqdm\n",
+ "from grams.prelude import GRAMS, ROOT_DIR, DATA_DIR, I, O, M, WikidataSemanticModelHelper\n",
+ "sys.path.append(str(ROOT_DIR / \"sm_annotator\"))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "cwd = ROOT_DIR / \"examples/semtab2020_novartis\"\n",
+ "cfg = OmegaConf.load(ROOT_DIR / \"grams.yaml\")\n",
+ "grams = GRAMS(DATA_DIR, cfg)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "pycharm": {
+ "name": "#%% Run Novartis related tables and show the results\n"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "gt = [\n",
+ " ([O.SemanticModel.from_json(sm) for sm in r['semantic_models']], I.W2WTable.from_json(r['table']))\n",
+ " for r in [M.deserialize_json(infile) for infile in glob.glob(str(cwd / \"tables/*.json\"))]\n",
+ "]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "5518e077a08541b3851cfef787a9a37d",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ " 0%| | 0/50 [00:00, ?it/s]"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "annotations = [grams.annotate(table) for sms, table in tqdm(gt)]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {
+ "pycharm": {
+ "name": "#%% To get the performance, use the\n"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "fd7da42a63924f35b2fc286a84d50ec2",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "Output()"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "72d5a3e69288437e87dd7f4eb5c1ee36",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "SlowTunnelWidget(js_endpoint=(0, ''), py_endpoint=(0, ''), tunnel_id='97243f34-8de4-41f4-9598-edf04b5b9e68')"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "b9647aa2133f444fafe270712bb04d15",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "HTML(value='')"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "application/javascript": [
+ "\n",
+ " \n",
+ " function repeatUntilSuccess(fn, timeout, maxTry) {\n",
+ " if (fn() === true) {\n",
+ " return;\n",
+ " }\n",
+ " if (maxTry === undefined) {\n",
+ " maxTry = 10;\n",
+ " }\n",
+ " if (maxTry === 0) {\n",
+ " console.error(\"Max retries error\");\n",
+ " alert(\"max retries error\");\n",
+ " throw new Error(\"Max retries error\");\n",
+ " } else {\n",
+ " setTimeout(function () {\n",
+ " repeatUntilSuccess(fn, timeout, maxTry - 1);\n",
+ " }, timeout);\n",
+ " }\n",
+ " }\n",
+ " \n",
+ " if (window.IPyApps === undefined) {\n",
+ " window.IPyApps = new Map();\n",
+ " }\n",
+ " \n",
+ " function setupApp01386d9505984c01a3385d6d9a9fd93b() {\n",
+ " if (window.IPyCallback === undefined) {\n",
+ " return false;\n",
+ " }\n",
+ " let tunnel = window.IPyCallback.get('97243f34-8de4-41f4-9598-edf04b5b9e68');\n",
+ " if (tunnel === undefined) {\n",
+ " return false;\n",
+ " }\n",
+ " \n",
+ " // use the tunnel first to send out the code, after the application is rendered, the listening function \n",
+ " // is going to be replaced by the listener in the application, so we don't have to worry.\n",
+ " tunnel.on_receive(function (version, msg) {\n",
+ " let payload = JSON.parse(msg);\n",
+ " if (payload.id !== 'get_source_code') {\n",
+ " alert('invalid calling order. you need to set the source code first');\n",
+ " console.error(\"invalid call order. waiting for source code but get:\", payload);\n",
+ " return;\n",
+ " }\n",
+ " eval(payload.response);\n",
+ " let shadowDOM = true;\n",
+ " window.Annotator.renderDevVizSemModelApp('01386d95-0598-4c01-a338-5d6d9a9fd93b', tunnel, undefined, shadowDOM);\n",
+ " });\n",
+ " tunnel.send_msg(JSON.stringify({ url: '/get_source_code', params: null, id: 'get_source_code' }));\n",
+ " return true;\n",
+ " }\n",
+ " repeatUntilSuccess(setupApp01386d9505984c01a3385d6d9a9fd93b, 50, 10);\n",
+ " "
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "92cc6fb942224166b6b3ea57a40b1826",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "SlowTunnelWidget(js_endpoint=(0, ''), py_endpoint=(0, ''), tunnel_id='d5a9f1b2-0353-4a34-bc06-2bf5c512d893')"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "application/javascript": [
+ "\n",
+ " \n",
+ " function repeatUntilSuccess(fn, timeout, maxTry) {\n",
+ " if (fn() === true) {\n",
+ " return;\n",
+ " }\n",
+ " if (maxTry === undefined) {\n",
+ " maxTry = 10;\n",
+ " }\n",
+ " if (maxTry === 0) {\n",
+ " console.error(\"Max retries error\");\n",
+ " alert(\"max retries error\");\n",
+ " throw new Error(\"Max retries error\");\n",
+ " } else {\n",
+ " setTimeout(function () {\n",
+ " repeatUntilSuccess(fn, timeout, maxTry - 1);\n",
+ " }, timeout);\n",
+ " }\n",
+ " }\n",
+ " \n",
+ " \n",
+ " function setupSliderApp() {\n",
+ " let container = window.document.getElementById('01386d95-0598-4c01-a338-5d6d9a9fd93b');\n",
+ " if (container === null || window.IPyCallback === undefined) {\n",
+ " return false;\n",
+ " }\n",
+ " \n",
+ " let tunnel = window.IPyCallback.get('d5a9f1b2-0353-4a34-bc06-2bf5c512d893');\n",
+ " if (tunnel === undefined) {\n",
+ " return false;\n",
+ " }\n",
+ " \n",
+ " let div = window.document.createElement(\"div\");\n",
+ " div.id = 'dc56c274-6dc0-4803-b86b-536c47455ff3';\n",
+ " div.style = \"margin-bottom: 8px\";\n",
+ " container.parentElement.prepend(div);\n",
+ " \n",
+ " // use the tunnel first to send out the code, after the application is rendered, the listening function \n",
+ " // is going to be replaced by the listener in the application, so we don't have to worry.\n",
+ " tunnel.on_receive(function (version, msg) {\n",
+ " let payload = JSON.parse(msg);\n",
+ " if (payload.id !== 'get_source_code') {\n",
+ " alert('invalid calling order. you need to set the source code first');\n",
+ " return;\n",
+ " }\n",
+ " window.eval(payload.response);\n",
+ " window.Slider.renderApp('dc56c274-6dc0-4803-b86b-536c47455ff3', tunnel);\n",
+ " });\n",
+ " tunnel.send_msg(JSON.stringify({ url: '/get_source_code', params: null, id: 'get_source_code' }));\n",
+ " return true;\n",
+ " }\n",
+ " repeatUntilSuccess(setupSliderApp, 50, 10);\n",
+ " "
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 13,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "from sm_annotator.prelude import Annotator, SMVisualizer, widgets, GRAMSAnnotatorAssistant, SliderApp\n",
+ "\n",
+ "qnodes = grams.qnodes.cache_dict()\n",
+ "wdprops = grams.wdprops\n",
+ "wdclasses = grams.wdclasses.cache_dict()\n",
+ "\n",
+ "assistant = GRAMSAnnotatorAssistant([\n",
+ " dict(table=table, sg=annotation.sg, dg=annotation.dg)\n",
+ " for annotation, (sms, table) in zip(annotations, gt)\n",
+ "], qnodes, wdprops)\n",
+ "\n",
+ "sm_visualizer_inputs_slider = [\n",
+ " dict(description='', args=(table.id, '', WikidataSemanticModelHelper.minify_sm(sms[0]), WikidataSemanticModelHelper.minify_sm(annotation.sm), table, True)) \n",
+ " for annotation, (sms, table) in zip(annotations, gt)\n",
+ "]\n",
+ "sm_visualizer = SMVisualizer(qnodes, wdclasses, wdprops, cwd / \"ground-truth\", \n",
+ " eshost='', username='', password='', dev=False, assistant=assistant)\n",
+ "slider = SliderApp(sm_visualizer, sm_visualizer.visualize)\n",
+ "slider.render(same_tab=True)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "slider.set_data(sm_visualizer_inputs_slider)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "grams",
+ "language": "python",
+ "name": "grams"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.8.5"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/examples/semtab2020_novartis/tables/09JYR2PD.csv b/examples/semtab2020_novartis/tables/09JYR2PD.csv
new file mode 100644
index 0000000..51ef492
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/09JYR2PD.csv
@@ -0,0 +1,21 @@
+col0,col1,col2
+loiasis,filariasis,pain
+trichuriasis,parasitic helminthiasis infectious disease,diarrhea
+cryptosporidiosis,intestinal parasite infection,fever
+croup,respiratory disease,cough
+enterobiasis,parasitic helminthiasis infectious disease,irritability
+Argentine hemorrhagic fever,viral infectious disease,headache
+babesiosis,parasitic protozoa infectious disease,headache
+trichomonas cervicitis,trichomoniasis,Strawberry cervix
+chikungunya,neglected tropical disease,fever
+Mycoplasma pneumoniae infection,atypical pneumonia,headache
+herpes zoster,viral infectious disease,headache
+abscess,suppuration,pain
+scarlet fever,streptococcal infection,fever
+shigellosis,bacterial infectious disease,fever
+scabies,acariasis,fever
+brucellosis,zoonosis,fatigue
+dracunculiasis,nematode infection,vomiting
+fasciolosis,parasitic helminthiasis infectious disease,anemia
+Japanese encephalitis,brain diseases,headache
+campylobacteriosis,gastroenteritis,headache
diff --git a/examples/semtab2020_novartis/tables/09JYR2PD.json b/examples/semtab2020_novartis/tables/09JYR2PD.json
new file mode 100644
index 0000000..77e4f12
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/09JYR2PD.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["loiasis","trichuriasis","cryptosporidiosis","croup","enterobiasis","Argentine hemorrhagic fever","babesiosis","trichomonas cervicitis","chikungunya","Mycoplasma pneumoniae infection","herpes zoster","abscess","scarlet fever","shigellosis","scabies","brucellosis","dracunculiasis","fasciolosis","Japanese encephalitis","campylobacteriosis"]},{"index":1,"name":"col1","values":["filariasis","parasitic helminthiasis infectious disease","intestinal parasite infection","respiratory disease","parasitic helminthiasis infectious disease","viral infectious disease","parasitic protozoa infectious disease","trichomoniasis","neglected tropical disease","atypical pneumonia","viral infectious disease","suppuration","streptococcal infection","bacterial infectious disease","acariasis","zoonosis","nematode infection","parasitic helminthiasis infectious disease","brain diseases","gastroenteritis"]},{"index":2,"name":"col2","values":["pain","diarrhea","fever","cough","irritability","headache","headache","Strawberry cervix","fever","headache","headache","pain","fever","fever","fever","fatigue","vomiting","anemia","headache","headache"]}],"metadata":{"table_id":"09JYR2PD","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q1760607","qnode_id":"Q1760607"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q815753","qnode_id":"Q815753"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q81938","qnode_id":"Q81938"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q2264130","qnode_id":"Q2264130"}],[{"start":0,"end":42,"url":"http://www.wikidata.org/entity/Q578994","qnode_id":"Q578994"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q40878","qnode_id":"Q40878"}]],[[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q1359898","qnode_id":"Q1359898"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q475510","qnode_id":"Q475510"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q38933","qnode_id":"Q38933"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q836356","qnode_id":"Q836356"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q3286546","qnode_id":"Q3286546"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q35805","qnode_id":"Q35805"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q1992236","qnode_id":"Q1992236"}],[{"start":0,"end":42,"url":"http://www.wikidata.org/entity/Q578994","qnode_id":"Q578994"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q29299","qnode_id":"Q29299"}]],[[{"start":0,"end":27,"url":"http://www.wikidata.org/entity/Q2583514","qnode_id":"Q2583514"}],[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q1928978","qnode_id":"Q1928978"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q86","qnode_id":"Q86"}]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q797668","qnode_id":"Q797668"}],[{"start":0,"end":37,"url":"http://www.wikidata.org/entity/Q353172","qnode_id":"Q353172"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q86","qnode_id":"Q86"}]],[[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q55790882","qnode_id":"Q55790882"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q745865","qnode_id":"Q745865"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q7622511","qnode_id":"Q7622511"}]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q243257","qnode_id":"Q243257"}],[{"start":0,"end":26,"url":"http://www.wikidata.org/entity/Q929451","qnode_id":"Q929451"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q38933","qnode_id":"Q38933"}]],[[{"start":0,"end":31,"url":"http://www.wikidata.org/entity/Q79054860","qnode_id":"Q79054860"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q2633267","qnode_id":"Q2633267"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q86","qnode_id":"Q86"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q182155","qnode_id":"Q182155"}],[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q1928978","qnode_id":"Q1928978"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q86","qnode_id":"Q86"}]],[[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q164655","qnode_id":"Q164655"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q3504924","qnode_id":"Q3504924"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q81938","qnode_id":"Q81938"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q180266","qnode_id":"Q180266"}],[],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q38933","qnode_id":"Q38933"}]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q327298","qnode_id":"Q327298"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q727028","qnode_id":"Q727028"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q38933","qnode_id":"Q38933"}]],[[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q167178","qnode_id":"Q167178"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q337998","qnode_id":"Q337998"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q38933","qnode_id":"Q38933"}]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q156050","qnode_id":"Q156050"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q182672","qnode_id":"Q182672"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q9690","qnode_id":"Q9690"}]],[[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q388646","qnode_id":"Q388646"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q2072680","qnode_id":"Q2072680"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q127076","qnode_id":"Q127076"}]],[[],[{"start":0,"end":42,"url":"http://www.wikidata.org/entity/Q578994","qnode_id":"Q578994"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q5445","qnode_id":"Q5445"}]],[[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q738292","qnode_id":"Q738292"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q576349","qnode_id":"Q576349"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q86","qnode_id":"Q86"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q683861","qnode_id":"Q683861"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q156103","qnode_id":"Q156103"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q86","qnode_id":"Q86"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q18123741:0","abs_uri":"http://www.wikidata.org/entity/Q18123741","rel_uri":"wd:Q18123741","approximation":false,"readable_label":"infectious disease (Q18123741)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"col-2","col_index":2,"label":"col2"}],"edges":[{"source":"http://www.wikidata.org/entity/Q18123741:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q18123741:0","target":"col-1","abs_uri":"http://www.wikidata.org/prop/P279","rel_uri":"p:P279","approximation":false,"readable_label":"subclass of (P279)","id":2},{"source":"http://www.wikidata.org/entity/Q18123741:0","target":"col-2","abs_uri":"http://www.wikidata.org/prop/P780","rel_uri":"p:P780","approximation":false,"readable_label":"symptoms (P780)","id":3}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/09JYR2PD.links.tsv b/examples/semtab2020_novartis/tables/09JYR2PD.links.tsv
new file mode 100644
index 0000000..380ddb8
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/09JYR2PD.links.tsv
@@ -0,0 +1,58 @@
+0 0 Q1760607
+0 1 Q815753
+0 2 Q81938
+1 0 Q2264130
+1 1 Q578994
+1 2 Q40878
+2 0 Q1359898
+2 1 Q475510
+2 2 Q38933
+3 0 Q836356
+3 1 Q3286546
+3 2 Q35805
+4 0 Q1992236
+4 1 Q578994
+4 2 Q29299
+5 0 Q2583514
+5 1 Q1928978
+5 2 Q86
+6 0 Q797668
+6 1 Q353172
+6 2 Q86
+7 0 Q55790882
+7 1 Q745865
+7 2 Q7622511
+8 0 Q243257
+8 1 Q929451
+8 2 Q38933
+9 0 Q79054860
+9 1 Q2633267
+9 2 Q86
+10 0 Q182155
+10 1 Q1928978
+10 2 Q86
+11 0 Q164655
+11 1 Q3504924
+11 2 Q81938
+12 0 Q180266
+12 2 Q38933
+13 0 Q327298
+13 1 Q727028
+13 2 Q38933
+14 0 Q167178
+14 1 Q337998
+14 2 Q38933
+15 0 Q156050
+15 1 Q182672
+15 2 Q9690
+16 0 Q388646
+16 1 Q2072680
+16 2 Q127076
+17 1 Q578994
+17 2 Q5445
+18 0 Q738292
+18 1 Q576349
+18 2 Q86
+19 0 Q683861
+19 1 Q156103
+19 2 Q86
diff --git a/examples/semtab2020_novartis/tables/3MQ7IT3G.csv b/examples/semtab2020_novartis/tables/3MQ7IT3G.csv
new file mode 100644
index 0000000..bb456f3
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/3MQ7IT3G.csv
@@ -0,0 +1,21 @@
+col0,col1,col2
+hypothetical protein CPn0012,Hypothetical protein CPn0012,calmodulin dependent kinase signaling pathway
+SIRT5,Sirtuin 5,protein deacetylation
+SIRT3,Sirtuin 3,protein deacetylation
+SIRT2,Sirtuin 2,protein deacetylation
+APOC3,Apolipoprotein C3,transport
+APOC4,Apolipoprotein C4,transport
+ANXA1,Annexin A1,phagocytosis
+GHRL,obestatin,decidualization
+APOC2,Apolipoprotein C2,transport
+APOC1,Apolipoprotein C1,transport
+APOD,Apolipoprotein D,aging
+FURIN,"Furin, paired basic amino acid cleaving enzyme",proteolysis
+LPL,Lipoprotein lipase,lipid metabolism
+INS,proinsulin,positive regulation of cell differentiation
+APOH,Apolipoprotein H,plasminogen activation
+LCAT,Lecithin-cholesterol acyltransferase,lipid metabolism
+APOB,Apolipoprotein B,spermatogenesis
+POMC,corticotropin,signal transduction
+GCG,glucagon,cellular proliferation
+AGT,Angiotensinogen,vasodilation
diff --git a/examples/semtab2020_novartis/tables/3MQ7IT3G.json b/examples/semtab2020_novartis/tables/3MQ7IT3G.json
new file mode 100644
index 0000000..87d6ff4
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/3MQ7IT3G.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["hypothetical protein CPn0012","SIRT5","SIRT3","SIRT2","APOC3","APOC4","ANXA1","GHRL","APOC2","APOC1","APOD","FURIN","LPL","INS","APOH","LCAT","APOB","POMC","GCG","AGT"]},{"index":1,"name":"col1","values":["Hypothetical protein CPn0012","Sirtuin 5","Sirtuin 3","Sirtuin 2","Apolipoprotein C3","Apolipoprotein C4","Annexin A1","obestatin","Apolipoprotein C2","Apolipoprotein C1","Apolipoprotein D","Furin, paired basic amino acid cleaving enzyme","Lipoprotein lipase","proinsulin","Apolipoprotein H","Lecithin-cholesterol acyltransferase","Apolipoprotein B","corticotropin","glucagon","Angiotensinogen"]},{"index":2,"name":"col2","values":["calmodulin dependent kinase signaling pathway","protein deacetylation","protein deacetylation","protein deacetylation","transport","transport","phagocytosis","decidualization","transport","transport","aging","proteolysis","lipid metabolism","positive regulation of cell differentiation","plasminogen activation","lipid metabolism","spermatogenesis","signal transduction","cellular proliferation","vasodilation"]}],"metadata":{"table_id":"3MQ7IT3G","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q23328780","qnode_id":"Q23328780"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q23601229","qnode_id":"Q23601229"}],[{"start":0,"end":45,"url":"http://www.wikidata.org/entity/Q26249991","qnode_id":"Q26249991"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18037156","qnode_id":"Q18037156"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q21125222","qnode_id":"Q21125222"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q14914414","qnode_id":"Q14914414"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18037158","qnode_id":"Q18037158"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q21116855","qnode_id":"Q21116855"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q14914414","qnode_id":"Q14914414"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18036605","qnode_id":"Q18036605"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q21108304","qnode_id":"Q21108304"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q14914414","qnode_id":"Q14914414"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14910515","qnode_id":"Q14910515"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q4068038","qnode_id":"Q4068038"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q1435575","qnode_id":"Q1435575"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14910792","qnode_id":"Q14910792"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q4068037","qnode_id":"Q4068037"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q1435575","qnode_id":"Q1435575"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14908222","qnode_id":"Q14908222"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q4769148","qnode_id":"Q4769148"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q184726","qnode_id":"Q184726"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q21163357","qnode_id":"Q21163357"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q905680","qnode_id":"Q905680"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q1207365","qnode_id":"Q1207365"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14916272","qnode_id":"Q14916272"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q3445743","qnode_id":"Q3445743"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q1435575","qnode_id":"Q1435575"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14904550","qnode_id":"Q14904550"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q1977297","qnode_id":"Q1977297"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q1435575","qnode_id":"Q1435575"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18247781","qnode_id":"Q18247781"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q798252","qnode_id":"Q798252"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q332154","qnode_id":"Q332154"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14916427","qnode_id":"Q14916427"}],[{"start":0,"end":46,"url":"http://www.wikidata.org/entity/Q581324","qnode_id":"Q581324"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q33123","qnode_id":"Q33123"}]],[[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q14864755","qnode_id":"Q14864755"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q420014","qnode_id":"Q420014"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q2792936","qnode_id":"Q2792936"}]],[[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q21163221","qnode_id":"Q21163221"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q426173","qnode_id":"Q426173"}],[{"start":0,"end":43,"url":"http://www.wikidata.org/entity/Q14758939","qnode_id":"Q14758939"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14911006","qnode_id":"Q14911006"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q648315","qnode_id":"Q648315"}],[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q14599493","qnode_id":"Q14599493"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14905444","qnode_id":"Q14905444"}],[{"start":0,"end":36,"url":"http://www.wikidata.org/entity/Q424716","qnode_id":"Q424716"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q2792936","qnode_id":"Q2792936"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14890615","qnode_id":"Q14890615"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q648343","qnode_id":"Q648343"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q36961","qnode_id":"Q36961"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14819793","qnode_id":"Q14819793"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q185690","qnode_id":"Q185690"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q828130","qnode_id":"Q828130"}]],[[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q14821391","qnode_id":"Q14821391"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q170617","qnode_id":"Q170617"}],[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q189101","qnode_id":"Q189101"}]],[[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q14859647","qnode_id":"Q14859647"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q267200","qnode_id":"Q267200"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q14859627","qnode_id":"Q14859627"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/entity/Q7187","rel_uri":"wd:Q7187","approximation":false,"readable_label":"gene (Q7187)"},{"id":"col-2","col_index":2,"label":"col2"},{"id":"http://www.wikidata.org/entity/Q2996394:0","abs_uri":"http://www.wikidata.org/entity/Q2996394","rel_uri":"wd:Q2996394","approximation":false,"readable_label":"biological process (Q2996394)"},{"id":"col-1","col_index":1,"label":"col1"}],"edges":[{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-1","abs_uri":"http://www.wikidata.org/prop/P688","rel_uri":"p:P688","approximation":false,"readable_label":"encodes (P688)","id":2},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"http://www.wikidata.org/entity/Q2996394:0","abs_uri":"http://www.wikidata.org/prop/P682","rel_uri":"p:P682","approximation":false,"readable_label":"biological process (P682)","id":3},{"source":"http://www.wikidata.org/entity/Q2996394:0","target":"col-2","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":4}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/3MQ7IT3G.links.tsv b/examples/semtab2020_novartis/tables/3MQ7IT3G.links.tsv
new file mode 100644
index 0000000..7298603
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/3MQ7IT3G.links.tsv
@@ -0,0 +1,60 @@
+0 0 Q23328780
+0 1 Q23601229
+0 2 Q26249991
+1 0 Q18037156
+1 1 Q21125222
+1 2 Q14914414
+2 0 Q18037158
+2 1 Q21116855
+2 2 Q14914414
+3 0 Q18036605
+3 1 Q21108304
+3 2 Q14914414
+4 0 Q14910515
+4 1 Q4068038
+4 2 Q1435575
+5 0 Q14910792
+5 1 Q4068037
+5 2 Q1435575
+6 0 Q14908222
+6 1 Q4769148
+6 2 Q184726
+7 0 Q21163357
+7 1 Q905680
+7 2 Q1207365
+8 0 Q14916272
+8 1 Q3445743
+8 2 Q1435575
+9 0 Q14904550
+9 1 Q1977297
+9 2 Q1435575
+10 0 Q18247781
+10 1 Q798252
+10 2 Q332154
+11 0 Q14916427
+11 1 Q581324
+11 2 Q33123
+12 0 Q14864755
+12 1 Q420014
+12 2 Q2792936
+13 0 Q21163221
+13 1 Q426173
+13 2 Q14758939
+14 0 Q14911006
+14 1 Q648315
+14 2 Q14599493
+15 0 Q14905444
+15 1 Q424716
+15 2 Q2792936
+16 0 Q14890615
+16 1 Q648343
+16 2 Q36961
+17 0 Q14819793
+17 1 Q185690
+17 2 Q828130
+18 0 Q14821391
+18 1 Q170617
+18 2 Q189101
+19 0 Q14859647
+19 1 Q267200
+19 2 Q14859627
diff --git a/examples/semtab2020_novartis/tables/3W4Z4171.csv b/examples/semtab2020_novartis/tables/3W4Z4171.csv
new file mode 100644
index 0000000..435c6af
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/3W4Z4171.csv
@@ -0,0 +1,21 @@
+col0,col1,col2
+Mycoplasma neumoniae infection,27.804,6.937
+ehrlichiosiv,2.0,1.0
+ryptosporidiosis,10.0,2.018
+ascasiasis,3.0060000000000002,1.01
+babesiosks,4.016,1.01
+erthema infectiosum,17.874,9.9
+Japnese encephalitis,15.075,4.95
+camylobacteriosis,7.0,1.008
+shgellosis,3.964,0.997
+tck-borne encephalitis,14.097999999999999,7.056
+salmonelloss,13.972,5.97
+chikugunya,12.048,2.988
+infectious mononuclersis,4.0,4.016
+scaroet fever,5.015,1.98
+orniuhosis,18.829,4.975
+lithriosis,67.335,0.996
+hepatiws A,48.0,15.06
+buucellosis,4.985,5.0
+legionnaires' disese,15.92,2.018
+yllow fever,6.017999999999999,3.024
diff --git a/examples/semtab2020_novartis/tables/3W4Z4171.json b/examples/semtab2020_novartis/tables/3W4Z4171.json
new file mode 100644
index 0000000..50d9d49
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/3W4Z4171.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["Mycoplasma neumoniae infection","ehrlichiosiv","ryptosporidiosis","ascasiasis","babesiosks","erthema infectiosum","Japnese encephalitis","camylobacteriosis","shgellosis","tck-borne encephalitis","salmonelloss","chikugunya","infectious mononuclersis","scaroet fever","orniuhosis","lithriosis","hepatiws A","buucellosis","legionnaires' disese","yllow fever"]},{"index":1,"name":"col1","values":["27.804","2.0","10.0","3.0060000000000002","4.016","17.874","15.075","7.0","3.964","14.097999999999999","13.972","12.048","4.0","5.015","18.829","67.335","48.0","4.985","15.92","6.017999999999999"]},{"index":2,"name":"col2","values":["6.937","1.0","2.018","1.01","1.01","9.9","4.95","1.008","0.997","7.056","5.97","2.988","4.016","1.98","4.975","0.996","15.06","5.0","2.018","3.024"]}],"metadata":{"table_id":"3W4Z4171","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":30,"url":"http://www.wikidata.org/entity/Q79054860","qnode_id":"Q79054860"}],[],[]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q2845432","qnode_id":"Q2845432"}],[],[]],[[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1359898","qnode_id":"Q1359898"}],[],[]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q842428","qnode_id":"Q842428"}],[],[]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q797668","qnode_id":"Q797668"}],[],[]],[[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q753654","qnode_id":"Q753654"}],[],[]],[[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q738292","qnode_id":"Q738292"}],[],[]],[[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q683861","qnode_id":"Q683861"}],[],[]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q327298","qnode_id":"Q327298"}],[],[]],[[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q326663","qnode_id":"Q326663"}],[],[]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q326648","qnode_id":"Q326648"}],[],[]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q243257","qnode_id":"Q243257"}],[],[]],[[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q207367","qnode_id":"Q207367"}],[],[]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q180266","qnode_id":"Q180266"}],[],[]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q164727","qnode_id":"Q164727"}],[],[]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q160653","qnode_id":"Q160653"}],[],[]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q157661","qnode_id":"Q157661"}],[],[]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q156050","qnode_id":"Q156050"}],[],[]],[[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q154882","qnode_id":"Q154882"}],[],[]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q154874","qnode_id":"Q154874"}],[],[]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q18123741:0","abs_uri":"http://www.wikidata.org/entity/Q18123741","rel_uri":"wd:Q18123741","approximation":false,"readable_label":"infectious disease (Q18123741)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"col-2","col_index":2,"label":"col2"}],"edges":[{"source":"http://www.wikidata.org/entity/Q18123741:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q18123741:0","target":"col-1","abs_uri":"http://www.wikidata.org/prop/P3487","rel_uri":"p:P3487","approximation":false,"readable_label":"maximal incubation period in humans (P3487)","id":2},{"source":"http://www.wikidata.org/entity/Q18123741:0","target":"col-2","abs_uri":"http://www.wikidata.org/prop/P3488","rel_uri":"p:P3488","approximation":false,"readable_label":"minimal incubation period in humans (P3488)","id":3}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/3W4Z4171.links.tsv b/examples/semtab2020_novartis/tables/3W4Z4171.links.tsv
new file mode 100644
index 0000000..40e0586
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/3W4Z4171.links.tsv
@@ -0,0 +1,20 @@
+0 0 Q79054860
+1 0 Q2845432
+2 0 Q1359898
+3 0 Q842428
+4 0 Q797668
+5 0 Q753654
+6 0 Q738292
+7 0 Q683861
+8 0 Q327298
+9 0 Q326663
+10 0 Q326648
+11 0 Q243257
+12 0 Q207367
+13 0 Q180266
+14 0 Q164727
+15 0 Q160653
+16 0 Q157661
+17 0 Q156050
+18 0 Q154882
+19 0 Q154874
diff --git a/examples/semtab2020_novartis/tables/3XS3Q318.csv b/examples/semtab2020_novartis/tables/3XS3Q318.csv
new file mode 100644
index 0000000..3f4a1bb
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/3XS3Q318.csv
@@ -0,0 +1,21 @@
+col0,col1,col2
+pstein syndrome,MYH9,MYH9-related disorder
+frontometaph{seal dysplasia,FLNA,otopalatodigital syndrome spectrum disorder
+Multile familial trichoepithelioma,CYLD,Trichoepithelioma
+"Platyspondylic lethal skeletal dysplasia, Worrance type",COL2A1,thanatophoric dysplasia
+Encephaloraniocutaneous lipomatosis,FGFR1,lipomatosis
+Kowarski syndroe,GH1,failure to thrive
+Bruton-type agammcglobulinemia,BTK,agammaglobulinemia
+progressive familial intrajepatic cholestasiv,ABCB11,liver disease
+herefitary spastic paraplegia 4,SPAST,hereditary spastic paraplegia
+prietal foramina,MSX2,neural tube defect
+congenital mwscular dystrophy type 1C,FKRP,genetic disease
+Dyggve-Melchior-Clausen synerome,DYM,genetic disease
+glycjnuria,SLC6A20,genetic disease
+progressive pseudorhematoid arthropathy of childhood,CCN6,genetic disease
+Fuhrman syndrome,WNT7A,genetic disease
+Forne robinson pascoe syndrome,MAP3K7,genetic disease
+spondylocarpotarsal synostosis syndrpme,FLNB,genetic disease
+Bepharo-cheilo-odontic syndrome,CDH1,syndrome
+Autosmal recessive primary microcephaly,CEP135,microcephaly
+Absence of fingerprints-conenital milia syndrome,SMARCAD1,Adermatoglyphia
diff --git a/examples/semtab2020_novartis/tables/3XS3Q318.json b/examples/semtab2020_novartis/tables/3XS3Q318.json
new file mode 100644
index 0000000..7041f30
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/3XS3Q318.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["pstein syndrome","frontometaph{seal dysplasia","Multile familial trichoepithelioma","Platyspondylic lethal skeletal dysplasia, Worrance type","Encephaloraniocutaneous lipomatosis","Kowarski syndroe","Bruton-type agammcglobulinemia","progressive familial intrajepatic cholestasiv","herefitary spastic paraplegia 4","prietal foramina","congenital mwscular dystrophy type 1C","Dyggve-Melchior-Clausen synerome","glycjnuria","progressive pseudorhematoid arthropathy of childhood","Fuhrman syndrome","Forne robinson pascoe syndrome","spondylocarpotarsal synostosis syndrpme","Bepharo-cheilo-odontic syndrome","Autosmal recessive primary microcephaly","Absence of fingerprints-conenital milia syndrome"]},{"index":1,"name":"col1","values":["MYH9","FLNA","CYLD","COL2A1","FGFR1","GH1","BTK","ABCB11","SPAST","MSX2","FKRP","DYM","SLC6A20","CCN6","WNT7A","MAP3K7","FLNB","CDH1","CEP135","SMARCAD1"]},{"index":2,"name":"col2","values":["MYH9-related disorder","otopalatodigital syndrome spectrum disorder","Trichoepithelioma","thanatophoric dysplasia","lipomatosis","failure to thrive","agammaglobulinemia","liver disease","hereditary spastic paraplegia","neural tube defect","genetic disease","genetic disease","genetic disease","genetic disease","genetic disease","genetic disease","genetic disease","syndrome","microcephaly","Adermatoglyphia"]}],"metadata":{"table_id":"3XS3Q318","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q1347729","qnode_id":"Q1347729"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18029747","qnode_id":"Q18029747"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q3843790","qnode_id":"Q3843790"}]],[[{"start":0,"end":27,"url":"http://www.wikidata.org/entity/Q3042143","qnode_id":"Q3042143"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q17928815","qnode_id":"Q17928815"}],[{"start":0,"end":43,"url":"http://www.wikidata.org/entity/Q3281416","qnode_id":"Q3281416"}]],[[{"start":0,"end":34,"url":"http://www.wikidata.org/entity/Q928767","qnode_id":"Q928767"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q17911655","qnode_id":"Q17911655"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q2452660","qnode_id":"Q2452660"}]],[[{"start":0,"end":55,"url":"http://www.wikidata.org/entity/Q7202845","qnode_id":"Q7202845"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q14877681","qnode_id":"Q14877681"}],[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q1787020","qnode_id":"Q1787020"}]],[[{"start":0,"end":35,"url":"http://www.wikidata.org/entity/Q17540092","qnode_id":"Q17540092"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14914258","qnode_id":"Q14914258"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q1691351","qnode_id":"Q1691351"}]],[[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q17082044","qnode_id":"Q17082044"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q18025887","qnode_id":"Q18025887"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q1497481","qnode_id":"Q1497481"}]],[[{"start":0,"end":30,"url":"http://www.wikidata.org/entity/Q283108","qnode_id":"Q283108"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q14906040","qnode_id":"Q14906040"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q1047559","qnode_id":"Q1047559"}]],[[{"start":0,"end":45,"url":"http://www.wikidata.org/entity/Q1018534","qnode_id":"Q1018534"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q15315873","qnode_id":"Q15315873"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q929737","qnode_id":"Q929737"}]],[[{"start":0,"end":31,"url":"http://www.wikidata.org/entity/Q2308013","qnode_id":"Q2308013"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18031665","qnode_id":"Q18031665"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q657516","qnode_id":"Q657516"}]],[[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q18987133","qnode_id":"Q18987133"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18029465","qnode_id":"Q18029465"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q548213","qnode_id":"Q548213"}]],[[{"start":0,"end":37,"url":"http://www.wikidata.org/entity/Q1781515","qnode_id":"Q1781515"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18046025","qnode_id":"Q18046025"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q200779","qnode_id":"Q200779"}]],[[{"start":0,"end":32,"url":"http://www.wikidata.org/entity/Q1268671","qnode_id":"Q1268671"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q18041271","qnode_id":"Q18041271"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q200779","qnode_id":"Q200779"}]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q1532449","qnode_id":"Q1532449"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q18041212","qnode_id":"Q18041212"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q200779","qnode_id":"Q200779"}]],[[{"start":0,"end":52,"url":"http://www.wikidata.org/entity/Q11825217","qnode_id":"Q11825217"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18033472","qnode_id":"Q18033472"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q200779","qnode_id":"Q200779"}]],[[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q18411838","qnode_id":"Q18411838"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18032363","qnode_id":"Q18032363"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q200779","qnode_id":"Q200779"}]],[[{"start":0,"end":30,"url":"http://www.wikidata.org/entity/Q17540795","qnode_id":"Q17540795"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q18031834","qnode_id":"Q18031834"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q200779","qnode_id":"Q200779"}]],[[{"start":0,"end":39,"url":"http://www.wikidata.org/entity/Q20828711","qnode_id":"Q20828711"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q17928818","qnode_id":"Q17928818"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q200779","qnode_id":"Q200779"}]],[[{"start":0,"end":31,"url":"http://www.wikidata.org/entity/Q17540023","qnode_id":"Q17540023"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q17859787","qnode_id":"Q17859787"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q179630","qnode_id":"Q179630"}]],[[{"start":0,"end":39,"url":"http://www.wikidata.org/entity/Q22965392","qnode_id":"Q22965392"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q18034561","qnode_id":"Q18034561"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q431643","qnode_id":"Q431643"}]],[[{"start":0,"end":48,"url":"http://www.wikidata.org/entity/Q22965397","qnode_id":"Q22965397"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q18042596","qnode_id":"Q18042596"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q356410","qnode_id":"Q356410"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q3311537:0","abs_uri":"http://www.wikidata.org/entity/Q3311537","rel_uri":"wd:Q3311537","approximation":false,"readable_label":"hereditary disorder (Q3311537)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/entity/Q7187","rel_uri":"wd:Q7187","approximation":false,"readable_label":"gene (Q7187)"},{"id":"col-2","col_index":2,"label":"col2"}],"edges":[{"source":"http://www.wikidata.org/entity/Q3311537:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q3311537:0","target":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/prop/P2293","rel_uri":"p:P2293","approximation":false,"readable_label":"genetic association (P2293)","id":2},{"source":"http://www.wikidata.org/entity/Q3311537:0","target":"col-2","abs_uri":"http://www.wikidata.org/prop/P279","rel_uri":"p:P279","approximation":false,"readable_label":"subclass of (P279)","id":3},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-1","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":4}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/3XS3Q318.links.tsv b/examples/semtab2020_novartis/tables/3XS3Q318.links.tsv
new file mode 100644
index 0000000..72777cf
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/3XS3Q318.links.tsv
@@ -0,0 +1,60 @@
+0 0 Q1347729
+0 1 Q18029747
+0 2 Q3843790
+1 0 Q3042143
+1 1 Q17928815
+1 2 Q3281416
+2 0 Q928767
+2 1 Q17911655
+2 2 Q2452660
+3 0 Q7202845
+3 1 Q14877681
+3 2 Q1787020
+4 0 Q17540092
+4 1 Q14914258
+4 2 Q1691351
+5 0 Q17082044
+5 1 Q18025887
+5 2 Q1497481
+6 0 Q283108
+6 1 Q14906040
+6 2 Q1047559
+7 0 Q1018534
+7 1 Q15315873
+7 2 Q929737
+8 0 Q2308013
+8 1 Q18031665
+8 2 Q657516
+9 0 Q18987133
+9 1 Q18029465
+9 2 Q548213
+10 0 Q1781515
+10 1 Q18046025
+10 2 Q200779
+11 0 Q1268671
+11 1 Q18041271
+11 2 Q200779
+12 0 Q1532449
+12 1 Q18041212
+12 2 Q200779
+13 0 Q11825217
+13 1 Q18033472
+13 2 Q200779
+14 0 Q18411838
+14 1 Q18032363
+14 2 Q200779
+15 0 Q17540795
+15 1 Q18031834
+15 2 Q200779
+16 0 Q20828711
+16 1 Q17928818
+16 2 Q200779
+17 0 Q17540023
+17 1 Q17859787
+17 2 Q179630
+18 0 Q22965392
+18 1 Q18034561
+18 2 Q431643
+19 0 Q22965397
+19 1 Q18042596
+19 2 Q356410
diff --git a/examples/semtab2020_novartis/tables/4B1GJLSN.csv b/examples/semtab2020_novartis/tables/4B1GJLSN.csv
new file mode 100644
index 0000000..fdb01c6
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/4B1GJLSN.csv
@@ -0,0 +1,21 @@
+col0,col1,col2,col3
+LDHC,Ldhc,amyotrophic lateral sclerosis,protein-coding gene
+GAK,Gak,Parkinson disease,protein-coding gene
+MLH3,MLH3,colorectal cancer,protein-coding gene
+MTG1,MTG1,systemic lupus erythematosus,protein-coding gene
+NFIA,Nfia,Coeliac Disease,protein-coding gene
+HLA-DQA1,H2-Aa,Coeliac Disease,protein-coding gene
+CD247,Cd247,Coeliac Disease,protein-coding gene
+DSG3,Dsg3,Parkinson disease,protein-coding gene
+COL13A1,Col13a1,Parkinson disease,protein-coding gene
+BST1,Bst1,Parkinson disease,protein-coding gene
+LRRK2,Lrrk2,Parkinson disease,protein-coding gene
+MAPT,Mapt,Parkinson disease,protein-coding gene
+SNCA,Snca,Parkinson disease,protein-coding gene
+NSF,Nsf,Parkinson disease,protein-coding gene
+SP140,Sp140,multiple sclerosis,protein-coding gene
+BTNL2,Btnl2,multiple sclerosis,protein-coding gene
+ZMIZ1,Zmiz1,multiple sclerosis,protein-coding gene
+SAMD12,Samd12,multiple sclerosis,protein-coding gene
+PDZRN4,Pdzrn4,multiple sclerosis,protein-coding gene
+MPV17L2,Mpv17l2,multiple sclerosis,protein-coding gene
diff --git a/examples/semtab2020_novartis/tables/4B1GJLSN.json b/examples/semtab2020_novartis/tables/4B1GJLSN.json
new file mode 100644
index 0000000..8c1b6fd
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/4B1GJLSN.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["LDHC","GAK","MLH3","MTG1","NFIA","HLA-DQA1","CD247","DSG3","COL13A1","BST1","LRRK2","MAPT","SNCA","NSF","SP140","BTNL2","ZMIZ1","SAMD12","PDZRN4","MPV17L2"]},{"index":1,"name":"col1","values":["Ldhc","Gak","MLH3","MTG1","Nfia","H2-Aa","Cd247","Dsg3","Col13a1","Bst1","Lrrk2","Mapt","Snca","Nsf","Sp140","Btnl2","Zmiz1","Samd12","Pdzrn4","Mpv17l2"]},{"index":2,"name":"col2","values":["amyotrophic lateral sclerosis","Parkinson disease","colorectal cancer","systemic lupus erythematosus","Coeliac Disease","Coeliac Disease","Coeliac Disease","Parkinson disease","Parkinson disease","Parkinson disease","Parkinson disease","Parkinson disease","Parkinson disease","Parkinson disease","multiple sclerosis","multiple sclerosis","multiple sclerosis","multiple sclerosis","multiple sclerosis","multiple sclerosis"]},{"index":3,"name":"col3","values":["protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene"]}],"metadata":{"table_id":"4B1GJLSN","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18028510","qnode_id":"Q18028510"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18252024","qnode_id":"Q18252024"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q206901","qnode_id":"Q206901"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q18297975","qnode_id":"Q18297975"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q21133384","qnode_id":"Q21133384"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q11085","qnode_id":"Q11085"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18038559","qnode_id":"Q18038559"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q27540428","qnode_id":"Q27540428"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q188874","qnode_id":"Q188874"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18048603","qnode_id":"Q18048603"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q27541864","qnode_id":"Q27541864"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q1485","qnode_id":"Q1485"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18030075","qnode_id":"Q18030075"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18252623","qnode_id":"Q18252623"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q11088","qnode_id":"Q11088"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q18026947","qnode_id":"Q18026947"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18251052","qnode_id":"Q18251052"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q11088","qnode_id":"Q11088"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q17857829","qnode_id":"Q17857829"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18248212","qnode_id":"Q18248212"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q11088","qnode_id":"Q11088"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q3011469","qnode_id":"Q3011469"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18250256","qnode_id":"Q18250256"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q11085","qnode_id":"Q11085"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q5145896","qnode_id":"Q5145896"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q18248419","qnode_id":"Q18248419"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q11085","qnode_id":"Q11085"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q17853626","qnode_id":"Q17853626"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18248004","qnode_id":"Q18248004"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q11085","qnode_id":"Q11085"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q15331136","qnode_id":"Q15331136"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q15331141","qnode_id":"Q15331141"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q11085","qnode_id":"Q11085"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14865307","qnode_id":"Q14865307"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14865308","qnode_id":"Q14865308"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q11085","qnode_id":"Q11085"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14862268","qnode_id":"Q14862268"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14862269","qnode_id":"Q14862269"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q11085","qnode_id":"Q11085"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q14860336","qnode_id":"Q14860336"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q14860344","qnode_id":"Q14860344"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q11085","qnode_id":"Q11085"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18036441","qnode_id":"Q18036441"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q24392704","qnode_id":"Q24392704"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q8277","qnode_id":"Q8277"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18042335","qnode_id":"Q18042335"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18306381","qnode_id":"Q18306381"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q8277","qnode_id":"Q8277"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18042957","qnode_id":"Q18042957"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18302467","qnode_id":"Q18302467"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q8277","qnode_id":"Q8277"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q18057719","qnode_id":"Q18057719"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q18302170","qnode_id":"Q18302170"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q8277","qnode_id":"Q8277"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q18039725","qnode_id":"Q18039725"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q18298787","qnode_id":"Q18298787"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q8277","qnode_id":"Q8277"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q18047766","qnode_id":"Q18047766"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q18298324","qnode_id":"Q18298324"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q8277","qnode_id":"Q8277"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/entity/Q7187","rel_uri":"wd:Q7187","approximation":false,"readable_label":"gene (Q7187)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"http://www.wikidata.org/entity/Q7187:1","abs_uri":"http://www.wikidata.org/entity/Q7187","rel_uri":"wd:Q7187","approximation":false,"readable_label":"gene (Q7187)"},{"id":"col-2","col_index":2,"label":"col2"},{"id":"http://www.wikidata.org/entity/Q12136:0","abs_uri":"http://www.wikidata.org/entity/Q12136","rel_uri":"wd:Q12136","approximation":false,"readable_label":"disease (Q12136)"},{"id":"col-3","col_index":3,"label":"col3"}],"edges":[{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"http://www.wikidata.org/entity/Q7187:1","abs_uri":"http://www.wikidata.org/prop/P684","rel_uri":"p:P684","approximation":false,"readable_label":"ortholog (P684)","id":2},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"http://www.wikidata.org/entity/Q12136:0","abs_uri":"http://www.wikidata.org/prop/P2293","rel_uri":"p:P2293","approximation":false,"readable_label":"genetic association (P2293)","id":3},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-3","abs_uri":"http://www.wikidata.org/prop/P279","rel_uri":"p:P279","approximation":false,"readable_label":"subclass of (P279)","id":4},{"source":"http://www.wikidata.org/entity/Q7187:1","target":"col-1","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":5},{"source":"http://www.wikidata.org/entity/Q12136:0","target":"col-2","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":6}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/4B1GJLSN.links.tsv b/examples/semtab2020_novartis/tables/4B1GJLSN.links.tsv
new file mode 100644
index 0000000..c138504
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/4B1GJLSN.links.tsv
@@ -0,0 +1,80 @@
+0 0 Q18028510
+0 1 Q18252024
+0 2 Q206901
+0 3 Q20747295
+1 0 Q18297975
+1 1 Q21133384
+1 2 Q11085
+1 3 Q20747295
+2 0 Q18038559
+2 1 Q27540428
+2 2 Q188874
+2 3 Q20747295
+3 0 Q18048603
+3 1 Q27541864
+3 2 Q1485
+3 3 Q20747295
+4 0 Q18030075
+4 1 Q18252623
+4 2 Q11088
+4 3 Q20747295
+5 0 Q18026947
+5 1 Q18251052
+5 2 Q11088
+5 3 Q20747295
+6 0 Q17857829
+6 1 Q18248212
+6 2 Q11088
+6 3 Q20747295
+7 0 Q3011469
+7 1 Q18250256
+7 2 Q11085
+7 3 Q20747295
+8 0 Q5145896
+8 1 Q18248419
+8 2 Q11085
+8 3 Q20747295
+9 0 Q17853626
+9 1 Q18248004
+9 2 Q11085
+9 3 Q20747295
+10 0 Q15331136
+10 1 Q15331141
+10 2 Q11085
+10 3 Q20747295
+11 0 Q14865307
+11 1 Q14865308
+11 2 Q11085
+11 3 Q20747295
+12 0 Q14862268
+12 1 Q14862269
+12 2 Q11085
+12 3 Q20747295
+13 0 Q14860336
+13 1 Q14860344
+13 2 Q11085
+13 3 Q20747295
+14 0 Q18036441
+14 1 Q24392704
+14 2 Q8277
+14 3 Q20747295
+15 0 Q18042335
+15 1 Q18306381
+15 2 Q8277
+15 3 Q20747295
+16 0 Q18042957
+16 1 Q18302467
+16 2 Q8277
+16 3 Q20747295
+17 0 Q18057719
+17 1 Q18302170
+17 2 Q8277
+17 3 Q20747295
+18 0 Q18039725
+18 1 Q18298787
+18 2 Q8277
+18 3 Q20747295
+19 0 Q18047766
+19 1 Q18298324
+19 2 Q8277
+19 3 Q20747295
diff --git a/examples/semtab2020_novartis/tables/4RBZR30S.csv b/examples/semtab2020_novartis/tables/4RBZR30S.csv
new file mode 100644
index 0000000..4175068
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/4RBZR30S.csv
@@ -0,0 +1,21 @@
+col0,col1,col2,col3
+NexGen EBA Radiologic and Immunologic Biomarkers of Sterilizing Drug Activity in Tuberculosis,Task Applied Science,tuberculosis,National Institute of Allergy and Infectious Diseases
+Procure Tumor and Initiate Melanoma Tumor-Reactive Tumor Infiltration,John Wayne Cancer Institute,melanoma,John Wayne Cancer Institute
+Low-Dose Acetylsalicylic Acid in Treating Patients With Stage I-III Non-Small Cell Lung Cancer,Vanderbilt University,adenocarcinoma,Vanderbilt-Ingram Cancer Center
+Search for New Methods to Detect Acute Renal Failure,National Institutes of Health,acute kidney injury,National Institute of Diabetes and Digestive and Kidney Diseases
+A Study to Evaluate the Safety and Efficacy of Upadacitinib (ABT-494) for Induction and Maintenance Therapy in Subjects With Moderately to Severely Active Ulcerative Colitis (UC),Puerto Rico,colitis,AbbVie
+A Study of the Efficacy and Safety of Upadacitinib (ABT-494) in Participants With Moderately to Severely Active Ulcerative Colitis,Puerto Rico,colitis,AbbVie
+Evaluation of Upadacitinib in Adolescent and Adult Patients With Moderate to Severe Atopic Dermatitis (Eczema)- Measure Up 1,Puerto Rico,dermatitis,AbbVie
+"Collection of Blood, Bone Marrow, Tumor, or Tissue Samples From Patients With Cancer to Study Drug Resistance",National Institutes of Health,carcinoma,National Cancer Institute
+Gilead Sustained Virologic Response (SVR) Registry,Puerto Rico,hepatitis,Gilead Sciences
+A Gilead Sequence Registry of Subjects Who Did Not Achieve Sustained Virologic Response,Puerto Rico,hepatitis,Gilead Sciences
+AMG 151 Amgen Protocol Number 20100761,Puerto Rico,diabetes mellitus,Amgen
+Dasatinib (BMS-354835) Versus Imatinib Mesylate in Subjects With Chronic Myeloid Leukemia,Puerto Rico,leukemia,Bristol-Myers Squibb
+Covered Metal Stent for Benign Biliary Stricture Caused by Chronic Pancreatitis,University of Helsinki,cholestasis,Helsinki University Central Hospital
+"Study to Determine the Efficacy, Safety, Tolerability & Pharmacokinetics of RO 205-2349 in Patients With Type 2 Diabetes Mellitus",Puerto Rico,diabetes mellitus,Hoffmann-La Roche
+Study of Ro 205-2349 in Patients With Type 2 Diabetes Mellitus,Puerto Rico,diabetes mellitus,Hoffmann-La Roche
+A Study of DPP-IV Inhibitor in Patients With Type 2 Diabetes,Puerto Rico,diabetes mellitus,Hoffmann-La Roche
+GR270773 In The Treatment Of Suspected Or Confirmed Gram-Negative Severe Sepsis In Adults,Puerto Rico,toxemia,GlaxoSmithKline
+Benign Prostatic Hyperplasia Trial With Dutasteride And Tamsulosin Combination Treatment,Puerto Rico,hyperplasia,GlaxoSmithKline
+"Consistency Study of GlaxoSmithKline (GSK) Biologicals' MMR Vaccine (209762) (Priorix) Comparing Immunogenicity and Safety to Merck & Co., Inc.'s MMR Vaccine (M-M-R II), in Children 12 to 15 Months of Age",Puerto Rico,measles,GlaxoSmithKline
+"Safety and Immunogenicity Study of GlaxoSmithKline (GSK) Biologicals' Measles, Mumps and Rubella (MMR) Vaccine (209762) Compared to Merck & Co., Inc.'s MMR Vaccine in Healthy Children 12 to 15 Months of Age",Puerto Rico,measles,GlaxoSmithKline
diff --git a/examples/semtab2020_novartis/tables/4RBZR30S.json b/examples/semtab2020_novartis/tables/4RBZR30S.json
new file mode 100644
index 0000000..99aea4a
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/4RBZR30S.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["NexGen EBA Radiologic and Immunologic Biomarkers of Sterilizing Drug Activity in Tuberculosis","Procure Tumor and Initiate Melanoma Tumor-Reactive Tumor Infiltration","Low-Dose Acetylsalicylic Acid in Treating Patients With Stage I-III Non-Small Cell Lung Cancer","Search for New Methods to Detect Acute Renal Failure","A Study to Evaluate the Safety and Efficacy of Upadacitinib (ABT-494) for Induction and Maintenance Therapy in Subjects With Moderately to Severely Active Ulcerative Colitis (UC)","A Study of the Efficacy and Safety of Upadacitinib (ABT-494) in Participants With Moderately to Severely Active Ulcerative Colitis","Evaluation of Upadacitinib in Adolescent and Adult Patients With Moderate to Severe Atopic Dermatitis (Eczema)- Measure Up 1","Collection of Blood, Bone Marrow, Tumor, or Tissue Samples From Patients With Cancer to Study Drug Resistance","Gilead Sustained Virologic Response (SVR) Registry","A Gilead Sequence Registry of Subjects Who Did Not Achieve Sustained Virologic Response","AMG 151 Amgen Protocol Number 20100761","Dasatinib (BMS-354835) Versus Imatinib Mesylate in Subjects With Chronic Myeloid Leukemia","Covered Metal Stent for Benign Biliary Stricture Caused by Chronic Pancreatitis","Study to Determine the Efficacy, Safety, Tolerability & Pharmacokinetics of RO 205-2349 in Patients With Type 2 Diabetes Mellitus","Study of Ro 205-2349 in Patients With Type 2 Diabetes Mellitus","A Study of DPP-IV Inhibitor in Patients With Type 2 Diabetes","GR270773 In The Treatment Of Suspected Or Confirmed Gram-Negative Severe Sepsis In Adults","Benign Prostatic Hyperplasia Trial With Dutasteride And Tamsulosin Combination Treatment","Consistency Study of GlaxoSmithKline (GSK) Biologicals' MMR Vaccine (209762) (Priorix) Comparing Immunogenicity and Safety to Merck & Co., Inc.'s MMR Vaccine (M-M-R II), in Children 12 to 15 Months of Age","Safety and Immunogenicity Study of GlaxoSmithKline (GSK) Biologicals' Measles, Mumps and Rubella (MMR) Vaccine (209762) Compared to Merck & Co., Inc.'s MMR Vaccine in Healthy Children 12 to 15 Months of Age"]},{"index":1,"name":"col1","values":["Task Applied Science","John Wayne Cancer Institute","Vanderbilt University","National Institutes of Health","Puerto Rico","Puerto Rico","Puerto Rico","National Institutes of Health","Puerto Rico","Puerto Rico","Puerto Rico","Puerto Rico","University of Helsinki","Puerto Rico","Puerto Rico","Puerto Rico","Puerto Rico","Puerto Rico","Puerto Rico","Puerto Rico"]},{"index":2,"name":"col2","values":["tuberculosis","melanoma","adenocarcinoma","acute kidney injury","colitis","colitis","dermatitis","carcinoma","hepatitis","hepatitis","diabetes mellitus","leukemia","cholestasis","diabetes mellitus","diabetes mellitus","diabetes mellitus","toxemia","hyperplasia","measles","measles"]},{"index":3,"name":"col3","values":["National Institute of Allergy and Infectious Diseases","John Wayne Cancer Institute","Vanderbilt-Ingram Cancer Center","National Institute of Diabetes and Digestive and Kidney Diseases","AbbVie","AbbVie","AbbVie","National Cancer Institute","Gilead Sciences","Gilead Sciences","Amgen","Bristol-Myers Squibb","Helsinki University Central Hospital","Hoffmann-La Roche","Hoffmann-La Roche","Hoffmann-La Roche","GlaxoSmithKline","GlaxoSmithKline","GlaxoSmithKline","GlaxoSmithKline"]}],"metadata":{"table_id":"4RBZR30S","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":93,"url":"http://www.wikidata.org/entity/Q61864695","qnode_id":"Q61864695"}],[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q50037669","qnode_id":"Q50037669"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q12204","qnode_id":"Q12204"}],[{"start":0,"end":53,"url":"http://www.wikidata.org/entity/Q3519875","qnode_id":"Q3519875"}]],[[{"start":0,"end":69,"url":"http://www.wikidata.org/entity/Q61864701","qnode_id":"Q61864701"}],[{"start":0,"end":27,"url":"http://www.wikidata.org/entity/Q54559592","qnode_id":"Q54559592"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q180614","qnode_id":"Q180614"}],[{"start":0,"end":27,"url":"http://www.wikidata.org/entity/Q54559592","qnode_id":"Q54559592"}]],[[{"start":0,"end":94,"url":"http://www.wikidata.org/entity/Q61707464","qnode_id":"Q61707464"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q29052","qnode_id":"Q29052"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q356033","qnode_id":"Q356033"}],[{"start":0,"end":31,"url":"http://www.wikidata.org/entity/Q43079570","qnode_id":"Q43079570"}]],[[{"start":0,"end":52,"url":"http://www.wikidata.org/entity/Q61865370","qnode_id":"Q61865370"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q424337","qnode_id":"Q424337"}],[{"start":0,"end":64,"url":"http://www.wikidata.org/entity/Q29220409","qnode_id":"Q29220409"}]],[[{"start":0,"end":178,"url":"http://www.wikidata.org/entity/Q63577721","qnode_id":"Q63577721"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q1183","qnode_id":"Q1183"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q2453464","qnode_id":"Q2453464"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q14662364","qnode_id":"Q14662364"}]],[[{"start":0,"end":130,"url":"http://www.wikidata.org/entity/Q62025200","qnode_id":"Q62025200"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q1183","qnode_id":"Q1183"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q2453464","qnode_id":"Q2453464"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q14662364","qnode_id":"Q14662364"}]],[[{"start":0,"end":124,"url":"http://www.wikidata.org/entity/Q63397645","qnode_id":"Q63397645"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q1183","qnode_id":"Q1183"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q229256","qnode_id":"Q229256"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q14662364","qnode_id":"Q14662364"}]],[[{"start":0,"end":109,"url":"http://www.wikidata.org/entity/Q61865376","qnode_id":"Q61865376"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q33525","qnode_id":"Q33525"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q664846","qnode_id":"Q664846"}]],[[{"start":0,"end":50,"url":"http://www.wikidata.org/entity/Q61965937","qnode_id":"Q61965937"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q1183","qnode_id":"Q1183"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q131742","qnode_id":"Q131742"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q663596","qnode_id":"Q663596"}]],[[{"start":0,"end":87,"url":"http://www.wikidata.org/entity/Q61965936","qnode_id":"Q61965936"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q1183","qnode_id":"Q1183"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q131742","qnode_id":"Q131742"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q663596","qnode_id":"Q663596"}]],[[{"start":0,"end":38,"url":"http://www.wikidata.org/entity/Q61956729","qnode_id":"Q61956729"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q1183","qnode_id":"Q1183"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q12206","qnode_id":"Q12206"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q470517","qnode_id":"Q470517"}]],[[{"start":0,"end":89,"url":"http://www.wikidata.org/entity/Q62105926","qnode_id":"Q62105926"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q1183","qnode_id":"Q1183"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q29496","qnode_id":"Q29496"}],[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q266423","qnode_id":"Q266423"}]],[[{"start":0,"end":79,"url":"http://www.wikidata.org/entity/Q61864801","qnode_id":"Q61864801"}],[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q28695","qnode_id":"Q28695"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q1075923","qnode_id":"Q1075923"}],[{"start":0,"end":36,"url":"http://www.wikidata.org/entity/Q246315","qnode_id":"Q246315"}]],[[{"start":0,"end":129,"url":"http://www.wikidata.org/entity/Q66036961","qnode_id":"Q66036961"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q1183","qnode_id":"Q1183"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q12206","qnode_id":"Q12206"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q212646","qnode_id":"Q212646"}]],[[{"start":0,"end":62,"url":"http://www.wikidata.org/entity/Q66036959","qnode_id":"Q66036959"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q1183","qnode_id":"Q1183"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q12206","qnode_id":"Q12206"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q212646","qnode_id":"Q212646"}]],[[{"start":0,"end":60,"url":"http://www.wikidata.org/entity/Q62105120","qnode_id":"Q62105120"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q1183","qnode_id":"Q1183"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q12206","qnode_id":"Q12206"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q212646","qnode_id":"Q212646"}]],[[{"start":0,"end":89,"url":"http://www.wikidata.org/entity/Q66033114","qnode_id":"Q66033114"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q1183","qnode_id":"Q1183"}],[],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q212322","qnode_id":"Q212322"}]],[[{"start":0,"end":88,"url":"http://www.wikidata.org/entity/Q66033001","qnode_id":"Q66033001"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q1183","qnode_id":"Q1183"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q835051","qnode_id":"Q835051"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q212322","qnode_id":"Q212322"}]],[[{"start":0,"end":204,"url":"http://www.wikidata.org/entity/Q64640209","qnode_id":"Q64640209"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q1183","qnode_id":"Q1183"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q79793","qnode_id":"Q79793"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q212322","qnode_id":"Q212322"}]],[[{"start":0,"end":206,"url":"http://www.wikidata.org/entity/Q64609801","qnode_id":"Q64609801"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q1183","qnode_id":"Q1183"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q79793","qnode_id":"Q79793"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q212322","qnode_id":"Q212322"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q30612:0","abs_uri":"http://www.wikidata.org/entity/Q30612","rel_uri":"wd:Q30612","approximation":false,"readable_label":"clinical trial (Q30612)"},{"id":"col-2","col_index":2,"label":"col2"},{"id":"http://www.wikidata.org/entity/Q12136:0","abs_uri":"http://www.wikidata.org/entity/Q12136","rel_uri":"wd:Q12136","approximation":false,"readable_label":"disease (Q12136)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"col-3","col_index":3,"label":"col3"}],"edges":[{"source":"http://www.wikidata.org/entity/Q30612:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q30612:0","target":"col-1","abs_uri":"http://www.wikidata.org/prop/P6153","rel_uri":"p:P6153","approximation":false,"readable_label":"research site (P6153)","id":2},{"source":"http://www.wikidata.org/entity/Q30612:0","target":"http://www.wikidata.org/entity/Q12136:0","abs_uri":"http://www.wikidata.org/prop/P1050","rel_uri":"p:P1050","approximation":false,"readable_label":"medical condition (P1050)","id":3},{"source":"http://www.wikidata.org/entity/Q30612:0","target":"col-3","abs_uri":"http://www.wikidata.org/prop/P859","rel_uri":"p:P859","approximation":false,"readable_label":"sponsor (P859)","id":4},{"source":"http://www.wikidata.org/entity/Q12136:0","target":"col-2","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":5}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/4RBZR30S.links.tsv b/examples/semtab2020_novartis/tables/4RBZR30S.links.tsv
new file mode 100644
index 0000000..989c6cc
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/4RBZR30S.links.tsv
@@ -0,0 +1,79 @@
+0 0 Q61864695
+0 1 Q50037669
+0 2 Q12204
+0 3 Q3519875
+1 0 Q61864701
+1 1 Q54559592
+1 2 Q180614
+1 3 Q54559592
+2 0 Q61707464
+2 1 Q29052
+2 2 Q356033
+2 3 Q43079570
+3 0 Q61865370
+3 1 Q390551
+3 2 Q424337
+3 3 Q29220409
+4 0 Q63577721
+4 1 Q1183
+4 2 Q2453464
+4 3 Q14662364
+5 0 Q62025200
+5 1 Q1183
+5 2 Q2453464
+5 3 Q14662364
+6 0 Q63397645
+6 1 Q1183
+6 2 Q229256
+6 3 Q14662364
+7 0 Q61865376
+7 1 Q390551
+7 2 Q33525
+7 3 Q664846
+8 0 Q61965937
+8 1 Q1183
+8 2 Q131742
+8 3 Q663596
+9 0 Q61965936
+9 1 Q1183
+9 2 Q131742
+9 3 Q663596
+10 0 Q61956729
+10 1 Q1183
+10 2 Q12206
+10 3 Q470517
+11 0 Q62105926
+11 1 Q1183
+11 2 Q29496
+11 3 Q266423
+12 0 Q61864801
+12 1 Q28695
+12 2 Q1075923
+12 3 Q246315
+13 0 Q66036961
+13 1 Q1183
+13 2 Q12206
+13 3 Q212646
+14 0 Q66036959
+14 1 Q1183
+14 2 Q12206
+14 3 Q212646
+15 0 Q62105120
+15 1 Q1183
+15 2 Q12206
+15 3 Q212646
+16 0 Q66033114
+16 1 Q1183
+16 3 Q212322
+17 0 Q66033001
+17 1 Q1183
+17 2 Q835051
+17 3 Q212322
+18 0 Q64640209
+18 1 Q1183
+18 2 Q79793
+18 3 Q212322
+19 0 Q64609801
+19 1 Q1183
+19 2 Q79793
+19 3 Q212322
diff --git a/examples/semtab2020_novartis/tables/5Y26A605.csv b/examples/semtab2020_novartis/tables/5Y26A605.csv
new file mode 100644
index 0000000..39f6413
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/5Y26A605.csv
@@ -0,0 +1,21 @@
+col0,col1,col2,col3,col4
+DiGeorge syndrome,TBX1,Angelo DiGeorge,chromosomal deletion syndrome,medical genetics
+Marfan syndrome,FBN1,Antoine Marfan,connective tissue disease,medical genetics
+X-linked adrenoleukodystrophy,PEX5,Thomas Addison,leukodystrophy,endocrinology
+fragile X syndrome,FMR1,Julia Bell,overgrowth syndrome,neurology
+osteopetrosis,LRP5,Heinrich Albers-Schönberg,osteosclerosis,medical genetics
+Alagille syndrome,NOTCH2,Daniel Alagille,liver disease,medical genetics
+Sturge–Weber syndrome,GNAQ,Frederick Parkes Weber,phakomatosis,medical genetics
+Crouzon syndrome,FGFR2,Octave Crouzon,craniosynostosis,medical genetics
+Noonan syndrome,PTPN11,Jacqueline Noonan,syndrome,medical genetics
+Kabuki syndrome,KDM6A,kabuki,syndrome,medical genetics
+Fanconi anemia,FANCA,Guido Fanconi,congenital hypoplastic anemia,hematology
+paroxysmal nocturnal hemoglobinuria,PIGT,Ettore Marchiafava,hemoglobinuria,hematology
+Smith-Magenis syndrome,RAI1,R. Ellen Magenis,chromosomal deletion syndrome,neurology
+basal ganglia calcification,PDGFB,Karl Theodor Fahr,basal ganglia disease,neurology
+Kennedy disease,AR,William Kennedy,spinal muscular atrophy,neurology
+Landau–Kleffner syndrome,GRIN2A,Frank Kleffner,childhood electroclinical syndrome,psychiatry
+Behcet's disease,IL10,Hulusi Behçet,vasculitis,immunology
+Cockayne syndrome,ERCC2,Edward Alfred Cockayne,autosomal recessive disease,neurology
+hereditary hemorrhagic telangiectasia,SMAD4,William Osler,autosomal dominant disease,medical genetics
+Angelman syndrome,UBE3A,Harry Angelman,chromosomal anomaly with epilepsy as a major feature,neurology
diff --git a/examples/semtab2020_novartis/tables/5Y26A605.json b/examples/semtab2020_novartis/tables/5Y26A605.json
new file mode 100644
index 0000000..790a287
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/5Y26A605.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["DiGeorge syndrome","Marfan syndrome","X-linked adrenoleukodystrophy","fragile X syndrome","osteopetrosis","Alagille syndrome","Sturge–Weber syndrome","Crouzon syndrome","Noonan syndrome","Kabuki syndrome","Fanconi anemia","paroxysmal nocturnal hemoglobinuria","Smith-Magenis syndrome","basal ganglia calcification","Kennedy disease","Landau–Kleffner syndrome","Behcet's disease","Cockayne syndrome","hereditary hemorrhagic telangiectasia","Angelman syndrome"]},{"index":1,"name":"col1","values":["TBX1","FBN1","PEX5","FMR1","LRP5","NOTCH2","GNAQ","FGFR2","PTPN11","KDM6A","FANCA","PIGT","RAI1","PDGFB","AR","GRIN2A","IL10","ERCC2","SMAD4","UBE3A"]},{"index":2,"name":"col2","values":["Angelo DiGeorge","Antoine Marfan","Thomas Addison","Julia Bell","Heinrich Albers-Schönberg","Daniel Alagille","Frederick Parkes Weber","Octave Crouzon","Jacqueline Noonan","kabuki","Guido Fanconi","Ettore Marchiafava","R. Ellen Magenis","Karl Theodor Fahr","William Kennedy","Frank Kleffner","Hulusi Behçet","Edward Alfred Cockayne","William Osler","Harry Angelman"]},{"index":3,"name":"col3","values":["chromosomal deletion syndrome","connective tissue disease","leukodystrophy","overgrowth syndrome","osteosclerosis","liver disease","phakomatosis","craniosynostosis","syndrome","syndrome","congenital hypoplastic anemia","hemoglobinuria","chromosomal deletion syndrome","basal ganglia disease","spinal muscular atrophy","childhood electroclinical syndrome","vasculitis","autosomal recessive disease","autosomal dominant disease","chromosomal anomaly with epilepsy as a major feature"]},{"index":4,"name":"col4","values":["medical genetics","medical genetics","endocrinology","neurology","medical genetics","medical genetics","medical genetics","medical genetics","medical genetics","medical genetics","hematology","hematology","neurology","neurology","neurology","psychiatry","immunology","neurology","medical genetics","neurology"]}],"metadata":{"table_id":"5Y26A605","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q525642","qnode_id":"Q525642"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18031845","qnode_id":"Q18031845"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q4762867","qnode_id":"Q4762867"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q16918398","qnode_id":"Q16918398"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}]],[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q208562","qnode_id":"Q208562"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q17927651","qnode_id":"Q17927651"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q586765","qnode_id":"Q586765"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q1779300","qnode_id":"Q1779300"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}]],[[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q366964","qnode_id":"Q366964"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q7121037","qnode_id":"Q7121037"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q436039","qnode_id":"Q436039"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q1821559","qnode_id":"Q1821559"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q162606","qnode_id":"Q162606"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q221472","qnode_id":"Q221472"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14882338","qnode_id":"Q14882338"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q1309408","qnode_id":"Q1309408"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q7113674","qnode_id":"Q7113674"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q83042","qnode_id":"Q83042"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q1755568","qnode_id":"Q1755568"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18028670","qnode_id":"Q18028670"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q68069","qnode_id":"Q68069"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q1233526","qnode_id":"Q1233526"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}]],[[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q1544408","qnode_id":"Q1544408"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q18030183","qnode_id":"Q18030183"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q19281940","qnode_id":"Q19281940"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q929737","qnode_id":"Q929737"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}]],[[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q1886238","qnode_id":"Q1886238"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18026236","qnode_id":"Q18026236"}],[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q2129570","qnode_id":"Q2129570"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q748376","qnode_id":"Q748376"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}]],[[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q779250","qnode_id":"Q779250"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14911644","qnode_id":"Q14911644"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q2875373","qnode_id":"Q2875373"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q378183","qnode_id":"Q378183"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}]],[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q1543446","qnode_id":"Q1543446"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q14905494","qnode_id":"Q14905494"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q6120170","qnode_id":"Q6120170"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q179630","qnode_id":"Q179630"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}]],[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q1538227","qnode_id":"Q1538227"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18032310","qnode_id":"Q18032310"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q199701","qnode_id":"Q199701"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q179630","qnode_id":"Q179630"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}]],[[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q845779","qnode_id":"Q845779"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q17927056","qnode_id":"Q17927056"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q123927","qnode_id":"Q123927"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q5160440","qnode_id":"Q5160440"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q103824","qnode_id":"Q103824"}]],[[{"start":0,"end":35,"url":"http://www.wikidata.org/entity/Q1479494","qnode_id":"Q1479494"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18040451","qnode_id":"Q18040451"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q1371887","qnode_id":"Q1371887"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q1553850","qnode_id":"Q1553850"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q103824","qnode_id":"Q103824"}]],[[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q2295338","qnode_id":"Q2295338"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18035659","qnode_id":"Q18035659"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q7273611","qnode_id":"Q7273611"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q16918398","qnode_id":"Q16918398"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q83042","qnode_id":"Q83042"}]],[[{"start":0,"end":27,"url":"http://www.wikidata.org/entity/Q1947307","qnode_id":"Q1947307"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18030421","qnode_id":"Q18030421"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q90083","qnode_id":"Q90083"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q4866181","qnode_id":"Q4866181"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q83042","qnode_id":"Q83042"}]],[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q1995327","qnode_id":"Q1995327"}],[{"start":0,"end":2,"url":"http://www.wikidata.org/entity/Q14873015","qnode_id":"Q14873015"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q1262614","qnode_id":"Q1262614"}],[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q580290","qnode_id":"Q580290"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q83042","qnode_id":"Q83042"}]],[[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q1636310","qnode_id":"Q1636310"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q18026515","qnode_id":"Q18026515"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q5487713","qnode_id":"Q5487713"}],[{"start":0,"end":34,"url":"http://www.wikidata.org/entity/Q18553414","qnode_id":"Q18553414"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q7867","qnode_id":"Q7867"}]],[[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q911427","qnode_id":"Q911427"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14864464","qnode_id":"Q14864464"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q936634","qnode_id":"Q936634"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q644318","qnode_id":"Q644318"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q101929","qnode_id":"Q101929"}]],[[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q914389","qnode_id":"Q914389"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14908096","qnode_id":"Q14908096"}],[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q5341546","qnode_id":"Q5341546"}],[],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q83042","qnode_id":"Q83042"}]],[[{"start":0,"end":37,"url":"http://www.wikidata.org/entity/Q776881","qnode_id":"Q776881"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14904154","qnode_id":"Q14904154"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q369668","qnode_id":"Q369668"}],[{"start":0,"end":26,"url":"http://www.wikidata.org/entity/Q18553439","qnode_id":"Q18553439"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}]],[[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q535364","qnode_id":"Q535364"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14878336","qnode_id":"Q14878336"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q705052","qnode_id":"Q705052"}],[{"start":0,"end":52,"url":"http://www.wikidata.org/entity/Q55785627","qnode_id":"Q55785627"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q83042","qnode_id":"Q83042"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q42303753:0","abs_uri":"http://www.wikidata.org/entity/Q42303753","rel_uri":"wd:Q42303753","approximation":false,"readable_label":"designated intractable/rare diseases (Q42303753)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/entity/Q7187","rel_uri":"wd:Q7187","approximation":false,"readable_label":"gene (Q7187)"},{"id":"col-4","col_index":4,"label":"col4"},{"id":"http://www.wikidata.org/entity/Q930752:0","abs_uri":"http://www.wikidata.org/entity/Q930752","rel_uri":"wd:Q930752","approximation":false,"readable_label":"medical specialty (Q930752)"},{"id":"col-2","col_index":2,"label":"col2"},{"id":"col-3","col_index":3,"label":"col3"}],"edges":[{"source":"http://www.wikidata.org/entity/Q42303753:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q42303753:0","target":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/prop/P2293","rel_uri":"p:P2293","approximation":false,"readable_label":"genetic association (P2293)","id":2},{"source":"http://www.wikidata.org/entity/Q42303753:0","target":"col-2","abs_uri":"http://www.wikidata.org/prop/P138","rel_uri":"p:P138","approximation":false,"readable_label":"named after (P138)","id":3},{"source":"http://www.wikidata.org/entity/Q42303753:0","target":"col-3","abs_uri":"http://www.wikidata.org/prop/P279","rel_uri":"p:P279","approximation":false,"readable_label":"subclass of (P279)","id":4},{"source":"http://www.wikidata.org/entity/Q42303753:0","target":"http://www.wikidata.org/entity/Q930752:0","abs_uri":"http://www.wikidata.org/prop/P1995","rel_uri":"p:P1995","approximation":false,"readable_label":"health specialty (P1995)","id":5},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-1","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":6},{"source":"http://www.wikidata.org/entity/Q930752:0","target":"col-4","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":7}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/5Y26A605.links.tsv b/examples/semtab2020_novartis/tables/5Y26A605.links.tsv
new file mode 100644
index 0000000..c5c9e62
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/5Y26A605.links.tsv
@@ -0,0 +1,99 @@
+0 0 Q525642
+0 1 Q18031845
+0 2 Q4762867
+0 3 Q16918398
+0 4 Q1071953
+1 0 Q208562
+1 1 Q17927651
+1 2 Q586765
+1 3 Q1779300
+1 4 Q1071953
+2 0 Q366964
+2 1 Q7121037
+2 2 Q436039
+2 3 Q1821559
+2 4 Q162606
+3 0 Q221472
+3 1 Q14882338
+3 2 Q1309408
+3 3 Q7113674
+3 4 Q83042
+4 0 Q1755568
+4 1 Q18028670
+4 2 Q68069
+4 3 Q1233526
+4 4 Q1071953
+5 0 Q1544408
+5 1 Q18030183
+5 2 Q19281940
+5 3 Q929737
+5 4 Q1071953
+6 0 Q1886238
+6 1 Q18026236
+6 2 Q2129570
+6 3 Q748376
+6 4 Q1071953
+7 0 Q779250
+7 1 Q14911644
+7 2 Q2875373
+7 3 Q378183
+7 4 Q1071953
+8 0 Q1543446
+8 1 Q14905494
+8 2 Q6120170
+8 3 Q179630
+8 4 Q1071953
+9 0 Q1538227
+9 1 Q18032310
+9 2 Q199701
+9 3 Q179630
+9 4 Q1071953
+10 0 Q845779
+10 1 Q17927056
+10 2 Q123927
+10 3 Q5160440
+10 4 Q103824
+11 0 Q1479494
+11 1 Q18040451
+11 2 Q1371887
+11 3 Q1553850
+11 4 Q103824
+12 0 Q2295338
+12 1 Q18035659
+12 2 Q7273611
+12 3 Q16918398
+12 4 Q83042
+13 0 Q1947307
+13 1 Q18030421
+13 2 Q90083
+13 3 Q4866181
+13 4 Q83042
+14 0 Q1995327
+14 1 Q14873015
+14 2 Q1262614
+14 3 Q580290
+14 4 Q83042
+15 0 Q1636310
+15 1 Q18026515
+15 2 Q5487713
+15 3 Q18553414
+15 4 Q7867
+16 0 Q911427
+16 1 Q14864464
+16 2 Q936634
+16 3 Q644318
+16 4 Q101929
+17 0 Q914389
+17 1 Q14908096
+17 2 Q5341546
+17 4 Q83042
+18 0 Q776881
+18 1 Q14904154
+18 2 Q369668
+18 3 Q18553439
+18 4 Q1071953
+19 0 Q535364
+19 1 Q14878336
+19 2 Q705052
+19 3 Q55785627
+19 4 Q83042
diff --git a/examples/semtab2020_novartis/tables/61Q74GUH.csv b/examples/semtab2020_novartis/tables/61Q74GUH.csv
new file mode 100644
index 0000000..6c37ae9
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/61Q74GUH.csv
@@ -0,0 +1,21 @@
+col0,col1,col2,col3
+Osimertinib and Navitoclax in Treating Patients With EGFR-Positive Previously Treated Advanced or Metastatic Non-small Cell Lung Cancer,Ohio State University,carcinoma,National Cancer Institute
+A Comparison of Reduced Dose Total Body Irradiation (TBI) and Cyclophosphamide With Fludarabine and Melphalan Reduced Intensity Conditioning in Adults With Acute Lymphoblastic Leukaemia (ALL) in Complete Remission. (ALL-RIC),King's College London,leukemia,University of Birmingham
+Multi-DOSE Oral Ondansetron for Pediatric Acute GastroEnteritis,Alberta Children’s Hospital Foundation,diarrhea,University of Calgary
+"Safety, Tolerability, and Efficacy Study of Idiopathic Pulmonary Fibrosis",Vanderbilt University,pulmonary fibrosis,FibroGen (United States)
+Standard-Dose Combination Chemotherapy or High-Dose Combination Chemotherapy and Stem Cell Transplant in Treating Patients With Relapsed or Refractory Germ Cell Tumors,Vanderbilt University,teratoma,Alliance for Clinical Trials in Oncology
+Left Versus Right Radial Artery and Radiation Exposure in Patients With Predictors of Trans-radial Failure,New York University,coronary artery disease,New York University School of Medicine
+Safety and Efficacy Trial of RPC1063 for Moderate to Severe Ulcerative Colitis,Yonsei University,colitis,Celgene
+Open-Label Extension of RPC1063 as Therapy for Moderate to Severe Ulcerative Colitis,Yonsei University,colitis,Celgene
+"A Study to Determine Dose and Tolerability of CC-220 Monotherapy, in Combination With Dexamethasone, and in Combination With Dexamethasone and Daratumumab or Bortezomib in Subjects With Relapsed and Refractory Multiple Myeloma (MM)",University of Pennsylvania,multiple myeloma,Celgene
+A Study to Determine Dose and Regimen of Durvalumab as Monotherapy or in Combination With Pomalidomide With or Without Dexamethasone in Subjects With Relapsed and Refractory Multiple Myeloma,Cornell University,multiple myeloma,Celgene
+An Efficacy and Safety Study of Azacitidine Subcutaneous in Combination With Durvalumab (MEDI4736) in Previously Untreated Subjects With Higher-Risk Myelodysplastic Syndromes (MDS) or in Elderly Subjects With Acute Myeloid Leukemia (AML),University Medical Center Freiburg,leukemia,Celgene
+A Safety and Efficacy Study of Oral AG-120 Plus Subcutaneous Azacitidine and Oral AG-221 Plus Subcutaneous Azacitidine in Subjects With Newly Diagnosed Acute Myeloid Leukemia (AML),Massachusetts General Hospital,leukemia,Celgene
+An Efficacy and Safety Study of AG-221 (CC-90007) Versus Conventional Care Regimens in Older Subjects With Late Stage Acute Myeloid Leukemia Harboring an Isocitrate Dehydrogenase 2 Mutation,Yonsei University,leukemia,Celgene
+Combination Chemotherapy With or Without Temsirolimus in Treating Patients With Intermediate Risk Rhabdomyosarcoma,Columbia University,rhabdomyosarcoma,National Cancer Institute
+Chordoma Family Study,Massachusetts General Hospital,chordoma,National Cancer Institute
+Lenalidomide and Blinatumomab in Treating Patients With Relapsed Non-Hodgkin Lymphoma,Yale University,lymphoma,National Cancer Institute
+Genetic Analysis of Brain Tumors,Columbia University,astrocytoma,National Cancer Institute
+Atezolizumab in Treating Patients With Recurrent BCG-Unresponsive Non-muscle Invasive Bladder Cancer,Boston University,carcinoma,National Cancer Institute
+Long Term Safety and Efficacy Study of Tanezumab in Subjects With Osteoarthritis of the Hip or Knee,Yonsei University,osteoarthritis,Pfizer
+Pomalidomide and Dexamethasone With or Without Ixazomib in Treating Patients With Relapsed Multiple Myeloma,Decatur Memorial Hospital,multiple myeloma,Alliance for Clinical Trials in Oncology
diff --git a/examples/semtab2020_novartis/tables/61Q74GUH.json b/examples/semtab2020_novartis/tables/61Q74GUH.json
new file mode 100644
index 0000000..45ef659
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/61Q74GUH.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["Osimertinib and Navitoclax in Treating Patients With EGFR-Positive Previously Treated Advanced or Metastatic Non-small Cell Lung Cancer","A Comparison of Reduced Dose Total Body Irradiation (TBI) and Cyclophosphamide With Fludarabine and Melphalan Reduced Intensity Conditioning in Adults With Acute Lymphoblastic Leukaemia (ALL) in Complete Remission. (ALL-RIC)","Multi-DOSE Oral Ondansetron for Pediatric Acute GastroEnteritis","Safety, Tolerability, and Efficacy Study of Idiopathic Pulmonary Fibrosis","Standard-Dose Combination Chemotherapy or High-Dose Combination Chemotherapy and Stem Cell Transplant in Treating Patients With Relapsed or Refractory Germ Cell Tumors","Left Versus Right Radial Artery and Radiation Exposure in Patients With Predictors of Trans-radial Failure","Safety and Efficacy Trial of RPC1063 for Moderate to Severe Ulcerative Colitis","Open-Label Extension of RPC1063 as Therapy for Moderate to Severe Ulcerative Colitis","A Study to Determine Dose and Tolerability of CC-220 Monotherapy, in Combination With Dexamethasone, and in Combination With Dexamethasone and Daratumumab or Bortezomib in Subjects With Relapsed and Refractory Multiple Myeloma (MM)","A Study to Determine Dose and Regimen of Durvalumab as Monotherapy or in Combination With Pomalidomide With or Without Dexamethasone in Subjects With Relapsed and Refractory Multiple Myeloma","An Efficacy and Safety Study of Azacitidine Subcutaneous in Combination With Durvalumab (MEDI4736) in Previously Untreated Subjects With Higher-Risk Myelodysplastic Syndromes (MDS) or in Elderly Subjects With Acute Myeloid Leukemia (AML)","A Safety and Efficacy Study of Oral AG-120 Plus Subcutaneous Azacitidine and Oral AG-221 Plus Subcutaneous Azacitidine in Subjects With Newly Diagnosed Acute Myeloid Leukemia (AML)","An Efficacy and Safety Study of AG-221 (CC-90007) Versus Conventional Care Regimens in Older Subjects With Late Stage Acute Myeloid Leukemia Harboring an Isocitrate Dehydrogenase 2 Mutation","Combination Chemotherapy With or Without Temsirolimus in Treating Patients With Intermediate Risk Rhabdomyosarcoma","Chordoma Family Study","Lenalidomide and Blinatumomab in Treating Patients With Relapsed Non-Hodgkin Lymphoma","Genetic Analysis of Brain Tumors","Atezolizumab in Treating Patients With Recurrent BCG-Unresponsive Non-muscle Invasive Bladder Cancer","Long Term Safety and Efficacy Study of Tanezumab in Subjects With Osteoarthritis of the Hip or Knee","Pomalidomide and Dexamethasone With or Without Ixazomib in Treating Patients With Relapsed Multiple Myeloma"]},{"index":1,"name":"col1","values":["Ohio State University","King's College London","Alberta Children’s Hospital Foundation","Vanderbilt University","Vanderbilt University","New York University","Yonsei University","Yonsei University","University of Pennsylvania","Cornell University","University Medical Center Freiburg","Massachusetts General Hospital","Yonsei University","Columbia University","Massachusetts General Hospital","Yale University","Columbia University","Boston University","Yonsei University","Decatur Memorial Hospital"]},{"index":2,"name":"col2","values":["carcinoma","leukemia","diarrhea","pulmonary fibrosis","teratoma","coronary artery disease","colitis","colitis","multiple myeloma","multiple myeloma","leukemia","leukemia","leukemia","rhabdomyosarcoma","chordoma","lymphoma","astrocytoma","carcinoma","osteoarthritis","multiple myeloma"]},{"index":3,"name":"col3","values":["National Cancer Institute","University of Birmingham","University of Calgary","FibroGen (United States)","Alliance for Clinical Trials in Oncology","New York University School of Medicine","Celgene","Celgene","Celgene","Celgene","Celgene","Celgene","Celgene","National Cancer Institute","National Cancer Institute","National Cancer Institute","National Cancer Institute","National Cancer Institute","Pfizer","Alliance for Clinical Trials in Oncology"]}],"metadata":{"table_id":"61Q74GUH","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":135,"url":"http://www.wikidata.org/entity/Q61862251","qnode_id":"Q61862251"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q309331","qnode_id":"Q309331"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q33525","qnode_id":"Q33525"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q664846","qnode_id":"Q664846"}]],[[{"start":0,"end":224,"url":"http://www.wikidata.org/entity/Q61700075","qnode_id":"Q61700075"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q245247","qnode_id":"Q245247"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q29496","qnode_id":"Q29496"}],[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q223429","qnode_id":"Q223429"}]],[[{"start":0,"end":63,"url":"http://www.wikidata.org/entity/Q61865460","qnode_id":"Q61865460"}],[{"start":0,"end":38,"url":"http://www.wikidata.org/entity/Q30297090","qnode_id":"Q30297090"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q40878","qnode_id":"Q40878"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q1067471","qnode_id":"Q1067471"}]],[[{"start":0,"end":73,"url":"http://www.wikidata.org/entity/Q61864929","qnode_id":"Q61864929"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q29052","qnode_id":"Q29052"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q32446","qnode_id":"Q32446"}],[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q30283105","qnode_id":"Q30283105"}]],[[{"start":0,"end":167,"url":"http://www.wikidata.org/entity/Q61864694","qnode_id":"Q61864694"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q29052","qnode_id":"Q29052"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q200741","qnode_id":"Q200741"}],[{"start":0,"end":40,"url":"http://www.wikidata.org/entity/Q4732351","qnode_id":"Q4732351"}]],[[{"start":0,"end":106,"url":"http://www.wikidata.org/entity/Q61707493","qnode_id":"Q61707493"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q49210","qnode_id":"Q49210"}],[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q844935","qnode_id":"Q844935"}],[{"start":0,"end":38,"url":"http://www.wikidata.org/entity/Q3100195","qnode_id":"Q3100195"}]],[[{"start":0,"end":78,"url":"http://www.wikidata.org/entity/Q61862270","qnode_id":"Q61862270"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q39988","qnode_id":"Q39988"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q2453464","qnode_id":"Q2453464"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q842947","qnode_id":"Q842947"}]],[[{"start":0,"end":84,"url":"http://www.wikidata.org/entity/Q61862245","qnode_id":"Q61862245"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q39988","qnode_id":"Q39988"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q2453464","qnode_id":"Q2453464"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q842947","qnode_id":"Q842947"}]],[[{"start":0,"end":231,"url":"http://www.wikidata.org/entity/Q61862177","qnode_id":"Q61862177"}],[{"start":0,"end":26,"url":"http://www.wikidata.org/entity/Q49117","qnode_id":"Q49117"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q467635","qnode_id":"Q467635"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q842947","qnode_id":"Q842947"}]],[[{"start":0,"end":190,"url":"http://www.wikidata.org/entity/Q61862227","qnode_id":"Q61862227"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q49115","qnode_id":"Q49115"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q467635","qnode_id":"Q467635"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q842947","qnode_id":"Q842947"}]],[[{"start":0,"end":237,"url":"http://www.wikidata.org/entity/Q61862174","qnode_id":"Q61862174"}],[{"start":0,"end":34,"url":"http://www.wikidata.org/entity/Q127773","qnode_id":"Q127773"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q29496","qnode_id":"Q29496"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q842947","qnode_id":"Q842947"}]],[[{"start":0,"end":180,"url":"http://www.wikidata.org/entity/Q61862214","qnode_id":"Q61862214"}],[{"start":0,"end":30,"url":"http://www.wikidata.org/entity/Q126412","qnode_id":"Q126412"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q29496","qnode_id":"Q29496"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q842947","qnode_id":"Q842947"}]],[[{"start":0,"end":189,"url":"http://www.wikidata.org/entity/Q61862232","qnode_id":"Q61862232"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q39988","qnode_id":"Q39988"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q29496","qnode_id":"Q29496"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q842947","qnode_id":"Q842947"}]],[[{"start":0,"end":114,"url":"http://www.wikidata.org/entity/Q61862239","qnode_id":"Q61862239"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q49088","qnode_id":"Q49088"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1898141","qnode_id":"Q1898141"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q664846","qnode_id":"Q664846"}]],[[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q61865134","qnode_id":"Q61865134"}],[{"start":0,"end":30,"url":"http://www.wikidata.org/entity/Q126412","qnode_id":"Q126412"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q1076389","qnode_id":"Q1076389"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q664846","qnode_id":"Q664846"}]],[[{"start":0,"end":85,"url":"http://www.wikidata.org/entity/Q61862236","qnode_id":"Q61862236"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q49112","qnode_id":"Q49112"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q208414","qnode_id":"Q208414"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q664846","qnode_id":"Q664846"}]],[[{"start":0,"end":32,"url":"http://www.wikidata.org/entity/Q61865365","qnode_id":"Q61865365"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q49088","qnode_id":"Q49088"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q177755","qnode_id":"Q177755"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q664846","qnode_id":"Q664846"}]],[[],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q49110","qnode_id":"Q49110"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q33525","qnode_id":"Q33525"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q664846","qnode_id":"Q664846"}]],[[{"start":0,"end":99,"url":"http://www.wikidata.org/entity/Q61862248","qnode_id":"Q61862248"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q39988","qnode_id":"Q39988"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q62736","qnode_id":"Q62736"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q206921","qnode_id":"Q206921"}]],[[{"start":0,"end":107,"url":"http://www.wikidata.org/entity/Q61864779","qnode_id":"Q61864779"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q22033932","qnode_id":"Q22033932"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q467635","qnode_id":"Q467635"}],[{"start":0,"end":40,"url":"http://www.wikidata.org/entity/Q4732351","qnode_id":"Q4732351"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q30612:0","abs_uri":"http://www.wikidata.org/entity/Q30612","rel_uri":"wd:Q30612","approximation":false,"readable_label":"clinical trial (Q30612)"},{"id":"col-2","col_index":2,"label":"col2"},{"id":"http://www.wikidata.org/entity/Q12136:0","abs_uri":"http://www.wikidata.org/entity/Q12136","rel_uri":"wd:Q12136","approximation":false,"readable_label":"disease (Q12136)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"col-3","col_index":3,"label":"col3"}],"edges":[{"source":"http://www.wikidata.org/entity/Q30612:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q30612:0","target":"col-1","abs_uri":"http://www.wikidata.org/prop/P6153","rel_uri":"p:P6153","approximation":false,"readable_label":"research site (P6153)","id":2},{"source":"http://www.wikidata.org/entity/Q30612:0","target":"http://www.wikidata.org/entity/Q12136:0","abs_uri":"http://www.wikidata.org/prop/P1050","rel_uri":"p:P1050","approximation":false,"readable_label":"medical condition (P1050)","id":3},{"source":"http://www.wikidata.org/entity/Q30612:0","target":"col-3","abs_uri":"http://www.wikidata.org/prop/P859","rel_uri":"p:P859","approximation":false,"readable_label":"sponsor (P859)","id":4},{"source":"http://www.wikidata.org/entity/Q12136:0","target":"col-2","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":5}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/61Q74GUH.links.tsv b/examples/semtab2020_novartis/tables/61Q74GUH.links.tsv
new file mode 100644
index 0000000..ea5a98d
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/61Q74GUH.links.tsv
@@ -0,0 +1,79 @@
+0 0 Q61862251
+0 1 Q309331
+0 2 Q33525
+0 3 Q664846
+1 0 Q61700075
+1 1 Q245247
+1 2 Q29496
+1 3 Q223429
+2 0 Q61865460
+2 1 Q30297090
+2 2 Q40878
+2 3 Q1067471
+3 0 Q61864929
+3 1 Q29052
+3 2 Q32446
+3 3 Q30283105
+4 0 Q61864694
+4 1 Q29052
+4 2 Q200741
+4 3 Q4732351
+5 0 Q61707493
+5 1 Q49210
+5 2 Q844935
+5 3 Q3100195
+6 0 Q61862270
+6 1 Q39988
+6 2 Q2453464
+6 3 Q842947
+7 0 Q61862245
+7 1 Q39988
+7 2 Q2453464
+7 3 Q842947
+8 0 Q61862177
+8 1 Q49117
+8 2 Q467635
+8 3 Q842947
+9 0 Q61862227
+9 1 Q49115
+9 2 Q467635
+9 3 Q842947
+10 0 Q61862174
+10 1 Q127773
+10 2 Q29496
+10 3 Q842947
+11 0 Q61862214
+11 1 Q126412
+11 2 Q29496
+11 3 Q842947
+12 0 Q61862232
+12 1 Q39988
+12 2 Q29496
+12 3 Q842947
+13 0 Q61862239
+13 1 Q49088
+13 2 Q1898141
+13 3 Q664846
+14 0 Q61865134
+14 1 Q126412
+14 2 Q1076389
+14 3 Q664846
+15 0 Q61862236
+15 1 Q49112
+15 2 Q208414
+15 3 Q664846
+16 0 Q61865365
+16 1 Q49088
+16 2 Q177755
+16 3 Q664846
+17 1 Q49110
+17 2 Q33525
+17 3 Q664846
+18 0 Q61862248
+18 1 Q39988
+18 2 Q62736
+18 3 Q206921
+19 0 Q61864779
+19 1 Q22033932
+19 2 Q467635
+19 3 Q4732351
diff --git a/examples/semtab2020_novartis/tables/6FJO0SHP.csv b/examples/semtab2020_novartis/tables/6FJO0SHP.csv
new file mode 100644
index 0000000..c1d94a2
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/6FJO0SHP.csv
@@ -0,0 +1,21 @@
+col0,col1,col2
+T-Cel antigen Receptor (TCR) pathway during Staphylococcus aureus infection,ZAP70,toxic shock syndrome
+IM1 and megakaryocytes in obesity,F2,obesity
+Extracelllar vesicle-mediated signaling in recipient cells,MFGE8,cancer
+ErbB Signaling Pathwa|,AKT2,multiple sclerosis
+Lipid Metabolism Pahway,palmitic acid,obesity
+Photodynamic therapy-induced unolded protein response,WARS1,cancer
+Photodynamic therapy0induced NFE2L2 (NRF2) survival signaling,ABCG2,cancer
+Lgptin and adiponectin,acetyl coenzyme a,glucose metabolism disease
+Imatinie and Chronic Myeloid Leukemia,imatinib,chronic myeloid leukemia
+HypertrophyModel,EIF4E,heart disease
+"MicroRNA for Targeting Cancer Growth""and Vascularization in Glioblastoma",VEGFC,cancer
+Calcium Regulaion in the Cardiac Celo,epinephrine,cardiovascular disease
+H19 actio Rb-E2F1 signaling and CDK-Beta-catenin activity,SOX4,colorectal cancer
+PDGFR-beta pathwy,HRAS,ovarian cancer
+Folate-Alcohol andCancer Pathway Hypotheses,ethanol,oral cavity cancer
+Type II diabetes melltus,adenosine triphosphate,hyperglycemia
+TP53 etwork,CDKN2A,cancer
+Fatty Aid Omega Oxidation,lauric acid,lipid metabolism disorder
+Hepatitis C and Hepaocellular Carcinoma,PODXL,hepatocellular carcinoma
+Codene and Morphine Metabolim,morphine,Gilbert syndrome
diff --git a/examples/semtab2020_novartis/tables/6FJO0SHP.json b/examples/semtab2020_novartis/tables/6FJO0SHP.json
new file mode 100644
index 0000000..84a4c5d
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/6FJO0SHP.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["T-Cel antigen Receptor (TCR) pathway during Staphylococcus aureus infection","IM1 and megakaryocytes in obesity","Extracelllar vesicle-mediated signaling in recipient cells","ErbB Signaling Pathwa|","Lipid Metabolism Pahway","Photodynamic therapy-induced unolded protein response","Photodynamic therapy0induced NFE2L2 (NRF2) survival signaling","Lgptin and adiponectin","Imatinie and Chronic Myeloid Leukemia","HypertrophyModel","MicroRNA for Targeting Cancer Growth\"and Vascularization in Glioblastoma","Calcium Regulaion in the Cardiac Celo","H19 actio Rb-E2F1 signaling and CDK-Beta-catenin activity","PDGFR-beta pathwy","Folate-Alcohol andCancer Pathway Hypotheses","Type II diabetes melltus","TP53 etwork","Fatty Aid Omega Oxidation","Hepatitis C and Hepaocellular Carcinoma","Codene and Morphine Metabolim"]},{"index":1,"name":"col1","values":["ZAP70","F2","MFGE8","AKT2","palmitic acid","WARS1","ABCG2","acetyl coenzyme a","imatinib","EIF4E","VEGFC","epinephrine","SOX4","HRAS","ethanol","adenosine triphosphate","CDKN2A","lauric acid","PODXL","morphine"]},{"index":2,"name":"col2","values":["toxic shock syndrome","obesity","cancer","multiple sclerosis","obesity","cancer","cancer","glucose metabolism disease","chronic myeloid leukemia","heart disease","cancer","cardiovascular disease","colorectal cancer","ovarian cancer","oral cavity cancer","hyperglycemia","cancer","lipid metabolism disorder","hepatocellular carcinoma","Gilbert syndrome"]}],"metadata":{"table_id":"6FJO0SHP","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":75,"url":"http://www.wikidata.org/entity/Q30225430","qnode_id":"Q30225430"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14907110","qnode_id":"Q14907110"}],[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q1128440","qnode_id":"Q1128440"}]],[[{"start":0,"end":33,"url":"http://www.wikidata.org/entity/Q30230817","qnode_id":"Q30230817"}],[{"start":0,"end":2,"url":"http://www.wikidata.org/entity/Q14821011","qnode_id":"Q14821011"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}]],[[{"start":0,"end":58,"url":"http://www.wikidata.org/entity/Q30230820","qnode_id":"Q30230820"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q3307805","qnode_id":"Q3307805"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q12078","qnode_id":"Q12078"}]],[[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q30230897","qnode_id":"Q30230897"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q4652205","qnode_id":"Q4652205"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q8277","qnode_id":"Q8277"}]],[[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q44011216","qnode_id":"Q44011216"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q209727","qnode_id":"Q209727"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}]],[[{"start":0,"end":53,"url":"http://www.wikidata.org/entity/Q30230843","qnode_id":"Q30230843"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q7946718","qnode_id":"Q7946718"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q12078","qnode_id":"Q12078"}]],[[{"start":0,"end":61,"url":"http://www.wikidata.org/entity/Q30230842","qnode_id":"Q30230842"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q4650095","qnode_id":"Q4650095"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q12078","qnode_id":"Q12078"}]],[[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q30225533","qnode_id":"Q30225533"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q715317","qnode_id":"Q715317"}],[{"start":0,"end":26,"url":"http://www.wikidata.org/entity/Q18556224","qnode_id":"Q18556224"}]],[[{"start":0,"end":37,"url":"http://www.wikidata.org/entity/Q30230734","qnode_id":"Q30230734"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q177094","qnode_id":"Q177094"}],[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q729735","qnode_id":"Q729735"}]],[[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q30225572","qnode_id":"Q30225572"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q5408696","qnode_id":"Q5408696"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q190805","qnode_id":"Q190805"}]],[[{"start":0,"end":72,"url":"http://www.wikidata.org/entity/Q30230729","qnode_id":"Q30230729"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q5398950","qnode_id":"Q5398950"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q12078","qnode_id":"Q12078"}]],[[{"start":0,"end":37,"url":"http://www.wikidata.org/entity/Q30230893","qnode_id":"Q30230893"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q132621","qnode_id":"Q132621"}],[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q389735","qnode_id":"Q389735"}]],[[{"start":0,"end":57,"url":"http://www.wikidata.org/entity/Q30230883","qnode_id":"Q30230883"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18031651","qnode_id":"Q18031651"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q188874","qnode_id":"Q188874"}]],[[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q30230885","qnode_id":"Q30230885"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14884140","qnode_id":"Q14884140"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q172341","qnode_id":"Q172341"}]],[[{"start":0,"end":43,"url":"http://www.wikidata.org/entity/Q30225384","qnode_id":"Q30225384"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q153","qnode_id":"Q153"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q1143025","qnode_id":"Q1143025"}]],[[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q30225382","qnode_id":"Q30225382"}],[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q80863","qnode_id":"Q80863"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q271993","qnode_id":"Q271993"}]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q30225385","qnode_id":"Q30225385"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q5009957","qnode_id":"Q5009957"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q12078","qnode_id":"Q12078"}]],[[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q30230781","qnode_id":"Q30230781"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q422627","qnode_id":"Q422627"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q1476525","qnode_id":"Q1476525"}]],[[{"start":0,"end":39,"url":"http://www.wikidata.org/entity/Q30230736","qnode_id":"Q30230736"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18030638","qnode_id":"Q18030638"}],[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q1148337","qnode_id":"Q1148337"}]],[[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q30230768","qnode_id":"Q30230768"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q81225","qnode_id":"Q81225"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q752216","qnode_id":"Q752216"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q4915012:0","abs_uri":"http://www.wikidata.org/entity/Q4915012","rel_uri":"wd:Q4915012","approximation":false,"readable_label":"biological pathway (Q4915012)"},{"id":"col-2","col_index":2,"label":"col2"},{"id":"http://www.wikidata.org/entity/Q12136:0","abs_uri":"http://www.wikidata.org/entity/Q12136","rel_uri":"wd:Q12136","approximation":false,"readable_label":"disease (Q12136)"},{"id":"col-1","col_index":1,"label":"col1"}],"edges":[{"source":"http://www.wikidata.org/entity/Q4915012:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q4915012:0","target":"col-1","abs_uri":"http://www.wikidata.org/prop/P527","rel_uri":"p:P527","approximation":false,"readable_label":"has part (P527)","id":2},{"source":"http://www.wikidata.org/entity/Q4915012:0","target":"http://www.wikidata.org/entity/Q12136:0","abs_uri":"http://www.wikidata.org/prop/P1050","rel_uri":"p:P1050","approximation":false,"readable_label":"medical condition (P1050)","id":3},{"source":"http://www.wikidata.org/entity/Q12136:0","target":"col-2","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":4}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/6FJO0SHP.links.tsv b/examples/semtab2020_novartis/tables/6FJO0SHP.links.tsv
new file mode 100644
index 0000000..d288da7
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/6FJO0SHP.links.tsv
@@ -0,0 +1,60 @@
+0 0 Q30225430
+0 1 Q14907110
+0 2 Q1128440
+1 0 Q30230817
+1 1 Q14821011
+1 2 Q12174
+2 0 Q30230820
+2 1 Q3307805
+2 2 Q12078
+3 0 Q30230897
+3 1 Q4652205
+3 2 Q8277
+4 0 Q44011216
+4 1 Q209727
+4 2 Q12174
+5 0 Q30230843
+5 1 Q7946718
+5 2 Q12078
+6 0 Q30230842
+6 1 Q4650095
+6 2 Q12078
+7 0 Q30225533
+7 1 Q715317
+7 2 Q18556224
+8 0 Q30230734
+8 1 Q177094
+8 2 Q729735
+9 0 Q30225572
+9 1 Q5408696
+9 2 Q190805
+10 0 Q30230729
+10 1 Q5398950
+10 2 Q12078
+11 0 Q30230893
+11 1 Q132621
+11 2 Q389735
+12 0 Q30230883
+12 1 Q18031651
+12 2 Q188874
+13 0 Q30230885
+13 1 Q14884140
+13 2 Q172341
+14 0 Q30225384
+14 1 Q153
+14 2 Q1143025
+15 0 Q30225382
+15 1 Q80863
+15 2 Q271993
+16 0 Q30225385
+16 1 Q5009957
+16 2 Q12078
+17 0 Q30230781
+17 1 Q422627
+17 2 Q1476525
+18 0 Q30230736
+18 1 Q18030638
+18 2 Q1148337
+19 0 Q30230768
+19 1 Q81225
+19 2 Q752216
diff --git a/examples/semtab2020_novartis/tables/6UREB4EJ.csv b/examples/semtab2020_novartis/tables/6UREB4EJ.csv
new file mode 100644
index 0000000..100494e
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/6UREB4EJ.csv
@@ -0,0 +1,21 @@
+col0,col1,col2,col3
+Electrophysiology and Blood Flow in Patients With Schizophrenia and Their Siblings,National Institutes of Health,schizophrenia,National Institute of Mental Health
+PET Scanning in Parkinson s Disease,National Institutes of Health,Parkinson disease,National Institute of Mental Health
+Safety and Tolerability of a Modified Vaccinia Ankara (MVA)-Based Vaccine Modified to Express Brachyury and T-cell Costimulatory Molecules (MVA-Brachyury-TRICOM),National Institutes of Health,vaccinia,National Cancer Institute
+AZD6244 Hydrogen Sulfate for Children With Nervous System Tumors,National Institutes of Health,neurofibroma,National Cancer Institute
+"Pilot Study of Allogeneic Tumor Cell Vaccine With Metronomic Oral Cyclophosphamide and Celecoxib in Patients Undergoing Resection of Lung and Esophageal Cancers, Thymic Neoplasms, and Malignant Pleural Mesotheliomas",National Institutes of Health,sarcoma,National Cancer Institute
+Effects of Potent Antiretroviral Therapy on Kaposi s Sarcoma,National Institutes of Health,sarcoma,National Cancer Institute
+Administration of Anti-CD19-chimeric-antigen-receptor-transduced T Cells From the Original Transplant Donor to Patients With Recurrent or Persistent B-cell Malignancies After Allogeneic Stem Cell Transplantation,National Institutes of Health,lymphoma,National Cancer Institute
+Phase II Study of Dose-Adjusted EPOCH-Rituximab in Adults With Untreated Burkitt Lymphoma and c-MYC+ Diffuse Large B-Cell Lymphoma,National Institutes of Health,lymphoma,National Cancer Institute
+Topical Romidepsin to Treat Early-Stage Cutaneous T-Cell Lymphoma,National Institutes of Health,lymphoma,National Cancer Institute
+EPOCH and Rituximab to Treat Non-Hodgkin's Lymphoma in Patients With HIV Infection,National Institutes of Health,lymphoma,National Cancer Institute
+Natural History of Patients With Brain and Spinal Cord Tumors,National Institutes of Health,astrocytoma,National Cancer Institute
+Natural History and Tissue Acquisition Study of Adrenocortical Carcinoma,National Institutes of Health,carcinoma,National Cancer Institute
+PLX3397 in Children and Young Adults With Refractory Leukemias and Refractory Solid Tumors Including Neurofibromatosis Type 1 (NF1) Associated Plexiform Neurofibromas (PN),National Institutes of Health,leukemia,National Cancer Institute
+Combination Antibody Therapy for Relapsed Lymphoma and Chronic Lymphocytic Leukemia,National Institutes of Health,leukemia,National Cancer Institute
+"Pilot Study of Non-Myeloablative, HLA-Matched Allogeneic Stem Cell Transplantation for Pediatric Hematopoietic Malignancies",National Institutes of Health,leukemia,National Cancer Institute
+Study of Efficacy and Safety of CTL019 in Adult DLBCL Patients,Novartis,lymphoma,Novartis
+Vortioxetine for Binge Eating Disorder,University of Chicago,bulimia,University of Chicago
+Bone Mass Accrual in Adolescent Athletes,Massachusetts General Hospital,amenorrhea,Massachusetts General Hospital
+a4b7 Integrin in Eosinophilic Esophagitis,Massachusetts General Hospital,esophagitis,Massachusetts General Hospital
+REAL HEALTH-Diabetes: Reach Ahead for Lifestyle and Health-Diabetes,Massachusetts General Hospital,diabetes mellitus,Massachusetts General Hospital
diff --git a/examples/semtab2020_novartis/tables/6UREB4EJ.json b/examples/semtab2020_novartis/tables/6UREB4EJ.json
new file mode 100644
index 0000000..7fdef80
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/6UREB4EJ.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["Electrophysiology and Blood Flow in Patients With Schizophrenia and Their Siblings","PET Scanning in Parkinson s Disease","Safety and Tolerability of a Modified Vaccinia Ankara (MVA)-Based Vaccine Modified to Express Brachyury and T-cell Costimulatory Molecules (MVA-Brachyury-TRICOM)","AZD6244 Hydrogen Sulfate for Children With Nervous System Tumors","Pilot Study of Allogeneic Tumor Cell Vaccine With Metronomic Oral Cyclophosphamide and Celecoxib in Patients Undergoing Resection of Lung and Esophageal Cancers, Thymic Neoplasms, and Malignant Pleural Mesotheliomas","Effects of Potent Antiretroviral Therapy on Kaposi s Sarcoma","Administration of Anti-CD19-chimeric-antigen-receptor-transduced T Cells From the Original Transplant Donor to Patients With Recurrent or Persistent B-cell Malignancies After Allogeneic Stem Cell Transplantation","Phase II Study of Dose-Adjusted EPOCH-Rituximab in Adults With Untreated Burkitt Lymphoma and c-MYC+ Diffuse Large B-Cell Lymphoma","Topical Romidepsin to Treat Early-Stage Cutaneous T-Cell Lymphoma","EPOCH and Rituximab to Treat Non-Hodgkin's Lymphoma in Patients With HIV Infection","Natural History of Patients With Brain and Spinal Cord Tumors","Natural History and Tissue Acquisition Study of Adrenocortical Carcinoma","PLX3397 in Children and Young Adults With Refractory Leukemias and Refractory Solid Tumors Including Neurofibromatosis Type 1 (NF1) Associated Plexiform Neurofibromas (PN)","Combination Antibody Therapy for Relapsed Lymphoma and Chronic Lymphocytic Leukemia","Pilot Study of Non-Myeloablative, HLA-Matched Allogeneic Stem Cell Transplantation for Pediatric Hematopoietic Malignancies","Study of Efficacy and Safety of CTL019 in Adult DLBCL Patients","Vortioxetine for Binge Eating Disorder","Bone Mass Accrual in Adolescent Athletes","a4b7 Integrin in Eosinophilic Esophagitis","REAL HEALTH-Diabetes: Reach Ahead for Lifestyle and Health-Diabetes"]},{"index":1,"name":"col1","values":["National Institutes of Health","National Institutes of Health","National Institutes of Health","National Institutes of Health","National Institutes of Health","National Institutes of Health","National Institutes of Health","National Institutes of Health","National Institutes of Health","National Institutes of Health","National Institutes of Health","National Institutes of Health","National Institutes of Health","National Institutes of Health","National Institutes of Health","Novartis","University of Chicago","Massachusetts General Hospital","Massachusetts General Hospital","Massachusetts General Hospital"]},{"index":2,"name":"col2","values":["schizophrenia","Parkinson disease","vaccinia","neurofibroma","sarcoma","sarcoma","lymphoma","lymphoma","lymphoma","lymphoma","astrocytoma","carcinoma","leukemia","leukemia","leukemia","lymphoma","bulimia","amenorrhea","esophagitis","diabetes mellitus"]},{"index":3,"name":"col3","values":["National Institute of Mental Health","National Institute of Mental Health","National Cancer Institute","National Cancer Institute","National Cancer Institute","National Cancer Institute","National Cancer Institute","National Cancer Institute","National Cancer Institute","National Cancer Institute","National Cancer Institute","National Cancer Institute","National Cancer Institute","National Cancer Institute","National Cancer Institute","Novartis","University of Chicago","Massachusetts General Hospital","Massachusetts General Hospital","Massachusetts General Hospital"]}],"metadata":{"table_id":"6UREB4EJ","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":82,"url":"http://www.wikidata.org/entity/Q61694866","qnode_id":"Q61694866"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q41112","qnode_id":"Q41112"}],[{"start":0,"end":35,"url":"http://www.wikidata.org/entity/Q1967405","qnode_id":"Q1967405"}]],[[{"start":0,"end":35,"url":"http://www.wikidata.org/entity/Q61694894","qnode_id":"Q61694894"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q11085","qnode_id":"Q11085"}],[{"start":0,"end":35,"url":"http://www.wikidata.org/entity/Q1967405","qnode_id":"Q1967405"}]],[[{"start":0,"end":161,"url":"http://www.wikidata.org/entity/Q61864735","qnode_id":"Q61864735"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q27675547","qnode_id":"Q27675547"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q664846","qnode_id":"Q664846"}]],[[{"start":0,"end":64,"url":"http://www.wikidata.org/entity/Q61864912","qnode_id":"Q61864912"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q1418735","qnode_id":"Q1418735"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q664846","qnode_id":"Q664846"}]],[[],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q223911","qnode_id":"Q223911"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q664846","qnode_id":"Q664846"}]],[[{"start":0,"end":60,"url":"http://www.wikidata.org/entity/Q61694874","qnode_id":"Q61694874"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q223911","qnode_id":"Q223911"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q664846","qnode_id":"Q664846"}]],[[{"start":0,"end":211,"url":"http://www.wikidata.org/entity/Q61865036","qnode_id":"Q61865036"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q208414","qnode_id":"Q208414"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q664846","qnode_id":"Q664846"}]],[[{"start":0,"end":130,"url":"http://www.wikidata.org/entity/Q61865034","qnode_id":"Q61865034"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q208414","qnode_id":"Q208414"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q664846","qnode_id":"Q664846"}]],[[{"start":0,"end":65,"url":"http://www.wikidata.org/entity/Q61864741","qnode_id":"Q61864741"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q208414","qnode_id":"Q208414"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q664846","qnode_id":"Q664846"}]],[[{"start":0,"end":82,"url":"http://www.wikidata.org/entity/Q61694878","qnode_id":"Q61694878"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q208414","qnode_id":"Q208414"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q664846","qnode_id":"Q664846"}]],[[{"start":0,"end":61,"url":"http://www.wikidata.org/entity/Q61694881","qnode_id":"Q61694881"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q177755","qnode_id":"Q177755"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q664846","qnode_id":"Q664846"}]],[[{"start":0,"end":72,"url":"http://www.wikidata.org/entity/Q61864763","qnode_id":"Q61864763"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q33525","qnode_id":"Q33525"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q664846","qnode_id":"Q664846"}]],[[{"start":0,"end":171,"url":"http://www.wikidata.org/entity/Q61864692","qnode_id":"Q61864692"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q29496","qnode_id":"Q29496"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q664846","qnode_id":"Q664846"}]],[[{"start":0,"end":83,"url":"http://www.wikidata.org/entity/Q61694890","qnode_id":"Q61694890"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q29496","qnode_id":"Q29496"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q664846","qnode_id":"Q664846"}]],[[{"start":0,"end":123,"url":"http://www.wikidata.org/entity/Q61694884","qnode_id":"Q61694884"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q29496","qnode_id":"Q29496"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q664846","qnode_id":"Q664846"}]],[[{"start":0,"end":62,"url":"http://www.wikidata.org/entity/Q61862268","qnode_id":"Q61862268"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q507154","qnode_id":"Q507154"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q208414","qnode_id":"Q208414"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q507154","qnode_id":"Q507154"}]],[[{"start":0,"end":38,"url":"http://www.wikidata.org/entity/Q61862247","qnode_id":"Q61862247"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q131252","qnode_id":"Q131252"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q180913","qnode_id":"Q180913"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q131252","qnode_id":"Q131252"}]],[[{"start":0,"end":40,"url":"http://www.wikidata.org/entity/Q61865076","qnode_id":"Q61865076"}],[{"start":0,"end":30,"url":"http://www.wikidata.org/entity/Q126412","qnode_id":"Q126412"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q334655","qnode_id":"Q334655"}],[{"start":0,"end":30,"url":"http://www.wikidata.org/entity/Q126412","qnode_id":"Q126412"}]],[[{"start":0,"end":41,"url":"http://www.wikidata.org/entity/Q61862243","qnode_id":"Q61862243"}],[{"start":0,"end":30,"url":"http://www.wikidata.org/entity/Q126412","qnode_id":"Q126412"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q298230","qnode_id":"Q298230"}],[{"start":0,"end":30,"url":"http://www.wikidata.org/entity/Q126412","qnode_id":"Q126412"}]],[[{"start":0,"end":67,"url":"http://www.wikidata.org/entity/Q61864708","qnode_id":"Q61864708"}],[{"start":0,"end":30,"url":"http://www.wikidata.org/entity/Q126412","qnode_id":"Q126412"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q12206","qnode_id":"Q12206"}],[{"start":0,"end":30,"url":"http://www.wikidata.org/entity/Q126412","qnode_id":"Q126412"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q30612:0","abs_uri":"http://www.wikidata.org/entity/Q30612","rel_uri":"wd:Q30612","approximation":false,"readable_label":"clinical trial (Q30612)"},{"id":"col-2","col_index":2,"label":"col2"},{"id":"http://www.wikidata.org/entity/Q12136:0","abs_uri":"http://www.wikidata.org/entity/Q12136","rel_uri":"wd:Q12136","approximation":false,"readable_label":"disease (Q12136)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"col-3","col_index":3,"label":"col3"}],"edges":[{"source":"http://www.wikidata.org/entity/Q30612:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q30612:0","target":"col-1","abs_uri":"http://www.wikidata.org/prop/P6153","rel_uri":"p:P6153","approximation":false,"readable_label":"research site (P6153)","id":2},{"source":"http://www.wikidata.org/entity/Q30612:0","target":"http://www.wikidata.org/entity/Q12136:0","abs_uri":"http://www.wikidata.org/prop/P1050","rel_uri":"p:P1050","approximation":false,"readable_label":"medical condition (P1050)","id":3},{"source":"http://www.wikidata.org/entity/Q30612:0","target":"col-3","abs_uri":"http://www.wikidata.org/prop/P859","rel_uri":"p:P859","approximation":false,"readable_label":"sponsor (P859)","id":4},{"source":"http://www.wikidata.org/entity/Q12136:0","target":"col-2","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":5}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/6UREB4EJ.links.tsv b/examples/semtab2020_novartis/tables/6UREB4EJ.links.tsv
new file mode 100644
index 0000000..9c8366b
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/6UREB4EJ.links.tsv
@@ -0,0 +1,79 @@
+0 0 Q61694866
+0 1 Q390551
+0 2 Q41112
+0 3 Q1967405
+1 0 Q61694894
+1 1 Q390551
+1 2 Q11085
+1 3 Q1967405
+2 0 Q61864735
+2 1 Q390551
+2 2 Q27675547
+2 3 Q664846
+3 0 Q61864912
+3 1 Q390551
+3 2 Q1418735
+3 3 Q664846
+4 1 Q390551
+4 2 Q223911
+4 3 Q664846
+5 0 Q61694874
+5 1 Q390551
+5 2 Q223911
+5 3 Q664846
+6 0 Q61865036
+6 1 Q390551
+6 2 Q208414
+6 3 Q664846
+7 0 Q61865034
+7 1 Q390551
+7 2 Q208414
+7 3 Q664846
+8 0 Q61864741
+8 1 Q390551
+8 2 Q208414
+8 3 Q664846
+9 0 Q61694878
+9 1 Q390551
+9 2 Q208414
+9 3 Q664846
+10 0 Q61694881
+10 1 Q390551
+10 2 Q177755
+10 3 Q664846
+11 0 Q61864763
+11 1 Q390551
+11 2 Q33525
+11 3 Q664846
+12 0 Q61864692
+12 1 Q390551
+12 2 Q29496
+12 3 Q664846
+13 0 Q61694890
+13 1 Q390551
+13 2 Q29496
+13 3 Q664846
+14 0 Q61694884
+14 1 Q390551
+14 2 Q29496
+14 3 Q664846
+15 0 Q61862268
+15 1 Q507154
+15 2 Q208414
+15 3 Q507154
+16 0 Q61862247
+16 1 Q131252
+16 2 Q180913
+16 3 Q131252
+17 0 Q61865076
+17 1 Q126412
+17 2 Q334655
+17 3 Q126412
+18 0 Q61862243
+18 1 Q126412
+18 2 Q298230
+18 3 Q126412
+19 0 Q61864708
+19 1 Q126412
+19 2 Q12206
+19 3 Q126412
diff --git a/examples/semtab2020_novartis/tables/779ATGSV.csv b/examples/semtab2020_novartis/tables/779ATGSV.csv
new file mode 100644
index 0000000..f4f2569
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/779ATGSV.csv
@@ -0,0 +1,21 @@
+col0,col1,col2,col3,col4
+ABL1 BCR-ABL,imatinib,ABL1,imatinib,human chromosome 22
+ALK EML4-ALK C1156Y–L1198F,crizotinib,ALK,lorlatinib,human chromosome 2
+ALK ALK FUSION G1269A,ceritinib,ALK,alectinib,human chromosome 2
+ALK EML4-ALK C1156Y,lorlatinib,ALK,crizotinib,human chromosome 2
+ALK ALK FUSION F1245C,ceritinib,ALK,crizotinib,human chromosome 2
+ALK EML4-ALK V1180L,ceritinib,ALK,crizotinib,human chromosome 2
+ALK ALK FUSION I1171,ceritinib,ALK,crizotinib,human chromosome 2
+ALK EML4-ALK G1269A,ceritinib,ALK,crizotinib,human chromosome 2
+ALK EML4-ALK S1206Y,ceritinib,ALK,crizotinib,human chromosome 2
+ALK EML4-ALK L1196M,ceritinib,ALK,crizotinib,human chromosome 2
+ALK EML4-ALK T1151INST,tanespimycin,ALK,crizotinib,human chromosome 2
+ALK ALK FUSION G1202R,tanespimycin,ALK,crizotinib,human chromosome 2
+ROS1 CD74-ROS1 G2101A,foretinib,ROS1,crizotinib,human chromosome 6
+ROS1 CD74-ROS1 L2026M,foretinib,ROS1,crizotinib,human chromosome 6
+ABL1 BCR-ABL T315I,ponatinib,ABL1,imatinib mesylate,human chromosome 9
+ABL1 BCR-ABL E255K,dasatinib,ABL1,axitinib,human chromosome 9
+ABL1 BCR-ABL F486S,nilotinib,ABL1,axitinib,human chromosome 9
+ABL1 BCR-ABL F317L,ponatinib,ABL1,axitinib,human chromosome 9
+BRAF PAPSS1-BRAF,trametinib,BRAF,vemurafenib,human chromosome 7
+BRAF TRIM24-BRAF,trametinib,BRAF,vemurafenib,human chromosome 7
diff --git a/examples/semtab2020_novartis/tables/779ATGSV.json b/examples/semtab2020_novartis/tables/779ATGSV.json
new file mode 100644
index 0000000..0a261d7
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/779ATGSV.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["ABL1 BCR-ABL","ALK EML4-ALK C1156Y–L1198F","ALK ALK FUSION G1269A","ALK EML4-ALK C1156Y","ALK ALK FUSION F1245C","ALK EML4-ALK V1180L","ALK ALK FUSION I1171","ALK EML4-ALK G1269A","ALK EML4-ALK S1206Y","ALK EML4-ALK L1196M","ALK EML4-ALK T1151INST","ALK ALK FUSION G1202R","ROS1 CD74-ROS1 G2101A","ROS1 CD74-ROS1 L2026M","ABL1 BCR-ABL T315I","ABL1 BCR-ABL E255K","ABL1 BCR-ABL F486S","ABL1 BCR-ABL F317L","BRAF PAPSS1-BRAF","BRAF TRIM24-BRAF"]},{"index":1,"name":"col1","values":["imatinib","crizotinib","ceritinib","lorlatinib","ceritinib","ceritinib","ceritinib","ceritinib","ceritinib","ceritinib","tanespimycin","tanespimycin","foretinib","foretinib","ponatinib","dasatinib","nilotinib","ponatinib","trametinib","trametinib"]},{"index":2,"name":"col2","values":["ABL1","ALK","ALK","ALK","ALK","ALK","ALK","ALK","ALK","ALK","ALK","ALK","ROS1","ROS1","ABL1","ABL1","ABL1","ABL1","BRAF","BRAF"]},{"index":3,"name":"col3","values":["imatinib","lorlatinib","alectinib","crizotinib","crizotinib","crizotinib","crizotinib","crizotinib","crizotinib","crizotinib","crizotinib","crizotinib","crizotinib","crizotinib","imatinib mesylate","axitinib","axitinib","axitinib","vemurafenib","vemurafenib"]},{"index":4,"name":"col4","values":["human chromosome 22","human chromosome 2","human chromosome 2","human chromosome 2","human chromosome 2","human chromosome 2","human chromosome 2","human chromosome 2","human chromosome 2","human chromosome 2","human chromosome 2","human chromosome 2","human chromosome 6","human chromosome 6","human chromosome 9","human chromosome 9","human chromosome 9","human chromosome 9","human chromosome 7","human chromosome 7"]}],"metadata":{"table_id":"779ATGSV","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q27653851","qnode_id":"Q27653851"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q177094","qnode_id":"Q177094"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q177094","qnode_id":"Q177094"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q753805","qnode_id":"Q753805"}]],[[{"start":0,"end":26,"url":"http://www.wikidata.org/entity/Q28371308","qnode_id":"Q28371308"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q5186964","qnode_id":"Q5186964"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q17824987","qnode_id":"Q17824987"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q27285820","qnode_id":"Q27285820"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q638893","qnode_id":"Q638893"}]],[[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q28371512","qnode_id":"Q28371512"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q21011233","qnode_id":"Q21011233"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q17824987","qnode_id":"Q17824987"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q21099132","qnode_id":"Q21099132"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q638893","qnode_id":"Q638893"}]],[[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q27919341","qnode_id":"Q27919341"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q27285820","qnode_id":"Q27285820"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q17824987","qnode_id":"Q17824987"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q5186964","qnode_id":"Q5186964"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q638893","qnode_id":"Q638893"}]],[[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q28371511","qnode_id":"Q28371511"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q21011233","qnode_id":"Q21011233"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q17824987","qnode_id":"Q17824987"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q5186964","qnode_id":"Q5186964"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q638893","qnode_id":"Q638893"}]],[[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q28371484","qnode_id":"Q28371484"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q21011233","qnode_id":"Q21011233"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q17824987","qnode_id":"Q17824987"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q5186964","qnode_id":"Q5186964"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q638893","qnode_id":"Q638893"}]],[[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q28371483","qnode_id":"Q28371483"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q21011233","qnode_id":"Q21011233"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q17824987","qnode_id":"Q17824987"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q5186964","qnode_id":"Q5186964"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q638893","qnode_id":"Q638893"}]],[[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q28371262","qnode_id":"Q28371262"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q21011233","qnode_id":"Q21011233"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q17824987","qnode_id":"Q17824987"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q5186964","qnode_id":"Q5186964"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q638893","qnode_id":"Q638893"}]],[[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q28371083","qnode_id":"Q28371083"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q21011233","qnode_id":"Q21011233"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q17824987","qnode_id":"Q17824987"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q5186964","qnode_id":"Q5186964"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q638893","qnode_id":"Q638893"}]],[[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q28045565","qnode_id":"Q28045565"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q21011233","qnode_id":"Q21011233"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q17824987","qnode_id":"Q17824987"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q5186964","qnode_id":"Q5186964"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q638893","qnode_id":"Q638893"}]],[[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q28371084","qnode_id":"Q28371084"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q4552288","qnode_id":"Q4552288"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q17824987","qnode_id":"Q17824987"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q5186964","qnode_id":"Q5186964"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q638893","qnode_id":"Q638893"}]],[[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q28371081","qnode_id":"Q28371081"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q4552288","qnode_id":"Q4552288"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q17824987","qnode_id":"Q17824987"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q5186964","qnode_id":"Q5186964"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q638893","qnode_id":"Q638893"}]],[[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q28371476","qnode_id":"Q28371476"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q5469311","qnode_id":"Q5469311"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18031208","qnode_id":"Q18031208"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q5186964","qnode_id":"Q5186964"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q540857","qnode_id":"Q540857"}]],[[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q28371474","qnode_id":"Q28371474"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q5469311","qnode_id":"Q5469311"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18031208","qnode_id":"Q18031208"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q5186964","qnode_id":"Q5186964"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q540857","qnode_id":"Q540857"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q27906612","qnode_id":"Q27906612"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q198728","qnode_id":"Q198728"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q27114666","qnode_id":"Q27114666"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q27906817","qnode_id":"Q27906817"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q419940","qnode_id":"Q419940"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q4830631","qnode_id":"Q4830631"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q29938666","qnode_id":"Q29938666"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q412327","qnode_id":"Q412327"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q4830631","qnode_id":"Q4830631"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q28371204","qnode_id":"Q28371204"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q198728","qnode_id":"Q198728"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q4830631","qnode_id":"Q4830631"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}]],[[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q28422200","qnode_id":"Q28422200"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q7833138","qnode_id":"Q7833138"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q17853226","qnode_id":"Q17853226"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q423111","qnode_id":"Q423111"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q657319","qnode_id":"Q657319"}]],[[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q28371240","qnode_id":"Q28371240"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q7833138","qnode_id":"Q7833138"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q17853226","qnode_id":"Q17853226"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q423111","qnode_id":"Q423111"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q657319","qnode_id":"Q657319"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q28419128:0","abs_uri":"http://www.wikidata.org/entity/Q28419128","rel_uri":"wd:Q28419128","approximation":false,"readable_label":"transcript fusion (Q28419128)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"http://www.wikidata.org/entity/Q11173:0","abs_uri":"http://www.wikidata.org/entity/Q11173","rel_uri":"wd:Q11173","approximation":false,"readable_label":"chemical compound (Q11173)"},{"id":"col-2","col_index":2,"label":"col2"},{"id":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/entity/Q7187","rel_uri":"wd:Q7187","approximation":false,"readable_label":"gene (Q7187)"},{"id":"col-3","col_index":3,"label":"col3"},{"id":"http://www.wikidata.org/entity/Q11173:1","abs_uri":"http://www.wikidata.org/entity/Q11173","rel_uri":"wd:Q11173","approximation":false,"readable_label":"chemical compound (Q11173)"},{"id":"col-4","col_index":4,"label":"col4"},{"id":"http://www.wikidata.org/entity/Q37748:0","abs_uri":"http://www.wikidata.org/entity/Q37748","rel_uri":"wd:Q37748","approximation":false,"readable_label":"chromosome (Q37748)"}],"edges":[{"source":"http://www.wikidata.org/entity/Q28419128:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q28419128:0","target":"http://www.wikidata.org/entity/Q11173:0","abs_uri":"http://www.wikidata.org/prop/P3354","rel_uri":"p:P3354","approximation":false,"readable_label":"positive therapeutic predictor (P3354)","id":2},{"source":"http://www.wikidata.org/entity/Q28419128:0","target":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/prop/P3433","rel_uri":"p:P3433","approximation":false,"readable_label":"biological variant of (P3433)","id":3},{"source":"http://www.wikidata.org/entity/Q28419128:0","target":"http://www.wikidata.org/entity/Q11173:1","abs_uri":"http://www.wikidata.org/prop/P3355","rel_uri":"p:P3355","approximation":false,"readable_label":"negative therapeutic predictor (P3355)","id":4},{"source":"http://www.wikidata.org/entity/Q28419128:0","target":"http://www.wikidata.org/entity/Q37748:0","abs_uri":"http://www.wikidata.org/prop/P1057","rel_uri":"p:P1057","approximation":false,"readable_label":"chromosome (P1057)","id":5},{"source":"http://www.wikidata.org/entity/Q11173:0","target":"col-1","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":6},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-2","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":7},{"source":"http://www.wikidata.org/entity/Q11173:1","target":"col-3","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":8},{"source":"http://www.wikidata.org/entity/Q37748:0","target":"col-4","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":9}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/779ATGSV.links.tsv b/examples/semtab2020_novartis/tables/779ATGSV.links.tsv
new file mode 100644
index 0000000..86b51cc
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/779ATGSV.links.tsv
@@ -0,0 +1,100 @@
+0 0 Q27653851
+0 1 Q177094
+0 2 Q14873998
+0 3 Q177094
+0 4 Q753805
+1 0 Q28371308
+1 1 Q5186964
+1 2 Q17824987
+1 3 Q27285820
+1 4 Q638893
+2 0 Q28371512
+2 1 Q21011233
+2 2 Q17824987
+2 3 Q21099132
+2 4 Q638893
+3 0 Q27919341
+3 1 Q27285820
+3 2 Q17824987
+3 3 Q5186964
+3 4 Q638893
+4 0 Q28371511
+4 1 Q21011233
+4 2 Q17824987
+4 3 Q5186964
+4 4 Q638893
+5 0 Q28371484
+5 1 Q21011233
+5 2 Q17824987
+5 3 Q5186964
+5 4 Q638893
+6 0 Q28371483
+6 1 Q21011233
+6 2 Q17824987
+6 3 Q5186964
+6 4 Q638893
+7 0 Q28371262
+7 1 Q21011233
+7 2 Q17824987
+7 3 Q5186964
+7 4 Q638893
+8 0 Q28371083
+8 1 Q21011233
+8 2 Q17824987
+8 3 Q5186964
+8 4 Q638893
+9 0 Q28045565
+9 1 Q21011233
+9 2 Q17824987
+9 3 Q5186964
+9 4 Q638893
+10 0 Q28371084
+10 1 Q4552288
+10 2 Q17824987
+10 3 Q5186964
+10 4 Q638893
+11 0 Q28371081
+11 1 Q4552288
+11 2 Q17824987
+11 3 Q5186964
+11 4 Q638893
+12 0 Q28371476
+12 1 Q5469311
+12 2 Q18031208
+12 3 Q5186964
+12 4 Q540857
+13 0 Q28371474
+13 1 Q5469311
+13 2 Q18031208
+13 3 Q5186964
+13 4 Q540857
+14 0 Q27906612
+14 1 Q198728
+14 2 Q14873998
+14 3 Q27114666
+14 4 Q840604
+15 0 Q27906817
+15 1 Q419940
+15 2 Q14873998
+15 3 Q4830631
+15 4 Q840604
+16 0 Q29938666
+16 1 Q412327
+16 2 Q14873998
+16 3 Q4830631
+16 4 Q840604
+17 0 Q28371204
+17 1 Q198728
+17 2 Q14873998
+17 3 Q4830631
+17 4 Q840604
+18 0 Q28422200
+18 1 Q7833138
+18 2 Q17853226
+18 3 Q423111
+18 4 Q657319
+19 0 Q28371240
+19 1 Q7833138
+19 2 Q17853226
+19 3 Q423111
+19 4 Q657319
diff --git a/examples/semtab2020_novartis/tables/7QUE5XLF.csv b/examples/semtab2020_novartis/tables/7QUE5XLF.csv
new file mode 100644
index 0000000..dce5547
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/7QUE5XLF.csv
@@ -0,0 +1,21 @@
+col0,col1,col2,col3,col4
+PTEN EXPRESSION,trastuzumab,PTEN,human chromosome 10,Fulvestrant / everolimus combination therapy
+TP53 DELETERIOUS MUTATION,adjuvant therapy,TP53,human chromosome 17,ro-5045337
+DNMT1 EXPRESSION,decitabine,DNMT1,human chromosome 19,cisplatin
+TP53 OVEREXPRESSION,etoposide / mitomycin / cisplatin combination therapy,TP53,human chromosome 17,cisplatin
+CCND1 OVEREXPRESSION,palbociclib,CCND1,human chromosome 11,tamoxifen
+CD274 EXPRESSION,nivolumab,CD274,human chromosome 9,radiation therapy
+chr9:g.133748414T>A (ABL1:p.F359I),dasatinib,ABL1,human chromosome 9,imatinib
+ABL1 Y393C,dasatinib,ABL1,human chromosome 9,imatinib
+ABL1 Y342H,dasatinib,ABL1,human chromosome 9,imatinib
+ABL1 V289I,dasatinib,ABL1,human chromosome 9,imatinib
+KIT W557_K558DELWK,imatinib,KIT,human chromosome 4,imatinib
+ZEB1 EXPRESSION,Salinomycin / doxorubicin combination therapy,ZEB1,human chromosome 10,doxorubicin
+ERBB3 OVEREXPRESSION,afatinib / selumetinib combination therapy,ERBB3,human chromosome 12,cetuximab
+NRG1 EXPRESSION,cisplatin,NRG1,human chromosome 8,cetuximab
+chr9:g.133748289T>G (ABL1:p.F317C),nilotinib,ABL1,human chromosome 9,dasatinib
+chr9:g.133748288T>A (ABL1:p.F317I),nilotinib,ABL1,human chromosome 9,dasatinib
+chr9:g.133748288T>G (ABL1:p.F317V),nilotinib,ABL1,human chromosome 9,dasatinib
+chr9:g.133748282A>G (ABL1:p.T315A),nilotinib,ABL1,human chromosome 9,dasatinib
+CDK6 OVEREXPRESSION,Fulvestrant / palbociclib combination therapy,CDK6,human chromosome 7,Fulvestrant
+AREG EXPRESSION,Panitumumab,AREG,human chromosome 4,crizotinib
diff --git a/examples/semtab2020_novartis/tables/7QUE5XLF.json b/examples/semtab2020_novartis/tables/7QUE5XLF.json
new file mode 100644
index 0000000..5d6543b
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/7QUE5XLF.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["PTEN EXPRESSION","TP53 DELETERIOUS MUTATION","DNMT1 EXPRESSION","TP53 OVEREXPRESSION","CCND1 OVEREXPRESSION","CD274 EXPRESSION","chr9:g.133748414T>A (ABL1:p.F359I)","ABL1 Y393C","ABL1 Y342H","ABL1 V289I","KIT W557_K558DELWK","ZEB1 EXPRESSION","ERBB3 OVEREXPRESSION","NRG1 EXPRESSION","chr9:g.133748289T>G (ABL1:p.F317C)","chr9:g.133748288T>A (ABL1:p.F317I)","chr9:g.133748288T>G (ABL1:p.F317V)","chr9:g.133748282A>G (ABL1:p.T315A)","CDK6 OVEREXPRESSION","AREG EXPRESSION"]},{"index":1,"name":"col1","values":["trastuzumab","adjuvant therapy","decitabine","etoposide / mitomycin / cisplatin combination therapy","palbociclib","nivolumab","dasatinib","dasatinib","dasatinib","dasatinib","imatinib","Salinomycin / doxorubicin combination therapy","afatinib / selumetinib combination therapy","cisplatin","nilotinib","nilotinib","nilotinib","nilotinib","Fulvestrant / palbociclib combination therapy","Panitumumab"]},{"index":2,"name":"col2","values":["PTEN","TP53","DNMT1","TP53","CCND1","CD274","ABL1","ABL1","ABL1","ABL1","KIT","ZEB1","ERBB3","NRG1","ABL1","ABL1","ABL1","ABL1","CDK6","AREG"]},{"index":3,"name":"col3","values":["human chromosome 10","human chromosome 17","human chromosome 19","human chromosome 17","human chromosome 11","human chromosome 9","human chromosome 9","human chromosome 9","human chromosome 9","human chromosome 9","human chromosome 4","human chromosome 10","human chromosome 12","human chromosome 8","human chromosome 9","human chromosome 9","human chromosome 9","human chromosome 9","human chromosome 7","human chromosome 4"]},{"index":4,"name":"col4","values":["Fulvestrant / everolimus combination therapy","ro-5045337","cisplatin","cisplatin","tamoxifen","radiation therapy","imatinib","imatinib","imatinib","imatinib","imatinib","doxorubicin","cetuximab","cetuximab","dasatinib","dasatinib","dasatinib","dasatinib","Fulvestrant","crizotinib"]}],"metadata":{"table_id":"7QUE5XLF","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q28445020","qnode_id":"Q28445020"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q412616","qnode_id":"Q412616"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14878377","qnode_id":"Q14878377"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q840737","qnode_id":"Q840737"}],[{"start":0,"end":44,"url":"http://www.wikidata.org/entity/Q44843816","qnode_id":"Q44843816"}]],[[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q28371191","qnode_id":"Q28371191"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q2481887","qnode_id":"Q2481887"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14818098","qnode_id":"Q14818098"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q220677","qnode_id":"Q220677"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q27287118","qnode_id":"Q27287118"}]],[[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q28445036","qnode_id":"Q28445036"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q1181878","qnode_id":"Q1181878"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14907998","qnode_id":"Q14907998"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q510786","qnode_id":"Q510786"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q412415","qnode_id":"Q412415"}]],[[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q32964718","qnode_id":"Q32964718"}],[{"start":0,"end":53,"url":"http://www.wikidata.org/entity/Q60242592","qnode_id":"Q60242592"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14818098","qnode_id":"Q14818098"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q220677","qnode_id":"Q220677"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q412415","qnode_id":"Q412415"}]],[[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q28370964","qnode_id":"Q28370964"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q15269707","qnode_id":"Q15269707"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q15311861","qnode_id":"Q15311861"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q847096","qnode_id":"Q847096"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q412178","qnode_id":"Q412178"}]],[[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q28444998","qnode_id":"Q28444998"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q7041828","qnode_id":"Q7041828"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18039630","qnode_id":"Q18039630"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q180507","qnode_id":"Q180507"}]],[[{"start":0,"end":34,"url":"http://www.wikidata.org/entity/Q38153149","qnode_id":"Q38153149"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q419940","qnode_id":"Q419940"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q177094","qnode_id":"Q177094"}]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q32965330","qnode_id":"Q32965330"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q419940","qnode_id":"Q419940"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q177094","qnode_id":"Q177094"}]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q32965310","qnode_id":"Q32965310"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q419940","qnode_id":"Q419940"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q177094","qnode_id":"Q177094"}]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q32965306","qnode_id":"Q32965306"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q419940","qnode_id":"Q419940"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q177094","qnode_id":"Q177094"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q29938433","qnode_id":"Q29938433"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q177094","qnode_id":"Q177094"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q20969938","qnode_id":"Q20969938"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q836605","qnode_id":"Q836605"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q177094","qnode_id":"Q177094"}]],[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q28445066","qnode_id":"Q28445066"}],[{"start":0,"end":45,"url":"http://www.wikidata.org/entity/Q44843885","qnode_id":"Q44843885"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q15997276","qnode_id":"Q15997276"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q840737","qnode_id":"Q840737"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q18936","qnode_id":"Q18936"}]],[[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q28445003","qnode_id":"Q28445003"}],[{"start":0,"end":42,"url":"http://www.wikidata.org/entity/Q58645166","qnode_id":"Q58645166"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q17917132","qnode_id":"Q17917132"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q847102","qnode_id":"Q847102"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q420296","qnode_id":"Q420296"}]],[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q28445021","qnode_id":"Q28445021"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q412415","qnode_id":"Q412415"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14914188","qnode_id":"Q14914188"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q572848","qnode_id":"Q572848"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q420296","qnode_id":"Q420296"}]],[[{"start":0,"end":34,"url":"http://www.wikidata.org/entity/Q38152934","qnode_id":"Q38152934"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q412327","qnode_id":"Q412327"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q419940","qnode_id":"Q419940"}]],[[{"start":0,"end":34,"url":"http://www.wikidata.org/entity/Q38152933","qnode_id":"Q38152933"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q412327","qnode_id":"Q412327"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q419940","qnode_id":"Q419940"}]],[[{"start":0,"end":34,"url":"http://www.wikidata.org/entity/Q38152931","qnode_id":"Q38152931"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q412327","qnode_id":"Q412327"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q419940","qnode_id":"Q419940"}]],[[{"start":0,"end":34,"url":"http://www.wikidata.org/entity/Q38105235","qnode_id":"Q38105235"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q412327","qnode_id":"Q412327"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q419940","qnode_id":"Q419940"}]],[[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q28445172","qnode_id":"Q28445172"}],[{"start":0,"end":45,"url":"http://www.wikidata.org/entity/Q44844065","qnode_id":"Q44844065"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14911762","qnode_id":"Q14911762"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q657319","qnode_id":"Q657319"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q5508491","qnode_id":"Q5508491"}]],[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q28445022","qnode_id":"Q28445022"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q417775","qnode_id":"Q417775"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14866198","qnode_id":"Q14866198"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q836605","qnode_id":"Q836605"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q5186964","qnode_id":"Q5186964"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q15304597:0","abs_uri":"http://www.wikidata.org/entity/Q15304597","rel_uri":"wd:Q15304597","approximation":false,"readable_label":"sequence variant (Q15304597)"},{"id":"col-2","col_index":2,"label":"col2"},{"id":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/entity/Q7187","rel_uri":"wd:Q7187","approximation":false,"readable_label":"gene (Q7187)"},{"id":"col-3","col_index":3,"label":"col3"},{"id":"http://www.wikidata.org/entity/Q37748:0","abs_uri":"http://www.wikidata.org/entity/Q37748","rel_uri":"wd:Q37748","approximation":false,"readable_label":"chromosome (Q37748)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"col-4","col_index":4,"label":"col4"}],"edges":[{"source":"http://www.wikidata.org/entity/Q15304597:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q15304597:0","target":"col-1","abs_uri":"http://www.wikidata.org/prop/P3354","rel_uri":"p:P3354","approximation":false,"readable_label":"positive therapeutic predictor (P3354)","id":2},{"source":"http://www.wikidata.org/entity/Q15304597:0","target":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/prop/P3433","rel_uri":"p:P3433","approximation":false,"readable_label":"biological variant of (P3433)","id":3},{"source":"http://www.wikidata.org/entity/Q15304597:0","target":"http://www.wikidata.org/entity/Q37748:0","abs_uri":"http://www.wikidata.org/prop/P1057","rel_uri":"p:P1057","approximation":false,"readable_label":"chromosome (P1057)","id":4},{"source":"http://www.wikidata.org/entity/Q15304597:0","target":"col-4","abs_uri":"http://www.wikidata.org/prop/P3355","rel_uri":"p:P3355","approximation":false,"readable_label":"negative therapeutic predictor (P3355)","id":5},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-2","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":6},{"source":"http://www.wikidata.org/entity/Q37748:0","target":"col-3","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":7}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/7QUE5XLF.links.tsv b/examples/semtab2020_novartis/tables/7QUE5XLF.links.tsv
new file mode 100644
index 0000000..5e2506e
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/7QUE5XLF.links.tsv
@@ -0,0 +1,100 @@
+0 0 Q28445020
+0 1 Q412616
+0 2 Q14878377
+0 3 Q840737
+0 4 Q44843816
+1 0 Q28371191
+1 1 Q2481887
+1 2 Q14818098
+1 3 Q220677
+1 4 Q27287118
+2 0 Q28445036
+2 1 Q1181878
+2 2 Q14907998
+2 3 Q510786
+2 4 Q412415
+3 0 Q32964718
+3 1 Q60242592
+3 2 Q14818098
+3 3 Q220677
+3 4 Q412415
+4 0 Q28370964
+4 1 Q15269707
+4 2 Q15311861
+4 3 Q847096
+4 4 Q412178
+5 0 Q28444998
+5 1 Q7041828
+5 2 Q18039630
+5 3 Q840604
+5 4 Q180507
+6 0 Q38153149
+6 1 Q419940
+6 2 Q14873998
+6 3 Q840604
+6 4 Q177094
+7 0 Q32965330
+7 1 Q419940
+7 2 Q14873998
+7 3 Q840604
+7 4 Q177094
+8 0 Q32965310
+8 1 Q419940
+8 2 Q14873998
+8 3 Q840604
+8 4 Q177094
+9 0 Q32965306
+9 1 Q419940
+9 2 Q14873998
+9 3 Q840604
+9 4 Q177094
+10 0 Q29938433
+10 1 Q177094
+10 2 Q20969938
+10 3 Q836605
+10 4 Q177094
+11 0 Q28445066
+11 1 Q44843885
+11 2 Q15997276
+11 3 Q840737
+11 4 Q18936
+12 0 Q28445003
+12 1 Q58645166
+12 2 Q17917132
+12 3 Q847102
+12 4 Q420296
+13 0 Q28445021
+13 1 Q412415
+13 2 Q14914188
+13 3 Q572848
+13 4 Q420296
+14 0 Q38152934
+14 1 Q412327
+14 2 Q14873998
+14 3 Q840604
+14 4 Q419940
+15 0 Q38152933
+15 1 Q412327
+15 2 Q14873998
+15 3 Q840604
+15 4 Q419940
+16 0 Q38152931
+16 1 Q412327
+16 2 Q14873998
+16 3 Q840604
+16 4 Q419940
+17 0 Q38105235
+17 1 Q412327
+17 2 Q14873998
+17 3 Q840604
+17 4 Q419940
+18 0 Q28445172
+18 1 Q44844065
+18 2 Q14911762
+18 3 Q657319
+18 4 Q5508491
+19 0 Q28445022
+19 1 Q417775
+19 2 Q14866198
+19 3 Q836605
+19 4 Q5186964
diff --git a/examples/semtab2020_novartis/tables/7XVEWDHG.csv b/examples/semtab2020_novartis/tables/7XVEWDHG.csv
new file mode 100644
index 0000000..acfd2f5
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/7XVEWDHG.csv
@@ -0,0 +1,21 @@
+col0,col1,col2
+rac-tilidine,Opium Law,(+)-tilidine
+Validol,Armenian Soviet Encyclopedia,carbon
+potassium iodide,Great Encyclopedia of Cyril and Methodius,potassium
+sodium chloride,Brockhaus and Efron Encyclopedic Dictionary,sodium
+arsenic trioxide,Brockhaus and Efron Encyclopedic Dictionary,oxygen
+heroin,Larousse Encyclopedia online,carbon
+L-aspartic Acid,Armenian Soviet Encyclopedia,carbon
+codeine,Armenian Soviet Encyclopedia,carbon
+L-phenylalanine,Armenian Soviet Encyclopedia,carbon
+glutathione,Armenian Soviet Encyclopedia,carbon
+thiamine ion,Armenian Soviet Encyclopedia,carbon
+cocaine,Armenian Soviet Encyclopedia,carbon
+progesterone,Armenian Soviet Encyclopedia,carbon
+calcium carbonate,Armenian Soviet Encyclopedia,carbon
+pethidine,Opium Law,carbon
+lysergic acid diethylamide,Opium Law,carbon
+sodium bicarbonate,Armenian Soviet Encyclopedia,hydrogen
+methenamine,Armenian Soviet Encyclopedia,hydrogen
+adenine,Armenian Soviet Encyclopedia,hydrogen
+caffeine,Ottův slovník naučný,hydrogen
diff --git a/examples/semtab2020_novartis/tables/7XVEWDHG.json b/examples/semtab2020_novartis/tables/7XVEWDHG.json
new file mode 100644
index 0000000..16ea514
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/7XVEWDHG.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["rac-tilidine","Validol","potassium iodide","sodium chloride","arsenic trioxide","heroin","L-aspartic Acid","codeine","L-phenylalanine","glutathione","thiamine ion","cocaine","progesterone","calcium carbonate","pethidine","lysergic acid diethylamide","sodium bicarbonate","methenamine","adenine","caffeine"]},{"index":1,"name":"col1","values":["Opium Law","Armenian Soviet Encyclopedia","Great Encyclopedia of Cyril and Methodius","Brockhaus and Efron Encyclopedic Dictionary","Brockhaus and Efron Encyclopedic Dictionary","Larousse Encyclopedia online","Armenian Soviet Encyclopedia","Armenian Soviet Encyclopedia","Armenian Soviet Encyclopedia","Armenian Soviet Encyclopedia","Armenian Soviet Encyclopedia","Armenian Soviet Encyclopedia","Armenian Soviet Encyclopedia","Armenian Soviet Encyclopedia","Opium Law","Opium Law","Armenian Soviet Encyclopedia","Armenian Soviet Encyclopedia","Armenian Soviet Encyclopedia","Ottův slovník naučný"]},{"index":2,"name":"col2","values":["(+)-tilidine","carbon","potassium","sodium","oxygen","carbon","carbon","carbon","carbon","carbon","carbon","carbon","carbon","carbon","carbon","carbon","hydrogen","hydrogen","hydrogen","hydrogen"]}],"metadata":{"table_id":"7XVEWDHG","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q45024802","qnode_id":"Q45024802"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q316572","qnode_id":"Q316572"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q421108","qnode_id":"Q421108"}]],[[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12831520","qnode_id":"Q12831520"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q121874","qnode_id":"Q121874"}],[{"start":0,"end":41,"url":"http://www.wikidata.org/entity/Q4091875","qnode_id":"Q4091875"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q703","qnode_id":"Q703"}]],[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q2314","qnode_id":"Q2314"}],[{"start":0,"end":43,"url":"http://www.wikidata.org/entity/Q602358","qnode_id":"Q602358"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q658","qnode_id":"Q658"}]],[[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q7739","qnode_id":"Q7739"}],[{"start":0,"end":43,"url":"http://www.wikidata.org/entity/Q602358","qnode_id":"Q602358"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q629","qnode_id":"Q629"}]],[[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q60168","qnode_id":"Q60168"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q17329836","qnode_id":"Q17329836"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q178450","qnode_id":"Q178450"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q174723","qnode_id":"Q174723"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q170545","qnode_id":"Q170545"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q116907","qnode_id":"Q116907"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q83187","qnode_id":"Q83187"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q41576","qnode_id":"Q41576"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q26963","qnode_id":"Q26963"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q23767","qnode_id":"Q23767"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q55434","qnode_id":"Q55434"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q316572","qnode_id":"Q316572"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":26,"url":"http://www.wikidata.org/entity/Q23118","qnode_id":"Q23118"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q316572","qnode_id":"Q316572"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q179731","qnode_id":"Q179731"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q556","qnode_id":"Q556"}]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q71969","qnode_id":"Q71969"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q556","qnode_id":"Q556"}]],[[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q15277","qnode_id":"Q15277"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q556","qnode_id":"Q556"}]],[[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q60235","qnode_id":"Q60235"}],[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q2041543","qnode_id":"Q2041543"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q556","qnode_id":"Q556"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q12140:0","abs_uri":"http://www.wikidata.org/entity/Q12140","rel_uri":"wd:Q12140","approximation":false,"readable_label":"medication (Q12140)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"col-2","col_index":2,"label":"col2"}],"edges":[{"source":"http://www.wikidata.org/entity/Q12140:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q12140:0","target":"col-1","abs_uri":"http://www.wikidata.org/prop/P1343","rel_uri":"p:P1343","approximation":false,"readable_label":"described by source (P1343)","id":2},{"source":"http://www.wikidata.org/entity/Q12140:0","target":"col-2","abs_uri":"http://www.wikidata.org/prop/P527","rel_uri":"p:P527","approximation":false,"readable_label":"has part (P527)","id":3}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/7XVEWDHG.links.tsv b/examples/semtab2020_novartis/tables/7XVEWDHG.links.tsv
new file mode 100644
index 0000000..49b92ce
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/7XVEWDHG.links.tsv
@@ -0,0 +1,60 @@
+0 0 Q45024802
+0 1 Q316572
+0 2 Q421108
+1 0 Q12831520
+1 1 Q2657718
+1 2 Q623
+2 0 Q121874
+2 1 Q4091875
+2 2 Q703
+3 0 Q2314
+3 1 Q602358
+3 2 Q658
+4 0 Q7739
+4 1 Q602358
+4 2 Q629
+5 0 Q60168
+5 1 Q17329836
+5 2 Q623
+6 0 Q178450
+6 1 Q2657718
+6 2 Q623
+7 0 Q174723
+7 1 Q2657718
+7 2 Q623
+8 0 Q170545
+8 1 Q2657718
+8 2 Q623
+9 0 Q116907
+9 1 Q2657718
+9 2 Q623
+10 0 Q83187
+10 1 Q2657718
+10 2 Q623
+11 0 Q41576
+11 1 Q2657718
+11 2 Q623
+12 0 Q26963
+12 1 Q2657718
+12 2 Q623
+13 0 Q23767
+13 1 Q2657718
+13 2 Q623
+14 0 Q55434
+14 1 Q316572
+14 2 Q623
+15 0 Q23118
+15 1 Q316572
+15 2 Q623
+16 0 Q179731
+16 1 Q2657718
+16 2 Q556
+17 0 Q71969
+17 1 Q2657718
+17 2 Q556
+18 0 Q15277
+18 1 Q2657718
+18 2 Q556
+19 0 Q60235
+19 1 Q2041543
+19 2 Q556
diff --git a/examples/semtab2020_novartis/tables/8GXR654P.csv b/examples/semtab2020_novartis/tables/8GXR654P.csv
new file mode 100644
index 0000000..37c4e5b
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/8GXR654P.csv
@@ -0,0 +1,21 @@
+col0,col1,col2
+NIKBIA,human chromosome 14,reverse strand
+TGM1,human chromosome 14,reverse strand
+GCH1,human chromosome 14,reverse strand
+CTSG,human chromosome 14,reverse strand
+GALC,human chromosome 14,reverse strand
+MYH7,human chromosome 14,reverse strand
+SGRPIND,human chromosome 14,reverse strand
+SERPIN1,human chromosome 14,reverse strand
+WARS1,human chromosome 14,reverse strand
+CIDEB,human chromosome 14,reverse strand
+ACTN1,human chromosome 14,reverse strand
+OR10G,human chromosome 14,reverse strand
+OR5DU1,human chromosome 14,reverse strand
+NGB,human chromosome 14,reverse strand
+DTAGE5,human chromosome 14,forward strand
+MGAT2,human chromosome 14,forward strand
+MARK3,human chromosome 14,forward strand
+LGAS3,human chromosome 14,forward strand
+KTN1,human chromosome 14,forward strand
+KLC1,human chromosome 14,forward strand
diff --git a/examples/semtab2020_novartis/tables/8GXR654P.json b/examples/semtab2020_novartis/tables/8GXR654P.json
new file mode 100644
index 0000000..f135ac0
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/8GXR654P.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["NIKBIA","TGM1","GCH1","CTSG","GALC","MYH7","SGRPIND","SERPIN1","WARS1","CIDEB","ACTN1","OR10G","OR5DU1","NGB","DTAGE5","MGAT2","MARK3","LGAS3","KTN1","KLC1"]},{"index":1,"name":"col1","values":["human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14"]},{"index":2,"name":"col2","values":["reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","forward strand","forward strand","forward strand","forward strand","forward strand","forward strand"]}],"metadata":{"table_id":"8GXR654P","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q15325101","qnode_id":"Q15325101"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q15311830","qnode_id":"Q15311830"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14916168","qnode_id":"Q14916168"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14906548","qnode_id":"Q14906548"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14905422","qnode_id":"Q14905422"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14876107","qnode_id":"Q14876107"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q14860763","qnode_id":"Q14860763"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q14860537","qnode_id":"Q14860537"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q7946718","qnode_id":"Q7946718"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q5011619","qnode_id":"Q5011619"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q4676901","qnode_id":"Q4676901"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q1521828","qnode_id":"Q1521828"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q1521496","qnode_id":"Q1521496"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q418553","qnode_id":"Q418553"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q18029091","qnode_id":"Q18029091"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18029079","qnode_id":"Q18029079"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18028880","qnode_id":"Q18028880"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18028529","qnode_id":"Q18028529"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18028418","qnode_id":"Q18028418"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18028325","qnode_id":"Q18028325"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/entity/Q7187","rel_uri":"wd:Q7187","approximation":false,"readable_label":"gene (Q7187)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"http://www.wikidata.org/entity/Q37748:0","abs_uri":"http://www.wikidata.org/entity/Q37748","rel_uri":"wd:Q37748","approximation":false,"readable_label":"chromosome (Q37748)"},{"id":"col-2","col_index":2,"label":"col2"}],"edges":[{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"http://www.wikidata.org/entity/Q37748:0","abs_uri":"http://www.wikidata.org/prop/P1057","rel_uri":"p:P1057","approximation":false,"readable_label":"chromosome (P1057)","id":2},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-2","abs_uri":"http://www.wikidata.org/prop/P2548","rel_uri":"p:P2548","approximation":false,"readable_label":"strand orientation (P2548)","id":3},{"source":"http://www.wikidata.org/entity/Q37748:0","target":"col-1","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":4}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/8GXR654P.links.tsv b/examples/semtab2020_novartis/tables/8GXR654P.links.tsv
new file mode 100644
index 0000000..e8699b9
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/8GXR654P.links.tsv
@@ -0,0 +1,60 @@
+0 0 Q15325101
+0 1 Q138955
+0 2 Q22809711
+1 0 Q15311830
+1 1 Q138955
+1 2 Q22809711
+2 0 Q14916168
+2 1 Q138955
+2 2 Q22809711
+3 0 Q14906548
+3 1 Q138955
+3 2 Q22809711
+4 0 Q14905422
+4 1 Q138955
+4 2 Q22809711
+5 0 Q14876107
+5 1 Q138955
+5 2 Q22809711
+6 0 Q14860763
+6 1 Q138955
+6 2 Q22809711
+7 0 Q14860537
+7 1 Q138955
+7 2 Q22809711
+8 0 Q7946718
+8 1 Q138955
+8 2 Q22809711
+9 0 Q5011619
+9 1 Q138955
+9 2 Q22809711
+10 0 Q4676901
+10 1 Q138955
+10 2 Q22809711
+11 0 Q1521828
+11 1 Q138955
+11 2 Q22809711
+12 0 Q1521496
+12 1 Q138955
+12 2 Q22809711
+13 0 Q418553
+13 1 Q138955
+13 2 Q22809711
+14 0 Q18029091
+14 1 Q138955
+14 2 Q22809680
+15 0 Q18029079
+15 1 Q138955
+15 2 Q22809680
+16 0 Q18028880
+16 1 Q138955
+16 2 Q22809680
+17 0 Q18028529
+17 1 Q138955
+17 2 Q22809680
+18 0 Q18028418
+18 1 Q138955
+18 2 Q22809680
+19 0 Q18028325
+19 1 Q138955
+19 2 Q22809680
diff --git a/examples/semtab2020_novartis/tables/AABG0C8A.csv b/examples/semtab2020_novartis/tables/AABG0C8A.csv
new file mode 100644
index 0000000..b484472
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/AABG0C8A.csv
@@ -0,0 +1,21 @@
+col0,col1,col2
+foscaret,reverse-transcriptase inhibitor,Cytomegaloviral disease
+dinqprostone,oxytocics,uterine cancer
+levocetirilne,H1 antagonist,urticaria
+ceuivastatin,statin,arteriosclerosis
+Fluvastauin,statin,arteriosclerosis
+cetiriine,H1 antagonist,sinusitis
+febuxstat,Antigout agents,hyperuricemia
+captrpril,ACE inhibitor,hypertension
+igabatrin,enzyme inhibitor,epilepsy
+cromolyq,immunologic factor,asthma
+sitavastatin,statin,lipedema
+biowin,vitamin B,Inherited metabolic disorder
+eporrostenol,antihypertensive drug,pulmonary hypertension
+acetic aci,indicators and reagents,otitis externa
+aminonevulinic acid,photosensitizer,actinic keratosis
+rouuvastatin,statin,arteriosclerosis
+fexofnadine,H1 antagonist,rhinitis
+amoxicilin,antibiotic,gonorrhea
+tretjnoin,keratolytic,acne
+adapaene,non-steroidal anti-inflammatory drug,acne
diff --git a/examples/semtab2020_novartis/tables/AABG0C8A.json b/examples/semtab2020_novartis/tables/AABG0C8A.json
new file mode 100644
index 0000000..aecc06d
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/AABG0C8A.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["foscaret","dinqprostone","levocetirilne","ceuivastatin","Fluvastauin","cetiriine","febuxstat","captrpril","igabatrin","cromolyq","sitavastatin","biowin","eporrostenol","acetic aci","aminonevulinic acid","rouuvastatin","fexofnadine","amoxicilin","tretjnoin","adapaene"]},{"index":1,"name":"col1","values":["reverse-transcriptase inhibitor","oxytocics","H1 antagonist","statin","statin","H1 antagonist","Antigout agents","ACE inhibitor","enzyme inhibitor","immunologic factor","statin","vitamin B","antihypertensive drug","indicators and reagents","photosensitizer","statin","H1 antagonist","antibiotic","keratolytic","non-steroidal anti-inflammatory drug"]},{"index":2,"name":"col2","values":["Cytomegaloviral disease","uterine cancer","urticaria","arteriosclerosis","arteriosclerosis","sinusitis","hyperuricemia","hypertension","epilepsy","asthma","lipedema","Inherited metabolic disorder","pulmonary hypertension","otitis externa","actinic keratosis","arteriosclerosis","rhinitis","gonorrhea","acne","acne"]}],"metadata":{"table_id":"AABG0C8A","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q420387","qnode_id":"Q420387"}],[{"start":0,"end":31,"url":"http://www.wikidata.org/entity/Q421559","qnode_id":"Q421559"}],[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q2076966","qnode_id":"Q2076966"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q416554","qnode_id":"Q416554"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q50430113","qnode_id":"Q50430113"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q1209744","qnode_id":"Q1209744"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q421091","qnode_id":"Q421091"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q1328275","qnode_id":"Q1328275"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q187440","qnode_id":"Q187440"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q423439","qnode_id":"Q423439"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q954845","qnode_id":"Q954845"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q184559","qnode_id":"Q184559"}]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q417942","qnode_id":"Q417942"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q954845","qnode_id":"Q954845"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q184559","qnode_id":"Q184559"}]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q423075","qnode_id":"Q423075"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q1328275","qnode_id":"Q1328275"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q183344","qnode_id":"Q183344"}]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q417296","qnode_id":"Q417296"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q17173047","qnode_id":"Q17173047"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q49970","qnode_id":"Q49970"}]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q421119","qnode_id":"Q421119"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q288280","qnode_id":"Q288280"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q41861","qnode_id":"Q41861"}]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q421663","qnode_id":"Q421663"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q427492","qnode_id":"Q427492"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q41571","qnode_id":"Q41571"}]],[[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q416427","qnode_id":"Q416427"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q50349184","qnode_id":"Q50349184"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q35869","qnode_id":"Q35869"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q412677","qnode_id":"Q412677"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q954845","qnode_id":"Q954845"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q1827605","qnode_id":"Q1827605"}]],[[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q181354","qnode_id":"Q181354"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q183206","qnode_id":"Q183206"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q1758393","qnode_id":"Q1758393"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q412050","qnode_id":"Q412050"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q575890","qnode_id":"Q575890"}],[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q1128595","qnode_id":"Q1128595"}]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q47512","qnode_id":"Q47512"}],[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q50377175","qnode_id":"Q50377175"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q680873","qnode_id":"Q680873"}]],[[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q238474","qnode_id":"Q238474"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q18388377","qnode_id":"Q18388377"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q422225","qnode_id":"Q422225"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q415159","qnode_id":"Q415159"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q954845","qnode_id":"Q954845"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q184559","qnode_id":"Q184559"}]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q415122","qnode_id":"Q415122"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q1328275","qnode_id":"Q1328275"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q114085","qnode_id":"Q114085"}]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q201928","qnode_id":"Q201928"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q12187","qnode_id":"Q12187"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q101896","qnode_id":"Q101896"}]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q29417","qnode_id":"Q29417"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q445580","qnode_id":"Q445580"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q79928","qnode_id":"Q79928"}]],[[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q352348","qnode_id":"Q352348"}],[{"start":0,"end":36,"url":"http://www.wikidata.org/entity/Q188724","qnode_id":"Q188724"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q79928","qnode_id":"Q79928"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q134856:0","abs_uri":"http://www.wikidata.org/entity/Q134856","rel_uri":"wd:Q134856","approximation":false,"readable_label":"carboxylic acid (Q134856)"},{"id":"col-2","col_index":2,"label":"col2"},{"id":"http://www.wikidata.org/entity/Q12136:0","abs_uri":"http://www.wikidata.org/entity/Q12136","rel_uri":"wd:Q12136","approximation":false,"readable_label":"disease (Q12136)"},{"id":"col-1","col_index":1,"label":"col1"}],"edges":[{"source":"http://www.wikidata.org/entity/Q134856:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q134856:0","target":"col-1","abs_uri":"http://www.wikidata.org/prop/P2868","rel_uri":"p:P2868","approximation":false,"readable_label":"subject has role (P2868)","id":2},{"source":"http://www.wikidata.org/entity/Q134856:0","target":"http://www.wikidata.org/entity/Q12136:0","abs_uri":"http://www.wikidata.org/prop/P2175","rel_uri":"p:P2175","approximation":false,"readable_label":"medical condition treated (P2175)","id":3},{"source":"http://www.wikidata.org/entity/Q12136:0","target":"col-2","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":4}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/AABG0C8A.links.tsv b/examples/semtab2020_novartis/tables/AABG0C8A.links.tsv
new file mode 100644
index 0000000..7891b37
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/AABG0C8A.links.tsv
@@ -0,0 +1,60 @@
+0 0 Q420387
+0 1 Q421559
+0 2 Q2076966
+1 0 Q416554
+1 1 Q50430113
+1 2 Q1209744
+2 0 Q421091
+2 1 Q1328275
+2 2 Q187440
+3 0 Q423439
+3 1 Q954845
+3 2 Q184559
+4 0 Q417942
+4 1 Q954845
+4 2 Q184559
+5 0 Q423075
+5 1 Q1328275
+5 2 Q183344
+6 0 Q417296
+6 1 Q17173047
+6 2 Q49970
+7 0 Q421119
+7 1 Q288280
+7 2 Q41861
+8 0 Q421663
+8 1 Q427492
+8 2 Q41571
+9 0 Q416427
+9 1 Q50349184
+9 2 Q35869
+10 0 Q412677
+10 1 Q954845
+10 2 Q1827605
+11 0 Q181354
+11 1 Q183206
+11 2 Q1758393
+12 0 Q412050
+12 1 Q575890
+12 2 Q1128595
+13 0 Q47512
+13 1 Q50377175
+13 2 Q680873
+14 0 Q238474
+14 1 Q18388377
+14 2 Q422225
+15 0 Q415159
+15 1 Q954845
+15 2 Q184559
+16 0 Q415122
+16 1 Q1328275
+16 2 Q114085
+17 0 Q201928
+17 1 Q12187
+17 2 Q101896
+18 0 Q29417
+18 1 Q445580
+18 2 Q79928
+19 0 Q352348
+19 1 Q188724
+19 2 Q79928
diff --git a/examples/semtab2020_novartis/tables/AQRRDDX4.csv b/examples/semtab2020_novartis/tables/AQRRDDX4.csv
new file mode 100644
index 0000000..ccea258
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/AQRRDDX4.csv
@@ -0,0 +1,21 @@
+col0,col1,col2
+GZMH,human chromosome 14,reverse strand
+BRF1,human chromosome 14,reverse strand
+GWF2A1,human chromosome 14,reverse strand
+GPX2,human chromosome 14,reverse strand
+GPR33,human chromosome 14,reverse strand
+GMFB,human chromosome 14,reverse strand
+FKBP3,human chromosome 14,reverse strand
+ERH,human chromosome 14,reverse strand
+DIO2,human chromosome 14,reverse strand
+DAD1,human chromosome 14,reverse strand
+CMA1,human chromosome 14,reverse strand
+CKB,human chromosome 14,reverse strand
+FOXN3,human chromosome 14,reverse strand
+CFL2,human chromosome 14,reverse strand
+CEBPE,human chromosome 14,reverse strand
+HNTPD5,human chromosome 14,reverse strand
+FP36L1,human chromosome 14,reverse strand
+AKT1,human chromosome 14,reverse strand
+ACYP1,human chromosome 14,reverse strand
+ESR2,human chromosome 14,reverse strand
diff --git a/examples/semtab2020_novartis/tables/AQRRDDX4.json b/examples/semtab2020_novartis/tables/AQRRDDX4.json
new file mode 100644
index 0000000..be1f8ac
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/AQRRDDX4.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["GZMH","BRF1","GWF2A1","GPX2","GPR33","GMFB","FKBP3","ERH","DIO2","DAD1","CMA1","CKB","FOXN3","CFL2","CEBPE","HNTPD5","FP36L1","AKT1","ACYP1","ESR2"]},{"index":1,"name":"col1","values":["human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14"]},{"index":2,"name":"col2","values":["reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand"]}],"metadata":{"table_id":"AQRRDDX4","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18026716","qnode_id":"Q18026716"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18026651","qnode_id":"Q18026651"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q18026610","qnode_id":"Q18026610"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18026459","qnode_id":"Q18026459"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18026407","qnode_id":"Q18026407"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18026203","qnode_id":"Q18026203"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q17928565","qnode_id":"Q17928565"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q17917549","qnode_id":"Q17917549"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q17914005","qnode_id":"Q17914005"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q17912304","qnode_id":"Q17912304"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q17907800","qnode_id":"Q17907800"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q17861992","qnode_id":"Q17861992"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q17861767","qnode_id":"Q17861767"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q17861475","qnode_id":"Q17861475"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q17861114","qnode_id":"Q17861114"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q17859612","qnode_id":"Q17859612"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q17853452","qnode_id":"Q17853452"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q17816452","qnode_id":"Q17816452"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q17709510","qnode_id":"Q17709510"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q15329133","qnode_id":"Q15329133"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/entity/Q7187","rel_uri":"wd:Q7187","approximation":false,"readable_label":"gene (Q7187)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"http://www.wikidata.org/entity/Q37748:0","abs_uri":"http://www.wikidata.org/entity/Q37748","rel_uri":"wd:Q37748","approximation":false,"readable_label":"chromosome (Q37748)"},{"id":"col-2","col_index":2,"label":"col2"}],"edges":[{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"http://www.wikidata.org/entity/Q37748:0","abs_uri":"http://www.wikidata.org/prop/P1057","rel_uri":"p:P1057","approximation":false,"readable_label":"chromosome (P1057)","id":2},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-2","abs_uri":"http://www.wikidata.org/prop/P2548","rel_uri":"p:P2548","approximation":false,"readable_label":"strand orientation (P2548)","id":3},{"source":"http://www.wikidata.org/entity/Q37748:0","target":"col-1","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":4}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/AQRRDDX4.links.tsv b/examples/semtab2020_novartis/tables/AQRRDDX4.links.tsv
new file mode 100644
index 0000000..901177c
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/AQRRDDX4.links.tsv
@@ -0,0 +1,60 @@
+0 0 Q18026716
+0 1 Q138955
+0 2 Q22809711
+1 0 Q18026651
+1 1 Q138955
+1 2 Q22809711
+2 0 Q18026610
+2 1 Q138955
+2 2 Q22809711
+3 0 Q18026459
+3 1 Q138955
+3 2 Q22809711
+4 0 Q18026407
+4 1 Q138955
+4 2 Q22809711
+5 0 Q18026203
+5 1 Q138955
+5 2 Q22809711
+6 0 Q17928565
+6 1 Q138955
+6 2 Q22809711
+7 0 Q17917549
+7 1 Q138955
+7 2 Q22809711
+8 0 Q17914005
+8 1 Q138955
+8 2 Q22809711
+9 0 Q17912304
+9 1 Q138955
+9 2 Q22809711
+10 0 Q17907800
+10 1 Q138955
+10 2 Q22809711
+11 0 Q17861992
+11 1 Q138955
+11 2 Q22809711
+12 0 Q17861767
+12 1 Q138955
+12 2 Q22809711
+13 0 Q17861475
+13 1 Q138955
+13 2 Q22809711
+14 0 Q17861114
+14 1 Q138955
+14 2 Q22809711
+15 0 Q17859612
+15 1 Q138955
+15 2 Q22809711
+16 0 Q17853452
+16 1 Q138955
+16 2 Q22809711
+17 0 Q17816452
+17 1 Q138955
+17 2 Q22809711
+18 0 Q17709510
+18 1 Q138955
+18 2 Q22809711
+19 0 Q15329133
+19 1 Q138955
+19 2 Q22809711
diff --git a/examples/semtab2020_novartis/tables/AV1MZ4ZK.csv b/examples/semtab2020_novartis/tables/AV1MZ4ZK.csv
new file mode 100644
index 0000000..e04d73e
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/AV1MZ4ZK.csv
@@ -0,0 +1,21 @@
+col0,col1,col2,col3
+ALK EML4-ALK C1156Y–L1196M,crizotinib,ALK,human chromosome 2
+ABL1 BCR-ABL F317C,dasatinib monohydrate,ABL1,human chromosome 9
+ABL1 BCR-ABL F317S,dasatinib monohydrate,ABL1,human chromosome 9
+ABL1 BCR-ABL T315A,dasatinib monohydrate,ABL1,human chromosome 9
+ABL1 BCR-ABL E459K,imatinib mesylate,ABL1,human chromosome 9
+ABL1 BCR-ABL L248R,axitinib,ABL1,human chromosome 9
+KIT A829P,imatinib,KIT,human chromosome 4
+KIT N822H,imatinib,KIT,human chromosome 4
+KRAS G13V,Panitumumab,KRAS,human chromosome 12
+FLT3 D835V,MLN-518,FLT3,human chromosome 13
+ABL1 BCR-ABL F317I,dasatinib monohydrate,ABL1,human chromosome 9
+ABL1 E279K,dasatinib monohydrate,ABL1,human chromosome 9
+ABL1 BCR-ABL L387M,dasatinib monohydrate,ABL1,human chromosome 9
+ABL1 BCR-ABL A397P,imatinib mesylate,ABL1,human chromosome 9
+ABL1 BCR-ABL V379I,imatinib mesylate,ABL1,human chromosome 9
+ABL1 BCR-ABL F317V,axitinib,ABL1,human chromosome 9
+ABL1 M343T,axitinib,ABL1,human chromosome 9
+ABL1 N336S,bosutinib,ABL1,human chromosome 9
+ABL1 BCR-ABL P480A,bosutinib,ABL1,human chromosome 9
+ABL1 BCR-ABL E453A,bosutinib,ABL1,human chromosome 9
diff --git a/examples/semtab2020_novartis/tables/AV1MZ4ZK.json b/examples/semtab2020_novartis/tables/AV1MZ4ZK.json
new file mode 100644
index 0000000..4310cbd
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/AV1MZ4ZK.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["ALK EML4-ALK C1156Y–L1196M","ABL1 BCR-ABL F317C","ABL1 BCR-ABL F317S","ABL1 BCR-ABL T315A","ABL1 BCR-ABL E459K","ABL1 BCR-ABL L248R","KIT A829P","KIT N822H","KRAS G13V","FLT3 D835V","ABL1 BCR-ABL F317I","ABL1 E279K","ABL1 BCR-ABL L387M","ABL1 BCR-ABL A397P","ABL1 BCR-ABL V379I","ABL1 BCR-ABL F317V","ABL1 M343T","ABL1 N336S","ABL1 BCR-ABL P480A","ABL1 BCR-ABL E453A"]},{"index":1,"name":"col1","values":["crizotinib","dasatinib monohydrate","dasatinib monohydrate","dasatinib monohydrate","imatinib mesylate","axitinib","imatinib","imatinib","Panitumumab","MLN-518","dasatinib monohydrate","dasatinib monohydrate","dasatinib monohydrate","imatinib mesylate","imatinib mesylate","axitinib","axitinib","bosutinib","bosutinib","bosutinib"]},{"index":2,"name":"col2","values":["ALK","ABL1","ABL1","ABL1","ABL1","ABL1","KIT","KIT","KRAS","FLT3","ABL1","ABL1","ABL1","ABL1","ABL1","ABL1","ABL1","ABL1","ABL1","ABL1"]},{"index":3,"name":"col3","values":["human chromosome 2","human chromosome 9","human chromosome 9","human chromosome 9","human chromosome 9","human chromosome 9","human chromosome 4","human chromosome 4","human chromosome 12","human chromosome 13","human chromosome 9","human chromosome 9","human chromosome 9","human chromosome 9","human chromosome 9","human chromosome 9","human chromosome 9","human chromosome 9","human chromosome 9","human chromosome 9"]}],"metadata":{"table_id":"AV1MZ4ZK","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":26,"url":"http://www.wikidata.org/entity/Q29920056","qnode_id":"Q29920056"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q5186964","qnode_id":"Q5186964"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q17824987","qnode_id":"Q17824987"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q638893","qnode_id":"Q638893"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q56241017","qnode_id":"Q56241017"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q27139135","qnode_id":"Q27139135"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q56241015","qnode_id":"Q56241015"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q27139135","qnode_id":"Q27139135"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q56241008","qnode_id":"Q56241008"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q27139135","qnode_id":"Q27139135"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q56241028","qnode_id":"Q56241028"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q27114666","qnode_id":"Q27114666"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q56241014","qnode_id":"Q56241014"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q4830631","qnode_id":"Q4830631"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q66084447","qnode_id":"Q66084447"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q177094","qnode_id":"Q177094"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q20969938","qnode_id":"Q20969938"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q836605","qnode_id":"Q836605"}]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q66084446","qnode_id":"Q66084446"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q177094","qnode_id":"Q177094"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q20969938","qnode_id":"Q20969938"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q836605","qnode_id":"Q836605"}]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q32964984","qnode_id":"Q32964984"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q417775","qnode_id":"Q417775"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q20969980","qnode_id":"Q20969980"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q847102","qnode_id":"Q847102"}]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q29938817","qnode_id":"Q29938817"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q27095683","qnode_id":"Q27095683"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14912399","qnode_id":"Q14912399"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q840734","qnode_id":"Q840734"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q32965379","qnode_id":"Q32965379"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q27139135","qnode_id":"Q27139135"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q32965019","qnode_id":"Q32965019"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q27139135","qnode_id":"Q27139135"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q32964888","qnode_id":"Q32964888"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q27139135","qnode_id":"Q27139135"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q32965334","qnode_id":"Q32965334"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q27114666","qnode_id":"Q27114666"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q32965324","qnode_id":"Q32965324"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q27114666","qnode_id":"Q27114666"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q32965015","qnode_id":"Q32965015"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q4830631","qnode_id":"Q4830631"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q32965011","qnode_id":"Q32965011"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q4830631","qnode_id":"Q4830631"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q32965445","qnode_id":"Q32965445"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q894611","qnode_id":"Q894611"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q32965063","qnode_id":"Q32965063"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q894611","qnode_id":"Q894611"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q32965059","qnode_id":"Q32965059"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q894611","qnode_id":"Q894611"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q27429979:0","abs_uri":"http://www.wikidata.org/entity/Q27429979","rel_uri":"wd:Q27429979","approximation":false,"readable_label":"missense variant (Q27429979)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"http://www.wikidata.org/entity/Q11173:0","abs_uri":"http://www.wikidata.org/entity/Q11173","rel_uri":"wd:Q11173","approximation":false,"readable_label":"chemical compound (Q11173)"},{"id":"col-2","col_index":2,"label":"col2"},{"id":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/entity/Q7187","rel_uri":"wd:Q7187","approximation":false,"readable_label":"gene (Q7187)"},{"id":"col-3","col_index":3,"label":"col3"},{"id":"http://www.wikidata.org/entity/Q37748:0","abs_uri":"http://www.wikidata.org/entity/Q37748","rel_uri":"wd:Q37748","approximation":false,"readable_label":"chromosome (Q37748)"}],"edges":[{"source":"http://www.wikidata.org/entity/Q27429979:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q27429979:0","target":"http://www.wikidata.org/entity/Q11173:0","abs_uri":"http://www.wikidata.org/prop/P3355","rel_uri":"p:P3355","approximation":false,"readable_label":"negative therapeutic predictor (P3355)","id":2},{"source":"http://www.wikidata.org/entity/Q27429979:0","target":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/prop/P3433","rel_uri":"p:P3433","approximation":false,"readable_label":"biological variant of (P3433)","id":3},{"source":"http://www.wikidata.org/entity/Q27429979:0","target":"http://www.wikidata.org/entity/Q37748:0","abs_uri":"http://www.wikidata.org/prop/P1057","rel_uri":"p:P1057","approximation":false,"readable_label":"chromosome (P1057)","id":4},{"source":"http://www.wikidata.org/entity/Q11173:0","target":"col-1","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":5},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-2","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":6},{"source":"http://www.wikidata.org/entity/Q37748:0","target":"col-3","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":7}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/AV1MZ4ZK.links.tsv b/examples/semtab2020_novartis/tables/AV1MZ4ZK.links.tsv
new file mode 100644
index 0000000..eb369e7
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/AV1MZ4ZK.links.tsv
@@ -0,0 +1,80 @@
+0 0 Q29920056
+0 1 Q5186964
+0 2 Q17824987
+0 3 Q638893
+1 0 Q56241017
+1 1 Q27139135
+1 2 Q14873998
+1 3 Q840604
+2 0 Q56241015
+2 1 Q27139135
+2 2 Q14873998
+2 3 Q840604
+3 0 Q56241008
+3 1 Q27139135
+3 2 Q14873998
+3 3 Q840604
+4 0 Q56241028
+4 1 Q27114666
+4 2 Q14873998
+4 3 Q840604
+5 0 Q56241014
+5 1 Q4830631
+5 2 Q14873998
+5 3 Q840604
+6 0 Q66084447
+6 1 Q177094
+6 2 Q20969938
+6 3 Q836605
+7 0 Q66084446
+7 1 Q177094
+7 2 Q20969938
+7 3 Q836605
+8 0 Q32964984
+8 1 Q417775
+8 2 Q20969980
+8 3 Q847102
+9 0 Q29938817
+9 1 Q27095683
+9 2 Q14912399
+9 3 Q840734
+10 0 Q32965379
+10 1 Q27139135
+10 2 Q14873998
+10 3 Q840604
+11 0 Q32965019
+11 1 Q27139135
+11 2 Q14873998
+11 3 Q840604
+12 0 Q32964888
+12 1 Q27139135
+12 2 Q14873998
+12 3 Q840604
+13 0 Q32965334
+13 1 Q27114666
+13 2 Q14873998
+13 3 Q840604
+14 0 Q32965324
+14 1 Q27114666
+14 2 Q14873998
+14 3 Q840604
+15 0 Q32965015
+15 1 Q4830631
+15 2 Q14873998
+15 3 Q840604
+16 0 Q32965011
+16 1 Q4830631
+16 2 Q14873998
+16 3 Q840604
+17 0 Q32965445
+17 1 Q894611
+17 2 Q14873998
+17 3 Q840604
+18 0 Q32965063
+18 1 Q894611
+18 2 Q14873998
+18 3 Q840604
+19 0 Q32965059
+19 1 Q894611
+19 2 Q14873998
+19 3 Q840604
diff --git a/examples/semtab2020_novartis/tables/BVV82N5B.csv b/examples/semtab2020_novartis/tables/BVV82N5B.csv
new file mode 100644
index 0000000..a8e2f40
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/BVV82N5B.csv
@@ -0,0 +1,21 @@
+col0,col1,col2,col3
+ABL1 BCR-ABL Y253H,nilotinib,ABL1,human chromosome 9
+ABL1 BCR-ABL H396R,imatinib,ABL1,human chromosome 9
+ABL1 BCR-ABL D276G,imatinib,ABL1,human chromosome 9
+ABL1 Y253F,imatinib,ABL1,human chromosome 9
+ABL1 BCR-ABL G250E,imatinib,ABL1,human chromosome 9
+ABL1 BCR-ABL L248V,imatinib,ABL1,human chromosome 9
+ABL1 BCR-ABL M244V,imatinib,ABL1,human chromosome 9
+ABL1 BCR-ABL F359V,imatinib,ABL1,human chromosome 9
+KIT V560D,sunitinib,KIT,human chromosome 4
+KIT D820A,imatinib,KIT,human chromosome 4
+KIT C809G,imatinib,KIT,human chromosome 4
+KIT N822K,imatinib,KIT,human chromosome 4
+KIT Y823D,imatinib,KIT,human chromosome 4
+KIT D820Y,imatinib,KIT,human chromosome 4
+KIT D816H,imatinib,KIT,human chromosome 4
+KIT K642E,imatinib,KIT,human chromosome 4
+KIT V560G,imatinib,KIT,human chromosome 4
+PIK3CA E545G,vemurafenib,PIK3CA,human chromosome 3
+GNAS R201C,Irinotecan / Vemurafenib / Cetuximab combination therapy,GNAS,human chromosome 20
+GNAS T393C,gefitinib,GNAS,human chromosome 20
diff --git a/examples/semtab2020_novartis/tables/BVV82N5B.json b/examples/semtab2020_novartis/tables/BVV82N5B.json
new file mode 100644
index 0000000..6aee266
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/BVV82N5B.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["ABL1 BCR-ABL Y253H","ABL1 BCR-ABL H396R","ABL1 BCR-ABL D276G","ABL1 Y253F","ABL1 BCR-ABL G250E","ABL1 BCR-ABL L248V","ABL1 BCR-ABL M244V","ABL1 BCR-ABL F359V","KIT V560D","KIT D820A","KIT C809G","KIT N822K","KIT Y823D","KIT D820Y","KIT D816H","KIT K642E","KIT V560G","PIK3CA E545G","GNAS R201C","GNAS T393C"]},{"index":1,"name":"col1","values":["nilotinib","imatinib","imatinib","imatinib","imatinib","imatinib","imatinib","imatinib","sunitinib","imatinib","imatinib","imatinib","imatinib","imatinib","imatinib","imatinib","imatinib","vemurafenib","Irinotecan / Vemurafenib / Cetuximab combination therapy","gefitinib"]},{"index":2,"name":"col2","values":["ABL1","ABL1","ABL1","ABL1","ABL1","ABL1","ABL1","ABL1","KIT","KIT","KIT","KIT","KIT","KIT","KIT","KIT","KIT","PIK3CA","GNAS","GNAS"]},{"index":3,"name":"col3","values":["human chromosome 9","human chromosome 9","human chromosome 9","human chromosome 9","human chromosome 9","human chromosome 9","human chromosome 9","human chromosome 9","human chromosome 4","human chromosome 4","human chromosome 4","human chromosome 4","human chromosome 4","human chromosome 4","human chromosome 4","human chromosome 4","human chromosome 4","human chromosome 3","human chromosome 20","human chromosome 20"]}],"metadata":{"table_id":"BVV82N5B","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q29938515","qnode_id":"Q29938515"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q412327","qnode_id":"Q412327"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q29938523","qnode_id":"Q29938523"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q177094","qnode_id":"Q177094"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q29938518","qnode_id":"Q29938518"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q177094","qnode_id":"Q177094"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q29938516","qnode_id":"Q29938516"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q177094","qnode_id":"Q177094"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q29938513","qnode_id":"Q29938513"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q177094","qnode_id":"Q177094"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q29938511","qnode_id":"Q29938511"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q177094","qnode_id":"Q177094"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q29938509","qnode_id":"Q29938509"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q177094","qnode_id":"Q177094"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q29938345","qnode_id":"Q29938345"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q177094","qnode_id":"Q177094"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873998","qnode_id":"Q14873998"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q840604","qnode_id":"Q840604"}]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q29938447","qnode_id":"Q29938447"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q417542","qnode_id":"Q417542"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q20969938","qnode_id":"Q20969938"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q836605","qnode_id":"Q836605"}]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q29938764","qnode_id":"Q29938764"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q177094","qnode_id":"Q177094"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q20969938","qnode_id":"Q20969938"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q836605","qnode_id":"Q836605"}]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q29938763","qnode_id":"Q29938763"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q177094","qnode_id":"Q177094"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q20969938","qnode_id":"Q20969938"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q836605","qnode_id":"Q836605"}]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q29938762","qnode_id":"Q29938762"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q177094","qnode_id":"Q177094"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q20969938","qnode_id":"Q20969938"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q836605","qnode_id":"Q836605"}]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q29938472","qnode_id":"Q29938472"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q177094","qnode_id":"Q177094"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q20969938","qnode_id":"Q20969938"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q836605","qnode_id":"Q836605"}]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q29938470","qnode_id":"Q29938470"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q177094","qnode_id":"Q177094"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q20969938","qnode_id":"Q20969938"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q836605","qnode_id":"Q836605"}]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q29938468","qnode_id":"Q29938468"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q177094","qnode_id":"Q177094"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q20969938","qnode_id":"Q20969938"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q836605","qnode_id":"Q836605"}]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q29938461","qnode_id":"Q29938461"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q177094","qnode_id":"Q177094"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q20969938","qnode_id":"Q20969938"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q836605","qnode_id":"Q836605"}]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q29938451","qnode_id":"Q29938451"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q177094","qnode_id":"Q177094"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q20969938","qnode_id":"Q20969938"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q836605","qnode_id":"Q836605"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q29938335","qnode_id":"Q29938335"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q423111","qnode_id":"Q423111"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q14887700","qnode_id":"Q14887700"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q668633","qnode_id":"Q668633"}]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q29938047","qnode_id":"Q29938047"}],[{"start":0,"end":56,"url":"http://www.wikidata.org/entity/Q43387526","qnode_id":"Q43387526"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14905160","qnode_id":"Q14905160"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q666752","qnode_id":"Q666752"}]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q29938321","qnode_id":"Q29938321"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q417824","qnode_id":"Q417824"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14905160","qnode_id":"Q14905160"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q666752","qnode_id":"Q666752"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q27429979:0","abs_uri":"http://www.wikidata.org/entity/Q27429979","rel_uri":"wd:Q27429979","approximation":false,"readable_label":"missense variant (Q27429979)"},{"id":"col-2","col_index":2,"label":"col2"},{"id":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/entity/Q7187","rel_uri":"wd:Q7187","approximation":false,"readable_label":"gene (Q7187)"},{"id":"col-3","col_index":3,"label":"col3"},{"id":"http://www.wikidata.org/entity/Q37748:0","abs_uri":"http://www.wikidata.org/entity/Q37748","rel_uri":"wd:Q37748","approximation":false,"readable_label":"chromosome (Q37748)"},{"id":"col-1","col_index":1,"label":"col1"}],"edges":[{"source":"http://www.wikidata.org/entity/Q27429979:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q27429979:0","target":"col-1","abs_uri":"http://www.wikidata.org/prop/P3355","rel_uri":"p:P3355","approximation":false,"readable_label":"negative therapeutic predictor (P3355)","id":2},{"source":"http://www.wikidata.org/entity/Q27429979:0","target":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/prop/P3433","rel_uri":"p:P3433","approximation":false,"readable_label":"biological variant of (P3433)","id":3},{"source":"http://www.wikidata.org/entity/Q27429979:0","target":"http://www.wikidata.org/entity/Q37748:0","abs_uri":"http://www.wikidata.org/prop/P1057","rel_uri":"p:P1057","approximation":false,"readable_label":"chromosome (P1057)","id":4},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-2","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":5},{"source":"http://www.wikidata.org/entity/Q37748:0","target":"col-3","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":6}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/BVV82N5B.links.tsv b/examples/semtab2020_novartis/tables/BVV82N5B.links.tsv
new file mode 100644
index 0000000..7c11ed0
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/BVV82N5B.links.tsv
@@ -0,0 +1,80 @@
+0 0 Q29938515
+0 1 Q412327
+0 2 Q14873998
+0 3 Q840604
+1 0 Q29938523
+1 1 Q177094
+1 2 Q14873998
+1 3 Q840604
+2 0 Q29938518
+2 1 Q177094
+2 2 Q14873998
+2 3 Q840604
+3 0 Q29938516
+3 1 Q177094
+3 2 Q14873998
+3 3 Q840604
+4 0 Q29938513
+4 1 Q177094
+4 2 Q14873998
+4 3 Q840604
+5 0 Q29938511
+5 1 Q177094
+5 2 Q14873998
+5 3 Q840604
+6 0 Q29938509
+6 1 Q177094
+6 2 Q14873998
+6 3 Q840604
+7 0 Q29938345
+7 1 Q177094
+7 2 Q14873998
+7 3 Q840604
+8 0 Q29938447
+8 1 Q417542
+8 2 Q20969938
+8 3 Q836605
+9 0 Q29938764
+9 1 Q177094
+9 2 Q20969938
+9 3 Q836605
+10 0 Q29938763
+10 1 Q177094
+10 2 Q20969938
+10 3 Q836605
+11 0 Q29938762
+11 1 Q177094
+11 2 Q20969938
+11 3 Q836605
+12 0 Q29938472
+12 1 Q177094
+12 2 Q20969938
+12 3 Q836605
+13 0 Q29938470
+13 1 Q177094
+13 2 Q20969938
+13 3 Q836605
+14 0 Q29938468
+14 1 Q177094
+14 2 Q20969938
+14 3 Q836605
+15 0 Q29938461
+15 1 Q177094
+15 2 Q20969938
+15 3 Q836605
+16 0 Q29938451
+16 1 Q177094
+16 2 Q20969938
+16 3 Q836605
+17 0 Q29938335
+17 1 Q423111
+17 2 Q14887700
+17 3 Q668633
+18 0 Q29938047
+18 1 Q43387526
+18 2 Q14905160
+18 3 Q666752
+19 0 Q29938321
+19 1 Q417824
+19 2 Q14905160
+19 3 Q666752
diff --git a/examples/semtab2020_novartis/tables/DTWAT6QK.csv b/examples/semtab2020_novartis/tables/DTWAT6QK.csv
new file mode 100644
index 0000000..75440e2
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/DTWAT6QK.csv
@@ -0,0 +1,21 @@
+col0,col1,col2,col3
+Solute carrier family 18 member A2,Major facilitator superfamily,SLC18A2,reserpine
+Free fatty acid receptor 2,G protein-coupled receptor 40-related receptor,FFAR2,valeric acid
+Free fatty acid receptor 3,G protein-coupled receptor 40-related receptor,FFAR3,valeric acid
+Sigma non-opioid intracellular receptor 1,ERG2/sigma1 receptor-like,SIGMAR1,dextromethorphan
+Protein kinase C zeta,Protein kinase-like domain superfamily,PRKCZ,arachidonic acid
+Transient receptor potential cation channel subfamily C member 6,Transient receptor potential channel,TRPC6,arachidonic acid
+Free fatty acid receptor 1,GPR40 receptor fatty acid,FFAR1,linoleic acid
+Peroxisome proliferator activated receptor gamma,"Zinc finger, NHR/GATA-type",PPARG,resveratrol
+Nuclear receptor subfamily 1 group I member 2,Nuclear hormone receptor,NR1I2,phenobarbital
+Lysine acetyltransferase 2B,Acyl-CoA N-acyltransferase,KAT2B,epigallocatechin gallate
+E1A binding protein p300,Histone acetyltransferase Rtt109/CBP,EP300,epigallocatechin gallate
+Nuclear receptor subfamily 1 group I member 3,Thyroid hormone receptor,NR1I3,(RS)-meclizine
+Nuclear receptor subfamily 3 group C member 1,Glucocorticoid receptor,NR3C1,desoximetasone
+Potassium voltage-gated channel subfamily B member 2,voltage-gated ion channel,KCNB2,quinine
+Acid sensing ion channel subunit 1,acid-sensing ion channel,ASIC1,ibuprofen
+Adrenoceptor beta 3,Beta 3 adrenoceptor,ADRB3,norepinephrine
+Adrenoceptor alpha 2C,Alpha 2C adrenoceptor,ADRA2C,norepinephrine
+Adrenoceptor alpha 2B,Alpha 2B adrenoceptor,ADRA2B,norepinephrine
+Adrenoceptor alpha 2A,Alpha 2A adrenoceptor,ADRA2A,norepinephrine
+Adrenoceptor alpha 1A,Alpha 1A adrenoceptor,ADRA1A,norepinephrine
diff --git a/examples/semtab2020_novartis/tables/DTWAT6QK.json b/examples/semtab2020_novartis/tables/DTWAT6QK.json
new file mode 100644
index 0000000..b0ce07c
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/DTWAT6QK.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["Solute carrier family 18 member A2","Free fatty acid receptor 2","Free fatty acid receptor 3","Sigma non-opioid intracellular receptor 1","Protein kinase C zeta","Transient receptor potential cation channel subfamily C member 6","Free fatty acid receptor 1","Peroxisome proliferator activated receptor gamma","Nuclear receptor subfamily 1 group I member 2","Lysine acetyltransferase 2B","E1A binding protein p300","Nuclear receptor subfamily 1 group I member 3","Nuclear receptor subfamily 3 group C member 1","Potassium voltage-gated channel subfamily B member 2","Acid sensing ion channel subunit 1","Adrenoceptor beta 3","Adrenoceptor alpha 2C","Adrenoceptor alpha 2B","Adrenoceptor alpha 2A","Adrenoceptor alpha 1A"]},{"index":1,"name":"col1","values":["Major facilitator superfamily","G protein-coupled receptor 40-related receptor","G protein-coupled receptor 40-related receptor","ERG2/sigma1 receptor-like","Protein kinase-like domain superfamily","Transient receptor potential channel","GPR40 receptor fatty acid","Zinc finger, NHR/GATA-type","Nuclear hormone receptor","Acyl-CoA N-acyltransferase","Histone acetyltransferase Rtt109/CBP","Thyroid hormone receptor","Glucocorticoid receptor","voltage-gated ion channel","acid-sensing ion channel","Beta 3 adrenoceptor","Alpha 2C adrenoceptor","Alpha 2B adrenoceptor","Alpha 2A adrenoceptor","Alpha 1A adrenoceptor"]},{"index":2,"name":"col2","values":["SLC18A2","FFAR2","FFAR3","SIGMAR1","PRKCZ","TRPC6","FFAR1","PPARG","NR1I2","KAT2B","EP300","NR1I3","NR3C1","KCNB2","ASIC1","ADRB3","ADRA2C","ADRA2B","ADRA2A","ADRA1A"]},{"index":3,"name":"col3","values":["reserpine","valeric acid","valeric acid","dextromethorphan","arachidonic acid","arachidonic acid","linoleic acid","resveratrol","phenobarbital","epigallocatechin gallate","epigallocatechin gallate","(RS)-meclizine","desoximetasone","quinine","ibuprofen","norepinephrine","norepinephrine","norepinephrine","norepinephrine","norepinephrine"]}],"metadata":{"table_id":"DTWAT6QK","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":34,"url":"http://www.wikidata.org/entity/Q2521094","qnode_id":"Q2521094"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q1886097","qnode_id":"Q1886097"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q14916234","qnode_id":"Q14916234"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q407841","qnode_id":"Q407841"}]],[[{"start":0,"end":26,"url":"http://www.wikidata.org/entity/Q5500223","qnode_id":"Q5500223"}],[{"start":0,"end":46,"url":"http://www.wikidata.org/entity/Q24781050","qnode_id":"Q24781050"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18026432","qnode_id":"Q18026432"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q407796","qnode_id":"Q407796"}]],[[{"start":0,"end":26,"url":"http://www.wikidata.org/entity/Q5500227","qnode_id":"Q5500227"}],[{"start":0,"end":46,"url":"http://www.wikidata.org/entity/Q24781050","qnode_id":"Q24781050"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18026426","qnode_id":"Q18026426"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q407796","qnode_id":"Q407796"}]],[[{"start":0,"end":41,"url":"http://www.wikidata.org/entity/Q408572","qnode_id":"Q408572"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q24776769","qnode_id":"Q24776769"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q14907850","qnode_id":"Q14907850"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q407781","qnode_id":"Q407781"}]],[[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q906403","qnode_id":"Q906403"}],[{"start":0,"end":38,"url":"http://www.wikidata.org/entity/Q24726164","qnode_id":"Q24726164"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18030782","qnode_id":"Q18030782"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q407699","qnode_id":"Q407699"}]],[[{"start":0,"end":64,"url":"http://www.wikidata.org/entity/Q7671469","qnode_id":"Q7671469"}],[{"start":0,"end":36,"url":"http://www.wikidata.org/entity/Q304043","qnode_id":"Q304043"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14914578","qnode_id":"Q14914578"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q407699","qnode_id":"Q407699"}]],[[{"start":0,"end":26,"url":"http://www.wikidata.org/entity/Q5500222","qnode_id":"Q5500222"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q24770516","qnode_id":"Q24770516"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18026423","qnode_id":"Q18026423"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q407426","qnode_id":"Q407426"}]],[[{"start":0,"end":48,"url":"http://www.wikidata.org/entity/Q1084878","qnode_id":"Q1084878"}],[{"start":0,"end":26,"url":"http://www.wikidata.org/entity/Q24768526","qnode_id":"Q24768526"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q20970208","qnode_id":"Q20970208"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q407329","qnode_id":"Q407329"}]],[[{"start":0,"end":45,"url":"http://www.wikidata.org/entity/Q409867","qnode_id":"Q409867"}],[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q422500","qnode_id":"Q422500"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14905074","qnode_id":"Q14905074"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q407241","qnode_id":"Q407241"}]],[[{"start":0,"end":27,"url":"http://www.wikidata.org/entity/Q7118491","qnode_id":"Q7118491"}],[{"start":0,"end":26,"url":"http://www.wikidata.org/entity/Q24771060","qnode_id":"Q24771060"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14908260","qnode_id":"Q14908260"}],[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q393339","qnode_id":"Q393339"}]],[[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q5323823","qnode_id":"Q5323823"}],[{"start":0,"end":36,"url":"http://www.wikidata.org/entity/Q24740044","qnode_id":"Q24740044"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14880807","qnode_id":"Q14880807"}],[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q393339","qnode_id":"Q393339"}]],[[{"start":0,"end":45,"url":"http://www.wikidata.org/entity/Q1500004","qnode_id":"Q1500004"}],[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q2539644","qnode_id":"Q2539644"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14905078","qnode_id":"Q14905078"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q386441","qnode_id":"Q386441"}]],[[{"start":0,"end":45,"url":"http://www.wikidata.org/entity/Q2663095","qnode_id":"Q2663095"}],[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q24742015","qnode_id":"Q24742015"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14877410","qnode_id":"Q14877410"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q385370","qnode_id":"Q385370"}]],[[{"start":0,"end":52,"url":"http://www.wikidata.org/entity/Q21107885","qnode_id":"Q21107885"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q354550","qnode_id":"Q354550"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18033991","qnode_id":"Q18033991"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q189522","qnode_id":"Q189522"}]],[[{"start":0,"end":34,"url":"http://www.wikidata.org/entity/Q21102075","qnode_id":"Q21102075"}],[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q4222244","qnode_id":"Q4222244"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q17709197","qnode_id":"Q17709197"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q186969","qnode_id":"Q186969"}]],[[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q4897175","qnode_id":"Q4897175"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q24770190","qnode_id":"Q24770190"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q17815528","qnode_id":"Q17815528"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q186242","qnode_id":"Q186242"}]],[[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q4734891","qnode_id":"Q4734891"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q24726750","qnode_id":"Q24726750"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q17710555","qnode_id":"Q17710555"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q186242","qnode_id":"Q186242"}]],[[],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q24786304","qnode_id":"Q24786304"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q17710538","qnode_id":"Q17710538"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q186242","qnode_id":"Q186242"}]],[[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q4734892","qnode_id":"Q4734892"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q24786565","qnode_id":"Q24786565"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q17710525","qnode_id":"Q17710525"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q186242","qnode_id":"Q186242"}]],[[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q4734884","qnode_id":"Q4734884"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q24786381","qnode_id":"Q24786381"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q17710494","qnode_id":"Q17710494"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q186242","qnode_id":"Q186242"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q8054:0","abs_uri":"http://www.wikidata.org/entity/Q8054","rel_uri":"wd:Q8054","approximation":false,"readable_label":"protein (Q8054)"},{"id":"col-2","col_index":2,"label":"col2"},{"id":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/entity/Q7187","rel_uri":"wd:Q7187","approximation":false,"readable_label":"gene (Q7187)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"col-3","col_index":3,"label":"col3"}],"edges":[{"source":"http://www.wikidata.org/entity/Q8054:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q8054:0","target":"col-1","abs_uri":"http://www.wikidata.org/prop/P361","rel_uri":"p:P361","approximation":false,"readable_label":"part of (P361)","id":2},{"source":"http://www.wikidata.org/entity/Q8054:0","target":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/prop/P702","rel_uri":"p:P702","approximation":false,"readable_label":"encoded by (P702)","id":3},{"source":"http://www.wikidata.org/entity/Q8054:0","target":"col-3","abs_uri":"http://www.wikidata.org/prop/P129","rel_uri":"p:P129","approximation":false,"readable_label":"physically interacts with (P129)","id":4},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-2","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":5}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/DTWAT6QK.links.tsv b/examples/semtab2020_novartis/tables/DTWAT6QK.links.tsv
new file mode 100644
index 0000000..6464a17
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/DTWAT6QK.links.tsv
@@ -0,0 +1,79 @@
+0 0 Q2521094
+0 1 Q1886097
+0 2 Q14916234
+0 3 Q407841
+1 0 Q5500223
+1 1 Q24781050
+1 2 Q18026432
+1 3 Q407796
+2 0 Q5500227
+2 1 Q24781050
+2 2 Q18026426
+2 3 Q407796
+3 0 Q408572
+3 1 Q24776769
+3 2 Q14907850
+3 3 Q407781
+4 0 Q906403
+4 1 Q24726164
+4 2 Q18030782
+4 3 Q407699
+5 0 Q7671469
+5 1 Q304043
+5 2 Q14914578
+5 3 Q407699
+6 0 Q5500222
+6 1 Q24770516
+6 2 Q18026423
+6 3 Q407426
+7 0 Q1084878
+7 1 Q24768526
+7 2 Q20970208
+7 3 Q407329
+8 0 Q409867
+8 1 Q422500
+8 2 Q14905074
+8 3 Q407241
+9 0 Q7118491
+9 1 Q24771060
+9 2 Q14908260
+9 3 Q393339
+10 0 Q5323823
+10 1 Q24740044
+10 2 Q14880807
+10 3 Q393339
+11 0 Q1500004
+11 1 Q2539644
+11 2 Q14905078
+11 3 Q386441
+12 0 Q2663095
+12 1 Q24742015
+12 2 Q14877410
+12 3 Q385370
+13 0 Q21107885
+13 1 Q354550
+13 2 Q18033991
+13 3 Q189522
+14 0 Q21102075
+14 1 Q4222244
+14 2 Q17709197
+14 3 Q186969
+15 0 Q4897175
+15 1 Q24770190
+15 2 Q17815528
+15 3 Q186242
+16 0 Q4734891
+16 1 Q24726750
+16 2 Q17710555
+16 3 Q186242
+17 1 Q24786304
+17 2 Q17710538
+17 3 Q186242
+18 0 Q4734892
+18 1 Q24786565
+18 2 Q17710525
+18 3 Q186242
+19 0 Q4734884
+19 1 Q24786381
+19 2 Q17710494
+19 3 Q186242
diff --git a/examples/semtab2020_novartis/tables/E02EDD05.csv b/examples/semtab2020_novartis/tables/E02EDD05.csv
new file mode 100644
index 0000000..fb5c70f
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/E02EDD05.csv
@@ -0,0 +1,21 @@
+col0,col1,col2,col3
+BACH2,Bach2,multiple sclerosis,protein-coding gene
+PTPRD,Ptprd,obesity,protein-coding gene
+KMO,Kmo,obesity,protein-coding gene
+SLMAP,Slmap,obesity,protein-coding gene
+RFC5,Rfc5,obesity,protein-coding gene
+PKP2,Pkp2,obesity,protein-coding gene
+RGS7,Rgs7,obesity,protein-coding gene
+MAP2K5,Map2k5,obesity,protein-coding gene
+UNC5C,Unc5c,obesity,protein-coding gene
+TSC2,Tsc2,obesity,protein-coding gene
+TMOD1,Tmod1,obesity,protein-coding gene
+TIMP2,Timp2,obesity,protein-coding gene
+TFAP2B,Tfap2b,obesity,protein-coding gene
+SYT1,Syt1,obesity,protein-coding gene
+SPTB,Sptb,obesity,protein-coding gene
+SNRPN,Snrpn,obesity,protein-coding gene
+SLIT1,Slit1,obesity,protein-coding gene
+SLC8A1,Slc8a1,obesity,protein-coding gene
+RYR2,Ryr2,obesity,protein-coding gene
+RSU1,Rsu1,obesity,protein-coding gene
diff --git a/examples/semtab2020_novartis/tables/E02EDD05.json b/examples/semtab2020_novartis/tables/E02EDD05.json
new file mode 100644
index 0000000..02c106c
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/E02EDD05.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["BACH2","PTPRD","KMO","SLMAP","RFC5","PKP2","RGS7","MAP2K5","UNC5C","TSC2","TMOD1","TIMP2","TFAP2B","SYT1","SPTB","SNRPN","SLIT1","SLC8A1","RYR2","RSU1"]},{"index":1,"name":"col1","values":["Bach2","Ptprd","Kmo","Slmap","Rfc5","Pkp2","Rgs7","Map2k5","Unc5c","Tsc2","Tmod1","Timp2","Tfap2b","Syt1","Sptb","Snrpn","Slit1","Slc8a1","Ryr2","Rsu1"]},{"index":2,"name":"col2","values":["multiple sclerosis","obesity","obesity","obesity","obesity","obesity","obesity","obesity","obesity","obesity","obesity","obesity","obesity","obesity","obesity","obesity","obesity","obesity","obesity","obesity"]},{"index":3,"name":"col3","values":["protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene"]}],"metadata":{"table_id":"E02EDD05","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18045327","qnode_id":"Q18045327"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18247915","qnode_id":"Q18247915"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q8277","qnode_id":"Q8277"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18030938","qnode_id":"Q18030938"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q24368425","qnode_id":"Q24368425"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q18033112","qnode_id":"Q18033112"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q18270862","qnode_id":"Q18270862"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18032648","qnode_id":"Q18032648"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18269898","qnode_id":"Q18269898"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18031100","qnode_id":"Q18031100"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18265314","qnode_id":"Q18265314"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18030571","qnode_id":"Q18030571"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18262540","qnode_id":"Q18262540"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18031117","qnode_id":"Q18031117"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18255637","qnode_id":"Q18255637"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q18030799","qnode_id":"Q18030799"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q18255591","qnode_id":"Q18255591"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18033169","qnode_id":"Q18033169"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18255248","qnode_id":"Q18255248"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18032199","qnode_id":"Q18032199"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18255142","qnode_id":"Q18255142"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18032024","qnode_id":"Q18032024"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18255035","qnode_id":"Q18255035"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18031996","qnode_id":"Q18031996"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18255006","qnode_id":"Q18255006"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q18031953","qnode_id":"Q18031953"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q18254838","qnode_id":"Q18254838"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18031809","qnode_id":"Q18031809"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18254440","qnode_id":"Q18254440"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18031692","qnode_id":"Q18031692"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18254300","qnode_id":"Q18254300"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18031635","qnode_id":"Q18031635"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18254222","qnode_id":"Q18254222"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18031587","qnode_id":"Q18031587"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18254171","qnode_id":"Q18254171"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q18031549","qnode_id":"Q18031549"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q18254165","qnode_id":"Q18254165"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18031341","qnode_id":"Q18031341"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18253897","qnode_id":"Q18253897"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18031334","qnode_id":"Q18031334"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18253881","qnode_id":"Q18253881"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/entity/Q7187","rel_uri":"wd:Q7187","approximation":false,"readable_label":"gene (Q7187)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"http://www.wikidata.org/entity/Q7187:1","abs_uri":"http://www.wikidata.org/entity/Q7187","rel_uri":"wd:Q7187","approximation":false,"readable_label":"gene (Q7187)"},{"id":"col-2","col_index":2,"label":"col2"},{"id":"http://www.wikidata.org/entity/Q12136:0","abs_uri":"http://www.wikidata.org/entity/Q12136","rel_uri":"wd:Q12136","approximation":false,"readable_label":"disease (Q12136)"},{"id":"col-3","col_index":3,"label":"col3"}],"edges":[{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"http://www.wikidata.org/entity/Q7187:1","abs_uri":"http://www.wikidata.org/prop/P684","rel_uri":"p:P684","approximation":false,"readable_label":"ortholog (P684)","id":2},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"http://www.wikidata.org/entity/Q12136:0","abs_uri":"http://www.wikidata.org/prop/P2293","rel_uri":"p:P2293","approximation":false,"readable_label":"genetic association (P2293)","id":3},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-3","abs_uri":"http://www.wikidata.org/prop/P279","rel_uri":"p:P279","approximation":false,"readable_label":"subclass of (P279)","id":4},{"source":"http://www.wikidata.org/entity/Q7187:1","target":"col-1","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":5},{"source":"http://www.wikidata.org/entity/Q12136:0","target":"col-2","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":6}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/E02EDD05.links.tsv b/examples/semtab2020_novartis/tables/E02EDD05.links.tsv
new file mode 100644
index 0000000..fad4a09
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/E02EDD05.links.tsv
@@ -0,0 +1,80 @@
+0 0 Q18045327
+0 1 Q18247915
+0 2 Q8277
+0 3 Q20747295
+1 0 Q18030938
+1 1 Q24368425
+1 2 Q12174
+1 3 Q20747295
+2 0 Q18033112
+2 1 Q18270862
+2 2 Q12174
+2 3 Q20747295
+3 0 Q18032648
+3 1 Q18269898
+3 2 Q12174
+3 3 Q20747295
+4 0 Q18031100
+4 1 Q18265314
+4 2 Q12174
+4 3 Q20747295
+5 0 Q18030571
+5 1 Q18262540
+5 2 Q12174
+5 3 Q20747295
+6 0 Q18031117
+6 1 Q18255637
+6 2 Q12174
+6 3 Q20747295
+7 0 Q18030799
+7 1 Q18255591
+7 2 Q12174
+7 3 Q20747295
+8 0 Q18033169
+8 1 Q18255248
+8 2 Q12174
+8 3 Q20747295
+9 0 Q18032199
+9 1 Q18255142
+9 2 Q12174
+9 3 Q20747295
+10 0 Q18032024
+10 1 Q18255035
+10 2 Q12174
+10 3 Q20747295
+11 0 Q18031996
+11 1 Q18255006
+11 2 Q12174
+11 3 Q20747295
+12 0 Q18031953
+12 1 Q18254838
+12 2 Q12174
+12 3 Q20747295
+13 0 Q18031809
+13 1 Q18254440
+13 2 Q12174
+13 3 Q20747295
+14 0 Q18031692
+14 1 Q18254300
+14 2 Q12174
+14 3 Q20747295
+15 0 Q18031635
+15 1 Q18254222
+15 2 Q12174
+15 3 Q20747295
+16 0 Q18031587
+16 1 Q18254171
+16 2 Q12174
+16 3 Q20747295
+17 0 Q18031549
+17 1 Q18254165
+17 2 Q12174
+17 3 Q20747295
+18 0 Q18031341
+18 1 Q18253897
+18 2 Q12174
+18 3 Q20747295
+19 0 Q18031334
+19 1 Q18253881
+19 2 Q12174
+19 3 Q20747295
diff --git a/examples/semtab2020_novartis/tables/E9DCUMM2.csv b/examples/semtab2020_novartis/tables/E9DCUMM2.csv
new file mode 100644
index 0000000..f93bfc5
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/E9DCUMM2.csv
@@ -0,0 +1,21 @@
+col0,col1,col2
+Alzheimer's disease,neurology,confusion with time and space
+Guillain–Barré Syndrome,neurology,ascending paralysis
+carpal tunnel syndrome,neurology,wrist pain
+ataxia telangiectasia,neurology,thymic hypoplasia
+polycystic liver disease,medical genetics,hepatic cyst
+narcolepsy,neurology,Ganguester
+Tourette syndrome,neurology,tic
+Marfan syndrome,medical genetics,pectus carinatum
+Wilson disease,endocrinology,jaundice
+Parkinson disease,neurology,spasticity
+gout,rheumatology,swelling
+Huntington disease,neurology,dementia
+cataract,ophthalmology,glare
+Pneumothorax,pulmonology,fatigue
+lung cancer,oncology,fatigue
+Crohn's disease,gastroenterology,fatigue
+Williams-Beuren syndrome,pediatrics,supravalvular aortic stenosis
+DiGeorge syndrome,medical genetics,thymic hypoplasia
+Trimethylaminuria,endocrinology,fetor
+Hajdu-Cheney syndrome,rheumatology,acroosteolysis
diff --git a/examples/semtab2020_novartis/tables/E9DCUMM2.json b/examples/semtab2020_novartis/tables/E9DCUMM2.json
new file mode 100644
index 0000000..d37800e
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/E9DCUMM2.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["Alzheimer's disease","Guillain–Barré Syndrome","carpal tunnel syndrome","ataxia telangiectasia","polycystic liver disease","narcolepsy","Tourette syndrome","Marfan syndrome","Wilson disease","Parkinson disease","gout","Huntington disease","cataract","Pneumothorax","lung cancer","Crohn's disease","Williams-Beuren syndrome","DiGeorge syndrome","Trimethylaminuria","Hajdu-Cheney syndrome"]},{"index":1,"name":"col1","values":["neurology","neurology","neurology","neurology","medical genetics","neurology","neurology","medical genetics","endocrinology","neurology","rheumatology","neurology","ophthalmology","pulmonology","oncology","gastroenterology","pediatrics","medical genetics","endocrinology","rheumatology"]},{"index":2,"name":"col2","values":["confusion with time and space","ascending paralysis","wrist pain","thymic hypoplasia","hepatic cyst","Ganguester","tic","pectus carinatum","jaundice","spasticity","swelling","dementia","glare","fatigue","fatigue","fatigue","supravalvular aortic stenosis","thymic hypoplasia","fetor","acroosteolysis"]}],"metadata":{"table_id":"E9DCUMM2","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q11081","qnode_id":"Q11081"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q83042","qnode_id":"Q83042"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q30141302","qnode_id":"Q30141302"}]],[[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q205214","qnode_id":"Q205214"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q83042","qnode_id":"Q83042"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q21756893","qnode_id":"Q21756893"}]],[[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q332293","qnode_id":"Q332293"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q83042","qnode_id":"Q83042"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q8038367","qnode_id":"Q8038367"}]],[[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q387082","qnode_id":"Q387082"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q83042","qnode_id":"Q83042"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q7799616","qnode_id":"Q7799616"}]],[[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q246002","qnode_id":"Q246002"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q1811141","qnode_id":"Q1811141"}]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q189561","qnode_id":"Q189561"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q83042","qnode_id":"Q83042"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q944473","qnode_id":"Q944473"}]],[[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q191779","qnode_id":"Q191779"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q83042","qnode_id":"Q83042"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q738538","qnode_id":"Q738538"}]],[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q208562","qnode_id":"Q208562"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q164218","qnode_id":"Q164218"}]],[[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q117121","qnode_id":"Q117121"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q162606","qnode_id":"Q162606"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q133244","qnode_id":"Q133244"}]],[[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q11085","qnode_id":"Q11085"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q83042","qnode_id":"Q83042"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q117060","qnode_id":"Q117060"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q133087","qnode_id":"Q133087"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q327657","qnode_id":"Q327657"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q109186","qnode_id":"Q109186"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q190564","qnode_id":"Q190564"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q83042","qnode_id":"Q83042"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q83030","qnode_id":"Q83030"}]],[[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q127724","qnode_id":"Q127724"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q161437","qnode_id":"Q161437"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q72759","qnode_id":"Q72759"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q203601","qnode_id":"Q203601"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q203337","qnode_id":"Q203337"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q9690","qnode_id":"Q9690"}]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q47912","qnode_id":"Q47912"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q162555","qnode_id":"Q162555"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q9690","qnode_id":"Q9690"}]],[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q1472","qnode_id":"Q1472"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q120569","qnode_id":"Q120569"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q9690","qnode_id":"Q9690"}]],[[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q558077","qnode_id":"Q558077"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q123028","qnode_id":"Q123028"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q16874615","qnode_id":"Q16874615"}]],[[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q525642","qnode_id":"Q525642"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q7799616","qnode_id":"Q7799616"}]],[[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q506433","qnode_id":"Q506433"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q162606","qnode_id":"Q162606"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q5445966","qnode_id":"Q5445966"}]],[[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q632228","qnode_id":"Q632228"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q327657","qnode_id":"Q327657"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q4676384","qnode_id":"Q4676384"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q929833:0","abs_uri":"http://www.wikidata.org/entity/Q929833","rel_uri":"wd:Q929833","approximation":false,"readable_label":"rare disease (Q929833)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"http://www.wikidata.org/entity/Q930752:0","abs_uri":"http://www.wikidata.org/entity/Q930752","rel_uri":"wd:Q930752","approximation":false,"readable_label":"medical specialty (Q930752)"},{"id":"col-2","col_index":2,"label":"col2"}],"edges":[{"source":"http://www.wikidata.org/entity/Q929833:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q929833:0","target":"http://www.wikidata.org/entity/Q930752:0","abs_uri":"http://www.wikidata.org/prop/P1995","rel_uri":"p:P1995","approximation":false,"readable_label":"health specialty (P1995)","id":2},{"source":"http://www.wikidata.org/entity/Q929833:0","target":"col-2","abs_uri":"http://www.wikidata.org/prop/P780","rel_uri":"p:P780","approximation":false,"readable_label":"symptoms (P780)","id":3},{"source":"http://www.wikidata.org/entity/Q930752:0","target":"col-1","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":4}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/E9DCUMM2.links.tsv b/examples/semtab2020_novartis/tables/E9DCUMM2.links.tsv
new file mode 100644
index 0000000..21e634d
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/E9DCUMM2.links.tsv
@@ -0,0 +1,60 @@
+0 0 Q11081
+0 1 Q83042
+0 2 Q30141302
+1 0 Q205214
+1 1 Q83042
+1 2 Q21756893
+2 0 Q332293
+2 1 Q83042
+2 2 Q8038367
+3 0 Q387082
+3 1 Q83042
+3 2 Q7799616
+4 0 Q246002
+4 1 Q1071953
+4 2 Q1811141
+5 0 Q189561
+5 1 Q83042
+5 2 Q944473
+6 0 Q191779
+6 1 Q83042
+6 2 Q738538
+7 0 Q208562
+7 1 Q1071953
+7 2 Q164218
+8 0 Q117121
+8 1 Q162606
+8 2 Q133244
+9 0 Q11085
+9 1 Q83042
+9 2 Q117060
+10 0 Q133087
+10 1 Q327657
+10 2 Q109186
+11 0 Q190564
+11 1 Q83042
+11 2 Q83030
+12 0 Q127724
+12 1 Q161437
+12 2 Q72759
+13 0 Q203601
+13 1 Q203337
+13 2 Q9690
+14 0 Q47912
+14 1 Q162555
+14 2 Q9690
+15 0 Q1472
+15 1 Q120569
+15 2 Q9690
+16 0 Q558077
+16 1 Q123028
+16 2 Q16874615
+17 0 Q525642
+17 1 Q1071953
+17 2 Q7799616
+18 0 Q506433
+18 1 Q162606
+18 2 Q5445966
+19 0 Q632228
+19 1 Q327657
+19 2 Q4676384
diff --git a/examples/semtab2020_novartis/tables/F40WSTOZ.csv b/examples/semtab2020_novartis/tables/F40WSTOZ.csv
new file mode 100644
index 0000000..b117e8a
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/F40WSTOZ.csv
@@ -0,0 +1,21 @@
+col0,col1,col2
+myotonia congenita,neurology,myotonia
+Postural orthostatic tachycardia syndrome,cardiology,asthenia
+Mal de parkinso,endocrinology,self-injury
+neuroacanthocytosis,neurology,Acanthocyte
+Antley-Bixler syndrome,medical genetics,craniosynostosis
+McCune–Albright syndrome,medical genetics,precocious puberty
+achromatopsia,ophthalmology,amblyopia
+aortic valve stenosis,cardiology,syncope
+Fabry disease,endocrinology,fatigue
+hereditary stomatocytosis,hematology,stomatocyte
+Van der Woude syndrome,medical genetics,cleft palate
+Southeast Asian ovalocytosis,hematology,Elliptocyte
+hereditary elliptocytosis,hematology,Elliptocyte
+Saethre-Chotzen syndrome,rheumatology,craniosynostosis
+Timothy syndrome,neurology,syncope
+MOMO syndrome,genetics,obesity
+osteitis fibrosa cystica,endocrinology,Brown tumor
+fatal familial insomnia,psychiatry,agrypnia excitata
+acrodermatitis enteropathica,endocrinology,blister
+galactosemia,endocrinology,lactose intolerance
diff --git a/examples/semtab2020_novartis/tables/F40WSTOZ.json b/examples/semtab2020_novartis/tables/F40WSTOZ.json
new file mode 100644
index 0000000..4b78b25
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/F40WSTOZ.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["myotonia congenita","Postural orthostatic tachycardia syndrome","Mal de parkinso","neuroacanthocytosis","Antley-Bixler syndrome","McCune–Albright syndrome","achromatopsia","aortic valve stenosis","Fabry disease","hereditary stomatocytosis","Van der Woude syndrome","Southeast Asian ovalocytosis","hereditary elliptocytosis","Saethre-Chotzen syndrome","Timothy syndrome","MOMO syndrome","osteitis fibrosa cystica","fatal familial insomnia","acrodermatitis enteropathica","galactosemia"]},{"index":1,"name":"col1","values":["neurology","cardiology","endocrinology","neurology","medical genetics","medical genetics","ophthalmology","cardiology","endocrinology","hematology","medical genetics","hematology","hematology","rheumatology","neurology","genetics","endocrinology","psychiatry","endocrinology","endocrinology"]},{"index":2,"name":"col2","values":["myotonia","asthenia","self-injury","Acanthocyte","craniosynostosis","precocious puberty","amblyopia","syncope","fatigue","stomatocyte","cleft palate","Elliptocyte","Elliptocyte","craniosynostosis","syncope","obesity","Brown tumor","agrypnia excitata","blister","lactose intolerance"]}],"metadata":{"table_id":"F40WSTOZ","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q587420","qnode_id":"Q587420"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q83042","qnode_id":"Q83042"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q1277793","qnode_id":"Q1277793"}]],[[{"start":0,"end":41,"url":"http://www.wikidata.org/entity/Q620760","qnode_id":"Q620760"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q10379","qnode_id":"Q10379"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q633403","qnode_id":"Q633403"}]],[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q727436","qnode_id":"Q727436"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q162606","qnode_id":"Q162606"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q622527","qnode_id":"Q622527"}]],[[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q746781","qnode_id":"Q746781"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q83042","qnode_id":"Q83042"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q415844","qnode_id":"Q415844"}]],[[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q585011","qnode_id":"Q585011"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q378183","qnode_id":"Q378183"}]],[[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q727008","qnode_id":"Q727008"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q224513","qnode_id":"Q224513"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q432396","qnode_id":"Q432396"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q161437","qnode_id":"Q161437"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q207855","qnode_id":"Q207855"}]],[[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q531019","qnode_id":"Q531019"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q10379","qnode_id":"Q10379"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q180007","qnode_id":"Q180007"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q615645","qnode_id":"Q615645"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q162606","qnode_id":"Q162606"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q9690","qnode_id":"Q9690"}]],[[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q3973817","qnode_id":"Q3973817"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q103824","qnode_id":"Q103824"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q3973815","qnode_id":"Q3973815"}]],[[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q2033532","qnode_id":"Q2033532"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q3889390","qnode_id":"Q3889390"}]],[[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q3358864","qnode_id":"Q3358864"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q103824","qnode_id":"Q103824"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q3358860","qnode_id":"Q3358860"}]],[[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q2298020","qnode_id":"Q2298020"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q103824","qnode_id":"Q103824"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q3358860","qnode_id":"Q3358860"}]],[[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q3508686","qnode_id":"Q3508686"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q327657","qnode_id":"Q327657"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q378183","qnode_id":"Q378183"}]],[[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q3508705","qnode_id":"Q3508705"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q83042","qnode_id":"Q83042"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q180007","qnode_id":"Q180007"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q3079989","qnode_id":"Q3079989"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q7162","qnode_id":"Q7162"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}]],[[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q799615","qnode_id":"Q799615"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q162606","qnode_id":"Q162606"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q4976425","qnode_id":"Q4976425"}]],[[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q862872","qnode_id":"Q862872"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q7867","qnode_id":"Q7867"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q397554","qnode_id":"Q397554"}]],[[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q937818","qnode_id":"Q937818"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q162606","qnode_id":"Q162606"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q276469","qnode_id":"Q276469"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q774483","qnode_id":"Q774483"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q162606","qnode_id":"Q162606"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q219223","qnode_id":"Q219223"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q929833:0","abs_uri":"http://www.wikidata.org/entity/Q929833","rel_uri":"wd:Q929833","approximation":false,"readable_label":"rare disease (Q929833)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"col-2","col_index":2,"label":"col2"}],"edges":[{"source":"http://www.wikidata.org/entity/Q929833:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q929833:0","target":"col-1","abs_uri":"http://www.wikidata.org/prop/P1995","rel_uri":"p:P1995","approximation":false,"readable_label":"health specialty (P1995)","id":2},{"source":"http://www.wikidata.org/entity/Q929833:0","target":"col-2","abs_uri":"http://www.wikidata.org/prop/P780","rel_uri":"p:P780","approximation":false,"readable_label":"symptoms (P780)","id":3}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/F40WSTOZ.links.tsv b/examples/semtab2020_novartis/tables/F40WSTOZ.links.tsv
new file mode 100644
index 0000000..4a0752e
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/F40WSTOZ.links.tsv
@@ -0,0 +1,60 @@
+0 0 Q587420
+0 1 Q83042
+0 2 Q1277793
+1 0 Q620760
+1 1 Q10379
+1 2 Q633403
+2 0 Q727436
+2 1 Q162606
+2 2 Q622527
+3 0 Q746781
+3 1 Q83042
+3 2 Q415844
+4 0 Q585011
+4 1 Q1071953
+4 2 Q378183
+5 0 Q727008
+5 1 Q1071953
+5 2 Q224513
+6 0 Q432396
+6 1 Q161437
+6 2 Q207855
+7 0 Q531019
+7 1 Q10379
+7 2 Q180007
+8 0 Q615645
+8 1 Q162606
+8 2 Q9690
+9 0 Q3973817
+9 1 Q103824
+9 2 Q3973815
+10 0 Q2033532
+10 1 Q1071953
+10 2 Q3889390
+11 0 Q3358864
+11 1 Q103824
+11 2 Q3358860
+12 0 Q2298020
+12 1 Q103824
+12 2 Q3358860
+13 0 Q3508686
+13 1 Q327657
+13 2 Q378183
+14 0 Q3508705
+14 1 Q83042
+14 2 Q180007
+15 0 Q3079989
+15 1 Q7162
+15 2 Q12174
+16 0 Q799615
+16 1 Q162606
+16 2 Q4976425
+17 0 Q862872
+17 1 Q7867
+17 2 Q397554
+18 0 Q937818
+18 1 Q162606
+18 2 Q276469
+19 0 Q774483
+19 1 Q162606
+19 2 Q219223
diff --git a/examples/semtab2020_novartis/tables/FJ6IZAGM.csv b/examples/semtab2020_novartis/tables/FJ6IZAGM.csv
new file mode 100644
index 0000000..4a39296
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/FJ6IZAGM.csv
@@ -0,0 +1,21 @@
+col0,col1,col2
+theobromine,Ottův slovník naučný,carbon
+L-alanine,Brockhaus and Efron Encyclopedic Dictionary,carbon
+succinic acid,Brockhaus and Efron Encyclopedic Dictionary,carbon
+quinine,Brockhaus and Efron Encyclopedic Dictionary,carbon
+zolpidem,Opium Law,carbon
+D-methamphetamine,Opium Law,carbon
+inulin,Armenian Soviet Encyclopedia,hydrogen
+cortisol,Armenian Soviet Encyclopedia,hydrogen
+phenmetrazine,Opium Law,carbon
+glycine,Armenian Soviet Encyclopedia,carbon
+testosterone,Armenian Soviet Encyclopedia,carbon
+Phendimetrazine,Opium Law,carbon
+dextroamphetamine,Opium Law,hydrogen
+nitrous oxide,Armenian Soviet Encyclopedia,oxygen
+ergotamine,Brockhaus and Efron Encyclopedic Dictionary,carbon
+hydroquinone,Brockhaus and Efron Encyclopedic Dictionary,carbon
+Hyoscyamine,Brockhaus and Efron Encyclopedic Dictionary,carbon
+pemoline,Opium Law,carbon
+phentermine,Opium Law,carbon
+calcium cyanamide,Armenian Soviet Encyclopedia,calcium
diff --git a/examples/semtab2020_novartis/tables/FJ6IZAGM.json b/examples/semtab2020_novartis/tables/FJ6IZAGM.json
new file mode 100644
index 0000000..5c7e0f2
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/FJ6IZAGM.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["theobromine","L-alanine","succinic acid","quinine","zolpidem","D-methamphetamine","inulin","cortisol","phenmetrazine","glycine","testosterone","Phendimetrazine","dextroamphetamine","nitrous oxide","ergotamine","hydroquinone","Hyoscyamine","pemoline","phentermine","calcium cyanamide"]},{"index":1,"name":"col1","values":["Ottův slovník naučný","Brockhaus and Efron Encyclopedic Dictionary","Brockhaus and Efron Encyclopedic Dictionary","Brockhaus and Efron Encyclopedic Dictionary","Opium Law","Opium Law","Armenian Soviet Encyclopedia","Armenian Soviet Encyclopedia","Opium Law","Armenian Soviet Encyclopedia","Armenian Soviet Encyclopedia","Opium Law","Opium Law","Armenian Soviet Encyclopedia","Brockhaus and Efron Encyclopedic Dictionary","Brockhaus and Efron Encyclopedic Dictionary","Brockhaus and Efron Encyclopedic Dictionary","Opium Law","Opium Law","Armenian Soviet Encyclopedia"]},{"index":2,"name":"col2","values":["carbon","carbon","carbon","carbon","carbon","carbon","hydrogen","hydrogen","carbon","carbon","carbon","carbon","hydrogen","oxygen","carbon","carbon","carbon","carbon","carbon","calcium"]}],"metadata":{"table_id":"FJ6IZAGM","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q206844","qnode_id":"Q206844"}],[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q2041543","qnode_id":"Q2041543"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q218642","qnode_id":"Q218642"}],[{"start":0,"end":43,"url":"http://www.wikidata.org/entity/Q602358","qnode_id":"Q602358"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q213050","qnode_id":"Q213050"}],[{"start":0,"end":43,"url":"http://www.wikidata.org/entity/Q602358","qnode_id":"Q602358"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q189522","qnode_id":"Q189522"}],[{"start":0,"end":43,"url":"http://www.wikidata.org/entity/Q602358","qnode_id":"Q602358"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q218842","qnode_id":"Q218842"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q316572","qnode_id":"Q316572"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q191924","qnode_id":"Q191924"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q316572","qnode_id":"Q316572"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q201552","qnode_id":"Q201552"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q556","qnode_id":"Q556"}]],[[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q190875","qnode_id":"Q190875"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q556","qnode_id":"Q556"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q3329406","qnode_id":"Q3329406"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q316572","qnode_id":"Q316572"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q620730","qnode_id":"Q620730"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q1318776","qnode_id":"Q1318776"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q1649219","qnode_id":"Q1649219"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q316572","qnode_id":"Q316572"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q1706418","qnode_id":"Q1706418"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q316572","qnode_id":"Q316572"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q556","qnode_id":"Q556"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q905750","qnode_id":"Q905750"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q629","qnode_id":"Q629"}]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q419186","qnode_id":"Q419186"}],[{"start":0,"end":43,"url":"http://www.wikidata.org/entity/Q602358","qnode_id":"Q602358"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q419164","qnode_id":"Q419164"}],[{"start":0,"end":43,"url":"http://www.wikidata.org/entity/Q602358","qnode_id":"Q602358"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q413762","qnode_id":"Q413762"}],[{"start":0,"end":43,"url":"http://www.wikidata.org/entity/Q602358","qnode_id":"Q602358"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q419008","qnode_id":"Q419008"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q316572","qnode_id":"Q316572"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q418157","qnode_id":"Q418157"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q316572","qnode_id":"Q316572"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q367994","qnode_id":"Q367994"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q706","qnode_id":"Q706"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q12140:0","abs_uri":"http://www.wikidata.org/entity/Q12140","rel_uri":"wd:Q12140","approximation":false,"readable_label":"medication (Q12140)"},{"id":"col-2","col_index":2,"label":"col2"},{"id":"http://www.wikidata.org/entity/Q11344:0","abs_uri":"http://www.wikidata.org/entity/Q11344","rel_uri":"wd:Q11344","approximation":false,"readable_label":"chemical element (Q11344)"},{"id":"col-1","col_index":1,"label":"col1"}],"edges":[{"source":"http://www.wikidata.org/entity/Q12140:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q12140:0","target":"col-1","abs_uri":"http://www.wikidata.org/prop/P1343","rel_uri":"p:P1343","approximation":false,"readable_label":"described by source (P1343)","id":2},{"source":"http://www.wikidata.org/entity/Q12140:0","target":"http://www.wikidata.org/entity/Q11344:0","abs_uri":"http://www.wikidata.org/prop/P527","rel_uri":"p:P527","approximation":false,"readable_label":"has part (P527)","id":3},{"source":"http://www.wikidata.org/entity/Q11344:0","target":"col-2","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":4}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/FJ6IZAGM.links.tsv b/examples/semtab2020_novartis/tables/FJ6IZAGM.links.tsv
new file mode 100644
index 0000000..a416381
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/FJ6IZAGM.links.tsv
@@ -0,0 +1,60 @@
+0 0 Q206844
+0 1 Q2041543
+0 2 Q623
+1 0 Q218642
+1 1 Q602358
+1 2 Q623
+2 0 Q213050
+2 1 Q602358
+2 2 Q623
+3 0 Q189522
+3 1 Q602358
+3 2 Q623
+4 0 Q218842
+4 1 Q316572
+4 2 Q623
+5 0 Q191924
+5 1 Q316572
+5 2 Q623
+6 0 Q201552
+6 1 Q2657718
+6 2 Q556
+7 0 Q190875
+7 1 Q2657718
+7 2 Q556
+8 0 Q3329406
+8 1 Q316572
+8 2 Q623
+9 0 Q620730
+9 1 Q2657718
+9 2 Q623
+10 0 Q1318776
+10 1 Q2657718
+10 2 Q623
+11 0 Q1649219
+11 1 Q316572
+11 2 Q623
+12 0 Q1706418
+12 1 Q316572
+12 2 Q556
+13 0 Q905750
+13 1 Q2657718
+13 2 Q629
+14 0 Q419186
+14 1 Q602358
+14 2 Q623
+15 0 Q419164
+15 1 Q602358
+15 2 Q623
+16 0 Q413762
+16 1 Q602358
+16 2 Q623
+17 0 Q419008
+17 1 Q316572
+17 2 Q623
+18 0 Q418157
+18 1 Q316572
+18 2 Q623
+19 0 Q367994
+19 1 Q2657718
+19 2 Q706
diff --git a/examples/semtab2020_novartis/tables/FVRBAOU9.csv b/examples/semtab2020_novartis/tables/FVRBAOU9.csv
new file mode 100644
index 0000000..0ddacd3
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/FVRBAOU9.csv
@@ -0,0 +1,21 @@
+col0,col1,col2,col3,col4
+Efficacy and Safety Study of Saxagliptin + Metformin Immediate Release (IR) Versus Metformin IR Alone in Type 2 Diabetes Mellitus,166,Puerto Rico,metformin,2010-02-01
+Effect of Insulin Detemir and Insulin Glargine on Blood Glucose Control in Subjects With Type 2 Diabetes,457,Puerto Rico,metformin,2010-06-01
+Study of Pegylated Human Recombinant Arginase for Liver Cancer,15,Hong Kong,doxorubicin,2009-08-01
+Combination Chemotherapy and Surgery in Treating Young Patients With Wilms Tumor,249,Puerto Rico,doxorubicin,2016-12-31
+Phase II Randomized Trial Evaluating Neoadjuvant Therapy With Neratinib and/or Trastuzumab Followed by Postoperative Trastuzumab in Women With Locally Advanced HER2-positive Breast Cancer,141,Puerto Rico,doxorubicin,2015-09-01
+Combination Chemotherapy and Radiation Therapy in Treating Young Patients With Newly Diagnosed Hodgkin Lymphoma,166,Puerto Rico,doxorubicin,2012-03-01
+Risk-Adapted Chemotherapy in Treating Younger Patients With Newly Diagnosed Standard-Risk Acute Lymphoblastic Leukemia or Localized B-Lineage Lymphoblastic Lymphoma,9349,Puerto Rico,doxorubicin,2019-03-31
+"Cyclophosphamide, Doxorubicin, Vincristine, Prednisone, Rituximab Pateinets With Aggresive NHL",60,Puerto Rico,doxorubicin,2010-11-01
+Linagliptin in Combination With Metformin in Treatment Naive Patients With Type 2 Diabetes Mellitus and Insufficient Glycaemic Control,689,Hong Kong,metformin,2013-03-01
+Efficacy and Safety of TAK-875 Compared to Glimepiride When Used With Metformin in Participants With Type 2 Diabetes,2454,Hong Kong,metformin,2014-03-01
+The Efficacy of Insulin Degludec/Liraglutide as add-on Therapy in Controlling Glycaemia in Adults With Type 2 Diabetes Inadequately Controlled on Sulphonylurea With or Without Metformin Therapy,435,Puerto Rico,metformin,2013-10-23
+A Study Comparing the Effect of Dulaglutide With Liraglutide in Type 2 Diabetes,599,Puerto Rico,metformin,2013-11-01
+Efficacy and Safety of Semaglutide Versus Dulaglutide as add-on to Metformin in Subjects With Type 2 Diabetes.,1201,Hong Kong,metformin,2017-04-10
+Study of Copanlisib in Combination With Standard Immunochemotherapy in Relapsed Indolent Non-Hodgkin's Lymphoma (iNHL),544,Hong Kong,doxorubicin,2021-09-30
+"A Study of Brentuximab Vedotin + Adriamycin, Vinblastine, and Dacarbazine in Pediatric Participants With Advanced Stage Newly Diagnosed Hodgkin Lymphoma",55,Hong Kong,doxorubicin,2020-06-30
+Daptomycin Versus Vancomycin in Participants With Skin Infections Due to MRSA,250,Puerto Rico,antibiotic,2012-09-01
+"A Study to Evaluate the Effectiveness, Safety, and Tolerability of Canagliflozin in Combination With Metformin in the Treatment of Patients With Type 2 Diabetes Mellitus With Inadequate Glycemic Control With Diet and Exercise",1186,Puerto Rico,metformin,2014-12-02
+Efficacy and Safety of FIAsp Compared to Insulin Aspart in Combination With Insulin Glargine and Metformin in Adults With Type 2 Diabetes,881,Puerto Rico,metformin,2015-01-22
+Efficacy and Safety of Switching From Sitagliptin to Liraglutide in Subjects With Type 2 Diabetes Not Achieving Adequate Glycaemic Control on Sitagliptin and Metformin,407,Puerto Rico,metformin,2015-06-15
+Efficacy and Safety of Semaglutide Once-weekly Versus Placebo as add-on to Basal Insulin Alone or Basal Insulin in Combination With Metformin in Subjects With Type 2 Diabetes,397,Puerto Rico,metformin,2015-11-21
diff --git a/examples/semtab2020_novartis/tables/FVRBAOU9.json b/examples/semtab2020_novartis/tables/FVRBAOU9.json
new file mode 100644
index 0000000..e355db7
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/FVRBAOU9.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["Efficacy and Safety Study of Saxagliptin + Metformin Immediate Release (IR) Versus Metformin IR Alone in Type 2 Diabetes Mellitus","Effect of Insulin Detemir and Insulin Glargine on Blood Glucose Control in Subjects With Type 2 Diabetes","Study of Pegylated Human Recombinant Arginase for Liver Cancer","Combination Chemotherapy and Surgery in Treating Young Patients With Wilms Tumor","Phase II Randomized Trial Evaluating Neoadjuvant Therapy With Neratinib and/or Trastuzumab Followed by Postoperative Trastuzumab in Women With Locally Advanced HER2-positive Breast Cancer","Combination Chemotherapy and Radiation Therapy in Treating Young Patients With Newly Diagnosed Hodgkin Lymphoma","Risk-Adapted Chemotherapy in Treating Younger Patients With Newly Diagnosed Standard-Risk Acute Lymphoblastic Leukemia or Localized B-Lineage Lymphoblastic Lymphoma","Cyclophosphamide, Doxorubicin, Vincristine, Prednisone, Rituximab Pateinets With Aggresive NHL","Linagliptin in Combination With Metformin in Treatment Naive Patients With Type 2 Diabetes Mellitus and Insufficient Glycaemic Control","Efficacy and Safety of TAK-875 Compared to Glimepiride When Used With Metformin in Participants With Type 2 Diabetes","The Efficacy of Insulin Degludec/Liraglutide as add-on Therapy in Controlling Glycaemia in Adults With Type 2 Diabetes Inadequately Controlled on Sulphonylurea With or Without Metformin Therapy","A Study Comparing the Effect of Dulaglutide With Liraglutide in Type 2 Diabetes","Efficacy and Safety of Semaglutide Versus Dulaglutide as add-on to Metformin in Subjects With Type 2 Diabetes.","Study of Copanlisib in Combination With Standard Immunochemotherapy in Relapsed Indolent Non-Hodgkin's Lymphoma (iNHL)","A Study of Brentuximab Vedotin + Adriamycin, Vinblastine, and Dacarbazine in Pediatric Participants With Advanced Stage Newly Diagnosed Hodgkin Lymphoma","Daptomycin Versus Vancomycin in Participants With Skin Infections Due to MRSA","A Study to Evaluate the Effectiveness, Safety, and Tolerability of Canagliflozin in Combination With Metformin in the Treatment of Patients With Type 2 Diabetes Mellitus With Inadequate Glycemic Control With Diet and Exercise","Efficacy and Safety of FIAsp Compared to Insulin Aspart in Combination With Insulin Glargine and Metformin in Adults With Type 2 Diabetes","Efficacy and Safety of Switching From Sitagliptin to Liraglutide in Subjects With Type 2 Diabetes Not Achieving Adequate Glycaemic Control on Sitagliptin and Metformin","Efficacy and Safety of Semaglutide Once-weekly Versus Placebo as add-on to Basal Insulin Alone or Basal Insulin in Combination With Metformin in Subjects With Type 2 Diabetes"]},{"index":1,"name":"col1","values":["166","457","15","249","141","166","9349","60","689","2454","435","599","1201","544","55","250","1186","881","407","397"]},{"index":2,"name":"col2","values":["Puerto Rico","Puerto Rico","Hong Kong","Puerto Rico","Puerto Rico","Puerto Rico","Puerto Rico","Puerto Rico","Hong Kong","Hong Kong","Puerto Rico","Puerto Rico","Hong Kong","Hong Kong","Hong Kong","Puerto Rico","Puerto Rico","Puerto Rico","Puerto Rico","Puerto Rico"]},{"index":3,"name":"col3","values":["metformin","metformin","doxorubicin","doxorubicin","doxorubicin","doxorubicin","doxorubicin","doxorubicin","metformin","metformin","metformin","metformin","metformin","doxorubicin","doxorubicin","antibiotic","metformin","metformin","metformin","metformin"]},{"index":4,"name":"col4","values":["2010-02-01","2010-06-01","2009-08-01","2016-12-31","2015-09-01","2012-03-01","2019-03-31","2010-11-01","2013-03-01","2014-03-01","2013-10-23","2013-11-01","2017-04-10","2021-09-30","2020-06-30","2012-09-01","2014-12-02","2015-01-22","2015-06-15","2015-11-21"]}],"metadata":{"table_id":"FVRBAOU9","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":129,"url":"http://www.wikidata.org/entity/Q64679115","qnode_id":"Q64679115"}],[],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q1183","qnode_id":"Q1183"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q19484","qnode_id":"Q19484"}],[]],[[{"start":0,"end":104,"url":"http://www.wikidata.org/entity/Q64675464","qnode_id":"Q64675464"}],[],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q1183","qnode_id":"Q1183"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q19484","qnode_id":"Q19484"}],[]],[[{"start":0,"end":62,"url":"http://www.wikidata.org/entity/Q64661921","qnode_id":"Q64661921"}],[],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q8646","qnode_id":"Q8646"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q18936","qnode_id":"Q18936"}],[]],[[{"start":0,"end":80,"url":"http://www.wikidata.org/entity/Q64665472","qnode_id":"Q64665472"}],[],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q1183","qnode_id":"Q1183"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q18936","qnode_id":"Q18936"}],[]],[[{"start":0,"end":187,"url":"http://www.wikidata.org/entity/Q64660363","qnode_id":"Q64660363"}],[],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q1183","qnode_id":"Q1183"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q18936","qnode_id":"Q18936"}],[]],[[{"start":0,"end":111,"url":"http://www.wikidata.org/entity/Q64659803","qnode_id":"Q64659803"}],[],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q1183","qnode_id":"Q1183"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q18936","qnode_id":"Q18936"}],[]],[[{"start":0,"end":164,"url":"http://www.wikidata.org/entity/Q64652731","qnode_id":"Q64652731"}],[],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q1183","qnode_id":"Q1183"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q18936","qnode_id":"Q18936"}],[]],[[{"start":0,"end":94,"url":"http://www.wikidata.org/entity/Q64647103","qnode_id":"Q64647103"}],[],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q1183","qnode_id":"Q1183"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q18936","qnode_id":"Q18936"}],[]],[[{"start":0,"end":134,"url":"http://www.wikidata.org/entity/Q64647714","qnode_id":"Q64647714"}],[],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q8646","qnode_id":"Q8646"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q19484","qnode_id":"Q19484"}],[]],[[{"start":0,"end":116,"url":"http://www.wikidata.org/entity/Q64647563","qnode_id":"Q64647563"}],[],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q8646","qnode_id":"Q8646"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q19484","qnode_id":"Q19484"}],[]],[[{"start":0,"end":193,"url":"http://www.wikidata.org/entity/Q64644833","qnode_id":"Q64644833"}],[],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q1183","qnode_id":"Q1183"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q19484","qnode_id":"Q19484"}],[]],[[{"start":0,"end":79,"url":"http://www.wikidata.org/entity/Q64644409","qnode_id":"Q64644409"}],[],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q1183","qnode_id":"Q1183"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q19484","qnode_id":"Q19484"}],[]],[[{"start":0,"end":110,"url":"http://www.wikidata.org/entity/Q66407730","qnode_id":"Q66407730"}],[],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q8646","qnode_id":"Q8646"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q19484","qnode_id":"Q19484"}],[]],[[{"start":0,"end":118,"url":"http://www.wikidata.org/entity/Q66411175","qnode_id":"Q66411175"}],[],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q8646","qnode_id":"Q8646"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q18936","qnode_id":"Q18936"}],[]],[[{"start":0,"end":152,"url":"http://www.wikidata.org/entity/Q66077685","qnode_id":"Q66077685"}],[],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q8646","qnode_id":"Q8646"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q18936","qnode_id":"Q18936"}],[]],[[{"start":0,"end":77,"url":"http://www.wikidata.org/entity/Q63322487","qnode_id":"Q63322487"}],[],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q1183","qnode_id":"Q1183"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q12187","qnode_id":"Q12187"}],[]],[[{"start":0,"end":225,"url":"http://www.wikidata.org/entity/Q64626358","qnode_id":"Q64626358"}],[],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q1183","qnode_id":"Q1183"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q19484","qnode_id":"Q19484"}],[]],[[{"start":0,"end":137,"url":"http://www.wikidata.org/entity/Q64625261","qnode_id":"Q64625261"}],[],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q1183","qnode_id":"Q1183"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q19484","qnode_id":"Q19484"}],[]],[[{"start":0,"end":167,"url":"http://www.wikidata.org/entity/Q64622566","qnode_id":"Q64622566"}],[],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q1183","qnode_id":"Q1183"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q19484","qnode_id":"Q19484"}],[]],[[{"start":0,"end":174,"url":"http://www.wikidata.org/entity/Q64598494","qnode_id":"Q64598494"}],[],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q1183","qnode_id":"Q1183"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q19484","qnode_id":"Q19484"}],[]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q30612:0","abs_uri":"http://www.wikidata.org/entity/Q30612","rel_uri":"wd:Q30612","approximation":false,"readable_label":"clinical trial (Q30612)"},{"id":"col-3","col_index":3,"label":"col3"},{"id":"http://www.wikidata.org/entity/Q12140:0","abs_uri":"http://www.wikidata.org/entity/Q12140","rel_uri":"wd:Q12140","approximation":false,"readable_label":"medication (Q12140)"},{"id":"col-2","col_index":2,"label":"col2"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"col-4","col_index":4,"label":"col4"}],"edges":[{"source":"http://www.wikidata.org/entity/Q30612:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q30612:0","target":"col-2","abs_uri":"http://www.wikidata.org/prop/P131","rel_uri":"p:P131","approximation":false,"readable_label":"located in the administrative territorial entity (P131)","id":2},{"source":"http://www.wikidata.org/entity/Q30612:0","target":"http://www.wikidata.org/entity/Q12140:0","abs_uri":"http://www.wikidata.org/prop/P4844","rel_uri":"p:P4844","approximation":false,"readable_label":"research intervention (P4844)","id":3},{"source":"http://www.wikidata.org/entity/Q30612:0","target":"col-1","abs_uri":"http://www.wikidata.org/prop/P1132","rel_uri":"p:P1132","approximation":false,"readable_label":"number of participants (P1132)","id":4},{"source":"http://www.wikidata.org/entity/Q30612:0","target":"col-4","abs_uri":"http://www.wikidata.org/prop/P582","rel_uri":"p:P582","approximation":false,"readable_label":"end time (P582)","id":5},{"source":"http://www.wikidata.org/entity/Q12140:0","target":"col-3","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":6}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/FVRBAOU9.links.tsv b/examples/semtab2020_novartis/tables/FVRBAOU9.links.tsv
new file mode 100644
index 0000000..a4189a6
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/FVRBAOU9.links.tsv
@@ -0,0 +1,60 @@
+0 0 Q64679115
+0 2 Q1183
+0 3 Q19484
+1 0 Q64675464
+1 2 Q1183
+1 3 Q19484
+2 0 Q64661921
+2 2 Q8646
+2 3 Q18936
+3 0 Q64665472
+3 2 Q1183
+3 3 Q18936
+4 0 Q64660363
+4 2 Q1183
+4 3 Q18936
+5 0 Q64659803
+5 2 Q1183
+5 3 Q18936
+6 0 Q64652731
+6 2 Q1183
+6 3 Q18936
+7 0 Q64647103
+7 2 Q1183
+7 3 Q18936
+8 0 Q64647714
+8 2 Q8646
+8 3 Q19484
+9 0 Q64647563
+9 2 Q8646
+9 3 Q19484
+10 0 Q64644833
+10 2 Q1183
+10 3 Q19484
+11 0 Q64644409
+11 2 Q1183
+11 3 Q19484
+12 0 Q66407730
+12 2 Q8646
+12 3 Q19484
+13 0 Q66411175
+13 2 Q8646
+13 3 Q18936
+14 0 Q66077685
+14 2 Q8646
+14 3 Q18936
+15 0 Q63322487
+15 2 Q1183
+15 3 Q12187
+16 0 Q64626358
+16 2 Q1183
+16 3 Q19484
+17 0 Q64625261
+17 2 Q1183
+17 3 Q19484
+18 0 Q64622566
+18 2 Q1183
+18 3 Q19484
+19 0 Q64598494
+19 2 Q1183
+19 3 Q19484
diff --git a/examples/semtab2020_novartis/tables/GE958EIH.csv b/examples/semtab2020_novartis/tables/GE958EIH.csv
new file mode 100644
index 0000000..501193b
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/GE958EIH.csv
@@ -0,0 +1,21 @@
+col0,col1,col2,col3
+retinol,vitamin A,all-trans-retinol binding,US pregnancy category X
+alitretinoin,retinoid,9-cis-retinoic acid biosynthetic process,US pregnancy category D
+neomycin,aminoglycoside,neomycin metabolic process,US pregnancy category D
+carboplatin,platinum-based antineoplastic,response to carboplatin,US pregnancy category D
+cisplatin,platinum-based antineoplastic,cellular response to cisplatin,US pregnancy category D
+streptomycin,aminoglycoside,response to streptomycin,US pregnancy category D
+fenofibrate,Peroxisome proliferator activated receptor alpha,response to fenofibrate,US pregnancy category C
+nalidixic acid,bactericide,nalidixic acid transporter activity,US pregnancy category B
+ergocalciferol,vitamin D,vitamin D2 metabolic process,US pregnancy category A
+cholecalciferol,Secosteroid,vitamin D3 metabolic process,US pregnancy category A
+pyridoxine,vitamin B6,pyridoxine biosynthetic process,US pregnancy category A
+riboflavin,food coloring,riboflavin metabolic process,US pregnancy category A
+(E)-phytonadione,vitamin K,cellular response to phylloquinone,US pregnancy category C
+dactinomycin,actinomycin,response to actinomycin D,Australian pregnancy category D
+tetracycline,tetracycline antibiotic,tetracycline transporter activity,Australian pregnancy category D
+doxorubicin,anthracycline antibiotic,doxorubicin metabolic process,Australian pregnancy category D
+hydroxyurea,urea,cellular response to hydroxyurea,Australian pregnancy category D
+perphenazine,phenothiazine,response to perphenazine,Australian pregnancy category C
+ribavirin,nucleoside analogue,response to ribavirin,Australian pregnancy category X
+fluconazole,azole,fluconazole transport,Australian pregnancy category D
diff --git a/examples/semtab2020_novartis/tables/GE958EIH.json b/examples/semtab2020_novartis/tables/GE958EIH.json
new file mode 100644
index 0000000..c056a8e
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/GE958EIH.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["retinol","alitretinoin","neomycin","carboplatin","cisplatin","streptomycin","fenofibrate","nalidixic acid","ergocalciferol","cholecalciferol","pyridoxine","riboflavin","(E)-phytonadione","dactinomycin","tetracycline","doxorubicin","hydroxyurea","perphenazine","ribavirin","fluconazole"]},{"index":1,"name":"col1","values":["vitamin A","retinoid","aminoglycoside","platinum-based antineoplastic","platinum-based antineoplastic","aminoglycoside","Peroxisome proliferator activated receptor alpha","bactericide","vitamin D","Secosteroid","vitamin B6","food coloring","vitamin K","actinomycin","tetracycline antibiotic","anthracycline antibiotic","urea","phenothiazine","nucleoside analogue","azole"]},{"index":2,"name":"col2","values":["all-trans-retinol binding","9-cis-retinoic acid biosynthetic process","neomycin metabolic process","response to carboplatin","cellular response to cisplatin","response to streptomycin","response to fenofibrate","nalidixic acid transporter activity","vitamin D2 metabolic process","vitamin D3 metabolic process","pyridoxine biosynthetic process","riboflavin metabolic process","cellular response to phylloquinone","response to actinomycin D","tetracycline transporter activity","doxorubicin metabolic process","cellular response to hydroxyurea","response to perphenazine","response to ribavirin","fluconazole transport"]},{"index":3,"name":"col3","values":["US pregnancy category X","US pregnancy category D","US pregnancy category D","US pregnancy category D","US pregnancy category D","US pregnancy category D","US pregnancy category C","US pregnancy category B","US pregnancy category A","US pregnancy category A","US pregnancy category A","US pregnancy category A","US pregnancy category C","Australian pregnancy category D","Australian pregnancy category D","Australian pregnancy category D","Australian pregnancy category D","Australian pregnancy category C","Australian pregnancy category X","Australian pregnancy category D"]}],"metadata":{"table_id":"GE958EIH","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q424976","qnode_id":"Q424976"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q18225","qnode_id":"Q18225"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q22314329","qnode_id":"Q22314329"}],[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q28123619","qnode_id":"Q28123619"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q3611854","qnode_id":"Q3611854"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q420418","qnode_id":"Q420418"}],[{"start":0,"end":40,"url":"http://www.wikidata.org/entity/Q14916460","qnode_id":"Q14916460"}],[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q28123618","qnode_id":"Q28123618"}]],[[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q423098","qnode_id":"Q423098"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q28742","qnode_id":"Q28742"}],[{"start":0,"end":26,"url":"http://www.wikidata.org/entity/Q22282305","qnode_id":"Q22282305"}],[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q28123618","qnode_id":"Q28123618"}]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q415588","qnode_id":"Q415588"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q7202320","qnode_id":"Q7202320"}],[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q22274634","qnode_id":"Q22274634"}],[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q28123618","qnode_id":"Q28123618"}]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q412415","qnode_id":"Q412415"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q7202320","qnode_id":"Q7202320"}],[{"start":0,"end":30,"url":"http://www.wikidata.org/entity/Q21095320","qnode_id":"Q21095320"}],[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q28123618","qnode_id":"Q28123618"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q192717","qnode_id":"Q192717"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q28742","qnode_id":"Q28742"}],[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q22273545","qnode_id":"Q22273545"}],[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q28123618","qnode_id":"Q28123618"}]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q419724","qnode_id":"Q419724"}],[{"start":0,"end":48,"url":"http://www.wikidata.org/entity/Q7169506","qnode_id":"Q7169506"}],[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q21108024","qnode_id":"Q21108024"}],[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q28123617","qnode_id":"Q28123617"}]],[[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q281082","qnode_id":"Q281082"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q804539","qnode_id":"Q804539"}],[{"start":0,"end":35,"url":"http://www.wikidata.org/entity/Q22324615","qnode_id":"Q22324615"}],[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q28123616","qnode_id":"Q28123616"}]],[[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q139200","qnode_id":"Q139200"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q175621","qnode_id":"Q175621"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q22284352","qnode_id":"Q22284352"}],[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q28123615","qnode_id":"Q28123615"}]],[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q139347","qnode_id":"Q139347"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q139660","qnode_id":"Q139660"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q22282132","qnode_id":"Q22282132"}],[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q28123615","qnode_id":"Q28123615"}]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q423746","qnode_id":"Q423746"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q205130","qnode_id":"Q205130"}],[{"start":0,"end":31,"url":"http://www.wikidata.org/entity/Q21123303","qnode_id":"Q21123303"}],[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q28123615","qnode_id":"Q28123615"}]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q130365","qnode_id":"Q130365"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q753009","qnode_id":"Q753009"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q14907829","qnode_id":"Q14907829"}],[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q28123615","qnode_id":"Q28123615"}]],[[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q186093","qnode_id":"Q186093"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q182338","qnode_id":"Q182338"}],[{"start":0,"end":34,"url":"http://www.wikidata.org/entity/Q22273427","qnode_id":"Q22273427"}],[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q28123617","qnode_id":"Q28123617"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q186127","qnode_id":"Q186127"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q343465","qnode_id":"Q343465"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q22273340","qnode_id":"Q22273340"}],[]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q193045","qnode_id":"Q193045"}],[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q422676","qnode_id":"Q422676"}],[{"start":0,"end":33,"url":"http://www.wikidata.org/entity/Q21121674","qnode_id":"Q21121674"}],[]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q18936","qnode_id":"Q18936"}],[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q417589","qnode_id":"Q417589"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q21097417","qnode_id":"Q21097417"}],[]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q212272","qnode_id":"Q212272"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q48318","qnode_id":"Q48318"}],[{"start":0,"end":32,"url":"http://www.wikidata.org/entity/Q21095318","qnode_id":"Q21095318"}],[]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q423520","qnode_id":"Q423520"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q16023748","qnode_id":"Q16023748"}],[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q22274450","qnode_id":"Q22274450"}],[]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q421862","qnode_id":"Q421862"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q2689559","qnode_id":"Q2689559"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q22274464","qnode_id":"Q22274464"}],[]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q411478","qnode_id":"Q411478"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q419639","qnode_id":"Q419639"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q22290460","qnode_id":"Q22290460"}],[]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q12140:0","abs_uri":"http://www.wikidata.org/entity/Q12140","rel_uri":"wd:Q12140","approximation":false,"readable_label":"medication (Q12140)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"col-2","col_index":2,"label":"col2"},{"id":"col-3","col_index":3,"label":"col3"}],"edges":[{"source":"http://www.wikidata.org/entity/Q12140:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q12140:0","target":"col-1","abs_uri":"http://www.wikidata.org/prop/P279","rel_uri":"p:P279","approximation":false,"readable_label":"subclass of (P279)","id":2},{"source":"http://www.wikidata.org/entity/Q12140:0","target":"col-2","abs_uri":"http://www.wikidata.org/prop/P361","rel_uri":"p:P361","approximation":false,"readable_label":"part of (P361)","id":3},{"source":"http://www.wikidata.org/entity/Q12140:0","target":"col-3","abs_uri":"http://www.wikidata.org/prop/P3489","rel_uri":"p:P3489","approximation":false,"readable_label":"pregnancy category (P3489)","id":4}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/GE958EIH.links.tsv b/examples/semtab2020_novartis/tables/GE958EIH.links.tsv
new file mode 100644
index 0000000..07b1e3c
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/GE958EIH.links.tsv
@@ -0,0 +1,73 @@
+0 0 Q424976
+0 1 Q18225
+0 2 Q22314329
+0 3 Q28123619
+1 0 Q3611854
+1 1 Q420418
+1 2 Q14916460
+1 3 Q28123618
+2 0 Q423098
+2 1 Q28742
+2 2 Q22282305
+2 3 Q28123618
+3 0 Q415588
+3 1 Q7202320
+3 2 Q22274634
+3 3 Q28123618
+4 0 Q412415
+4 1 Q7202320
+4 2 Q21095320
+4 3 Q28123618
+5 0 Q192717
+5 1 Q28742
+5 2 Q22273545
+5 3 Q28123618
+6 0 Q419724
+6 1 Q7169506
+6 2 Q21108024
+6 3 Q28123617
+7 0 Q281082
+7 1 Q804539
+7 2 Q22324615
+7 3 Q28123616
+8 0 Q139200
+8 1 Q175621
+8 2 Q22284352
+8 3 Q28123615
+9 0 Q139347
+9 1 Q139660
+9 2 Q22282132
+9 3 Q28123615
+10 0 Q423746
+10 1 Q205130
+10 2 Q21123303
+10 3 Q28123615
+11 0 Q130365
+11 1 Q753009
+11 2 Q14907829
+11 3 Q28123615
+12 0 Q186093
+12 1 Q182338
+12 2 Q22273427
+12 3 Q28123617
+13 0 Q186127
+13 1 Q343465
+13 2 Q22273340
+14 0 Q193045
+14 1 Q422676
+14 2 Q21121674
+15 0 Q18936
+15 1 Q417589
+15 2 Q21097417
+16 0 Q212272
+16 1 Q48318
+16 2 Q21095318
+17 0 Q423520
+17 1 Q16023748
+17 2 Q22274450
+18 0 Q421862
+18 1 Q2689559
+18 2 Q22274464
+19 0 Q411478
+19 1 Q419639
+19 2 Q22290460
diff --git a/examples/semtab2020_novartis/tables/HFOA410A.csv b/examples/semtab2020_novartis/tables/HFOA410A.csv
new file mode 100644
index 0000000..813d979
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/HFOA410A.csv
@@ -0,0 +1,21 @@
+col0,col1,col2
+Stÿve-Wiedemann syndrome,LIFR,bent bone dysplasia
+high myopia-sensorineural deafness sndrome,SLITRK6,syndromic genetic deafness
+Mbjeed syndrome,LPIN2,autoinflammatory syndrome with immune deficiency
+"Neurogenic scapuloperoneal syndroe, Kaeser type",DES,qualitative or quantitative defects of desmin
+Gordon-Holmes syndrope,RNF216,rare genetic movement disorder
+Familial#partial lipodystrophy,PPARG,partial lipodystrophy
+Leqz microphthalmia syndrome,NAA10,Syndromic microphthalmia
+myofibrillar#myopathy 3,MYOT,myofibrillar myopathy
+"spondylometaphyseal dysplasia, Kozlowski!type",TRPV4,spondylometaphyseal dysplasia
+X-linke recessive chondrodysplasia punctata,ARSL,X-linked chondrodysplasia punctata
+branchiootic sydrome 1,EYA1,branchiootic syndrome
+Hyper-IgD syndromg,MVK,hyperimmunoglobulin syndrome
+hereditaty folate malabsorption,SLC46A1,vitamin metabolic disorder
+Trismus-pseudocamptodactyly syndrom,MYH8,distal arthrogryposis
+Fried syndrom,AP1S2,X-linked intellectual disability
+FUAXE intellectual disability,Fragile Mental Retardation 2,X-linked intellectual disability
+punctate palmoplantcr keratoderma,AAGAB,palmoplantar keratosis
+Myhresyndrome,SMAD4,multiple abnormalities
+rhabdoid txmor predisposition syndrome,SMARCA4,rhabdoid tumor
+Majewski's polydatyly syndrome,NEK1,short rib – polydactyly syndrome
diff --git a/examples/semtab2020_novartis/tables/HFOA410A.json b/examples/semtab2020_novartis/tables/HFOA410A.json
new file mode 100644
index 0000000..3d5ad18
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/HFOA410A.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["Stÿve-Wiedemann syndrome","high myopia-sensorineural deafness sndrome","Mbjeed syndrome","Neurogenic scapuloperoneal syndroe, Kaeser type","Gordon-Holmes syndrope","Familial#partial lipodystrophy","Leqz microphthalmia syndrome","myofibrillar#myopathy 3","spondylometaphyseal dysplasia, Kozlowski!type","X-linke recessive chondrodysplasia punctata","branchiootic sydrome 1","Hyper-IgD syndromg","hereditaty folate malabsorption","Trismus-pseudocamptodactyly syndrom","Fried syndrom","FUAXE intellectual disability","punctate palmoplantcr keratoderma","Myhresyndrome","rhabdoid txmor predisposition syndrome","Majewski's polydatyly syndrome"]},{"index":1,"name":"col1","values":["LIFR","SLITRK6","LPIN2","DES","RNF216","PPARG","NAA10","MYOT","TRPV4","ARSL","EYA1","MVK","SLC46A1","MYH8","AP1S2","Fragile Mental Retardation 2","AAGAB","SMAD4","SMARCA4","NEK1"]},{"index":2,"name":"col2","values":["bent bone dysplasia","syndromic genetic deafness","autoinflammatory syndrome with immune deficiency","qualitative or quantitative defects of desmin","rare genetic movement disorder","partial lipodystrophy","Syndromic microphthalmia","myofibrillar myopathy","spondylometaphyseal dysplasia","X-linked chondrodysplasia punctata","branchiootic syndrome","hyperimmunoglobulin syndrome","vitamin metabolic disorder","distal arthrogryposis","X-linked intellectual disability","X-linked intellectual disability","palmoplantar keratosis","multiple abnormalities","rhabdoid tumor","short rib – polydactyly syndrome"]}],"metadata":{"table_id":"HFOA410A","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q9182808","qnode_id":"Q9182808"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14914453","qnode_id":"Q14914453"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q55788810","qnode_id":"Q55788810"}]],[[{"start":0,"end":42,"url":"http://www.wikidata.org/entity/Q17539935","qnode_id":"Q17539935"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q18047459","qnode_id":"Q18047459"}],[{"start":0,"end":26,"url":"http://www.wikidata.org/entity/Q55788734","qnode_id":"Q55788734"}]],[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q6737634","qnode_id":"Q6737634"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18034562","qnode_id":"Q18034562"}],[{"start":0,"end":48,"url":"http://www.wikidata.org/entity/Q55787012","qnode_id":"Q55787012"}]],[[{"start":0,"end":47,"url":"http://www.wikidata.org/entity/Q17538998","qnode_id":"Q17538998"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q14864844","qnode_id":"Q14864844"}],[{"start":0,"end":45,"url":"http://www.wikidata.org/entity/Q55786006","qnode_id":"Q55786006"}]],[[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q18020927","qnode_id":"Q18020927"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q18041033","qnode_id":"Q18041033"}],[{"start":0,"end":30,"url":"http://www.wikidata.org/entity/Q55785843","qnode_id":"Q55785843"}]],[[{"start":0,"end":30,"url":"http://www.wikidata.org/entity/Q5432945","qnode_id":"Q5432945"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q20970208","qnode_id":"Q20970208"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q53661802","qnode_id":"Q53661802"}]],[[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q6523439","qnode_id":"Q6523439"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18032787","qnode_id":"Q18032787"}],[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q29982037","qnode_id":"Q29982037"}]],[[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q1394711","qnode_id":"Q1394711"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18034331","qnode_id":"Q18034331"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q23893414","qnode_id":"Q23893414"}]],[[{"start":0,"end":45,"url":"http://www.wikidata.org/entity/Q11695513","qnode_id":"Q11695513"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q15324120","qnode_id":"Q15324120"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q19309317","qnode_id":"Q19309317"}]],[[{"start":0,"end":43,"url":"http://www.wikidata.org/entity/Q8041561","qnode_id":"Q8041561"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q17832136","qnode_id":"Q17832136"}],[{"start":0,"end":34,"url":"http://www.wikidata.org/entity/Q18987139","qnode_id":"Q18987139"}]],[[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q1440027","qnode_id":"Q1440027"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q17917742","qnode_id":"Q17917742"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q18966109","qnode_id":"Q18966109"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q1640810","qnode_id":"Q1640810"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q14913011","qnode_id":"Q14913011"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q18555244","qnode_id":"Q18555244"}]],[[{"start":0,"end":31,"url":"http://www.wikidata.org/entity/Q1609733","qnode_id":"Q1609733"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q18048901","qnode_id":"Q18048901"}],[{"start":0,"end":26,"url":"http://www.wikidata.org/entity/Q18553423","qnode_id":"Q18553423"}]],[[{"start":0,"end":35,"url":"http://www.wikidata.org/entity/Q11838430","qnode_id":"Q11838430"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18029745","qnode_id":"Q18029745"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q18553375","qnode_id":"Q18553375"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q18411789","qnode_id":"Q18411789"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18033520","qnode_id":"Q18033520"}],[{"start":0,"end":32,"url":"http://www.wikidata.org/entity/Q8041560","qnode_id":"Q8041560"}]],[[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q21051307","qnode_id":"Q21051307"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q5477662","qnode_id":"Q5477662"}],[{"start":0,"end":32,"url":"http://www.wikidata.org/entity/Q8041560","qnode_id":"Q8041560"}]],[[{"start":0,"end":33,"url":"http://www.wikidata.org/entity/Q7260026","qnode_id":"Q7260026"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18046294","qnode_id":"Q18046294"}],[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q7128426","qnode_id":"Q7128426"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q17042115","qnode_id":"Q17042115"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14904154","qnode_id":"Q14904154"}],[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q6934911","qnode_id":"Q6934911"}]],[[{"start":0,"end":38,"url":"http://www.wikidata.org/entity/Q1325512","qnode_id":"Q1325512"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q18031595","qnode_id":"Q18031595"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q6743515","qnode_id":"Q6743515"}]],[[{"start":0,"end":30,"url":"http://www.wikidata.org/entity/Q2753372","qnode_id":"Q2753372"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18030033","qnode_id":"Q18030033"}],[{"start":0,"end":32,"url":"http://www.wikidata.org/entity/Q4420146","qnode_id":"Q4420146"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q3311537:0","abs_uri":"http://www.wikidata.org/entity/Q3311537","rel_uri":"wd:Q3311537","approximation":false,"readable_label":"hereditary disorder (Q3311537)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/entity/Q7187","rel_uri":"wd:Q7187","approximation":false,"readable_label":"gene (Q7187)"},{"id":"col-2","col_index":2,"label":"col2"}],"edges":[{"source":"http://www.wikidata.org/entity/Q3311537:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q3311537:0","target":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/prop/P2293","rel_uri":"p:P2293","approximation":false,"readable_label":"genetic association (P2293)","id":2},{"source":"http://www.wikidata.org/entity/Q3311537:0","target":"col-2","abs_uri":"http://www.wikidata.org/prop/P279","rel_uri":"p:P279","approximation":false,"readable_label":"subclass of (P279)","id":3},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-1","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":4}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/HFOA410A.links.tsv b/examples/semtab2020_novartis/tables/HFOA410A.links.tsv
new file mode 100644
index 0000000..eccb972
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/HFOA410A.links.tsv
@@ -0,0 +1,60 @@
+0 0 Q9182808
+0 1 Q14914453
+0 2 Q55788810
+1 0 Q17539935
+1 1 Q18047459
+1 2 Q55788734
+2 0 Q6737634
+2 1 Q18034562
+2 2 Q55787012
+3 0 Q17538998
+3 1 Q14864844
+3 2 Q55786006
+4 0 Q18020927
+4 1 Q18041033
+4 2 Q55785843
+5 0 Q5432945
+5 1 Q20970208
+5 2 Q53661802
+6 0 Q6523439
+6 1 Q18032787
+6 2 Q29982037
+7 0 Q1394711
+7 1 Q18034331
+7 2 Q23893414
+8 0 Q11695513
+8 1 Q15324120
+8 2 Q19309317
+9 0 Q8041561
+9 1 Q17832136
+9 2 Q18987139
+10 0 Q1440027
+10 1 Q17917742
+10 2 Q18966109
+11 0 Q1640810
+11 1 Q14913011
+11 2 Q18555244
+12 0 Q1609733
+12 1 Q18048901
+12 2 Q18553423
+13 0 Q11838430
+13 1 Q18029745
+13 2 Q18553375
+14 0 Q18411789
+14 1 Q18033520
+14 2 Q8041560
+15 0 Q21051307
+15 1 Q5477662
+15 2 Q8041560
+16 0 Q7260026
+16 1 Q18046294
+16 2 Q7128426
+17 0 Q17042115
+17 1 Q14904154
+17 2 Q6934911
+18 0 Q1325512
+18 1 Q18031595
+18 2 Q6743515
+19 0 Q2753372
+19 1 Q18030033
+19 2 Q4420146
diff --git a/examples/semtab2020_novartis/tables/HIFECYUR.csv b/examples/semtab2020_novartis/tables/HIFECYUR.csv
new file mode 100644
index 0000000..5b9f2da
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/HIFECYUR.csv
@@ -0,0 +1,21 @@
+col0,col1,col2
+Mycoplasma pneumoniae infection,28,7
+ehrlichiosis,2,1
+cryptosporidiosis,10,2
+ascariasis,3,1
+babesiosis,4,1
+erythema infectiosum,18,10
+Japanese encephalitis,15,5
+campylobacteriosis,7,1
+shigellosis,4,1
+tick-borne encephalitis,14,7
+salmonellosis,14,6
+chikungunya,12,3
+infectious mononucleosis,4,4
+scarlet fever,5,2
+ornithosis,19,5
+listeriosis,67,1
+hepatitis A,48,15
+brucellosis,5,5
+legionnaires' disease,16,2
+yellow fever,6,3
diff --git a/examples/semtab2020_novartis/tables/HIFECYUR.json b/examples/semtab2020_novartis/tables/HIFECYUR.json
new file mode 100644
index 0000000..1af3b2d
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/HIFECYUR.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["Mycoplasma pneumoniae infection","ehrlichiosis","cryptosporidiosis","ascariasis","babesiosis","erythema infectiosum","Japanese encephalitis","campylobacteriosis","shigellosis","tick-borne encephalitis","salmonellosis","chikungunya","infectious mononucleosis","scarlet fever","ornithosis","listeriosis","hepatitis A","brucellosis","legionnaires' disease","yellow fever"]},{"index":1,"name":"col1","values":["28","2","10","3","4","18","15","7","4","14","14","12","4","5","19","67","48","5","16","6"]},{"index":2,"name":"col2","values":["7","1","2","1","1","10","5","1","1","7","6","3","4","2","5","1","15","5","2","3"]}],"metadata":{"table_id":"HIFECYUR","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":31,"url":"http://www.wikidata.org/entity/Q79054860","qnode_id":"Q79054860"}],[],[]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q2845432","qnode_id":"Q2845432"}],[],[]],[[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q1359898","qnode_id":"Q1359898"}],[],[]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q842428","qnode_id":"Q842428"}],[],[]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q797668","qnode_id":"Q797668"}],[],[]],[[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q753654","qnode_id":"Q753654"}],[],[]],[[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q738292","qnode_id":"Q738292"}],[],[]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q683861","qnode_id":"Q683861"}],[],[]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q327298","qnode_id":"Q327298"}],[],[]],[[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q326663","qnode_id":"Q326663"}],[],[]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q326648","qnode_id":"Q326648"}],[],[]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q243257","qnode_id":"Q243257"}],[],[]],[[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q207367","qnode_id":"Q207367"}],[],[]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q180266","qnode_id":"Q180266"}],[],[]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q164727","qnode_id":"Q164727"}],[],[]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q160653","qnode_id":"Q160653"}],[],[]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q157661","qnode_id":"Q157661"}],[],[]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q156050","qnode_id":"Q156050"}],[],[]],[[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q154882","qnode_id":"Q154882"}],[],[]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q154874","qnode_id":"Q154874"}],[],[]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q18123741:0","abs_uri":"http://www.wikidata.org/entity/Q18123741","rel_uri":"wd:Q18123741","approximation":false,"readable_label":"infectious disease (Q18123741)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"col-2","col_index":2,"label":"col2"}],"edges":[{"source":"http://www.wikidata.org/entity/Q18123741:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q18123741:0","target":"col-1","abs_uri":"http://www.wikidata.org/prop/P3487","rel_uri":"p:P3487","approximation":false,"readable_label":"maximal incubation period in humans (P3487)","id":2},{"source":"http://www.wikidata.org/entity/Q18123741:0","target":"col-2","abs_uri":"http://www.wikidata.org/prop/P3488","rel_uri":"p:P3488","approximation":false,"readable_label":"minimal incubation period in humans (P3488)","id":3}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/HIFECYUR.links.tsv b/examples/semtab2020_novartis/tables/HIFECYUR.links.tsv
new file mode 100644
index 0000000..40e0586
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/HIFECYUR.links.tsv
@@ -0,0 +1,20 @@
+0 0 Q79054860
+1 0 Q2845432
+2 0 Q1359898
+3 0 Q842428
+4 0 Q797668
+5 0 Q753654
+6 0 Q738292
+7 0 Q683861
+8 0 Q327298
+9 0 Q326663
+10 0 Q326648
+11 0 Q243257
+12 0 Q207367
+13 0 Q180266
+14 0 Q164727
+15 0 Q160653
+16 0 Q157661
+17 0 Q156050
+18 0 Q154882
+19 0 Q154874
diff --git a/examples/semtab2020_novartis/tables/I24JCB76.csv b/examples/semtab2020_novartis/tables/I24JCB76.csv
new file mode 100644
index 0000000..6b3f8fa
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/I24JCB76.csv
@@ -0,0 +1,21 @@
+col0,col1,col2
+intellectual disability,psychiatry,learning disability
+hysteria,psychiatry,histrionic personality disorder
+post-traumatic stress disorder,psychiatry,acute stress disorder
+dysgraphia,neuropsychology,agraphia
+hypochondriasis,psychiatry,hemophilia
+attention deficit hyperactivity disorder,psychiatry,autism
+psychosis,psychiatry,dissociative identity disorder
+antisocial personality disorder,psychiatry,dissociative identity disorder
+schizophrenia,psychiatry,dissociative identity disorder
+Asperger syndrome,psychiatry,Alzheimer's disease
+sleep apnea,neuropsychology,amnesia
+kleptomania,psychology,kleptophilia
+erectile dysfunction,psychology,Impotence
+nocturnal enuresis,psychology,nocturia
+obsessive-compulsive personality disorder,psychology,obsessive-compulsive disorder
+dependent personality disorder,psychology,codependency
+personality disorder,psychology,dissociative identity disorder
+insomnia,psychology,amnesia
+aphasia,neuropsychology,aphasia
+borderline personality disorder,psychiatry,dependent personality disorder
diff --git a/examples/semtab2020_novartis/tables/I24JCB76.json b/examples/semtab2020_novartis/tables/I24JCB76.json
new file mode 100644
index 0000000..132ecfe
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/I24JCB76.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["intellectual disability","hysteria","post-traumatic stress disorder","dysgraphia","hypochondriasis","attention deficit hyperactivity disorder","psychosis","antisocial personality disorder","schizophrenia","Asperger syndrome","sleep apnea","kleptomania","erectile dysfunction","nocturnal enuresis","obsessive-compulsive personality disorder","dependent personality disorder","personality disorder","insomnia","aphasia","borderline personality disorder"]},{"index":1,"name":"col1","values":["psychiatry","psychiatry","psychiatry","neuropsychology","psychiatry","psychiatry","psychiatry","psychiatry","psychiatry","psychiatry","neuropsychology","psychology","psychology","psychology","psychology","psychology","psychology","psychology","neuropsychology","psychiatry"]},{"index":2,"name":"col2","values":["learning disability","histrionic personality disorder","acute stress disorder","agraphia","hemophilia","autism","dissociative identity disorder","dissociative identity disorder","dissociative identity disorder","Alzheimer's disease","amnesia","kleptophilia","Impotence","nocturia","obsessive-compulsive disorder","codependency","dissociative identity disorder","amnesia","aphasia","dependent personality disorder"]}],"metadata":{"table_id":"I24JCB76","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q183560","qnode_id":"Q183560"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q7867","qnode_id":"Q7867"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q860740","qnode_id":"Q860740"}]],[[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q144119","qnode_id":"Q144119"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q7867","qnode_id":"Q7867"}],[{"start":0,"end":31,"url":"http://www.wikidata.org/entity/Q845787","qnode_id":"Q845787"}]],[[{"start":0,"end":30,"url":"http://www.wikidata.org/entity/Q202387","qnode_id":"Q202387"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q7867","qnode_id":"Q7867"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q424221","qnode_id":"Q424221"}]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q584560","qnode_id":"Q584560"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q3872","qnode_id":"Q3872"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q395931","qnode_id":"Q395931"}]],[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q105434","qnode_id":"Q105434"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q7867","qnode_id":"Q7867"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q134003","qnode_id":"Q134003"}]],[[{"start":0,"end":40,"url":"http://www.wikidata.org/entity/Q181923","qnode_id":"Q181923"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q7867","qnode_id":"Q7867"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q38404","qnode_id":"Q38404"}]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q170082","qnode_id":"Q170082"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q7867","qnode_id":"Q7867"}],[{"start":0,"end":30,"url":"http://www.wikidata.org/entity/Q18657","qnode_id":"Q18657"}]],[[{"start":0,"end":31,"url":"http://www.wikidata.org/entity/Q118418","qnode_id":"Q118418"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q7867","qnode_id":"Q7867"}],[{"start":0,"end":30,"url":"http://www.wikidata.org/entity/Q18657","qnode_id":"Q18657"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q41112","qnode_id":"Q41112"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q7867","qnode_id":"Q7867"}],[{"start":0,"end":30,"url":"http://www.wikidata.org/entity/Q18657","qnode_id":"Q18657"}]],[[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q161790","qnode_id":"Q161790"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q7867","qnode_id":"Q7867"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q11081","qnode_id":"Q11081"}]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q213600","qnode_id":"Q213600"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q3872","qnode_id":"Q3872"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q11072","qnode_id":"Q11072"}]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q212021","qnode_id":"Q212021"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q9418","qnode_id":"Q9418"}],[]],[[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q184674","qnode_id":"Q184674"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q9418","qnode_id":"Q9418"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q3149465","qnode_id":"Q3149465"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q318005","qnode_id":"Q318005"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q9418","qnode_id":"Q9418"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q1426443","qnode_id":"Q1426443"}]],[[{"start":0,"end":41,"url":"http://www.wikidata.org/entity/Q231578","qnode_id":"Q231578"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q9418","qnode_id":"Q9418"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q178190","qnode_id":"Q178190"}]],[[{"start":0,"end":30,"url":"http://www.wikidata.org/entity/Q320719","qnode_id":"Q320719"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q9418","qnode_id":"Q9418"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q21109","qnode_id":"Q21109"}]],[[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q270673","qnode_id":"Q270673"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q9418","qnode_id":"Q9418"}],[{"start":0,"end":30,"url":"http://www.wikidata.org/entity/Q18657","qnode_id":"Q18657"}]],[[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q1869874","qnode_id":"Q1869874"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q9418","qnode_id":"Q9418"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q11072","qnode_id":"Q11072"}]],[[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q2836","qnode_id":"Q2836"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q3872","qnode_id":"Q3872"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q16526687","qnode_id":"Q16526687"}]],[[{"start":0,"end":31,"url":"http://www.wikidata.org/entity/Q208166","qnode_id":"Q208166"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q7867","qnode_id":"Q7867"}],[{"start":0,"end":30,"url":"http://www.wikidata.org/entity/Q320719","qnode_id":"Q320719"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q12136:0","abs_uri":"http://www.wikidata.org/entity/Q12136","rel_uri":"wd:Q12136","approximation":false,"readable_label":"disease (Q12136)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"col-2","col_index":2,"label":"col2"}],"edges":[{"source":"http://www.wikidata.org/entity/Q12136:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q12136:0","target":"col-1","abs_uri":"http://www.wikidata.org/prop/P1995","rel_uri":"p:P1995","approximation":false,"readable_label":"health specialty (P1995)","id":2},{"source":"http://www.wikidata.org/entity/Q12136:0","target":"col-2","abs_uri":"http://www.wikidata.org/prop/P1889","rel_uri":"p:P1889","approximation":false,"readable_label":"different from (P1889)","id":3}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/I24JCB76.links.tsv b/examples/semtab2020_novartis/tables/I24JCB76.links.tsv
new file mode 100644
index 0000000..49b6f0f
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/I24JCB76.links.tsv
@@ -0,0 +1,59 @@
+0 0 Q183560
+0 1 Q7867
+0 2 Q860740
+1 0 Q144119
+1 1 Q7867
+1 2 Q845787
+2 0 Q202387
+2 1 Q7867
+2 2 Q424221
+3 0 Q584560
+3 1 Q3872
+3 2 Q395931
+4 0 Q105434
+4 1 Q7867
+4 2 Q134003
+5 0 Q181923
+5 1 Q7867
+5 2 Q38404
+6 0 Q170082
+6 1 Q7867
+6 2 Q18657
+7 0 Q118418
+7 1 Q7867
+7 2 Q18657
+8 0 Q41112
+8 1 Q7867
+8 2 Q18657
+9 0 Q161790
+9 1 Q7867
+9 2 Q11081
+10 0 Q213600
+10 1 Q3872
+10 2 Q11072
+11 0 Q212021
+11 1 Q9418
+12 0 Q184674
+12 1 Q9418
+12 2 Q3149465
+13 0 Q318005
+13 1 Q9418
+13 2 Q1426443
+14 0 Q231578
+14 1 Q9418
+14 2 Q178190
+15 0 Q320719
+15 1 Q9418
+15 2 Q21109
+16 0 Q270673
+16 1 Q9418
+16 2 Q18657
+17 0 Q1869874
+17 1 Q9418
+17 2 Q11072
+18 0 Q2836
+18 1 Q3872
+18 2 Q16526687
+19 0 Q208166
+19 1 Q7867
+19 2 Q320719
diff --git a/examples/semtab2020_novartis/tables/IUVV79LP.csv b/examples/semtab2020_novartis/tables/IUVV79LP.csv
new file mode 100644
index 0000000..b52367b
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/IUVV79LP.csv
@@ -0,0 +1,21 @@
+col0,col1,col2
+raloxifene,60,145
+dapsone,50,177
+(+-)-flurbiprofen,44,111
+Lovastatin,45,175
+hydrochlorothiazide,25,269
+carbimazole,15,124
+hydrocodone,15,198
+dalfampridine,20,159
+adenosine,15,235
+ibuprofen,30,77
+benzydamine,9,320
+prednisone,10,237
+methimazole,10,145.5
+methoxsalen,10,148
+warfarin,7.5,322
+magnesium sulfate,7,1137
+ephedrine,8,40
+magnesium oxide,7,5072
+sulfamethazine,4,200
+salicylamide,3,140
diff --git a/examples/semtab2020_novartis/tables/IUVV79LP.json b/examples/semtab2020_novartis/tables/IUVV79LP.json
new file mode 100644
index 0000000..0358f4f
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/IUVV79LP.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["raloxifene","dapsone","(+-)-flurbiprofen","Lovastatin","hydrochlorothiazide","carbimazole","hydrocodone","dalfampridine","adenosine","ibuprofen","benzydamine","prednisone","methimazole","methoxsalen","warfarin","magnesium sulfate","ephedrine","magnesium oxide","sulfamethazine","salicylamide"]},{"index":1,"name":"col1","values":["60","50","44","45","25","15","15","20","15","30","9","10","10","10","7.5","7","8","7","4","3"]},{"index":2,"name":"col2","values":["145","177","111","175","269","124","198","159","235","77","320","237","145.5","148","322","1137","40","5072","200","140"]}],"metadata":{"table_id":"IUVV79LP","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q425223","qnode_id":"Q425223"}],[],[]],[[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q422226","qnode_id":"Q422226"}],[],[]],[[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q419890","qnode_id":"Q419890"}],[],[]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q417740","qnode_id":"Q417740"}],[],[]],[[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q423930","qnode_id":"Q423930"}],[],[]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q414013","qnode_id":"Q414013"}],[],[]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q411441","qnode_id":"Q411441"}],[],[]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q372539","qnode_id":"Q372539"}],[],[]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q190012","qnode_id":"Q190012"}],[],[]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q186969","qnode_id":"Q186969"}],[],[]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q793143","qnode_id":"Q793143"}],[],[]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q424972","qnode_id":"Q424972"}],[],[]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q419663","qnode_id":"Q419663"}],[],[]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q408570","qnode_id":"Q408570"}],[],[]],[[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q407431","qnode_id":"Q407431"}],[],[]],[[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q288266","qnode_id":"Q288266"}],[],[]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q219626","qnode_id":"Q219626"}],[],[]],[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q214769","qnode_id":"Q214769"}],[],[]],[[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q3976823","qnode_id":"Q3976823"}],[],[]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q2496906","qnode_id":"Q2496906"}],[],[]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q12140:0","abs_uri":"http://www.wikidata.org/entity/Q12140","rel_uri":"wd:Q12140","approximation":false,"readable_label":"medication (Q12140)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"col-2","col_index":2,"label":"col2"}],"edges":[{"source":"http://www.wikidata.org/entity/Q12140:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q12140:0","target":"col-1","abs_uri":"http://www.wikidata.org/prop/P4250","rel_uri":"p:P4250","approximation":false,"readable_label":"defined daily dose (P4250)","id":2},{"source":"http://www.wikidata.org/entity/Q12140:0","target":"col-2","abs_uri":"http://www.wikidata.org/prop/P2101","rel_uri":"p:P2101","approximation":false,"readable_label":"melting point (P2101)","id":3}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/IUVV79LP.links.tsv b/examples/semtab2020_novartis/tables/IUVV79LP.links.tsv
new file mode 100644
index 0000000..9dcd666
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/IUVV79LP.links.tsv
@@ -0,0 +1,20 @@
+0 0 Q425223
+1 0 Q422226
+2 0 Q419890
+3 0 Q417740
+4 0 Q423930
+5 0 Q414013
+6 0 Q411441
+7 0 Q372539
+8 0 Q190012
+9 0 Q186969
+10 0 Q793143
+11 0 Q424972
+12 0 Q419663
+13 0 Q408570
+14 0 Q407431
+15 0 Q288266
+16 0 Q219626
+17 0 Q214769
+18 0 Q3976823
+19 0 Q2496906
diff --git a/examples/semtab2020_novartis/tables/L2NFO494.csv b/examples/semtab2020_novartis/tables/L2NFO494.csv
new file mode 100644
index 0000000..0e0c5ab
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/L2NFO494.csv
@@ -0,0 +1,21 @@
+col0,col1,col2
+IFI27,human chromosome 14,forward strand
+HSPA2,human chromosome 14,forward strand
+GSTZ1,human chromosome 14,forward strand
+FUT8,human chromosome 14,forward strand
+BDRB1,human chromosome 14,forward strand
+FNTB,human chromosome 14,forward strand
+FOXG1,human chromosome 14,forward strand
+EML1,human chromosome 14,forward strand
+EIF5,human chromosome 14,forward strand
+IF2T1,human chromosome 14,forward strand
+DYNE1H1,human chromosome 14,forward strand
+DLST,human chromosome 14,forward strand
+DIO3,human chromosome 14,forward strand
+COCH,human chromosome 14,forward strand
+CRIP2,human chromosome 14,forward strand
+CRIP1,human chromosome 14,forward strand
+CDKN3,human chromosome 14,forward strand
+CALM2,human chromosome 14,forward strand
+CALM1,human chromosome 14,forward strand
+BC2L2,human chromosome 14,forward strand
diff --git a/examples/semtab2020_novartis/tables/L2NFO494.json b/examples/semtab2020_novartis/tables/L2NFO494.json
new file mode 100644
index 0000000..e321496
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/L2NFO494.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["IFI27","HSPA2","GSTZ1","FUT8","BDRB1","FNTB","FOXG1","EML1","EIF5","IF2T1","DYNE1H1","DLST","DIO3","COCH","CRIP2","CRIP1","CDKN3","CALM2","CALM1","BC2L2"]},{"index":1,"name":"col1","values":["human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14"]},{"index":2,"name":"col2","values":["forward strand","forward strand","forward strand","forward strand","forward strand","forward strand","forward strand","forward strand","forward strand","forward strand","forward strand","forward strand","forward strand","forward strand","forward strand","forward strand","forward strand","forward strand","forward strand","forward strand"]}],"metadata":{"table_id":"L2NFO494","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18027570","qnode_id":"Q18027570"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18027345","qnode_id":"Q18027345"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18026605","qnode_id":"Q18026605"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18024871","qnode_id":"Q18024871"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18016454","qnode_id":"Q18016454"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q17928925","qnode_id":"Q17928925"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q17928632","qnode_id":"Q17928632"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q17916684","qnode_id":"Q17916684"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q17916091","qnode_id":"Q17916091"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q17916080","qnode_id":"Q17916080"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q17914038","qnode_id":"Q17914038"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q17914013","qnode_id":"Q17914013"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q17914006","qnode_id":"Q17914006"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q17913983","qnode_id":"Q17913983"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q17909782","qnode_id":"Q17909782"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q17909779","qnode_id":"Q17909779"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q17860603","qnode_id":"Q17860603"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q17855536","qnode_id":"Q17855536"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q17855525","qnode_id":"Q17855525"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q17847202","qnode_id":"Q17847202"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/entity/Q7187","rel_uri":"wd:Q7187","approximation":false,"readable_label":"gene (Q7187)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"http://www.wikidata.org/entity/Q37748:0","abs_uri":"http://www.wikidata.org/entity/Q37748","rel_uri":"wd:Q37748","approximation":false,"readable_label":"chromosome (Q37748)"},{"id":"col-2","col_index":2,"label":"col2"}],"edges":[{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"http://www.wikidata.org/entity/Q37748:0","abs_uri":"http://www.wikidata.org/prop/P1057","rel_uri":"p:P1057","approximation":false,"readable_label":"chromosome (P1057)","id":2},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-2","abs_uri":"http://www.wikidata.org/prop/P2548","rel_uri":"p:P2548","approximation":false,"readable_label":"strand orientation (P2548)","id":3},{"source":"http://www.wikidata.org/entity/Q37748:0","target":"col-1","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":4}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/L2NFO494.links.tsv b/examples/semtab2020_novartis/tables/L2NFO494.links.tsv
new file mode 100644
index 0000000..32465df
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/L2NFO494.links.tsv
@@ -0,0 +1,60 @@
+0 0 Q18027570
+0 1 Q138955
+0 2 Q22809680
+1 0 Q18027345
+1 1 Q138955
+1 2 Q22809680
+2 0 Q18026605
+2 1 Q138955
+2 2 Q22809680
+3 0 Q18024871
+3 1 Q138955
+3 2 Q22809680
+4 0 Q18016454
+4 1 Q138955
+4 2 Q22809680
+5 0 Q17928925
+5 1 Q138955
+5 2 Q22809680
+6 0 Q17928632
+6 1 Q138955
+6 2 Q22809680
+7 0 Q17916684
+7 1 Q138955
+7 2 Q22809680
+8 0 Q17916091
+8 1 Q138955
+8 2 Q22809680
+9 0 Q17916080
+9 1 Q138955
+9 2 Q22809680
+10 0 Q17914038
+10 1 Q138955
+10 2 Q22809680
+11 0 Q17914013
+11 1 Q138955
+11 2 Q22809680
+12 0 Q17914006
+12 1 Q138955
+12 2 Q22809680
+13 0 Q17913983
+13 1 Q138955
+13 2 Q22809680
+14 0 Q17909782
+14 1 Q138955
+14 2 Q22809680
+15 0 Q17909779
+15 1 Q138955
+15 2 Q22809680
+16 0 Q17860603
+16 1 Q138955
+16 2 Q22809680
+17 0 Q17855536
+17 1 Q138955
+17 2 Q22809680
+18 0 Q17855525
+18 1 Q138955
+18 2 Q22809680
+19 0 Q17847202
+19 1 Q138955
+19 2 Q22809680
diff --git a/examples/semtab2020_novartis/tables/L4HZ1ZOK.csv b/examples/semtab2020_novartis/tables/L4HZ1ZOK.csv
new file mode 100644
index 0000000..7acd82f
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/L4HZ1ZOK.csv
@@ -0,0 +1,21 @@
+col0,col1,col2
+chondromalacia patellae,rheumatology,bone disease
+relapsing polychondritis,rheumatology,Autoimmune connective tissue disorder
+ganglion or cyst of synovium/tendon/bursa,rheumatology,arthropathy
+patellofemoral pain syndrome,rheumatology,arthropathy
+temporomandibular joint pathology,rheumatology,arthropathy
+hemarthrosis,rheumatology,arthropathy
+joint effusion,rheumatology,arthropathy
+articular cartilage disease,rheumatology,arthropathy
+arthritis,rheumatology,arthropathy
+shoulder impingement syndrome,orthopedics,arthropathy
+De Quervain disease,rheumatology,occupational disease
+villonodular synovitis,rheumatology,synovitis
+tenosynovitis,physical medicine and rehabilitation,tendinitis
+tooth ankylosis,gastroenterology,genetic disease
+Felty's syndrome,rheumatology,syndrome
+septic arthritis,infectiology,arthritis
+periarthritis,rheumatology,arthritis
+discitis,rheumatology,arthritis
+reactive arthritis,rheumatology,arthritis
+polyarthritis,rheumatology,arthritis
diff --git a/examples/semtab2020_novartis/tables/L4HZ1ZOK.json b/examples/semtab2020_novartis/tables/L4HZ1ZOK.json
new file mode 100644
index 0000000..6f45311
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/L4HZ1ZOK.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["chondromalacia patellae","relapsing polychondritis","ganglion or cyst of synovium/tendon/bursa","patellofemoral pain syndrome","temporomandibular joint pathology","hemarthrosis","joint effusion","articular cartilage disease","arthritis","shoulder impingement syndrome","De Quervain disease","villonodular synovitis","tenosynovitis","tooth ankylosis","Felty's syndrome","septic arthritis","periarthritis","discitis","reactive arthritis","polyarthritis"]},{"index":1,"name":"col1","values":["rheumatology","rheumatology","rheumatology","rheumatology","rheumatology","rheumatology","rheumatology","rheumatology","rheumatology","orthopedics","rheumatology","rheumatology","physical medicine and rehabilitation","gastroenterology","rheumatology","infectiology","rheumatology","rheumatology","rheumatology","rheumatology"]},{"index":2,"name":"col2","values":["bone disease","Autoimmune connective tissue disorder","arthropathy","arthropathy","arthropathy","arthropathy","arthropathy","arthropathy","arthropathy","arthropathy","occupational disease","synovitis","tendinitis","genetic disease","syndrome","arthritis","arthritis","arthritis","arthritis","arthritis"]}],"metadata":{"table_id":"L4HZ1ZOK","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q1076086","qnode_id":"Q1076086"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q327657","qnode_id":"Q327657"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q4941552","qnode_id":"Q4941552"}]],[[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q187656","qnode_id":"Q187656"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q327657","qnode_id":"Q327657"}],[{"start":0,"end":37,"url":"http://www.wikidata.org/entity/Q2208472","qnode_id":"Q2208472"}]],[[{"start":0,"end":41,"url":"http://www.wikidata.org/entity/Q18558213","qnode_id":"Q18558213"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q327657","qnode_id":"Q327657"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q708176","qnode_id":"Q708176"}]],[[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q7144437","qnode_id":"Q7144437"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q327657","qnode_id":"Q327657"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q708176","qnode_id":"Q708176"}]],[[{"start":0,"end":33,"url":"http://www.wikidata.org/entity/Q2651722","qnode_id":"Q2651722"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q327657","qnode_id":"Q327657"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q708176","qnode_id":"Q708176"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q1642040","qnode_id":"Q1642040"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q327657","qnode_id":"Q327657"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q708176","qnode_id":"Q708176"}]],[[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q1499629","qnode_id":"Q1499629"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q327657","qnode_id":"Q327657"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q708176","qnode_id":"Q708176"}]],[[{"start":0,"end":27,"url":"http://www.wikidata.org/entity/Q938793","qnode_id":"Q938793"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q327657","qnode_id":"Q327657"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q708176","qnode_id":"Q708176"}]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q170990","qnode_id":"Q170990"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q327657","qnode_id":"Q327657"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q708176","qnode_id":"Q708176"}]],[[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q1660341","qnode_id":"Q1660341"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q216685","qnode_id":"Q216685"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q708176","qnode_id":"Q708176"}]],[[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q517202","qnode_id":"Q517202"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q327657","qnode_id":"Q327657"}],[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q637816","qnode_id":"Q637816"}]],[[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q19001412","qnode_id":"Q19001412"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q327657","qnode_id":"Q327657"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q538432","qnode_id":"Q538432"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q1505689","qnode_id":"Q1505689"}],[{"start":0,"end":36,"url":"http://www.wikidata.org/entity/Q2678675","qnode_id":"Q2678675"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q499574","qnode_id":"Q499574"}]],[[],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q120569","qnode_id":"Q120569"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q200779","qnode_id":"Q200779"}]],[[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1404470","qnode_id":"Q1404470"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q327657","qnode_id":"Q327657"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q179630","qnode_id":"Q179630"}]],[[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q924421","qnode_id":"Q924421"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q170990","qnode_id":"Q170990"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q3899850","qnode_id":"Q3899850"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q327657","qnode_id":"Q327657"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q170990","qnode_id":"Q170990"}]],[[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q2312030","qnode_id":"Q2312030"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q327657","qnode_id":"Q327657"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q170990","qnode_id":"Q170990"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q1327700","qnode_id":"Q1327700"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q327657","qnode_id":"Q327657"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q170990","qnode_id":"Q170990"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q1274464","qnode_id":"Q1274464"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q327657","qnode_id":"Q327657"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q170990","qnode_id":"Q170990"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q708176:0","abs_uri":"http://www.wikidata.org/entity/Q708176","rel_uri":"wd:Q708176","approximation":false,"readable_label":"arthropathy (Q708176)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"http://www.wikidata.org/entity/Q930752:0","abs_uri":"http://www.wikidata.org/entity/Q930752","rel_uri":"wd:Q930752","approximation":false,"readable_label":"medical specialty (Q930752)"},{"id":"col-2","col_index":2,"label":"col2"}],"edges":[{"source":"http://www.wikidata.org/entity/Q708176:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q708176:0","target":"http://www.wikidata.org/entity/Q930752:0","abs_uri":"http://www.wikidata.org/prop/P1995","rel_uri":"p:P1995","approximation":false,"readable_label":"health specialty (P1995)","id":2},{"source":"http://www.wikidata.org/entity/Q708176:0","target":"col-2","abs_uri":"http://www.wikidata.org/prop/P279","rel_uri":"p:P279","approximation":false,"readable_label":"subclass of (P279)","id":3},{"source":"http://www.wikidata.org/entity/Q930752:0","target":"col-1","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":4}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/L4HZ1ZOK.links.tsv b/examples/semtab2020_novartis/tables/L4HZ1ZOK.links.tsv
new file mode 100644
index 0000000..ec610a3
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/L4HZ1ZOK.links.tsv
@@ -0,0 +1,59 @@
+0 0 Q1076086
+0 1 Q327657
+0 2 Q4941552
+1 0 Q187656
+1 1 Q327657
+1 2 Q2208472
+2 0 Q18558213
+2 1 Q327657
+2 2 Q708176
+3 0 Q7144437
+3 1 Q327657
+3 2 Q708176
+4 0 Q2651722
+4 1 Q327657
+4 2 Q708176
+5 0 Q1642040
+5 1 Q327657
+5 2 Q708176
+6 0 Q1499629
+6 1 Q327657
+6 2 Q708176
+7 0 Q938793
+7 1 Q327657
+7 2 Q708176
+8 0 Q170990
+8 1 Q327657
+8 2 Q708176
+9 0 Q1660341
+9 1 Q216685
+9 2 Q708176
+10 0 Q517202
+10 1 Q327657
+10 2 Q637816
+11 0 Q19001412
+11 1 Q327657
+11 2 Q538432
+12 0 Q1505689
+12 1 Q2678675
+12 2 Q499574
+13 1 Q120569
+13 2 Q200779
+14 0 Q1404470
+14 1 Q327657
+14 2 Q179630
+15 0 Q924421
+15 1 Q788926
+15 2 Q170990
+16 0 Q3899850
+16 1 Q327657
+16 2 Q170990
+17 0 Q2312030
+17 1 Q327657
+17 2 Q170990
+18 0 Q1327700
+18 1 Q327657
+18 2 Q170990
+19 0 Q1274464
+19 1 Q327657
+19 2 Q170990
diff --git a/examples/semtab2020_novartis/tables/M8ZR2FPN.csv b/examples/semtab2020_novartis/tables/M8ZR2FPN.csv
new file mode 100644
index 0000000..7c46df0
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/M8ZR2FPN.csv
@@ -0,0 +1,21 @@
+col0,col1,col2,col3
+FLT3,Flt3,acute myeloid leukemia,protein-coding gene
+FGFR3,Fgfr3,SADDAN,protein-coding gene
+MAN2B1,Man2b1,alpha-mannosidosis,protein-coding gene
+CHRM3,Chrm3,Prune belly syndrome,protein-coding gene
+GRM7,Grm7,Cleft lip and cleft palate,protein-coding gene
+FHIT,Fhit,Cleft lip and cleft palate,protein-coding gene
+BMP6,Bmp6,Cleft lip and cleft palate,protein-coding gene
+IRF6,Irf6,Cleft lip and cleft palate,protein-coding gene
+LMNA,Lmna,progeria,protein-coding gene
+DAPK1,Dapk1,pancreatic cancer,protein-coding gene
+GTF2H1,Gtf2h1,pancreatic cancer,protein-coding gene
+GRID1,Grid1,pancreatic cancer,protein-coding gene
+DPP6,Dpp6,pancreatic cancer,protein-coding gene
+DAB2,Dab2,pancreatic cancer,protein-coding gene
+BICD1,Bicd1,pancreatic cancer,protein-coding gene
+BACH1,Bach1,pancreatic cancer,protein-coding gene
+NR5A2,Nr5a2,pancreatic cancer,protein-coding gene
+PRKN,Prkn,pancreatic cancer,protein-coding gene
+GATA3,Gata3,Hodgkin's lymphoma,protein-coding gene
+IL13,Il13,Hodgkin's lymphoma,protein-coding gene
diff --git a/examples/semtab2020_novartis/tables/M8ZR2FPN.json b/examples/semtab2020_novartis/tables/M8ZR2FPN.json
new file mode 100644
index 0000000..c3cf026
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/M8ZR2FPN.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["FLT3","FGFR3","MAN2B1","CHRM3","GRM7","FHIT","BMP6","IRF6","LMNA","DAPK1","GTF2H1","GRID1","DPP6","DAB2","BICD1","BACH1","NR5A2","PRKN","GATA3","IL13"]},{"index":1,"name":"col1","values":["Flt3","Fgfr3","Man2b1","Chrm3","Grm7","Fhit","Bmp6","Irf6","Lmna","Dapk1","Gtf2h1","Grid1","Dpp6","Dab2","Bicd1","Bach1","Nr5a2","Prkn","Gata3","Il13"]},{"index":2,"name":"col2","values":["acute myeloid leukemia","SADDAN","alpha-mannosidosis","Prune belly syndrome","Cleft lip and cleft palate","Cleft lip and cleft palate","Cleft lip and cleft palate","Cleft lip and cleft palate","progeria","pancreatic cancer","pancreatic cancer","pancreatic cancer","pancreatic cancer","pancreatic cancer","pancreatic cancer","pancreatic cancer","pancreatic cancer","pancreatic cancer","Hodgkin's lymphoma","Hodgkin's lymphoma"]},{"index":3,"name":"col3","values":["protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene"]}],"metadata":{"table_id":"M8ZR2FPN","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14912399","qnode_id":"Q14912399"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14912400","qnode_id":"Q14912400"}],[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q264118","qnode_id":"Q264118"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14914358","qnode_id":"Q14914358"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14914359","qnode_id":"Q14914359"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q259765","qnode_id":"Q259765"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q18028853","qnode_id":"Q18028853"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q18252197","qnode_id":"Q18252197"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q250449","qnode_id":"Q250449"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q17861955","qnode_id":"Q17861955"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18248323","qnode_id":"Q18248323"}],[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q250354","qnode_id":"Q250354"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18026554","qnode_id":"Q18026554"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18272878","qnode_id":"Q18272878"}],[{"start":0,"end":26,"url":"http://www.wikidata.org/entity/Q222634","qnode_id":"Q222634"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q17928393","qnode_id":"Q17928393"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18250591","qnode_id":"Q18250591"}],[{"start":0,"end":26,"url":"http://www.wikidata.org/entity/Q222634","qnode_id":"Q222634"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14903748","qnode_id":"Q14903748"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14903749","qnode_id":"Q14903749"}],[{"start":0,"end":26,"url":"http://www.wikidata.org/entity/Q222634","qnode_id":"Q222634"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14890118","qnode_id":"Q14890118"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14890119","qnode_id":"Q14890119"}],[{"start":0,"end":26,"url":"http://www.wikidata.org/entity/Q222634","qnode_id":"Q222634"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14911829","qnode_id":"Q14911829"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14911830","qnode_id":"Q14911830"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q213098","qnode_id":"Q213098"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q17912763","qnode_id":"Q17912763"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18263850","qnode_id":"Q18263850"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q212961","qnode_id":"Q212961"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q18026631","qnode_id":"Q18026631"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q18251020","qnode_id":"Q18251020"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q212961","qnode_id":"Q212961"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18026489","qnode_id":"Q18026489"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18250965","qnode_id":"Q18250965"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q212961","qnode_id":"Q212961"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q17914420","qnode_id":"Q17914420"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18250233","qnode_id":"Q18250233"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q212961","qnode_id":"Q212961"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q17912301","qnode_id":"Q17912301"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18248651","qnode_id":"Q18248651"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q212961","qnode_id":"Q212961"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q17848412","qnode_id":"Q17848412"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18247970","qnode_id":"Q18247970"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q212961","qnode_id":"Q212961"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q17836876","qnode_id":"Q17836876"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18247914","qnode_id":"Q18247914"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q212961","qnode_id":"Q212961"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q15312697","qnode_id":"Q15312697"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q15312701","qnode_id":"Q15312701"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q212961","qnode_id":"Q212961"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14864255","qnode_id":"Q14864255"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14864256","qnode_id":"Q14864256"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q212961","qnode_id":"Q212961"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18025464","qnode_id":"Q18025464"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18250752","qnode_id":"Q18250752"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q209369","qnode_id":"Q209369"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14878303","qnode_id":"Q14878303"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14878304","qnode_id":"Q14878304"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q209369","qnode_id":"Q209369"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/entity/Q7187","rel_uri":"wd:Q7187","approximation":false,"readable_label":"gene (Q7187)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"http://www.wikidata.org/entity/Q7187:1","abs_uri":"http://www.wikidata.org/entity/Q7187","rel_uri":"wd:Q7187","approximation":false,"readable_label":"gene (Q7187)"},{"id":"col-2","col_index":2,"label":"col2"},{"id":"http://www.wikidata.org/entity/Q12136:0","abs_uri":"http://www.wikidata.org/entity/Q12136","rel_uri":"wd:Q12136","approximation":false,"readable_label":"disease (Q12136)"},{"id":"col-3","col_index":3,"label":"col3"}],"edges":[{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"http://www.wikidata.org/entity/Q7187:1","abs_uri":"http://www.wikidata.org/prop/P684","rel_uri":"p:P684","approximation":false,"readable_label":"ortholog (P684)","id":2},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"http://www.wikidata.org/entity/Q12136:0","abs_uri":"http://www.wikidata.org/prop/P2293","rel_uri":"p:P2293","approximation":false,"readable_label":"genetic association (P2293)","id":3},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-3","abs_uri":"http://www.wikidata.org/prop/P279","rel_uri":"p:P279","approximation":false,"readable_label":"subclass of (P279)","id":4},{"source":"http://www.wikidata.org/entity/Q7187:1","target":"col-1","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":5},{"source":"http://www.wikidata.org/entity/Q12136:0","target":"col-2","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":6}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/M8ZR2FPN.links.tsv b/examples/semtab2020_novartis/tables/M8ZR2FPN.links.tsv
new file mode 100644
index 0000000..49f13ce
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/M8ZR2FPN.links.tsv
@@ -0,0 +1,80 @@
+0 0 Q14912399
+0 1 Q14912400
+0 2 Q264118
+0 3 Q20747295
+1 0 Q14914358
+1 1 Q14914359
+1 2 Q259765
+1 3 Q20747295
+2 0 Q18028853
+2 1 Q18252197
+2 2 Q250449
+2 3 Q20747295
+3 0 Q17861955
+3 1 Q18248323
+3 2 Q250354
+3 3 Q20747295
+4 0 Q18026554
+4 1 Q18272878
+4 2 Q222634
+4 3 Q20747295
+5 0 Q17928393
+5 1 Q18250591
+5 2 Q222634
+5 3 Q20747295
+6 0 Q14903748
+6 1 Q14903749
+6 2 Q222634
+6 3 Q20747295
+7 0 Q14890118
+7 1 Q14890119
+7 2 Q222634
+7 3 Q20747295
+8 0 Q14911829
+8 1 Q14911830
+8 2 Q213098
+8 3 Q20747295
+9 0 Q17912763
+9 1 Q18263850
+9 2 Q212961
+9 3 Q20747295
+10 0 Q18026631
+10 1 Q18251020
+10 2 Q212961
+10 3 Q20747295
+11 0 Q18026489
+11 1 Q18250965
+11 2 Q212961
+11 3 Q20747295
+12 0 Q17914420
+12 1 Q18250233
+12 2 Q212961
+12 3 Q20747295
+13 0 Q17912301
+13 1 Q18248651
+13 2 Q212961
+13 3 Q20747295
+14 0 Q17848412
+14 1 Q18247970
+14 2 Q212961
+14 3 Q20747295
+15 0 Q17836876
+15 1 Q18247914
+15 2 Q212961
+15 3 Q20747295
+16 0 Q15312697
+16 1 Q15312701
+16 2 Q212961
+16 3 Q20747295
+17 0 Q14864255
+17 1 Q14864256
+17 2 Q212961
+17 3 Q20747295
+18 0 Q18025464
+18 1 Q18250752
+18 2 Q209369
+18 3 Q20747295
+19 0 Q14878303
+19 1 Q14878304
+19 2 Q209369
+19 3 Q20747295
diff --git a/examples/semtab2020_novartis/tables/MQHMLU2B.csv b/examples/semtab2020_novartis/tables/MQHMLU2B.csv
new file mode 100644
index 0000000..e29ec37
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/MQHMLU2B.csv
@@ -0,0 +1,21 @@
+col0,col1,col2,col3,col4
+mumps,herpangina,23,mumps virus,fever
+scarlet fever,streptococcal infection,5,Streptococcus pyogenes,fever
+brucellosis,zoonosis,5,Brucella,fatigue
+leptospirosis,spirochetal diseases,20,Leptospira interrogans serovar Lai str. 56601,headache
+legionnaires' disease,atypical pneumonia,16,Legionella pneumophila,headache
+Q fever,primary bacterial infectious disease,60,Coxiella burnetii,headache
+cryptosporidiosis,intestinal parasite infection,10,Cryptosporidium parvum,fever
+exanthema subitum,viral infectious disease,12,Human herpesvirus 6,fever
+Japanese encephalitis,brain diseases,15,Japanese encephalitis virus,headache
+ehrlichiosis,tick-borne disease,2,Ehrlichia chaffeensis,headache
+babesiosis,parasitic protozoa infectious disease,4,Babesia,headache
+campylobacteriosis,gastroenteritis,7,Campylobacter jejuni,headache
+Lyme disease,primary bacterial infectious disease,30,Borrelia burgdorferi,myalgia
+chikungunya,neglected tropical disease,12,Chikungunya virus,fever
+shigellosis,bacterial infectious disease,4,Shigella,fever
+poliomyelitis,peripheral neuropathy,35,poliovirus,paralysis
+tuberculosis,mycobacterium infectious disease,8,Mycobacterium tuberculosis,cough
+malaria,parasitic protozoa infectious disease,30,Plasmodium,anemia
+pertussis,commensal bacterial infectious disease,21,Bordetella pertussis,fatigue
+typhoid fever,primary bacterial infectious disease,30,Salmonella enterica,headache
diff --git a/examples/semtab2020_novartis/tables/MQHMLU2B.json b/examples/semtab2020_novartis/tables/MQHMLU2B.json
new file mode 100644
index 0000000..3c40bab
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/MQHMLU2B.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["mumps","scarlet fever","brucellosis","leptospirosis","legionnaires' disease","Q fever","cryptosporidiosis","exanthema subitum","Japanese encephalitis","ehrlichiosis","babesiosis","campylobacteriosis","Lyme disease","chikungunya","shigellosis","poliomyelitis","tuberculosis","malaria","pertussis","typhoid fever"]},{"index":1,"name":"col1","values":["herpangina","streptococcal infection","zoonosis","spirochetal diseases","atypical pneumonia","primary bacterial infectious disease","intestinal parasite infection","viral infectious disease","brain diseases","tick-borne disease","parasitic protozoa infectious disease","gastroenteritis","primary bacterial infectious disease","neglected tropical disease","bacterial infectious disease","peripheral neuropathy","mycobacterium infectious disease","parasitic protozoa infectious disease","commensal bacterial infectious disease","primary bacterial infectious disease"]},{"index":2,"name":"col2","values":["23","5","5","20","16","60","10","12","15","2","4","7","30","12","4","35","8","30","21","30"]},{"index":3,"name":"col3","values":["mumps virus","Streptococcus pyogenes","Brucella","Leptospira interrogans serovar Lai str. 56601","Legionella pneumophila","Coxiella burnetii","Cryptosporidium parvum","Human herpesvirus 6","Japanese encephalitis virus","Ehrlichia chaffeensis","Babesia","Campylobacter jejuni","Borrelia burgdorferi","Chikungunya virus","Shigella","poliovirus","Mycobacterium tuberculosis","Plasmodium","Bordetella pertussis","Salmonella enterica"]},{"index":4,"name":"col4","values":["fever","fever","fatigue","headache","headache","headache","fever","fever","headache","headache","headache","headache","myalgia","fever","fever","paralysis","cough","anemia","fatigue","headache"]}],"metadata":{"table_id":"MQHMLU2B","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q176741","qnode_id":"Q176741"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q665258","qnode_id":"Q665258"}],[],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q1952649","qnode_id":"Q1952649"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q38933","qnode_id":"Q38933"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q180266","qnode_id":"Q180266"}],[],[],[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q131271","qnode_id":"Q131271"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q38933","qnode_id":"Q38933"}]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q156050","qnode_id":"Q156050"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q182672","qnode_id":"Q182672"}],[],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q313358","qnode_id":"Q313358"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q9690","qnode_id":"Q9690"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q155098","qnode_id":"Q155098"}],[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q3493588","qnode_id":"Q3493588"}],[],[{"start":0,"end":45,"url":"http://www.wikidata.org/entity/Q21065229","qnode_id":"Q21065229"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q86","qnode_id":"Q86"}]],[[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q154882","qnode_id":"Q154882"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q2633267","qnode_id":"Q2633267"}],[],[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q147885","qnode_id":"Q147885"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q86","qnode_id":"Q86"}]],[[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q164818","qnode_id":"Q164818"}],[{"start":0,"end":36,"url":"http://www.wikidata.org/entity/Q18553247","qnode_id":"Q18553247"}],[],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q133971","qnode_id":"Q133971"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q86","qnode_id":"Q86"}]],[[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q1359898","qnode_id":"Q1359898"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q475510","qnode_id":"Q475510"}],[],[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q134734","qnode_id":"Q134734"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q38933","qnode_id":"Q38933"}]],[[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q720032","qnode_id":"Q720032"}],[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q1928978","qnode_id":"Q1928978"}],[],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q6974","qnode_id":"Q6974"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q38933","qnode_id":"Q38933"}]],[[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q738292","qnode_id":"Q738292"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q576349","qnode_id":"Q576349"}],[],[{"start":0,"end":27,"url":"http://www.wikidata.org/entity/Q19838331","qnode_id":"Q19838331"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q86","qnode_id":"Q86"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q2845432","qnode_id":"Q2845432"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q184243","qnode_id":"Q184243"}],[],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q5348629","qnode_id":"Q5348629"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q86","qnode_id":"Q86"}]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q797668","qnode_id":"Q797668"}],[{"start":0,"end":37,"url":"http://www.wikidata.org/entity/Q353172","qnode_id":"Q353172"}],[],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q797656","qnode_id":"Q797656"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q86","qnode_id":"Q86"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q683861","qnode_id":"Q683861"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q156103","qnode_id":"Q156103"}],[],[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q265488","qnode_id":"Q265488"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q86","qnode_id":"Q86"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q201989","qnode_id":"Q201989"}],[{"start":0,"end":36,"url":"http://www.wikidata.org/entity/Q18553247","qnode_id":"Q18553247"}],[],[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q2723634","qnode_id":"Q2723634"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q474959","qnode_id":"Q474959"}]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q243257","qnode_id":"Q243257"}],[{"start":0,"end":26,"url":"http://www.wikidata.org/entity/Q929451","qnode_id":"Q929451"}],[],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q15794049","qnode_id":"Q15794049"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q38933","qnode_id":"Q38933"}]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q327298","qnode_id":"Q327298"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q727028","qnode_id":"Q727028"}],[],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q131029","qnode_id":"Q131029"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q38933","qnode_id":"Q38933"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q12195","qnode_id":"Q12195"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q945238","qnode_id":"Q945238"}],[],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q12438936","qnode_id":"Q12438936"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q107231","qnode_id":"Q107231"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q12204","qnode_id":"Q12204"}],[{"start":0,"end":32,"url":"http://www.wikidata.org/entity/Q4293079","qnode_id":"Q4293079"}],[],[{"start":0,"end":26,"url":"http://www.wikidata.org/entity/Q130971","qnode_id":"Q130971"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q35805","qnode_id":"Q35805"}]],[[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12156","qnode_id":"Q12156"}],[{"start":0,"end":37,"url":"http://www.wikidata.org/entity/Q353172","qnode_id":"Q353172"}],[],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q130948","qnode_id":"Q130948"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q5445","qnode_id":"Q5445"}]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q134859","qnode_id":"Q134859"}],[{"start":0,"end":38,"url":"http://www.wikidata.org/entity/Q18553248","qnode_id":"Q18553248"}],[],[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q137103","qnode_id":"Q137103"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q9690","qnode_id":"Q9690"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q83319","qnode_id":"Q83319"}],[{"start":0,"end":36,"url":"http://www.wikidata.org/entity/Q18553247","qnode_id":"Q18553247"}],[],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q2264864","qnode_id":"Q2264864"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q86","qnode_id":"Q86"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q12136:0","abs_uri":"http://www.wikidata.org/entity/Q12136","rel_uri":"wd:Q12136","approximation":false,"readable_label":"disease (Q12136)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"col-3","col_index":3,"label":"col3"},{"id":"col-4","col_index":4,"label":"col4"},{"id":"col-2","col_index":2,"label":"col2"}],"edges":[{"source":"http://www.wikidata.org/entity/Q12136:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q12136:0","target":"col-1","abs_uri":"http://www.wikidata.org/prop/P279","rel_uri":"p:P279","approximation":false,"readable_label":"subclass of (P279)","id":2},{"source":"http://www.wikidata.org/entity/Q12136:0","target":"col-3","abs_uri":"http://www.wikidata.org/prop/P828","rel_uri":"p:P828","approximation":false,"readable_label":"has cause (P828)","id":3},{"source":"http://www.wikidata.org/entity/Q12136:0","target":"col-4","abs_uri":"http://www.wikidata.org/prop/P780","rel_uri":"p:P780","approximation":false,"readable_label":"symptoms (P780)","id":4},{"source":"http://www.wikidata.org/entity/Q12136:0","target":"col-2","abs_uri":"http://www.wikidata.org/prop/P3487","rel_uri":"p:P3487","approximation":false,"readable_label":"maximal incubation period in humans (P3487)","id":5}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/MQHMLU2B.links.tsv b/examples/semtab2020_novartis/tables/MQHMLU2B.links.tsv
new file mode 100644
index 0000000..83d813e
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/MQHMLU2B.links.tsv
@@ -0,0 +1,79 @@
+0 0 Q176741
+0 1 Q665258
+0 3 Q1952649
+0 4 Q38933
+1 0 Q180266
+1 3 Q131271
+1 4 Q38933
+2 0 Q156050
+2 1 Q182672
+2 3 Q313358
+2 4 Q9690
+3 0 Q155098
+3 1 Q3493588
+3 3 Q21065229
+3 4 Q86
+4 0 Q154882
+4 1 Q2633267
+4 3 Q147885
+4 4 Q86
+5 0 Q164818
+5 1 Q18553247
+5 3 Q133971
+5 4 Q86
+6 0 Q1359898
+6 1 Q475510
+6 3 Q134734
+6 4 Q38933
+7 0 Q720032
+7 1 Q1928978
+7 3 Q6974
+7 4 Q38933
+8 0 Q738292
+8 1 Q576349
+8 3 Q19838331
+8 4 Q86
+9 0 Q2845432
+9 1 Q184243
+9 3 Q5348629
+9 4 Q86
+10 0 Q797668
+10 1 Q353172
+10 3 Q797656
+10 4 Q86
+11 0 Q683861
+11 1 Q156103
+11 3 Q265488
+11 4 Q86
+12 0 Q201989
+12 1 Q18553247
+12 3 Q2723634
+12 4 Q474959
+13 0 Q243257
+13 1 Q929451
+13 3 Q15794049
+13 4 Q38933
+14 0 Q327298
+14 1 Q727028
+14 3 Q131029
+14 4 Q38933
+15 0 Q12195
+15 1 Q945238
+15 3 Q12438936
+15 4 Q107231
+16 0 Q12204
+16 1 Q4293079
+16 3 Q130971
+16 4 Q35805
+17 0 Q12156
+17 1 Q353172
+17 3 Q130948
+17 4 Q5445
+18 0 Q134859
+18 1 Q18553248
+18 3 Q137103
+18 4 Q9690
+19 0 Q83319
+19 1 Q18553247
+19 3 Q2264864
+19 4 Q86
diff --git a/examples/semtab2020_novartis/tables/MS68S6OX.csv b/examples/semtab2020_novartis/tables/MS68S6OX.csv
new file mode 100644
index 0000000..f1c7ac7
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/MS68S6OX.csv
@@ -0,0 +1,21 @@
+col0,col1,col2,col3
+Technical Development of Cardiovascular Magnetic Resonance Imaging,National Institutes of Health,myocardial infarction,"National Heart, Lung, and Blood Institute"
+"Treatment of Uterine Fibroids With CDB-2914, an Experimental Selective Progesterone Receptor Antagonist",National Institutes of Health,leiomyoma,Eunice Kennedy Shriver National Institute of Child Health and Human Development
+Comparing Pump With Subcutaneous Injection Delivery of PTH 1-34 in the Management of Chronic Hypoparathyroidism,National Institutes of Health,hypoparathyroidism,Eunice Kennedy Shriver National Institute of Child Health and Human Development
+Evaluation and Treatment of Patients With Connective Tissue Disease,National Institutes of Health,osteogenesis imperfecta,Eunice Kennedy Shriver National Institute of Child Health and Human Development
+Genetic Factors in Atherosclerosis,National Institutes of Health,atherosclerosis,National Institute of Allergy and Infectious Diseases
+Brain Changes in Children and Adolescents With Behavioral Problems,National Institutes of Health,conduct disorder,National Institute of Mental Health
+PET Imaging of Peripheral Benzodiazepine Receptors in Patients With Neurocysticercosis Using [C-11]PBR28,National Institutes of Health,cysticercosis,National Institute of Mental Health
+Cholinergic Modulation of Condition and Emotion in Mood Disorders: Functional Neuroimaging Studies,National Institutes of Health,bipolar disorder,National Institute of Mental Health
+Evaluation of Patients With Mood and Anxiety Disorders and Healthy Volunteers,National Institutes of Health,bipolar disorder,National Institute of Mental Health
+PET Evaluation of Brain Peripheral Benzodiazepine Receptors Using [11C]PBR28 in HIV-Seropositive Patients With (MCMD),National Institutes of Health,dementia,National Institute of Mental Health
+Sorafenib to Treat Children and Young Adults With Neurofibromatosis Type 1 and Inoperable Plexiform Neurofibromas,National Institutes of Health,neurofibroma,National Cancer Institute
+Follow-up of Breast Cancer and Multiple Myeloma Patients Previously Enrolled in NIH Gene Therapy Studies,National Institutes of Health,multiple myeloma,National Cancer Institute
+BAY 43-9006 (Sorafenib) to Treat Patients With Kaposi's Sarcoma,National Institutes of Health,sarcoma,National Cancer Institute
+Phase II Study of Cediranib (AZD2171) in Patients With Alveolar Soft Part Sarcoma,National Institutes of Health,sarcoma,National Cancer Institute
+Safety of Batracylin in Patients With Solid Tumors and Lymphomas,National Institutes of Health,lymphoma,National Cancer Institute
+A Phase I Study of Intravenous Recombinant Human IL-15 in Adults With Refractory Metastatic Malignant Melanoma and Metastatic Renal Cell Cancer,National Institutes of Health,melanoma,National Cancer Institute
+Eligibility Screening of Patients With Central Nervous System Tumors for the National Cancer Institute s (NCI) Clinical Research Protocols,National Institutes of Health,astrocytoma,National Cancer Institute
+"A Phase I Study of ABT-888, an Oral Inhibitor of Poly(ADP-ribose) Polymerase and Temozolomide in Children With Recurrent/Refractory CNS Tumors",National Institutes of Health,astrocytoma,National Cancer Institute
+Mechanisms Behind Antidiabetic Effects by Gastric By-pass,Uppsala University,diabetes mellitus,Karolinska Institutet Innovations (Sweden)
+The Role of Bacteria and Genetic Variations in Cystic Fibrosis,University of Washington,cystic fibrosis,"National Heart, Lung, and Blood Institute"
diff --git a/examples/semtab2020_novartis/tables/MS68S6OX.json b/examples/semtab2020_novartis/tables/MS68S6OX.json
new file mode 100644
index 0000000..ae86c04
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/MS68S6OX.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["Technical Development of Cardiovascular Magnetic Resonance Imaging","Treatment of Uterine Fibroids With CDB-2914, an Experimental Selective Progesterone Receptor Antagonist","Comparing Pump With Subcutaneous Injection Delivery of PTH 1-34 in the Management of Chronic Hypoparathyroidism","Evaluation and Treatment of Patients With Connective Tissue Disease","Genetic Factors in Atherosclerosis","Brain Changes in Children and Adolescents With Behavioral Problems","PET Imaging of Peripheral Benzodiazepine Receptors in Patients With Neurocysticercosis Using [C-11]PBR28","Cholinergic Modulation of Condition and Emotion in Mood Disorders: Functional Neuroimaging Studies","Evaluation of Patients With Mood and Anxiety Disorders and Healthy Volunteers","PET Evaluation of Brain Peripheral Benzodiazepine Receptors Using [11C]PBR28 in HIV-Seropositive Patients With (MCMD)","Sorafenib to Treat Children and Young Adults With Neurofibromatosis Type 1 and Inoperable Plexiform Neurofibromas","Follow-up of Breast Cancer and Multiple Myeloma Patients Previously Enrolled in NIH Gene Therapy Studies","BAY 43-9006 (Sorafenib) to Treat Patients With Kaposi's Sarcoma","Phase II Study of Cediranib (AZD2171) in Patients With Alveolar Soft Part Sarcoma","Safety of Batracylin in Patients With Solid Tumors and Lymphomas","A Phase I Study of Intravenous Recombinant Human IL-15 in Adults With Refractory Metastatic Malignant Melanoma and Metastatic Renal Cell Cancer","Eligibility Screening of Patients With Central Nervous System Tumors for the National Cancer Institute s (NCI) Clinical Research Protocols","A Phase I Study of ABT-888, an Oral Inhibitor of Poly(ADP-ribose) Polymerase and Temozolomide in Children With Recurrent/Refractory CNS Tumors","Mechanisms Behind Antidiabetic Effects by Gastric By-pass","The Role of Bacteria and Genetic Variations in Cystic Fibrosis"]},{"index":1,"name":"col1","values":["National Institutes of Health","National Institutes of Health","National Institutes of Health","National Institutes of Health","National Institutes of Health","National Institutes of Health","National Institutes of Health","National Institutes of Health","National Institutes of Health","National Institutes of Health","National Institutes of Health","National Institutes of Health","National Institutes of Health","National Institutes of Health","National Institutes of Health","National Institutes of Health","National Institutes of Health","National Institutes of Health","Uppsala University","University of Washington"]},{"index":2,"name":"col2","values":["myocardial infarction","leiomyoma","hypoparathyroidism","osteogenesis imperfecta","atherosclerosis","conduct disorder","cysticercosis","bipolar disorder","bipolar disorder","dementia","neurofibroma","multiple myeloma","sarcoma","sarcoma","lymphoma","melanoma","astrocytoma","astrocytoma","diabetes mellitus","cystic fibrosis"]},{"index":3,"name":"col3","values":["National Heart, Lung, and Blood Institute","Eunice Kennedy Shriver National Institute of Child Health and Human Development","Eunice Kennedy Shriver National Institute of Child Health and Human Development","Eunice Kennedy Shriver National Institute of Child Health and Human Development","National Institute of Allergy and Infectious Diseases","National Institute of Mental Health","National Institute of Mental Health","National Institute of Mental Health","National Institute of Mental Health","National Institute of Mental Health","National Cancer Institute","National Cancer Institute","National Cancer Institute","National Cancer Institute","National Cancer Institute","National Cancer Institute","National Cancer Institute","National Cancer Institute","Karolinska Institutet Innovations (Sweden)","National Heart, Lung, and Blood Institute"]}],"metadata":{"table_id":"MS68S6OX","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":66,"url":"http://www.wikidata.org/entity/Q61865369","qnode_id":"Q61865369"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q12152","qnode_id":"Q12152"}],[{"start":0,"end":41,"url":"http://www.wikidata.org/entity/Q6973027","qnode_id":"Q6973027"}]],[[{"start":0,"end":103,"url":"http://www.wikidata.org/entity/Q61865361","qnode_id":"Q61865361"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q4667534","qnode_id":"Q4667534"}],[{"start":0,"end":79,"url":"http://www.wikidata.org/entity/Q5409765","qnode_id":"Q5409765"}]],[[{"start":0,"end":111,"url":"http://www.wikidata.org/entity/Q61865087","qnode_id":"Q61865087"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q1586088","qnode_id":"Q1586088"}],[{"start":0,"end":79,"url":"http://www.wikidata.org/entity/Q5409765","qnode_id":"Q5409765"}]],[[{"start":0,"end":67,"url":"http://www.wikidata.org/entity/Q61865354","qnode_id":"Q61865354"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q749409","qnode_id":"Q749409"}],[{"start":0,"end":79,"url":"http://www.wikidata.org/entity/Q5409765","qnode_id":"Q5409765"}]],[[{"start":0,"end":34,"url":"http://www.wikidata.org/entity/Q61865136","qnode_id":"Q61865136"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q12252367","qnode_id":"Q12252367"}],[{"start":0,"end":53,"url":"http://www.wikidata.org/entity/Q3519875","qnode_id":"Q3519875"}]],[[{"start":0,"end":66,"url":"http://www.wikidata.org/entity/Q61865335","qnode_id":"Q61865335"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q596474","qnode_id":"Q596474"}],[{"start":0,"end":35,"url":"http://www.wikidata.org/entity/Q1967405","qnode_id":"Q1967405"}]],[[{"start":0,"end":104,"url":"http://www.wikidata.org/entity/Q61865114","qnode_id":"Q61865114"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q246068","qnode_id":"Q246068"}],[{"start":0,"end":35,"url":"http://www.wikidata.org/entity/Q1967405","qnode_id":"Q1967405"}]],[[{"start":0,"end":98,"url":"http://www.wikidata.org/entity/Q61865356","qnode_id":"Q61865356"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q131755","qnode_id":"Q131755"}],[{"start":0,"end":35,"url":"http://www.wikidata.org/entity/Q1967405","qnode_id":"Q1967405"}]],[[{"start":0,"end":77,"url":"http://www.wikidata.org/entity/Q61865312","qnode_id":"Q61865312"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q131755","qnode_id":"Q131755"}],[{"start":0,"end":35,"url":"http://www.wikidata.org/entity/Q1967405","qnode_id":"Q1967405"}]],[[{"start":0,"end":117,"url":"http://www.wikidata.org/entity/Q61865116","qnode_id":"Q61865116"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q83030","qnode_id":"Q83030"}],[{"start":0,"end":35,"url":"http://www.wikidata.org/entity/Q1967405","qnode_id":"Q1967405"}]],[[{"start":0,"end":113,"url":"http://www.wikidata.org/entity/Q61865088","qnode_id":"Q61865088"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q1418735","qnode_id":"Q1418735"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q664846","qnode_id":"Q664846"}]],[[{"start":0,"end":104,"url":"http://www.wikidata.org/entity/Q61865128","qnode_id":"Q61865128"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q467635","qnode_id":"Q467635"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q664846","qnode_id":"Q664846"}]],[[{"start":0,"end":63,"url":"http://www.wikidata.org/entity/Q61865264","qnode_id":"Q61865264"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q223911","qnode_id":"Q223911"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q664846","qnode_id":"Q664846"}]],[[{"start":0,"end":81,"url":"http://www.wikidata.org/entity/Q61865057","qnode_id":"Q61865057"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q223911","qnode_id":"Q223911"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q664846","qnode_id":"Q664846"}]],[[{"start":0,"end":64,"url":"http://www.wikidata.org/entity/Q61865117","qnode_id":"Q61865117"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q208414","qnode_id":"Q208414"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q664846","qnode_id":"Q664846"}]],[[{"start":0,"end":143,"url":"http://www.wikidata.org/entity/Q61865048","qnode_id":"Q61865048"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q180614","qnode_id":"Q180614"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q664846","qnode_id":"Q664846"}]],[[{"start":0,"end":138,"url":"http://www.wikidata.org/entity/Q61865363","qnode_id":"Q61865363"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q177755","qnode_id":"Q177755"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q664846","qnode_id":"Q664846"}]],[[{"start":0,"end":142,"url":"http://www.wikidata.org/entity/Q61865052","qnode_id":"Q61865052"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q390551","qnode_id":"Q390551"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q177755","qnode_id":"Q177755"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q664846","qnode_id":"Q664846"}]],[[{"start":0,"end":57,"url":"http://www.wikidata.org/entity/Q61707436","qnode_id":"Q61707436"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q185246","qnode_id":"Q185246"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q12206","qnode_id":"Q12206"}],[{"start":0,"end":42,"url":"http://www.wikidata.org/entity/Q30263131","qnode_id":"Q30263131"}]],[[{"start":0,"end":62,"url":"http://www.wikidata.org/entity/Q61865362","qnode_id":"Q61865362"}],[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q219563","qnode_id":"Q219563"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q178194","qnode_id":"Q178194"}],[{"start":0,"end":41,"url":"http://www.wikidata.org/entity/Q6973027","qnode_id":"Q6973027"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q30612:0","abs_uri":"http://www.wikidata.org/entity/Q30612","rel_uri":"wd:Q30612","approximation":false,"readable_label":"clinical trial (Q30612)"},{"id":"col-2","col_index":2,"label":"col2"},{"id":"http://www.wikidata.org/entity/Q12136:0","abs_uri":"http://www.wikidata.org/entity/Q12136","rel_uri":"wd:Q12136","approximation":false,"readable_label":"disease (Q12136)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"col-3","col_index":3,"label":"col3"}],"edges":[{"source":"http://www.wikidata.org/entity/Q30612:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q30612:0","target":"col-1","abs_uri":"http://www.wikidata.org/prop/P6153","rel_uri":"p:P6153","approximation":false,"readable_label":"research site (P6153)","id":2},{"source":"http://www.wikidata.org/entity/Q30612:0","target":"http://www.wikidata.org/entity/Q12136:0","abs_uri":"http://www.wikidata.org/prop/P1050","rel_uri":"p:P1050","approximation":false,"readable_label":"medical condition (P1050)","id":3},{"source":"http://www.wikidata.org/entity/Q30612:0","target":"col-3","abs_uri":"http://www.wikidata.org/prop/P859","rel_uri":"p:P859","approximation":false,"readable_label":"sponsor (P859)","id":4},{"source":"http://www.wikidata.org/entity/Q12136:0","target":"col-2","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":5}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/MS68S6OX.links.tsv b/examples/semtab2020_novartis/tables/MS68S6OX.links.tsv
new file mode 100644
index 0000000..31a3346
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/MS68S6OX.links.tsv
@@ -0,0 +1,80 @@
+0 0 Q61865369
+0 1 Q390551
+0 2 Q12152
+0 3 Q6973027
+1 0 Q61865361
+1 1 Q390551
+1 2 Q4667534
+1 3 Q5409765
+2 0 Q61865087
+2 1 Q390551
+2 2 Q1586088
+2 3 Q5409765
+3 0 Q61865354
+3 1 Q390551
+3 2 Q749409
+3 3 Q5409765
+4 0 Q61865136
+4 1 Q390551
+4 2 Q12252367
+4 3 Q3519875
+5 0 Q61865335
+5 1 Q390551
+5 2 Q596474
+5 3 Q1967405
+6 0 Q61865114
+6 1 Q390551
+6 2 Q246068
+6 3 Q1967405
+7 0 Q61865356
+7 1 Q390551
+7 2 Q131755
+7 3 Q1967405
+8 0 Q61865312
+8 1 Q390551
+8 2 Q131755
+8 3 Q1967405
+9 0 Q61865116
+9 1 Q390551
+9 2 Q83030
+9 3 Q1967405
+10 0 Q61865088
+10 1 Q390551
+10 2 Q1418735
+10 3 Q664846
+11 0 Q61865128
+11 1 Q390551
+11 2 Q467635
+11 3 Q664846
+12 0 Q61865264
+12 1 Q390551
+12 2 Q223911
+12 3 Q664846
+13 0 Q61865057
+13 1 Q390551
+13 2 Q223911
+13 3 Q664846
+14 0 Q61865117
+14 1 Q390551
+14 2 Q208414
+14 3 Q664846
+15 0 Q61865048
+15 1 Q390551
+15 2 Q180614
+15 3 Q664846
+16 0 Q61865363
+16 1 Q390551
+16 2 Q177755
+16 3 Q664846
+17 0 Q61865052
+17 1 Q390551
+17 2 Q177755
+17 3 Q664846
+18 0 Q61707436
+18 1 Q185246
+18 2 Q12206
+18 3 Q30263131
+19 0 Q61865362
+19 1 Q219563
+19 2 Q178194
+19 3 Q6973027
diff --git a/examples/semtab2020_novartis/tables/MT7GAOG6.csv b/examples/semtab2020_novartis/tables/MT7GAOG6.csv
new file mode 100644
index 0000000..9d99d40
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/MT7GAOG6.csv
@@ -0,0 +1,21 @@
+col0,col1,col2
+Corticotropin-releasing hormone signalingpathway,AKT1,Alzheimer's disease
+Fluoopyrimidine Activity,SLC22A7,cancer
+TGF-B Signaling in Thyroid Cells for Epithelial-Mesenchymal Tansition,SNAI1,thyroid cancer
+MAPK and NFkB Sinalling Pathways Inhibited by Yersinia YopJ,RAF1,bubonic plague
+Association Between Physico-Chemical Features and Toxicity Associcted Pathways,WNT11,pancreatic cancer
+Toll-ike Receptor Signaling related to MyD88,TIRAP,cancer
+GHB etabolic pathway,γ-aminobutyric acid,succinic semialdehyde dehydrogenase deficiency
+Psion disease pathway,PDIA3,transmissible spongiform encephalopathy
+miRNA regulation of prostate cancer signalig pathways,MDM2,prostate cancer
+NO/cGMP/KG mediated Neuroprotection,L-Arginine,dementia
+Endotheoin Pathways,ADRA1A,hypertension
+Hypothesized Pathways in Pathogenesis of Cardiovaucular Disease,MAPK14,cardiovascular disease
+Tgif disruption of Sjh signaling,SHH,genetic disease
+Ovarian Infestility enes,PRLR,infertility
+Dell-type Dependept Selectivity of CCK2R Signaling,γ-aminobutyric acid,pertussis
+Initiation of tranvcription and translation elongation at the HIV-1 LTR,prostratin,human immunodeficiency virus infectious disease
+RAC1/PAK1/p380MMP2 Pathway,"Phosphatidylinositol 4,5-bisphosphate",ovarian cancer
+Pathways Afected in Adenoid Cystic Carcinoma,BRCA1,breast adenoid cystic carcinoma
+Lung fibrsis,PLAU,pulmonary fibrosis
+Nqvel intracellular omponents of RIG-I-like receptor (RLR) pathway,DDX58,viral infectious disease
diff --git a/examples/semtab2020_novartis/tables/MT7GAOG6.json b/examples/semtab2020_novartis/tables/MT7GAOG6.json
new file mode 100644
index 0000000..5496b60
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/MT7GAOG6.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["Corticotropin-releasing hormone signalingpathway","Fluoopyrimidine Activity","TGF-B Signaling in Thyroid Cells for Epithelial-Mesenchymal Tansition","MAPK and NFkB Sinalling Pathways Inhibited by Yersinia YopJ","Association Between Physico-Chemical Features and Toxicity Associcted Pathways","Toll-ike Receptor Signaling related to MyD88","GHB etabolic pathway","Psion disease pathway","miRNA regulation of prostate cancer signalig pathways","NO/cGMP/KG mediated Neuroprotection","Endotheoin Pathways","Hypothesized Pathways in Pathogenesis of Cardiovaucular Disease","Tgif disruption of Sjh signaling","Ovarian Infestility enes","Dell-type Dependept Selectivity of CCK2R Signaling","Initiation of tranvcription and translation elongation at the HIV-1 LTR","RAC1/PAK1/p380MMP2 Pathway","Pathways Afected in Adenoid Cystic Carcinoma","Lung fibrsis","Nqvel intracellular omponents of RIG-I-like receptor (RLR) pathway"]},{"index":1,"name":"col1","values":["AKT1","SLC22A7","SNAI1","RAF1","WNT11","TIRAP","γ-aminobutyric acid","PDIA3","MDM2","L-Arginine","ADRA1A","MAPK14","SHH","PRLR","γ-aminobutyric acid","prostratin","Phosphatidylinositol 4,5-bisphosphate","BRCA1","PLAU","DDX58"]},{"index":2,"name":"col2","values":["Alzheimer's disease","cancer","thyroid cancer","bubonic plague","pancreatic cancer","cancer","succinic semialdehyde dehydrogenase deficiency","transmissible spongiform encephalopathy","prostate cancer","dementia","hypertension","cardiovascular disease","genetic disease","infertility","pertussis","human immunodeficiency virus infectious disease","ovarian cancer","breast adenoid cystic carcinoma","pulmonary fibrosis","viral infectious disease"]}],"metadata":{"table_id":"MT7GAOG6","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":48,"url":"http://www.wikidata.org/entity/Q30225489","qnode_id":"Q30225489"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q17816452","qnode_id":"Q17816452"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q11081","qnode_id":"Q11081"}]],[[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q29883401","qnode_id":"Q29883401"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q18035898","qnode_id":"Q18035898"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q12078","qnode_id":"Q12078"}]],[[{"start":0,"end":69,"url":"http://www.wikidata.org/entity/Q30225429","qnode_id":"Q30225429"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q7391779","qnode_id":"Q7391779"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q826522","qnode_id":"Q826522"}]],[[{"start":0,"end":60,"url":"http://www.wikidata.org/entity/Q30225424","qnode_id":"Q30225424"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14903573","qnode_id":"Q14903573"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q217519","qnode_id":"Q217519"}]],[[{"start":0,"end":78,"url":"http://www.wikidata.org/entity/Q30225419","qnode_id":"Q30225419"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18032368","qnode_id":"Q18032368"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q212961","qnode_id":"Q212961"}]],[[{"start":0,"end":44,"url":"http://www.wikidata.org/entity/Q30225428","qnode_id":"Q30225428"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q282418","qnode_id":"Q282418"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q12078","qnode_id":"Q12078"}]],[[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q44017772","qnode_id":"Q44017772"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q210021","qnode_id":"Q210021"}],[{"start":0,"end":46,"url":"http://www.wikidata.org/entity/Q2823333","qnode_id":"Q2823333"}]],[[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q44017827","qnode_id":"Q44017827"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q7118820","qnode_id":"Q7118820"}],[{"start":0,"end":39,"url":"http://www.wikidata.org/entity/Q703961","qnode_id":"Q703961"}]],[[{"start":0,"end":53,"url":"http://www.wikidata.org/entity/Q44017754","qnode_id":"Q44017754"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18028978","qnode_id":"Q18028978"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q181257","qnode_id":"Q181257"}]],[[{"start":0,"end":35,"url":"http://www.wikidata.org/entity/Q44015288","qnode_id":"Q44015288"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q173670","qnode_id":"Q173670"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q83030","qnode_id":"Q83030"}]],[[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q30225477","qnode_id":"Q30225477"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q17710494","qnode_id":"Q17710494"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q41861","qnode_id":"Q41861"}]],[[{"start":0,"end":63,"url":"http://www.wikidata.org/entity/Q30225413","qnode_id":"Q30225413"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q15326910","qnode_id":"Q15326910"}],[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q389735","qnode_id":"Q389735"}]],[[{"start":0,"end":32,"url":"http://www.wikidata.org/entity/Q30225414","qnode_id":"Q30225414"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q14860072","qnode_id":"Q14860072"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q200779","qnode_id":"Q200779"}]],[[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q30230829","qnode_id":"Q30230829"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q2738985","qnode_id":"Q2738985"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q179399","qnode_id":"Q179399"}]],[[{"start":0,"end":50,"url":"http://www.wikidata.org/entity/Q30225416","qnode_id":"Q30225416"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q210021","qnode_id":"Q210021"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q134859","qnode_id":"Q134859"}]],[[{"start":0,"end":71,"url":"http://www.wikidata.org/entity/Q30225452","qnode_id":"Q30225452"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q3270742","qnode_id":"Q3270742"}],[{"start":0,"end":47,"url":"http://www.wikidata.org/entity/Q18556697","qnode_id":"Q18556697"}]],[[{"start":0,"end":26,"url":"http://www.wikidata.org/entity/Q30225450","qnode_id":"Q30225450"}],[{"start":0,"end":37,"url":"http://www.wikidata.org/entity/Q3083814","qnode_id":"Q3083814"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q172341","qnode_id":"Q172341"}]],[[{"start":0,"end":44,"url":"http://www.wikidata.org/entity/Q30225405","qnode_id":"Q30225405"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q227339","qnode_id":"Q227339"}],[{"start":0,"end":31,"url":"http://www.wikidata.org/entity/Q18556521","qnode_id":"Q18556521"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q30225397","qnode_id":"Q30225397"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q417169","qnode_id":"Q417169"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q32446","qnode_id":"Q32446"}]],[[{"start":0,"end":66,"url":"http://www.wikidata.org/entity/Q30225431","qnode_id":"Q30225431"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q907024","qnode_id":"Q907024"}],[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q1928978","qnode_id":"Q1928978"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q4915012:0","abs_uri":"http://www.wikidata.org/entity/Q4915012","rel_uri":"wd:Q4915012","approximation":false,"readable_label":"biological pathway (Q4915012)"},{"id":"col-2","col_index":2,"label":"col2"},{"id":"http://www.wikidata.org/entity/Q12136:0","abs_uri":"http://www.wikidata.org/entity/Q12136","rel_uri":"wd:Q12136","approximation":false,"readable_label":"disease (Q12136)"},{"id":"col-1","col_index":1,"label":"col1"}],"edges":[{"source":"http://www.wikidata.org/entity/Q4915012:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q4915012:0","target":"col-1","abs_uri":"http://www.wikidata.org/prop/P527","rel_uri":"p:P527","approximation":false,"readable_label":"has part (P527)","id":2},{"source":"http://www.wikidata.org/entity/Q4915012:0","target":"http://www.wikidata.org/entity/Q12136:0","abs_uri":"http://www.wikidata.org/prop/P1050","rel_uri":"p:P1050","approximation":false,"readable_label":"medical condition (P1050)","id":3},{"source":"http://www.wikidata.org/entity/Q12136:0","target":"col-2","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":4}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/MT7GAOG6.links.tsv b/examples/semtab2020_novartis/tables/MT7GAOG6.links.tsv
new file mode 100644
index 0000000..53f82a6
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/MT7GAOG6.links.tsv
@@ -0,0 +1,60 @@
+0 0 Q30225489
+0 1 Q17816452
+0 2 Q11081
+1 0 Q29883401
+1 1 Q18035898
+1 2 Q12078
+2 0 Q30225429
+2 1 Q7391779
+2 2 Q826522
+3 0 Q30225424
+3 1 Q14903573
+3 2 Q217519
+4 0 Q30225419
+4 1 Q18032368
+4 2 Q212961
+5 0 Q30225428
+5 1 Q282418
+5 2 Q12078
+6 0 Q44017772
+6 1 Q210021
+6 2 Q2823333
+7 0 Q44017827
+7 1 Q7118820
+7 2 Q703961
+8 0 Q44017754
+8 1 Q18028978
+8 2 Q181257
+9 0 Q44015288
+9 1 Q173670
+9 2 Q83030
+10 0 Q30225477
+10 1 Q17710494
+10 2 Q41861
+11 0 Q30225413
+11 1 Q15326910
+11 2 Q389735
+12 0 Q30225414
+12 1 Q14860072
+12 2 Q200779
+13 0 Q30230829
+13 1 Q2738985
+13 2 Q179399
+14 0 Q30225416
+14 1 Q210021
+14 2 Q134859
+15 0 Q30225452
+15 1 Q3270742
+15 2 Q18556697
+16 0 Q30225450
+16 1 Q3083814
+16 2 Q172341
+17 0 Q30225405
+17 1 Q227339
+17 2 Q18556521
+18 0 Q30225397
+18 1 Q417169
+18 2 Q32446
+19 0 Q30225431
+19 1 Q907024
+19 2 Q1928978
diff --git a/examples/semtab2020_novartis/tables/N6HGUVIJ.csv b/examples/semtab2020_novartis/tables/N6HGUVIJ.csv
new file mode 100644
index 0000000..16644e0
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/N6HGUVIJ.csv
@@ -0,0 +1,21 @@
+col0,col1,col2,col3
+ADORA3,Adora3,obesity,protein-coding gene
+AATK,Aatk,obesity,protein-coding gene
+SRL,Srl,obesity,protein-coding gene
+SLC30A8,Slc30a8,obesity,protein-coding gene
+UGT2B7,Ugt2b36,obesity,protein-coding gene
+ADCYAP1,Adcyap1,obesity,protein-coding gene
+TWIST1,Twist1,obesity,protein-coding gene
+DCC,Dcc,obesity,protein-coding gene
+PCSK2,Pcsk2,obesity,protein-coding gene
+ESR1,Esr1,obesity,protein-coding gene
+HMOX1,Hmox1,obesity,protein-coding gene
+TBXAS1,Tbxas1,obesity,protein-coding gene
+UBE3A,Ube3a,obesity,protein-coding gene
+SLC4A1,Slc4a1,obesity,protein-coding gene
+SERPING1,Serping1,obesity,protein-coding gene
+CYP2E1,Cyp2e1,obesity,protein-coding gene
+BMP2,dpp,obesity,protein-coding gene
+ABO,Abo,malaria,protein-coding gene
+BDNF,Bdnf,obesity,neurotrophin
+CDH4,cdh4,obesity,protein-coding gene
diff --git a/examples/semtab2020_novartis/tables/N6HGUVIJ.json b/examples/semtab2020_novartis/tables/N6HGUVIJ.json
new file mode 100644
index 0000000..99fccb1
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/N6HGUVIJ.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["ADORA3","AATK","SRL","SLC30A8","UGT2B7","ADCYAP1","TWIST1","DCC","PCSK2","ESR1","HMOX1","TBXAS1","UBE3A","SLC4A1","SERPING1","CYP2E1","BMP2","ABO","BDNF","CDH4"]},{"index":1,"name":"col1","values":["Adora3","Aatk","Srl","Slc30a8","Ugt2b36","Adcyap1","Twist1","Dcc","Pcsk2","Esr1","Hmox1","Tbxas1","Ube3a","Slc4a1","Serping1","Cyp2e1","dpp","Abo","Bdnf","cdh4"]},{"index":2,"name":"col2","values":["obesity","obesity","obesity","obesity","obesity","obesity","obesity","obesity","obesity","obesity","obesity","obesity","obesity","obesity","obesity","obesity","obesity","malaria","obesity","obesity"]},{"index":3,"name":"col3","values":["protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","protein-coding gene","neurotrophin","protein-coding gene"]}],"metadata":{"table_id":"N6HGUVIJ","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q4682275","qnode_id":"Q4682275"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q18247611","qnode_id":"Q18247611"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q4649948","qnode_id":"Q4649948"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18204234","qnode_id":"Q18204234"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q14908063","qnode_id":"Q14908063"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q14908064","qnode_id":"Q14908064"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q14907990","qnode_id":"Q14907990"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q14907991","qnode_id":"Q14907991"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q14907739","qnode_id":"Q14907739"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q14907740","qnode_id":"Q14907740"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q14907479","qnode_id":"Q14907479"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q14907480","qnode_id":"Q14907480"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q14906427","qnode_id":"Q14906427"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q14906434","qnode_id":"Q14906434"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q14905674","qnode_id":"Q14905674"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q14905675","qnode_id":"Q14905675"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14902484","qnode_id":"Q14902484"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14902485","qnode_id":"Q14902485"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14902310","qnode_id":"Q14902310"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14902311","qnode_id":"Q14902311"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14882921","qnode_id":"Q14882921"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14882922","qnode_id":"Q14882922"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q14881694","qnode_id":"Q14881694"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q14881695","qnode_id":"Q14881695"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14878336","qnode_id":"Q14878336"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14878337","qnode_id":"Q14878337"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q14877701","qnode_id":"Q14877701"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q14877708","qnode_id":"Q14877708"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q14865077","qnode_id":"Q14865077"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q14865078","qnode_id":"Q14865078"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q14864434","qnode_id":"Q14864434"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q14864435","qnode_id":"Q14864435"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14903153","qnode_id":"Q14903153"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q976767","qnode_id":"Q976767"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q14839826","qnode_id":"Q14839826"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q14839892","qnode_id":"Q14839892"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12156","qnode_id":"Q12156"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14863330","qnode_id":"Q14863330"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14863331","qnode_id":"Q14863331"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q420457","qnode_id":"Q420457"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q17859830","qnode_id":"Q17859830"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q29782671","qnode_id":"Q29782671"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q12174","qnode_id":"Q12174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/entity/Q7187","rel_uri":"wd:Q7187","approximation":false,"readable_label":"gene (Q7187)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"http://www.wikidata.org/entity/Q7187:1","abs_uri":"http://www.wikidata.org/entity/Q7187","rel_uri":"wd:Q7187","approximation":false,"readable_label":"gene (Q7187)"},{"id":"col-2","col_index":2,"label":"col2"},{"id":"http://www.wikidata.org/entity/Q12136:0","abs_uri":"http://www.wikidata.org/entity/Q12136","rel_uri":"wd:Q12136","approximation":false,"readable_label":"disease (Q12136)"},{"id":"col-3","col_index":3,"label":"col3"}],"edges":[{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"http://www.wikidata.org/entity/Q7187:1","abs_uri":"http://www.wikidata.org/prop/P684","rel_uri":"p:P684","approximation":false,"readable_label":"ortholog (P684)","id":2},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"http://www.wikidata.org/entity/Q12136:0","abs_uri":"http://www.wikidata.org/prop/P2293","rel_uri":"p:P2293","approximation":false,"readable_label":"genetic association (P2293)","id":3},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-3","abs_uri":"http://www.wikidata.org/prop/P279","rel_uri":"p:P279","approximation":false,"readable_label":"subclass of (P279)","id":4},{"source":"http://www.wikidata.org/entity/Q7187:1","target":"col-1","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":5},{"source":"http://www.wikidata.org/entity/Q12136:0","target":"col-2","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":6}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/N6HGUVIJ.links.tsv b/examples/semtab2020_novartis/tables/N6HGUVIJ.links.tsv
new file mode 100644
index 0000000..5cf0806
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/N6HGUVIJ.links.tsv
@@ -0,0 +1,80 @@
+0 0 Q4682275
+0 1 Q18247611
+0 2 Q12174
+0 3 Q20747295
+1 0 Q4649948
+1 1 Q18204234
+1 2 Q12174
+1 3 Q20747295
+2 0 Q14908063
+2 1 Q14908064
+2 2 Q12174
+2 3 Q20747295
+3 0 Q14907990
+3 1 Q14907991
+3 2 Q12174
+3 3 Q20747295
+4 0 Q14907739
+4 1 Q14907740
+4 2 Q12174
+4 3 Q20747295
+5 0 Q14907479
+5 1 Q14907480
+5 2 Q12174
+5 3 Q20747295
+6 0 Q14906427
+6 1 Q14906434
+6 2 Q12174
+6 3 Q20747295
+7 0 Q14905674
+7 1 Q14905675
+7 2 Q12174
+7 3 Q20747295
+8 0 Q14902484
+8 1 Q14902485
+8 2 Q12174
+8 3 Q20747295
+9 0 Q14902310
+9 1 Q14902311
+9 2 Q12174
+9 3 Q20747295
+10 0 Q14882921
+10 1 Q14882922
+10 2 Q12174
+10 3 Q20747295
+11 0 Q14881694
+11 1 Q14881695
+11 2 Q12174
+11 3 Q20747295
+12 0 Q14878336
+12 1 Q14878337
+12 2 Q12174
+12 3 Q20747295
+13 0 Q14877701
+13 1 Q14877708
+13 2 Q12174
+13 3 Q20747295
+14 0 Q14865077
+14 1 Q14865078
+14 2 Q12174
+14 3 Q20747295
+15 0 Q14864434
+15 1 Q14864435
+15 2 Q12174
+15 3 Q20747295
+16 0 Q14903153
+16 1 Q976767
+16 2 Q12174
+16 3 Q20747295
+17 0 Q14839826
+17 1 Q14839892
+17 2 Q12156
+17 3 Q20747295
+18 0 Q14863330
+18 1 Q14863331
+18 2 Q12174
+18 3 Q420457
+19 0 Q17859830
+19 1 Q29782671
+19 2 Q12174
+19 3 Q20747295
diff --git a/examples/semtab2020_novartis/tables/O54MRTYW.csv b/examples/semtab2020_novartis/tables/O54MRTYW.csv
new file mode 100644
index 0000000..88b0018
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/O54MRTYW.csv
@@ -0,0 +1,21 @@
+col0,col1,col2
+coccidiosis,infectiology,toltrazuril
+relapsing fever borreliosis,infectiology,doxycycline
+progressive multifocal leukoencephalopathy,neurology,mirtazapine
+parasitic helminthiasis infectious disease,infectiology,albendazole
+molluscum contagiosum,dermatology,cantharidin
+pyelonephritis,urology,ciprofloxacin
+sporotrichosis,infectiology,natamycin
+blastomycosis,infectiology,natamycin
+campylobacteriosis,infectiology,erythromycin
+Kaposi's sarcoma,oncology,dactinomycin
+otitis externa,otolaryngology,acetic acid
+bacterial infectious disease,infectiology,bismuth
+hypodermyasis,infectiology,clorsulon
+lymph node tuberculosis,infectiology,rifampicin
+tuberculous meningitis,neurology,rifampicin
+borderline leprosy,infectiology,clofazimine
+mansonelliasis,infectiology,ivermectin
+fungal meningitis,neurology,amphotericin B
+necatoriasis,infectiology,albendazole
+tinea pedis,infectiology,sodium hypochlorite
diff --git a/examples/semtab2020_novartis/tables/O54MRTYW.json b/examples/semtab2020_novartis/tables/O54MRTYW.json
new file mode 100644
index 0000000..3ee6366
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/O54MRTYW.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["coccidiosis","relapsing fever borreliosis","progressive multifocal leukoencephalopathy","parasitic helminthiasis infectious disease","molluscum contagiosum","pyelonephritis","sporotrichosis","blastomycosis","campylobacteriosis","Kaposi's sarcoma","otitis externa","bacterial infectious disease","hypodermyasis","lymph node tuberculosis","tuberculous meningitis","borderline leprosy","mansonelliasis","fungal meningitis","necatoriasis","tinea pedis"]},{"index":1,"name":"col1","values":["infectiology","infectiology","neurology","infectiology","dermatology","urology","infectiology","infectiology","infectiology","oncology","otolaryngology","infectiology","infectiology","infectiology","neurology","infectiology","infectiology","neurology","infectiology","infectiology"]},{"index":2,"name":"col2","values":["toltrazuril","doxycycline","mirtazapine","albendazole","cantharidin","ciprofloxacin","natamycin","natamycin","erythromycin","dactinomycin","acetic acid","bismuth","clorsulon","rifampicin","rifampicin","clofazimine","ivermectin","amphotericin B","albendazole","sodium hypochlorite"]}],"metadata":{"table_id":"O54MRTYW","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q487837","qnode_id":"Q487837"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q1470932","qnode_id":"Q1470932"}]],[[{"start":0,"end":27,"url":"http://www.wikidata.org/entity/Q690032","qnode_id":"Q690032"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q422442","qnode_id":"Q422442"}]],[[{"start":0,"end":42,"url":"http://www.wikidata.org/entity/Q704930","qnode_id":"Q704930"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q83042","qnode_id":"Q83042"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q421930","qnode_id":"Q421930"}]],[[{"start":0,"end":42,"url":"http://www.wikidata.org/entity/Q578994","qnode_id":"Q578994"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q411629","qnode_id":"Q411629"}]],[[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q659584","qnode_id":"Q659584"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q171171","qnode_id":"Q171171"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q410884","qnode_id":"Q410884"}]],[[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q506652","qnode_id":"Q506652"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q105650","qnode_id":"Q105650"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q256602","qnode_id":"Q256602"}]],[[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q767327","qnode_id":"Q767327"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q248466","qnode_id":"Q248466"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q627368","qnode_id":"Q627368"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q248466","qnode_id":"Q248466"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q683861","qnode_id":"Q683861"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q213511","qnode_id":"Q213511"}]],[[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q725345","qnode_id":"Q725345"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q162555","qnode_id":"Q162555"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q186127","qnode_id":"Q186127"}]],[[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q680873","qnode_id":"Q680873"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q189553","qnode_id":"Q189553"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q47512","qnode_id":"Q47512"}]],[[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q727028","qnode_id":"Q727028"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q942","qnode_id":"Q942"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q4138775","qnode_id":"Q4138775"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q2743167","qnode_id":"Q2743167"}]],[[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q4678337","qnode_id":"Q4678337"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q422652","qnode_id":"Q422652"}]],[[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q3854687","qnode_id":"Q3854687"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q83042","qnode_id":"Q83042"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q422652","qnode_id":"Q422652"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q4944687","qnode_id":"Q4944687"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q418611","qnode_id":"Q418611"}]],[[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q4280605","qnode_id":"Q4280605"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q415178","qnode_id":"Q415178"}]],[[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q5509169","qnode_id":"Q5509169"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q83042","qnode_id":"Q83042"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q412223","qnode_id":"Q412223"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q4118581","qnode_id":"Q4118581"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q411629","qnode_id":"Q411629"}]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q5609817","qnode_id":"Q5609817"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q407204","qnode_id":"Q407204"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q18123741:0","abs_uri":"http://www.wikidata.org/entity/Q18123741","rel_uri":"wd:Q18123741","approximation":false,"readable_label":"infectious disease (Q18123741)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"http://www.wikidata.org/entity/Q930752:0","abs_uri":"http://www.wikidata.org/entity/Q930752","rel_uri":"wd:Q930752","approximation":false,"readable_label":"medical specialty (Q930752)"},{"id":"col-2","col_index":2,"label":"col2"}],"edges":[{"source":"http://www.wikidata.org/entity/Q18123741:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q18123741:0","target":"http://www.wikidata.org/entity/Q930752:0","abs_uri":"http://www.wikidata.org/prop/P1995","rel_uri":"p:P1995","approximation":false,"readable_label":"health specialty (P1995)","id":2},{"source":"http://www.wikidata.org/entity/Q18123741:0","target":"col-2","abs_uri":"http://www.wikidata.org/prop/P2176","rel_uri":"p:P2176","approximation":false,"readable_label":"drug used for treatment (P2176)","id":3},{"source":"http://www.wikidata.org/entity/Q930752:0","target":"col-1","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":4}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/O54MRTYW.links.tsv b/examples/semtab2020_novartis/tables/O54MRTYW.links.tsv
new file mode 100644
index 0000000..3e4ce58
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/O54MRTYW.links.tsv
@@ -0,0 +1,60 @@
+0 0 Q487837
+0 1 Q788926
+0 2 Q1470932
+1 0 Q690032
+1 1 Q788926
+1 2 Q422442
+2 0 Q704930
+2 1 Q83042
+2 2 Q421930
+3 0 Q578994
+3 1 Q788926
+3 2 Q411629
+4 0 Q659584
+4 1 Q171171
+4 2 Q410884
+5 0 Q506652
+5 1 Q105650
+5 2 Q256602
+6 0 Q767327
+6 1 Q788926
+6 2 Q248466
+7 0 Q627368
+7 1 Q788926
+7 2 Q248466
+8 0 Q683861
+8 1 Q788926
+8 2 Q213511
+9 0 Q725345
+9 1 Q162555
+9 2 Q186127
+10 0 Q680873
+10 1 Q189553
+10 2 Q47512
+11 0 Q727028
+11 1 Q788926
+11 2 Q942
+12 0 Q4138775
+12 1 Q788926
+12 2 Q2743167
+13 0 Q4678337
+13 1 Q788926
+13 2 Q422652
+14 0 Q3854687
+14 1 Q83042
+14 2 Q422652
+15 0 Q4944687
+15 1 Q788926
+15 2 Q418611
+16 0 Q4280605
+16 1 Q788926
+16 2 Q415178
+17 0 Q5509169
+17 1 Q83042
+17 2 Q412223
+18 0 Q4118581
+18 1 Q788926
+18 2 Q411629
+19 0 Q5609817
+19 1 Q788926
+19 2 Q407204
diff --git a/examples/semtab2020_novartis/tables/OF3DIONL.csv b/examples/semtab2020_novartis/tables/OF3DIONL.csv
new file mode 100644
index 0000000..f86d696
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/OF3DIONL.csv
@@ -0,0 +1,21 @@
+col0,col1,col2
+nitroglycerin,Great Soviet Encyclopedia,hydrogen
+(R)-amphetamine,Opium Law,carbon
+ketobemidone,Opium Law,carbon
+etryptamine,Opium Law,carbon
+diethylpropion,Opium Law,carbon
+corticotropin,Armenian Soviet Encyclopedia,Corticotropin-like intermediate peptide
+calcium chloride,Armenian Soviet Encyclopedia,chlorine
+potassium chloride,Armenian Soviet Encyclopedia,chlorine
+magnesium oxide,Armenian Soviet Encyclopedia,oxygen
+creatine,Armenian Soviet Encyclopedia,carbon
+pyruvic acid,Armenian Soviet Encyclopedia,carbon
+salicylic acid,Armenian Soviet Encyclopedia,carbon
+heparin,Armenian Soviet Encyclopedia,carbon
+adenosine,Armenian Soviet Encyclopedia,carbon
+phenolphthalein,Armenian Soviet Encyclopedia,carbon
+L-Threonine,Armenian Soviet Encyclopedia,carbon
+L-Cysteine,Armenian Soviet Encyclopedia,carbon
+guanidine,Armenian Soviet Encyclopedia,carbon
+L-Serin,Armenian Soviet Encyclopedia,carbon
+L-Tryptophan,Armenian Soviet Encyclopedia,carbon
diff --git a/examples/semtab2020_novartis/tables/OF3DIONL.json b/examples/semtab2020_novartis/tables/OF3DIONL.json
new file mode 100644
index 0000000..375c80d
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/OF3DIONL.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["nitroglycerin","(R)-amphetamine","ketobemidone","etryptamine","diethylpropion","corticotropin","calcium chloride","potassium chloride","magnesium oxide","creatine","pyruvic acid","salicylic acid","heparin","adenosine","phenolphthalein","L-Threonine","L-Cysteine","guanidine","L-Serin","L-Tryptophan"]},{"index":1,"name":"col1","values":["Great Soviet Encyclopedia","Opium Law","Opium Law","Opium Law","Opium Law","Armenian Soviet Encyclopedia","Armenian Soviet Encyclopedia","Armenian Soviet Encyclopedia","Armenian Soviet Encyclopedia","Armenian Soviet Encyclopedia","Armenian Soviet Encyclopedia","Armenian Soviet Encyclopedia","Armenian Soviet Encyclopedia","Armenian Soviet Encyclopedia","Armenian Soviet Encyclopedia","Armenian Soviet Encyclopedia","Armenian Soviet Encyclopedia","Armenian Soviet Encyclopedia","Armenian Soviet Encyclopedia","Armenian Soviet Encyclopedia"]},{"index":2,"name":"col2","values":["hydrogen","carbon","carbon","carbon","carbon","Corticotropin-like intermediate peptide","chlorine","chlorine","oxygen","carbon","carbon","carbon","carbon","carbon","carbon","carbon","carbon","carbon","carbon","carbon"]}],"metadata":{"table_id":"OF3DIONL","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q162867","qnode_id":"Q162867"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q234535","qnode_id":"Q234535"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q556","qnode_id":"Q556"}]],[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q2506823","qnode_id":"Q2506823"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q316572","qnode_id":"Q316572"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q2471714","qnode_id":"Q2471714"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q316572","qnode_id":"Q316572"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q2394183","qnode_id":"Q2394183"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q316572","qnode_id":"Q316572"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q2356505","qnode_id":"Q2356505"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q316572","qnode_id":"Q316572"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q185690","qnode_id":"Q185690"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":39,"url":"http://www.wikidata.org/entity/Q1753256","qnode_id":"Q1753256"}]],[[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q208451","qnode_id":"Q208451"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q688","qnode_id":"Q688"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q184630","qnode_id":"Q184630"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q688","qnode_id":"Q688"}]],[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q214769","qnode_id":"Q214769"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q629","qnode_id":"Q629"}]],[[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q223600","qnode_id":"Q223600"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q213580","qnode_id":"Q213580"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q193572","qnode_id":"Q193572"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q190016","qnode_id":"Q190016"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q190012","qnode_id":"Q190012"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q187921","qnode_id":"Q187921"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q186521","qnode_id":"Q186521"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q186474","qnode_id":"Q186474"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q183309","qnode_id":"Q183309"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q183290","qnode_id":"Q183290"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q181003","qnode_id":"Q181003"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q2657718","qnode_id":"Q2657718"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q623","qnode_id":"Q623"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q12140:0","abs_uri":"http://www.wikidata.org/entity/Q12140","rel_uri":"wd:Q12140","approximation":false,"readable_label":"medication (Q12140)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"col-2","col_index":2,"label":"col2"}],"edges":[{"source":"http://www.wikidata.org/entity/Q12140:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q12140:0","target":"col-1","abs_uri":"http://www.wikidata.org/prop/P1343","rel_uri":"p:P1343","approximation":false,"readable_label":"described by source (P1343)","id":2},{"source":"http://www.wikidata.org/entity/Q12140:0","target":"col-2","abs_uri":"http://www.wikidata.org/prop/P527","rel_uri":"p:P527","approximation":false,"readable_label":"has part (P527)","id":3}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/OF3DIONL.links.tsv b/examples/semtab2020_novartis/tables/OF3DIONL.links.tsv
new file mode 100644
index 0000000..f7aff46
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/OF3DIONL.links.tsv
@@ -0,0 +1,60 @@
+0 0 Q162867
+0 1 Q234535
+0 2 Q556
+1 0 Q2506823
+1 1 Q316572
+1 2 Q623
+2 0 Q2471714
+2 1 Q316572
+2 2 Q623
+3 0 Q2394183
+3 1 Q316572
+3 2 Q623
+4 0 Q2356505
+4 1 Q316572
+4 2 Q623
+5 0 Q185690
+5 1 Q2657718
+5 2 Q1753256
+6 0 Q208451
+6 1 Q2657718
+6 2 Q688
+7 0 Q184630
+7 1 Q2657718
+7 2 Q688
+8 0 Q214769
+8 1 Q2657718
+8 2 Q629
+9 0 Q223600
+9 1 Q2657718
+9 2 Q623
+10 0 Q213580
+10 1 Q2657718
+10 2 Q623
+11 0 Q193572
+11 1 Q2657718
+11 2 Q623
+12 0 Q190016
+12 1 Q2657718
+12 2 Q623
+13 0 Q190012
+13 1 Q2657718
+13 2 Q623
+14 0 Q187921
+14 1 Q2657718
+14 2 Q623
+15 0 Q186521
+15 1 Q2657718
+15 2 Q623
+16 0 Q186474
+16 1 Q2657718
+16 2 Q623
+17 0 Q183309
+17 1 Q2657718
+17 2 Q623
+18 0 Q183290
+18 1 Q2657718
+18 2 Q623
+19 0 Q181003
+19 1 Q2657718
+19 2 Q623
diff --git a/examples/semtab2020_novartis/tables/OIVY0LAW.csv b/examples/semtab2020_novartis/tables/OIVY0LAW.csv
new file mode 100644
index 0000000..0c31b61
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/OIVY0LAW.csv
@@ -0,0 +1,21 @@
+col0,col1,col2
+herpes zoster,dermatology,aciclovir
+toxascariasis,infectiology,imidacloprid
+Plasmodium vivax malaria,infectiology,(RS)-primaquine
+fusariosis,dermatology,Voriconazole
+pulmonary tuberculosis,infectiology,linezolid
+bacterial conjunctivitis,ophthalmology,lomefloxacin
+Plasmodium falciparum malaria,infectiology,quinine
+gram-negative bacterial infection,infectiology,cefixime
+miliary tuberculosis,infectiology,rifampicin
+impetigo,dermatology,mupirocin
+hepatitis B,infectiology,tenofovir
+pneumonia,pulmonology,antibiotic
+subacute sclerosing panencephalitis,infectiology,acedoben sodium
+subacute bacterial endocarditis,cardiology,penicillin G procaine hydrate
+strongyloidiasis,infectiology,ivermectin
+trichuriasis,infectiology,albendazole
+enterobiasis,infectiology,albendazole
+vasomotor rhinitis,pulmonology,L-scopolamine
+cutaneous leishmaniasis,infectiology,meglumine
+visceral leishmaniasis,infectiology,meglumine
diff --git a/examples/semtab2020_novartis/tables/OIVY0LAW.json b/examples/semtab2020_novartis/tables/OIVY0LAW.json
new file mode 100644
index 0000000..24af33a
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/OIVY0LAW.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["herpes zoster","toxascariasis","Plasmodium vivax malaria","fusariosis","pulmonary tuberculosis","bacterial conjunctivitis","Plasmodium falciparum malaria","gram-negative bacterial infection","miliary tuberculosis","impetigo","hepatitis B","pneumonia","subacute sclerosing panencephalitis","subacute bacterial endocarditis","strongyloidiasis","trichuriasis","enterobiasis","vasomotor rhinitis","cutaneous leishmaniasis","visceral leishmaniasis"]},{"index":1,"name":"col1","values":["dermatology","infectiology","infectiology","dermatology","infectiology","ophthalmology","infectiology","infectiology","infectiology","dermatology","infectiology","pulmonology","infectiology","cardiology","infectiology","infectiology","infectiology","pulmonology","infectiology","infectiology"]},{"index":2,"name":"col2","values":["aciclovir","imidacloprid","(RS)-primaquine","Voriconazole","linezolid","lomefloxacin","quinine","cefixime","rifampicin","mupirocin","tenofovir","antibiotic","acedoben sodium","penicillin G procaine hydrate","ivermectin","albendazole","albendazole","L-scopolamine","meglumine","meglumine"]}],"metadata":{"table_id":"OIVY0LAW","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q182155","qnode_id":"Q182155"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q171171","qnode_id":"Q171171"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q147101","qnode_id":"Q147101"}]],[[],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q420098","qnode_id":"Q420098"}]],[[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q18554412","qnode_id":"Q18554412"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q419834","qnode_id":"Q419834"}]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q18928476","qnode_id":"Q18928476"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q171171","qnode_id":"Q171171"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q412236","qnode_id":"Q412236"}]],[[],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q411377","qnode_id":"Q411377"}]],[[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q18558195","qnode_id":"Q18558195"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q161437","qnode_id":"Q161437"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q203618","qnode_id":"Q203618"}]],[[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q18554672","qnode_id":"Q18554672"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q189522","qnode_id":"Q189522"}]],[[{"start":0,"end":33,"url":"http://www.wikidata.org/entity/Q19597369","qnode_id":"Q19597369"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q163901","qnode_id":"Q163901"}]],[[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q17583","qnode_id":"Q17583"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q422652","qnode_id":"Q422652"}]],[[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q28971","qnode_id":"Q28971"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q171171","qnode_id":"Q171171"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q413578","qnode_id":"Q413578"}]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q6853","qnode_id":"Q6853"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q155954","qnode_id":"Q155954"}]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q12192","qnode_id":"Q12192"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q203337","qnode_id":"Q203337"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q12187","qnode_id":"Q12187"}]],[[{"start":0,"end":35,"url":"http://www.wikidata.org/entity/Q2475919","qnode_id":"Q2475919"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q27280825","qnode_id":"Q27280825"}]],[[{"start":0,"end":31,"url":"http://www.wikidata.org/entity/Q2271749","qnode_id":"Q2271749"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q10379","qnode_id":"Q10379"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q13026783","qnode_id":"Q13026783"}]],[[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q2360849","qnode_id":"Q2360849"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q415178","qnode_id":"Q415178"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q2264130","qnode_id":"Q2264130"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q411629","qnode_id":"Q411629"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q1992236","qnode_id":"Q1992236"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q411629","qnode_id":"Q411629"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q2403156","qnode_id":"Q2403156"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q203337","qnode_id":"Q203337"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q337188","qnode_id":"Q337188"}]],[[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q2590966","qnode_id":"Q2590966"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q288875","qnode_id":"Q288875"}]],[[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q2046113","qnode_id":"Q2046113"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q288875","qnode_id":"Q288875"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q18123741:0","abs_uri":"http://www.wikidata.org/entity/Q18123741","rel_uri":"wd:Q18123741","approximation":false,"readable_label":"infectious disease (Q18123741)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"http://www.wikidata.org/entity/Q930752:0","abs_uri":"http://www.wikidata.org/entity/Q930752","rel_uri":"wd:Q930752","approximation":false,"readable_label":"medical specialty (Q930752)"},{"id":"col-2","col_index":2,"label":"col2"}],"edges":[{"source":"http://www.wikidata.org/entity/Q18123741:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q18123741:0","target":"http://www.wikidata.org/entity/Q930752:0","abs_uri":"http://www.wikidata.org/prop/P1995","rel_uri":"p:P1995","approximation":false,"readable_label":"health specialty (P1995)","id":2},{"source":"http://www.wikidata.org/entity/Q18123741:0","target":"col-2","abs_uri":"http://www.wikidata.org/prop/P2176","rel_uri":"p:P2176","approximation":false,"readable_label":"drug used for treatment (P2176)","id":3},{"source":"http://www.wikidata.org/entity/Q930752:0","target":"col-1","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":4}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/OIVY0LAW.links.tsv b/examples/semtab2020_novartis/tables/OIVY0LAW.links.tsv
new file mode 100644
index 0000000..60e23e6
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/OIVY0LAW.links.tsv
@@ -0,0 +1,58 @@
+0 0 Q182155
+0 1 Q171171
+0 2 Q147101
+1 1 Q788926
+1 2 Q420098
+2 0 Q18554412
+2 1 Q788926
+2 2 Q419834
+3 0 Q18928476
+3 1 Q171171
+3 2 Q412236
+4 1 Q788926
+4 2 Q411377
+5 0 Q18558195
+5 1 Q161437
+5 2 Q203618
+6 0 Q18554672
+6 1 Q788926
+6 2 Q189522
+7 0 Q19597369
+7 1 Q788926
+7 2 Q163901
+8 0 Q17583
+8 1 Q788926
+8 2 Q422652
+9 0 Q28971
+9 1 Q171171
+9 2 Q413578
+10 0 Q6853
+10 1 Q788926
+10 2 Q155954
+11 0 Q12192
+11 1 Q203337
+11 2 Q12187
+12 0 Q2475919
+12 1 Q788926
+12 2 Q27280825
+13 0 Q2271749
+13 1 Q10379
+13 2 Q13026783
+14 0 Q2360849
+14 1 Q788926
+14 2 Q415178
+15 0 Q2264130
+15 1 Q788926
+15 2 Q411629
+16 0 Q1992236
+16 1 Q788926
+16 2 Q411629
+17 0 Q2403156
+17 1 Q203337
+17 2 Q337188
+18 0 Q2590966
+18 1 Q788926
+18 2 Q288875
+19 0 Q2046113
+19 1 Q788926
+19 2 Q288875
diff --git a/examples/semtab2020_novartis/tables/P7AG5V6C.csv b/examples/semtab2020_novartis/tables/P7AG5V6C.csv
new file mode 100644
index 0000000..7e35b22
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/P7AG5V6C.csv
@@ -0,0 +1,21 @@
+col0,col1,col2
+raloxfene,60.6,145.58
+dapsoqe,49.7,176.646
+(+-)-flurbiprfen,44.44,110.778
+vastatin,44.595,175.7
+hydrochlorthiazide,25.124999999999996,269.80699999999996
+arbimazole,15.044999999999998,123.504
+hydrocodon,14.97,197.406
+ealfapridine,20.099999999999998,159.318
+adenotjne,15.075,236.88
+ibuprofeq,30.269999999999996,76.461
+benzdamine,8.91,320.64
+predisone,9.93,234.63
+methimaole,10.04,145.3545
+methoxsaen,9.95,149.48
+warfarkn,7.5,320.39
+magqesium sulfate,7.0280000000000005,1140.4109999999998
+epiedrine,8.048,40.08
+magneuium oxide,6.93,5051.7119999999995
+sulfametha{ine,4.02,198.2
+salicylamid,2.9699999999999998,138.88
diff --git a/examples/semtab2020_novartis/tables/P7AG5V6C.json b/examples/semtab2020_novartis/tables/P7AG5V6C.json
new file mode 100644
index 0000000..4ed8e44
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/P7AG5V6C.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["raloxfene","dapsoqe","(+-)-flurbiprfen","vastatin","hydrochlorthiazide","arbimazole","hydrocodon","ealfapridine","adenotjne","ibuprofeq","benzdamine","predisone","methimaole","methoxsaen","warfarkn","magqesium sulfate","epiedrine","magneuium oxide","sulfametha{ine","salicylamid"]},{"index":1,"name":"col1","values":["60.6","49.7","44.44","44.595","25.124999999999996","15.044999999999998","14.97","20.099999999999998","15.075","30.269999999999996","8.91","9.93","10.04","9.95","7.5","7.0280000000000005","8.048","6.93","4.02","2.9699999999999998"]},{"index":2,"name":"col2","values":["145.58","176.646","110.778","175.7","269.80699999999996","123.504","197.406","159.318","236.88","76.461","320.64","234.63","145.3545","149.48","320.39","1140.4109999999998","40.08","5051.7119999999995","198.2","138.88"]}],"metadata":{"table_id":"P7AG5V6C","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q425223","qnode_id":"Q425223"}],[],[]],[[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q422226","qnode_id":"Q422226"}],[],[]],[[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q419890","qnode_id":"Q419890"}],[],[]],[[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q417740","qnode_id":"Q417740"}],[],[]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q423930","qnode_id":"Q423930"}],[],[]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q414013","qnode_id":"Q414013"}],[],[]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q411441","qnode_id":"Q411441"}],[],[]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q372539","qnode_id":"Q372539"}],[],[]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q190012","qnode_id":"Q190012"}],[],[]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q186969","qnode_id":"Q186969"}],[],[]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q793143","qnode_id":"Q793143"}],[],[]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q424972","qnode_id":"Q424972"}],[],[]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q419663","qnode_id":"Q419663"}],[],[]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q408570","qnode_id":"Q408570"}],[],[]],[[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q407431","qnode_id":"Q407431"}],[],[]],[[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q288266","qnode_id":"Q288266"}],[],[]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q219626","qnode_id":"Q219626"}],[],[]],[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q214769","qnode_id":"Q214769"}],[],[]],[[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q3976823","qnode_id":"Q3976823"}],[],[]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q2496906","qnode_id":"Q2496906"}],[],[]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q12140:0","abs_uri":"http://www.wikidata.org/entity/Q12140","rel_uri":"wd:Q12140","approximation":false,"readable_label":"medication (Q12140)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"col-2","col_index":2,"label":"col2"}],"edges":[{"source":"http://www.wikidata.org/entity/Q12140:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q12140:0","target":"col-1","abs_uri":"http://www.wikidata.org/prop/P4250","rel_uri":"p:P4250","approximation":false,"readable_label":"defined daily dose (P4250)","id":2},{"source":"http://www.wikidata.org/entity/Q12140:0","target":"col-2","abs_uri":"http://www.wikidata.org/prop/P2101","rel_uri":"p:P2101","approximation":false,"readable_label":"melting point (P2101)","id":3}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/P7AG5V6C.links.tsv b/examples/semtab2020_novartis/tables/P7AG5V6C.links.tsv
new file mode 100644
index 0000000..9dcd666
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/P7AG5V6C.links.tsv
@@ -0,0 +1,20 @@
+0 0 Q425223
+1 0 Q422226
+2 0 Q419890
+3 0 Q417740
+4 0 Q423930
+5 0 Q414013
+6 0 Q411441
+7 0 Q372539
+8 0 Q190012
+9 0 Q186969
+10 0 Q793143
+11 0 Q424972
+12 0 Q419663
+13 0 Q408570
+14 0 Q407431
+15 0 Q288266
+16 0 Q219626
+17 0 Q214769
+18 0 Q3976823
+19 0 Q2496906
diff --git a/examples/semtab2020_novartis/tables/PNRJ3I2M.csv b/examples/semtab2020_novartis/tables/PNRJ3I2M.csv
new file mode 100644
index 0000000..370fc6c
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/PNRJ3I2M.csv
@@ -0,0 +1,21 @@
+col0,col1,col2
+Klippel–Trénaunay–Weber syndrome,medical genetics,Parkes Weber syndrome
+jaw-winking syndrome,medical genetics,Marcus Gunn pupil
+Fraser syndrome,medical genetics,Frasier syndrome
+Lyme disease,infectiology,Chronic Lyme disease
+cercarial dermatitis,infectiology,Seabather's eruption
+contagious pustular dermatitis,infectiology,ORF
+bubonic plague,infectiology,Black Death
+microcephaly,medical genetics,sperm microcephaly
+Edwards syndrome,medical genetics,orofaciodigital syndrome VIII
+Athletic pubalgia,emergency medicine,osteitis pubis
+snakebite,emergency medicine,Dry bite
+overnutrition,nutrition,forced induction
+cervix erosion,gynecology,ectropion
+air embolism,diving medicine,decompression sickness
+plague,infectiology,Black Death
+hemorrhoid,general surgery,Anorectal varices
+ascaridiasis,infectiology,ascariasis
+pharyngitis,otolaryngology,laryngitis
+decompression sickness,occupational medicine,air embolism
+endometriosis,gynecology,endometrosis
diff --git a/examples/semtab2020_novartis/tables/PNRJ3I2M.json b/examples/semtab2020_novartis/tables/PNRJ3I2M.json
new file mode 100644
index 0000000..cf7670d
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/PNRJ3I2M.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["Klippel–Trénaunay–Weber syndrome","jaw-winking syndrome","Fraser syndrome","Lyme disease","cercarial dermatitis","contagious pustular dermatitis","bubonic plague","microcephaly","Edwards syndrome","Athletic pubalgia","snakebite","overnutrition","cervix erosion","air embolism","plague","hemorrhoid","ascaridiasis","pharyngitis","decompression sickness","endometriosis"]},{"index":1,"name":"col1","values":["medical genetics","medical genetics","medical genetics","infectiology","infectiology","infectiology","infectiology","medical genetics","medical genetics","emergency medicine","emergency medicine","nutrition","gynecology","diving medicine","infectiology","general surgery","infectiology","otolaryngology","occupational medicine","gynecology"]},{"index":2,"name":"col2","values":["Parkes Weber syndrome","Marcus Gunn pupil","Frasier syndrome","Chronic Lyme disease","Seabather's eruption","ORF","Black Death","sperm microcephaly","orofaciodigital syndrome VIII","osteitis pubis","Dry bite","forced induction","ectropion","decompression sickness","Black Death","Anorectal varices","ascariasis","laryngitis","air embolism","endometrosis"]}],"metadata":{"table_id":"PNRJ3I2M","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":32,"url":"http://www.wikidata.org/entity/Q1774750","qnode_id":"Q1774750"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q7138441","qnode_id":"Q7138441"}]],[[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q1476789","qnode_id":"Q1476789"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q6758201","qnode_id":"Q6758201"}]],[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q1425572","qnode_id":"Q1425572"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q5493754","qnode_id":"Q5493754"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q201989","qnode_id":"Q201989"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q17103401","qnode_id":"Q17103401"}]],[[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q191672","qnode_id":"Q191672"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q7440153","qnode_id":"Q7440153"}]],[[{"start":0,"end":30,"url":"http://www.wikidata.org/entity/Q681970","qnode_id":"Q681970"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q235215","qnode_id":"Q235215"}]],[[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q217519","qnode_id":"Q217519"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q42005","qnode_id":"Q42005"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q431643","qnode_id":"Q431643"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q60589325","qnode_id":"Q60589325"}]],[[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q457737","qnode_id":"Q457737"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q21154047","qnode_id":"Q21154047"}]],[[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q3099702","qnode_id":"Q3099702"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q2861470","qnode_id":"Q2861470"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q1569702","qnode_id":"Q1569702"}]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q68854","qnode_id":"Q68854"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q2861470","qnode_id":"Q2861470"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q2454710","qnode_id":"Q2454710"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q16956975","qnode_id":"Q16956975"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q2138622","qnode_id":"Q2138622"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1949814","qnode_id":"Q1949814"}]],[[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q720523","qnode_id":"Q720523"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q1221899","qnode_id":"Q1221899"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q662298","qnode_id":"Q662298"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q1367304","qnode_id":"Q1367304"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q1423517","qnode_id":"Q1423517"}],[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q460591","qnode_id":"Q460591"}]],[[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q133780","qnode_id":"Q133780"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q42005","qnode_id":"Q42005"}]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q41478","qnode_id":"Q41478"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q932510","qnode_id":"Q932510"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q4770165","qnode_id":"Q4770165"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q18555290","qnode_id":"Q18555290"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q842428","qnode_id":"Q842428"}]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q2085267","qnode_id":"Q2085267"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q189553","qnode_id":"Q189553"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q1194557","qnode_id":"Q1194557"}]],[[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q460591","qnode_id":"Q460591"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q628764","qnode_id":"Q628764"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q1367304","qnode_id":"Q1367304"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q205764","qnode_id":"Q205764"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q1221899","qnode_id":"Q1221899"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q1340734","qnode_id":"Q1340734"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q12136:0","abs_uri":"http://www.wikidata.org/entity/Q12136","rel_uri":"wd:Q12136","approximation":false,"readable_label":"disease (Q12136)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"col-2","col_index":2,"label":"col2"}],"edges":[{"source":"http://www.wikidata.org/entity/Q12136:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q12136:0","target":"col-1","abs_uri":"http://www.wikidata.org/prop/P1995","rel_uri":"p:P1995","approximation":false,"readable_label":"health specialty (P1995)","id":2},{"source":"http://www.wikidata.org/entity/Q12136:0","target":"col-2","abs_uri":"http://www.wikidata.org/prop/P1889","rel_uri":"p:P1889","approximation":false,"readable_label":"different from (P1889)","id":3}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/PNRJ3I2M.links.tsv b/examples/semtab2020_novartis/tables/PNRJ3I2M.links.tsv
new file mode 100644
index 0000000..1902193
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/PNRJ3I2M.links.tsv
@@ -0,0 +1,60 @@
+0 0 Q1774750
+0 1 Q1071953
+0 2 Q7138441
+1 0 Q1476789
+1 1 Q1071953
+1 2 Q6758201
+2 0 Q1425572
+2 1 Q1071953
+2 2 Q5493754
+3 0 Q201989
+3 1 Q788926
+3 2 Q17103401
+4 0 Q191672
+4 1 Q788926
+4 2 Q7440153
+5 0 Q681970
+5 1 Q788926
+5 2 Q235215
+6 0 Q217519
+6 1 Q788926
+6 2 Q42005
+7 0 Q431643
+7 1 Q1071953
+7 2 Q60589325
+8 0 Q457737
+8 1 Q1071953
+8 2 Q21154047
+9 0 Q3099702
+9 1 Q2861470
+9 2 Q1569702
+10 0 Q68854
+10 1 Q2861470
+10 2 Q2454710
+11 0 Q16956975
+11 1 Q2138622
+11 2 Q1949814
+12 0 Q720523
+12 1 Q1221899
+12 2 Q662298
+13 0 Q1367304
+13 1 Q1423517
+13 2 Q460591
+14 0 Q133780
+14 1 Q788926
+14 2 Q42005
+15 0 Q41478
+15 1 Q932510
+15 2 Q4770165
+16 0 Q18555290
+16 1 Q788926
+16 2 Q842428
+17 0 Q2085267
+17 1 Q189553
+17 2 Q1194557
+18 0 Q460591
+18 1 Q628764
+18 2 Q1367304
+19 0 Q205764
+19 1 Q1221899
+19 2 Q1340734
diff --git a/examples/semtab2020_novartis/tables/V1MLK9TP.csv b/examples/semtab2020_novartis/tables/V1MLK9TP.csv
new file mode 100644
index 0000000..a5272f4
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/V1MLK9TP.csv
@@ -0,0 +1,21 @@
+col0,col1,col2
+immune dysregulation-polyendocuinopathy-enteropathy-X-linked syndrome,autoimmune disease,FOXP3
+Hereditary sensory an autonomic neuropathy type 6,hereditary sensory and autonomic neuropathy,DST
+Native American mypathy,genetic disease,STAC3
+hyerolethalus syndrome,autosomal recessive disease,HYLS1
+autosomal dominant sensosy ataxia,ataxia,RNF170
+IVIC Syndrone,syndrome with limb reduction defects,SALL4
+congenital disorder of glycoslation type IIc,leukocyte-adhesion deficiency syndrome,SLC35C1
+Dyggve-Melfhior-Clausen syndrome,genetic disease,DYM
+cortisone hductase deficiency 1,genetic disease,H6PD
+ulnar-mammary syodrome,syndrome,TBX3
+Kosaki overirowth syndrome,overgrowth syndrome,PDGFRB
+Fechtnersyndrome,Alport syndrome,MYH9
+Urismus-pseudocamstodactyly syndrome,distal arthrogryposis,MYH8
+DNA ligase IV deficigncy,genetic disease,LIG4
+clubbd thumb,brachydactyly,HOXD13
+auriculo condylar syndroe,external ear disease,GNAI3
+multiple ptergium syndrome,syndrome,CHRNA1
+BRCA2 hereditary breast and#ovarian cancer syndrome,hereditary breast ovarian cancer,BRCA2
+Farbr lipogranulomatosis,lipid storage disease,ASAH1
+"spondylopetahyseal dysplasia, Kozlowski type",spondylometaphyseal dysplasia,TRPV4
diff --git a/examples/semtab2020_novartis/tables/V1MLK9TP.json b/examples/semtab2020_novartis/tables/V1MLK9TP.json
new file mode 100644
index 0000000..f22644d
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/V1MLK9TP.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["immune dysregulation-polyendocuinopathy-enteropathy-X-linked syndrome","Hereditary sensory an autonomic neuropathy type 6","Native American mypathy","hyerolethalus syndrome","autosomal dominant sensosy ataxia","IVIC Syndrone","congenital disorder of glycoslation type IIc","Dyggve-Melfhior-Clausen syndrome","cortisone hductase deficiency 1","ulnar-mammary syodrome","Kosaki overirowth syndrome","Fechtnersyndrome","Urismus-pseudocamstodactyly syndrome","DNA ligase IV deficigncy","clubbd thumb","auriculo condylar syndroe","multiple ptergium syndrome","BRCA2 hereditary breast and#ovarian cancer syndrome","Farbr lipogranulomatosis","spondylopetahyseal dysplasia, Kozlowski type"]},{"index":1,"name":"col1","values":["autoimmune disease","hereditary sensory and autonomic neuropathy","genetic disease","autosomal recessive disease","ataxia","syndrome with limb reduction defects","leukocyte-adhesion deficiency syndrome","genetic disease","genetic disease","syndrome","overgrowth syndrome","Alport syndrome","distal arthrogryposis","genetic disease","brachydactyly","external ear disease","syndrome","hereditary breast ovarian cancer","lipid storage disease","spondylometaphyseal dysplasia"]},{"index":2,"name":"col2","values":["FOXP3","DST","STAC3","HYLS1","RNF170","SALL4","SLC35C1","DYM","H6PD","TBX3","PDGFRB","MYH9","MYH8","LIG4","HOXD13","GNAI3","CHRNA1","BRCA2","ASAH1","TRPV4"]}],"metadata":{"table_id":"V1MLK9TP","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":69,"url":"http://www.wikidata.org/entity/Q3508566","qnode_id":"Q3508566"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q8084905","qnode_id":"Q8084905"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q21163319","qnode_id":"Q21163319"}]],[[{"start":0,"end":49,"url":"http://www.wikidata.org/entity/Q29017175","qnode_id":"Q29017175"}],[{"start":0,"end":43,"url":"http://www.wikidata.org/entity/Q3702898","qnode_id":"Q3702898"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q20767283","qnode_id":"Q20767283"}]],[[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q11781607","qnode_id":"Q11781607"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q200779","qnode_id":"Q200779"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18053642","qnode_id":"Q18053642"}]],[[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q5955105","qnode_id":"Q5955105"}],[],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18053376","qnode_id":"Q18053376"}]],[[{"start":0,"end":33,"url":"http://www.wikidata.org/entity/Q29017155","qnode_id":"Q29017155"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q213373","qnode_id":"Q213373"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q18047094","qnode_id":"Q18047094"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q29014920","qnode_id":"Q29014920"}],[{"start":0,"end":36,"url":"http://www.wikidata.org/entity/Q55787046","qnode_id":"Q55787046"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18042947","qnode_id":"Q18042947"}]],[[{"start":0,"end":44,"url":"http://www.wikidata.org/entity/Q5160418","qnode_id":"Q5160418"}],[{"start":0,"end":38,"url":"http://www.wikidata.org/entity/Q1154422","qnode_id":"Q1154422"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q18041782","qnode_id":"Q18041782"}]],[[{"start":0,"end":32,"url":"http://www.wikidata.org/entity/Q1268671","qnode_id":"Q1268671"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q200779","qnode_id":"Q200779"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q18041271","qnode_id":"Q18041271"}]],[[{"start":0,"end":31,"url":"http://www.wikidata.org/entity/Q17084678","qnode_id":"Q17084678"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q200779","qnode_id":"Q200779"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18034383","qnode_id":"Q18034383"}]],[[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q7879712","qnode_id":"Q7879712"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q179630","qnode_id":"Q179630"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18031866","qnode_id":"Q18031866"}]],[[{"start":0,"end":26,"url":"http://www.wikidata.org/entity/Q48989325","qnode_id":"Q48989325"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q7113674","qnode_id":"Q7113674"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q18030425","qnode_id":"Q18030425"}]],[[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1399440","qnode_id":"Q1399440"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q1331116","qnode_id":"Q1331116"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18029747","qnode_id":"Q18029747"}]],[[{"start":0,"end":36,"url":"http://www.wikidata.org/entity/Q11838430","qnode_id":"Q11838430"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q18553375","qnode_id":"Q18553375"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18029745","qnode_id":"Q18029745"}]],[[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q6458655","qnode_id":"Q6458655"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q200779","qnode_id":"Q200779"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18028560","qnode_id":"Q18028560"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q5136452","qnode_id":"Q5136452"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q896643","qnode_id":"Q896643"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q18027230","qnode_id":"Q18027230"}]],[[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q29014971","qnode_id":"Q29014971"}],[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q18556075","qnode_id":"Q18556075"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18026228","qnode_id":"Q18026228"}]],[[{"start":0,"end":26,"url":"http://www.wikidata.org/entity/Q16889762","qnode_id":"Q16889762"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q179630","qnode_id":"Q179630"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q17861971","qnode_id":"Q17861971"}]],[[{"start":0,"end":51,"url":"http://www.wikidata.org/entity/Q19000869","qnode_id":"Q19000869"}],[{"start":0,"end":32,"url":"http://www.wikidata.org/entity/Q19000660","qnode_id":"Q19000660"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q17853272","qnode_id":"Q17853272"}]],[[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q1396345","qnode_id":"Q1396345"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q3540902","qnode_id":"Q3540902"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q17832193","qnode_id":"Q17832193"}]],[[{"start":0,"end":44,"url":"http://www.wikidata.org/entity/Q11695513","qnode_id":"Q11695513"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q19309317","qnode_id":"Q19309317"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q15324120","qnode_id":"Q15324120"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q200779:0","abs_uri":"http://www.wikidata.org/entity/Q200779","rel_uri":"wd:Q200779","approximation":false,"readable_label":"genetic disease (Q200779)"},{"id":"col-2","col_index":2,"label":"col2"},{"id":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/entity/Q7187","rel_uri":"wd:Q7187","approximation":false,"readable_label":"gene (Q7187)"},{"id":"col-1","col_index":1,"label":"col1"}],"edges":[{"source":"http://www.wikidata.org/entity/Q200779:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q200779:0","target":"col-1","abs_uri":"http://www.wikidata.org/prop/P279","rel_uri":"p:P279","approximation":false,"readable_label":"subclass of (P279)","id":2},{"source":"http://www.wikidata.org/entity/Q200779:0","target":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/prop/P2293","rel_uri":"p:P2293","approximation":false,"readable_label":"genetic association (P2293)","id":3},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-2","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":4}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/V1MLK9TP.links.tsv b/examples/semtab2020_novartis/tables/V1MLK9TP.links.tsv
new file mode 100644
index 0000000..c7cde42
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/V1MLK9TP.links.tsv
@@ -0,0 +1,59 @@
+0 0 Q3508566
+0 1 Q8084905
+0 2 Q21163319
+1 0 Q29017175
+1 1 Q3702898
+1 2 Q20767283
+2 0 Q11781607
+2 1 Q200779
+2 2 Q18053642
+3 0 Q5955105
+3 2 Q18053376
+4 0 Q29017155
+4 1 Q213373
+4 2 Q18047094
+5 0 Q29014920
+5 1 Q55787046
+5 2 Q18042947
+6 0 Q5160418
+6 1 Q1154422
+6 2 Q18041782
+7 0 Q1268671
+7 1 Q200779
+7 2 Q18041271
+8 0 Q17084678
+8 1 Q200779
+8 2 Q18034383
+9 0 Q7879712
+9 1 Q179630
+9 2 Q18031866
+10 0 Q48989325
+10 1 Q7113674
+10 2 Q18030425
+11 0 Q1399440
+11 1 Q1331116
+11 2 Q18029747
+12 0 Q11838430
+12 1 Q18553375
+12 2 Q18029745
+13 0 Q6458655
+13 1 Q200779
+13 2 Q18028560
+14 0 Q5136452
+14 1 Q896643
+14 2 Q18027230
+15 0 Q29014971
+15 1 Q18556075
+15 2 Q18026228
+16 0 Q16889762
+16 1 Q179630
+16 2 Q17861971
+17 0 Q19000869
+17 1 Q19000660
+17 2 Q17853272
+18 0 Q1396345
+18 1 Q3540902
+18 2 Q17832193
+19 0 Q11695513
+19 1 Q19309317
+19 2 Q15324120
diff --git a/examples/semtab2020_novartis/tables/WEMTI4U7.csv b/examples/semtab2020_novartis/tables/WEMTI4U7.csv
new file mode 100644
index 0000000..9d3583c
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/WEMTI4U7.csv
@@ -0,0 +1,21 @@
+col0,col1,col2
+hermaphroditism,obstetrics and gynaecology,hermaphroditism
+Dent disease,urology,DEND syndrome
+Frasier syndrome,urology,Fraser syndrome
+ossifying fibroma,oncology,Peripheral ossifying fibroma
+preterm birth,pediatrics,abortion
+IgA glomerulonephritis,urology,Henoch-Schoenlein purpura
+vaginal discharge,urology,vaginal lubrication
+Foix–Alajouanine syndrome,neurology,alcoholic cerebellar degeneration
+agammaglobulinemia,hematology,agammaglobulinemia
+Raynaud syndrome,cardiology,Raynaud disease
+atherosclerosis,cardiology,arteriosclerosis
+hemophilia,hematology,hypochondriasis
+paranoial schizophrenia,psychiatry,paranoia
+arteriosclerosis,cardiology,atherosclerosis
+Raynaud disease,cardiology,Raynaud syndrome
+Caput medusae,cardiology,Cruveilhier–Baumgarten disease
+mental disorder,psychiatry,somatic disease
+non-controlled substance abuse,psychiatry,substance dependence
+nominal aphasia,neuropsychology,Anomia
+hypoactive sexual desire disorder,psychiatry,sexual arousal disorder
diff --git a/examples/semtab2020_novartis/tables/WEMTI4U7.json b/examples/semtab2020_novartis/tables/WEMTI4U7.json
new file mode 100644
index 0000000..9a4d2bb
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/WEMTI4U7.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["hermaphroditism","Dent disease","Frasier syndrome","ossifying fibroma","preterm birth","IgA glomerulonephritis","vaginal discharge","Foix–Alajouanine syndrome","agammaglobulinemia","Raynaud syndrome","atherosclerosis","hemophilia","paranoial schizophrenia","arteriosclerosis","Raynaud disease","Caput medusae","mental disorder","non-controlled substance abuse","nominal aphasia","hypoactive sexual desire disorder"]},{"index":1,"name":"col1","values":["obstetrics and gynaecology","urology","urology","oncology","pediatrics","urology","urology","neurology","hematology","cardiology","cardiology","hematology","psychiatry","cardiology","cardiology","cardiology","psychiatry","psychiatry","neuropsychology","psychiatry"]},{"index":2,"name":"col2","values":["hermaphroditism","DEND syndrome","Fraser syndrome","Peripheral ossifying fibroma","abortion","Henoch-Schoenlein purpura","vaginal lubrication","alcoholic cerebellar degeneration","agammaglobulinemia","Raynaud disease","arteriosclerosis","hypochondriasis","paranoia","atherosclerosis","Raynaud syndrome","Cruveilhier–Baumgarten disease","somatic disease","substance dependence","Anomia","sexual arousal disorder"]}],"metadata":{"table_id":"WEMTI4U7","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q7847568","qnode_id":"Q7847568"}],[{"start":0,"end":26,"url":"http://www.wikidata.org/entity/Q80015","qnode_id":"Q80015"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q16674976","qnode_id":"Q16674976"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q4420121","qnode_id":"Q4420121"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q105650","qnode_id":"Q105650"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q56014276","qnode_id":"Q56014276"}]],[[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q5493754","qnode_id":"Q5493754"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q105650","qnode_id":"Q105650"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q1425572","qnode_id":"Q1425572"}]],[[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q7107615","qnode_id":"Q7107615"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q162555","qnode_id":"Q162555"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q7168708","qnode_id":"Q7168708"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q625506","qnode_id":"Q625506"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q123028","qnode_id":"Q123028"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q8452","qnode_id":"Q8452"}]],[[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q1146454","qnode_id":"Q1146454"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q105650","qnode_id":"Q105650"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q1035319","qnode_id":"Q1035319"}]],[[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q778502","qnode_id":"Q778502"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q105650","qnode_id":"Q105650"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q536554","qnode_id":"Q536554"}]],[[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q5464172","qnode_id":"Q5464172"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q83042","qnode_id":"Q83042"}],[{"start":0,"end":33,"url":"http://www.wikidata.org/entity/Q18816398","qnode_id":"Q18816398"}]],[[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q1047559","qnode_id":"Q1047559"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q103824","qnode_id":"Q103824"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q5659619","qnode_id":"Q5659619"}]],[[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q5142470","qnode_id":"Q5142470"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q10379","qnode_id":"Q10379"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q1053824","qnode_id":"Q1053824"}]],[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q12252367","qnode_id":"Q12252367"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q10379","qnode_id":"Q10379"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q184559","qnode_id":"Q184559"}]],[[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q134003","qnode_id":"Q134003"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q103824","qnode_id":"Q103824"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q105434","qnode_id":"Q105434"}]],[[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q28914244","qnode_id":"Q28914244"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q7867","qnode_id":"Q7867"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q1229994","qnode_id":"Q1229994"}]],[[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q184559","qnode_id":"Q184559"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q10379","qnode_id":"Q10379"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q12252367","qnode_id":"Q12252367"}]],[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q1053824","qnode_id":"Q1053824"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q10379","qnode_id":"Q10379"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q5142470","qnode_id":"Q5142470"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q1035226","qnode_id":"Q1035226"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q10379","qnode_id":"Q10379"}],[{"start":0,"end":30,"url":"http://www.wikidata.org/entity/Q3281266","qnode_id":"Q3281266"}]],[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q12135","qnode_id":"Q12135"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q7867","qnode_id":"Q7867"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q7101840","qnode_id":"Q7101840"}]],[[{"start":0,"end":30,"url":"http://www.wikidata.org/entity/Q182413","qnode_id":"Q182413"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q7867","qnode_id":"Q7867"}],[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q3378593","qnode_id":"Q3378593"}]],[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q567576","qnode_id":"Q567576"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q3872","qnode_id":"Q3872"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q2852294","qnode_id":"Q2852294"}]],[[{"start":0,"end":33,"url":"http://www.wikidata.org/entity/Q303555","qnode_id":"Q303555"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q7867","qnode_id":"Q7867"}],[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q2275677","qnode_id":"Q2275677"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q12136:0","abs_uri":"http://www.wikidata.org/entity/Q12136","rel_uri":"wd:Q12136","approximation":false,"readable_label":"disease (Q12136)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"col-2","col_index":2,"label":"col2"}],"edges":[{"source":"http://www.wikidata.org/entity/Q12136:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q12136:0","target":"col-1","abs_uri":"http://www.wikidata.org/prop/P1995","rel_uri":"p:P1995","approximation":false,"readable_label":"health specialty (P1995)","id":2},{"source":"http://www.wikidata.org/entity/Q12136:0","target":"col-2","abs_uri":"http://www.wikidata.org/prop/P1889","rel_uri":"p:P1889","approximation":false,"readable_label":"different from (P1889)","id":3}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/WEMTI4U7.links.tsv b/examples/semtab2020_novartis/tables/WEMTI4U7.links.tsv
new file mode 100644
index 0000000..579184a
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/WEMTI4U7.links.tsv
@@ -0,0 +1,60 @@
+0 0 Q7847568
+0 1 Q80015
+0 2 Q16674976
+1 0 Q4420121
+1 1 Q105650
+1 2 Q56014276
+2 0 Q5493754
+2 1 Q105650
+2 2 Q1425572
+3 0 Q7107615
+3 1 Q162555
+3 2 Q7168708
+4 0 Q625506
+4 1 Q123028
+4 2 Q8452
+5 0 Q1146454
+5 1 Q105650
+5 2 Q1035319
+6 0 Q778502
+6 1 Q105650
+6 2 Q536554
+7 0 Q5464172
+7 1 Q83042
+7 2 Q18816398
+8 0 Q1047559
+8 1 Q103824
+8 2 Q5659619
+9 0 Q5142470
+9 1 Q10379
+9 2 Q1053824
+10 0 Q12252367
+10 1 Q10379
+10 2 Q184559
+11 0 Q134003
+11 1 Q103824
+11 2 Q105434
+12 0 Q28914244
+12 1 Q7867
+12 2 Q1229994
+13 0 Q184559
+13 1 Q10379
+13 2 Q12252367
+14 0 Q1053824
+14 1 Q10379
+14 2 Q5142470
+15 0 Q1035226
+15 1 Q10379
+15 2 Q3281266
+16 0 Q12135
+16 1 Q7867
+16 2 Q7101840
+17 0 Q182413
+17 1 Q7867
+17 2 Q3378593
+18 0 Q567576
+18 1 Q3872
+18 2 Q2852294
+19 0 Q303555
+19 1 Q7867
+19 2 Q2275677
diff --git a/examples/semtab2020_novartis/tables/WKDRXIGC.csv b/examples/semtab2020_novartis/tables/WKDRXIGC.csv
new file mode 100644
index 0000000..af5b89f
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/WKDRXIGC.csv
@@ -0,0 +1,21 @@
+col0,col1,col2
+exodeoxyribonuclease V subunit gamma CTL0008,polyphosphate kinase complex,Chlamydia trachomatis 434/BU
+aromatic amino acid aminotransferase CTL0005,phosphatidylinositol 3-kinase complex,Chlamydia trachomatis 434/BU
+APOC4,extracellular region,Homo sapiens
+PF3D7_0725500,integral component of membrane,Plasmodium falciparum
+LCAT,extracellular,Homo sapiens
+APOC2,chylomicron,Homo sapiens
+APOC3,chylomicron,Homo sapiens
+APOH,cell surface,Homo sapiens
+APOD,endoplasmic reticulum,Homo sapiens
+APOC1,endoplasmic reticulum,Homo sapiens
+PROC,endoplasmic reticulum,Homo sapiens
+POMC,cytoplasm,Homo sapiens
+RFC5,cell nucleus,Plasmodium falciparum
+CST3,basement membrane,Homo sapiens
+IGHG1,cell membrane,Homo sapiens
+FURIN,cell membrane,Homo sapiens
+ANXA1,cell membrane,Homo sapiens
+APOB,cell membrane,Homo sapiens
+LPL,cell membrane,Homo sapiens
+PF3D7_1102000,cell membrane,Plasmodium falciparum
diff --git a/examples/semtab2020_novartis/tables/WKDRXIGC.json b/examples/semtab2020_novartis/tables/WKDRXIGC.json
new file mode 100644
index 0000000..c6050e0
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/WKDRXIGC.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["exodeoxyribonuclease V subunit gamma CTL0008","aromatic amino acid aminotransferase CTL0005","APOC4","PF3D7_0725500","LCAT","APOC2","APOC3","APOH","APOD","APOC1","PROC","POMC","RFC5","CST3","IGHG1","FURIN","ANXA1","APOB","LPL","PF3D7_1102000"]},{"index":1,"name":"col1","values":["polyphosphate kinase complex","phosphatidylinositol 3-kinase complex","extracellular region","integral component of membrane","extracellular","chylomicron","chylomicron","cell surface","endoplasmic reticulum","endoplasmic reticulum","endoplasmic reticulum","cytoplasm","cell nucleus","basement membrane","cell membrane","cell membrane","cell membrane","cell membrane","cell membrane","cell membrane"]},{"index":2,"name":"col2","values":["Chlamydia trachomatis 434/BU","Chlamydia trachomatis 434/BU","Homo sapiens","Plasmodium falciparum","Homo sapiens","Homo sapiens","Homo sapiens","Homo sapiens","Homo sapiens","Homo sapiens","Homo sapiens","Homo sapiens","Plasmodium falciparum","Homo sapiens","Homo sapiens","Homo sapiens","Homo sapiens","Homo sapiens","Homo sapiens","Plasmodium falciparum"]}],"metadata":{"table_id":"WKDRXIGC","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":44,"url":"http://www.wikidata.org/entity/Q21279826","qnode_id":"Q21279826"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q21763002","qnode_id":"Q21763002"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q20800254","qnode_id":"Q20800254"}]],[[{"start":0,"end":44,"url":"http://www.wikidata.org/entity/Q21280386","qnode_id":"Q21280386"}],[{"start":0,"end":37,"url":"http://www.wikidata.org/entity/Q14876059","qnode_id":"Q14876059"}],[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q20800254","qnode_id":"Q20800254"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14910792","qnode_id":"Q14910792"}],[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q14645596","qnode_id":"Q14645596"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q15978631","qnode_id":"Q15978631"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q18969067","qnode_id":"Q18969067"}],[{"start":0,"end":30,"url":"http://www.wikidata.org/entity/Q14327652","qnode_id":"Q14327652"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q311383","qnode_id":"Q311383"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14905444","qnode_id":"Q14905444"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q1385443","qnode_id":"Q1385443"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q15978631","qnode_id":"Q15978631"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14916272","qnode_id":"Q14916272"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q423126","qnode_id":"Q423126"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q15978631","qnode_id":"Q15978631"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14910515","qnode_id":"Q14910515"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q423126","qnode_id":"Q423126"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q15978631","qnode_id":"Q15978631"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14911006","qnode_id":"Q14911006"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q189094","qnode_id":"Q189094"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q15978631","qnode_id":"Q15978631"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18247781","qnode_id":"Q18247781"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q79927","qnode_id":"Q79927"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q15978631","qnode_id":"Q15978631"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14904550","qnode_id":"Q14904550"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q79927","qnode_id":"Q79927"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q15978631","qnode_id":"Q15978631"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14864945","qnode_id":"Q14864945"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q79927","qnode_id":"Q79927"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q15978631","qnode_id":"Q15978631"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14819793","qnode_id":"Q14819793"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q79899","qnode_id":"Q79899"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q15978631","qnode_id":"Q15978631"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18970838","qnode_id":"Q18970838"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q40260","qnode_id":"Q40260"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q311383","qnode_id":"Q311383"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14913588","qnode_id":"Q14913588"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q32846","qnode_id":"Q32846"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q15978631","qnode_id":"Q15978631"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18027713","qnode_id":"Q18027713"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q29548","qnode_id":"Q29548"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q15978631","qnode_id":"Q15978631"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14916427","qnode_id":"Q14916427"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q29548","qnode_id":"Q29548"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q15978631","qnode_id":"Q15978631"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14908222","qnode_id":"Q14908222"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q29548","qnode_id":"Q29548"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q15978631","qnode_id":"Q15978631"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14890615","qnode_id":"Q14890615"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q29548","qnode_id":"Q29548"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q15978631","qnode_id":"Q15978631"}]],[[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q14864755","qnode_id":"Q14864755"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q29548","qnode_id":"Q29548"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q15978631","qnode_id":"Q15978631"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q18970186","qnode_id":"Q18970186"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q29548","qnode_id":"Q29548"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q311383","qnode_id":"Q311383"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/entity/Q7187","rel_uri":"wd:Q7187","approximation":false,"readable_label":"gene (Q7187)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"http://www.wikidata.org/entity/Q5058355:0","abs_uri":"http://www.wikidata.org/entity/Q5058355","rel_uri":"wd:Q5058355","approximation":false,"readable_label":"cellular component (Q5058355)"},{"id":"col-2","col_index":2,"label":"col2"}],"edges":[{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"http://www.wikidata.org/entity/Q5058355:0","abs_uri":"http://www.wikidata.org/prop/P681","rel_uri":"p:P681","approximation":false,"readable_label":"cell component (P681)","id":2},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-2","abs_uri":"http://www.wikidata.org/prop/P703","rel_uri":"p:P703","approximation":false,"readable_label":"found in taxon (P703)","id":3},{"source":"http://www.wikidata.org/entity/Q5058355:0","target":"col-1","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":4}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/WKDRXIGC.links.tsv b/examples/semtab2020_novartis/tables/WKDRXIGC.links.tsv
new file mode 100644
index 0000000..ee1ed18
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/WKDRXIGC.links.tsv
@@ -0,0 +1,60 @@
+0 0 Q21279826
+0 1 Q21763002
+0 2 Q20800254
+1 0 Q21280386
+1 1 Q14876059
+1 2 Q20800254
+2 0 Q14910792
+2 1 Q14645596
+2 2 Q15978631
+3 0 Q18969067
+3 1 Q14327652
+3 2 Q311383
+4 0 Q14905444
+4 1 Q1385443
+4 2 Q15978631
+5 0 Q14916272
+5 1 Q423126
+5 2 Q15978631
+6 0 Q14910515
+6 1 Q423126
+6 2 Q15978631
+7 0 Q14911006
+7 1 Q189094
+7 2 Q15978631
+8 0 Q18247781
+8 1 Q79927
+8 2 Q15978631
+9 0 Q14904550
+9 1 Q79927
+9 2 Q15978631
+10 0 Q14864945
+10 1 Q79927
+10 2 Q15978631
+11 0 Q14819793
+11 1 Q79899
+11 2 Q15978631
+12 0 Q18970838
+12 1 Q40260
+12 2 Q311383
+13 0 Q14913588
+13 1 Q32846
+13 2 Q15978631
+14 0 Q18027713
+14 1 Q29548
+14 2 Q15978631
+15 0 Q14916427
+15 1 Q29548
+15 2 Q15978631
+16 0 Q14908222
+16 1 Q29548
+16 2 Q15978631
+17 0 Q14890615
+17 1 Q29548
+17 2 Q15978631
+18 0 Q14864755
+18 1 Q29548
+18 2 Q15978631
+19 0 Q18970186
+19 1 Q29548
+19 2 Q311383
diff --git a/examples/semtab2020_novartis/tables/XI5424YZ.csv b/examples/semtab2020_novartis/tables/XI5424YZ.csv
new file mode 100644
index 0000000..c78d28f
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/XI5424YZ.csv
@@ -0,0 +1,21 @@
+col0,col1,col2
+ARHGA5,human chromosome 14,forward strand
+ARG2,human chromosome 14,forward strand
+LTB4R,human chromosome 14,forward strand
+RNASE,human chromosome 14,forward strand
+TNASE3,human chromosome 14,forward strand
+PTGDR,human chromosome 14,forward strand
+JDP2,human chromosome 14,forward strand
+CHGA,human chromosome 14,forward strand
+SESPINA5,human chromosome 14,forward strand
+IL25,human chromosome 14,forward strand
+SERPNA3,human chromosome 14,forward strand
+FOS,human chromosome 14,forward strand
+TSHR,human chromosome 14,forward strand
+ANG,human chromosome 14,forward strand
+ARF6,human chromosome 14,forward strand
+PNP,human chromosome 14,forward strand
+APEX1,human chromosome 14,forward strand
+ESRRB,human chromosome 14,forward strand
+BKRB2,human chromosome 14,forward strand
+NPAS3,human chromosome 14,forward strand
diff --git a/examples/semtab2020_novartis/tables/XI5424YZ.json b/examples/semtab2020_novartis/tables/XI5424YZ.json
new file mode 100644
index 0000000..fd9358e
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/XI5424YZ.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["ARHGA5","ARG2","LTB4R","RNASE","TNASE3","PTGDR","JDP2","CHGA","SESPINA5","IL25","SERPNA3","FOS","TSHR","ANG","ARF6","PNP","APEX1","ESRRB","BKRB2","NPAS3"]},{"index":1,"name":"col1","values":["human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14","human chromosome 14"]},{"index":2,"name":"col2","values":["forward strand","forward strand","forward strand","forward strand","forward strand","forward strand","forward strand","forward strand","forward strand","forward strand","forward strand","forward strand","forward strand","forward strand","forward strand","forward strand","forward strand","forward strand","forward strand","forward strand"]}],"metadata":{"table_id":"XI5424YZ","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q17831820","qnode_id":"Q17831820"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q17831652","qnode_id":"Q17831652"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q15322329","qnode_id":"Q15322329"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q15311603","qnode_id":"Q15311603"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q15311600","qnode_id":"Q15311600"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14913390","qnode_id":"Q14913390"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14913016","qnode_id":"Q14913016"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14909191","qnode_id":"Q14909191"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q14907927","qnode_id":"Q14907927"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14907251","qnode_id":"Q14907251"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q14906371","qnode_id":"Q14906371"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q14906123","qnode_id":"Q14906123"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14904941","qnode_id":"Q14904941"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q14882120","qnode_id":"Q14882120"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14865053","qnode_id":"Q14865053"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q7261255","qnode_id":"Q7261255"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q5945174","qnode_id":"Q5945174"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q5401842","qnode_id":"Q5401842"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q4955260","qnode_id":"Q4955260"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q4044993","qnode_id":"Q4044993"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q138955","qnode_id":"Q138955"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/entity/Q7187","rel_uri":"wd:Q7187","approximation":false,"readable_label":"gene (Q7187)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"http://www.wikidata.org/entity/Q37748:0","abs_uri":"http://www.wikidata.org/entity/Q37748","rel_uri":"wd:Q37748","approximation":false,"readable_label":"chromosome (Q37748)"},{"id":"col-2","col_index":2,"label":"col2"}],"edges":[{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"http://www.wikidata.org/entity/Q37748:0","abs_uri":"http://www.wikidata.org/prop/P1057","rel_uri":"p:P1057","approximation":false,"readable_label":"chromosome (P1057)","id":2},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-2","abs_uri":"http://www.wikidata.org/prop/P2548","rel_uri":"p:P2548","approximation":false,"readable_label":"strand orientation (P2548)","id":3},{"source":"http://www.wikidata.org/entity/Q37748:0","target":"col-1","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":4}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/XI5424YZ.links.tsv b/examples/semtab2020_novartis/tables/XI5424YZ.links.tsv
new file mode 100644
index 0000000..fe4b060
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/XI5424YZ.links.tsv
@@ -0,0 +1,60 @@
+0 0 Q17831820
+0 1 Q138955
+0 2 Q22809680
+1 0 Q17831652
+1 1 Q138955
+1 2 Q22809680
+2 0 Q15322329
+2 1 Q138955
+2 2 Q22809680
+3 0 Q15311603
+3 1 Q138955
+3 2 Q22809680
+4 0 Q15311600
+4 1 Q138955
+4 2 Q22809680
+5 0 Q14913390
+5 1 Q138955
+5 2 Q22809680
+6 0 Q14913016
+6 1 Q138955
+6 2 Q22809680
+7 0 Q14909191
+7 1 Q138955
+7 2 Q22809680
+8 0 Q14907927
+8 1 Q138955
+8 2 Q22809680
+9 0 Q14907251
+9 1 Q138955
+9 2 Q22809680
+10 0 Q14906371
+10 1 Q138955
+10 2 Q22809680
+11 0 Q14906123
+11 1 Q138955
+11 2 Q22809680
+12 0 Q14904941
+12 1 Q138955
+12 2 Q22809680
+13 0 Q14882120
+13 1 Q138955
+13 2 Q22809680
+14 0 Q14865053
+14 1 Q138955
+14 2 Q22809680
+15 0 Q7261255
+15 1 Q138955
+15 2 Q22809680
+16 0 Q5945174
+16 1 Q138955
+16 2 Q22809680
+17 0 Q5401842
+17 1 Q138955
+17 2 Q22809680
+18 0 Q4955260
+18 1 Q138955
+18 2 Q22809680
+19 0 Q4044993
+19 1 Q138955
+19 2 Q22809680
diff --git a/examples/semtab2020_novartis/tables/XIL86R61.csv b/examples/semtab2020_novartis/tables/XIL86R61.csv
new file mode 100644
index 0000000..89fd5ae
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/XIL86R61.csv
@@ -0,0 +1,21 @@
+col0,col1,col2,col3,col4,col5,col6
+PocGH01_00066500,PocGH01_00_v2,reverse strand,PocGH01_00120100,"PIR protein, pseudogene",pseudogene,Plasmodium ovale curtisi
+PocGH01_00066300,PocGH01_00_v2,forward strand,PocGH01_00028800,"PIR protein, pseudogene",pseudogene,Plasmodium ovale curtisi
+PocGH01_00066100,PocGH01_00_v2,reverse strand,PocGH01_00135200,"PIR protein, pseudogene",pseudogene,Plasmodium ovale curtisi
+PocGH01_00066000,PocGH01_00_v2,reverse strand,PocGH01_00203300,"PIR protein, pseudogene",pseudogene,Plasmodium ovale curtisi
+PF3D7_1476900,Pf3D7_14_v3,reverse strand,PRCDC_1475900,"Plasmodium exported protein, unknown function, pseudogene",pseudogene,Plasmodium falciparum 3D7
+PF3D7_1364700,Pf3D7_13_v3,forward strand,PVP01_1107300,"WD repeat-containing protein, putative, pseudogene",pseudogene,Plasmodium falciparum 3D7
+PF3D7_1334900,Pf3D7_13_v3,reverse strand,PRCDC_1333900,"MSP7-like protein, fragment, pseudogene",pseudogene,Plasmodium falciparum 3D7
+PF3D7_1220200,Pf3D7_12_v3,reverse strand,PVP01_1438600,"conserved Plasmodium protein, unknown function, pseudogene",pseudogene,Plasmodium falciparum 3D7
+PF3D7_1000900,Pf3D7_10_v3,forward strand,PRCDC_1000300,"erythrocyte membrane protein 1 (PfEMP1), pseudogene",pseudogene,Plasmodium falciparum 3D7
+PF3D7_0832700,Pf3D7_08_v3,forward strand,PF3D7_0832200,"Plasmodium exported protein (PHISTa-like), unknown function, pseudogene",pseudogene,Plasmodium falciparum 3D7
+PF3D7_0800900,Pf3D7_08_v3,forward strand,PF3D7_0800800,"Plasmodium exported protein (hyp7), unknown function, pseudogene",pseudogene,Plasmodium falciparum 3D7
+PF3D7_0220400,Pf3D7_02_v3,forward strand,PRCDC_0219300,"DnaJ protein, putative, pseudogene",pseudogene,Plasmodium falciparum 3D7
+PF3D7_0400200,Pf3D7_04_v3,forward strand,VAR,"erythrocyte membrane protein 1 (PfEMP1), exon 2, pseudogene",pseudogene,Plasmodium falciparum 3D7
+VAR,Pf3D7_13_v3,reverse strand,PF3D7_0200800,"erythrocyte membrane protein 1 (PfEMP1), pseudogene",pseudogene,Plasmodium falciparum 3D7
+PF3D7_0221900,Pf3D7_02_v3,reverse strand,VAR,"erythrocyte membrane protein 1 (PfEMP1), pseudogene",pseudogene,Plasmodium falciparum 3D7
+PF3D7_0200800,Pf3D7_02_v3,reverse strand,VAR,"erythrocyte membrane protein 1 (PfEMP1), exon 2, pseudogene",pseudogene,Plasmodium falciparum 3D7
+VAR1CSA,Pf3D7_05_v3,forward strand,VAR1CSA,"erythrocyte membrane protein 1 (PfEMP1), pseudogene",pseudogene,Plasmodium falciparum 3D7
+AROM,Pf3D7_02_v3,forward strand,AROM,"pentafunctional AROM polypeptide, putative, pseudogene",pseudogene,Plasmodium falciparum 3D7
+LOC102637269,mouse chromosome 11,reverse strand,SEC61G,"SEC61, gamma subunit",protein-coding gene,house mouse
+Phf8-ps,mouse chromosome 17,reverse strand,RGD1310212,PHD finger protein 8,protein-coding gene,house mouse
diff --git a/examples/semtab2020_novartis/tables/XIL86R61.json b/examples/semtab2020_novartis/tables/XIL86R61.json
new file mode 100644
index 0000000..bcfee9e
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/XIL86R61.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["PocGH01_00066500","PocGH01_00066300","PocGH01_00066100","PocGH01_00066000","PF3D7_1476900","PF3D7_1364700","PF3D7_1334900","PF3D7_1220200","PF3D7_1000900","PF3D7_0832700","PF3D7_0800900","PF3D7_0220400","PF3D7_0400200","VAR","PF3D7_0221900","PF3D7_0200800","VAR1CSA","AROM","LOC102637269","Phf8-ps"]},{"index":1,"name":"col1","values":["PocGH01_00_v2","PocGH01_00_v2","PocGH01_00_v2","PocGH01_00_v2","Pf3D7_14_v3","Pf3D7_13_v3","Pf3D7_13_v3","Pf3D7_12_v3","Pf3D7_10_v3","Pf3D7_08_v3","Pf3D7_08_v3","Pf3D7_02_v3","Pf3D7_04_v3","Pf3D7_13_v3","Pf3D7_02_v3","Pf3D7_02_v3","Pf3D7_05_v3","Pf3D7_02_v3","mouse chromosome 11","mouse chromosome 17"]},{"index":2,"name":"col2","values":["reverse strand","forward strand","reverse strand","reverse strand","reverse strand","forward strand","reverse strand","reverse strand","forward strand","forward strand","forward strand","forward strand","forward strand","reverse strand","reverse strand","reverse strand","forward strand","forward strand","reverse strand","reverse strand"]},{"index":3,"name":"col3","values":["PocGH01_00120100","PocGH01_00028800","PocGH01_00135200","PocGH01_00203300","PRCDC_1475900","PVP01_1107300","PRCDC_1333900","PVP01_1438600","PRCDC_1000300","PF3D7_0832200","PF3D7_0800800","PRCDC_0219300","VAR","PF3D7_0200800","VAR","VAR","VAR1CSA","AROM","SEC61G","RGD1310212"]},{"index":4,"name":"col4","values":["PIR protein, pseudogene","PIR protein, pseudogene","PIR protein, pseudogene","PIR protein, pseudogene","Plasmodium exported protein, unknown function, pseudogene","WD repeat-containing protein, putative, pseudogene","MSP7-like protein, fragment, pseudogene","conserved Plasmodium protein, unknown function, pseudogene","erythrocyte membrane protein 1 (PfEMP1), pseudogene","Plasmodium exported protein (PHISTa-like), unknown function, pseudogene","Plasmodium exported protein (hyp7), unknown function, pseudogene","DnaJ protein, putative, pseudogene","erythrocyte membrane protein 1 (PfEMP1), exon 2, pseudogene","erythrocyte membrane protein 1 (PfEMP1), pseudogene","erythrocyte membrane protein 1 (PfEMP1), pseudogene","erythrocyte membrane protein 1 (PfEMP1), exon 2, pseudogene","erythrocyte membrane protein 1 (PfEMP1), pseudogene","pentafunctional AROM polypeptide, putative, pseudogene","SEC61, gamma subunit","PHD finger protein 8"]},{"index":5,"name":"col5","values":["pseudogene","pseudogene","pseudogene","pseudogene","pseudogene","pseudogene","pseudogene","pseudogene","pseudogene","pseudogene","pseudogene","pseudogene","pseudogene","pseudogene","pseudogene","pseudogene","pseudogene","pseudogene","protein-coding gene","protein-coding gene"]},{"index":6,"name":"col6","values":["Plasmodium ovale curtisi","Plasmodium ovale curtisi","Plasmodium ovale curtisi","Plasmodium ovale curtisi","Plasmodium falciparum 3D7","Plasmodium falciparum 3D7","Plasmodium falciparum 3D7","Plasmodium falciparum 3D7","Plasmodium falciparum 3D7","Plasmodium falciparum 3D7","Plasmodium falciparum 3D7","Plasmodium falciparum 3D7","Plasmodium falciparum 3D7","Plasmodium falciparum 3D7","Plasmodium falciparum 3D7","Plasmodium falciparum 3D7","Plasmodium falciparum 3D7","Plasmodium falciparum 3D7","house mouse","house mouse"]}],"metadata":{"table_id":"XIL86R61","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q61872392","qnode_id":"Q61872392"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q63024061","qnode_id":"Q63024061"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q59863437","qnode_id":"Q59863437"}],[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q64705523","qnode_id":"Q64705523"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q277338","qnode_id":"Q277338"}],[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q7201933","qnode_id":"Q7201933"}]],[[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q61872391","qnode_id":"Q61872391"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q63024061","qnode_id":"Q63024061"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q59859916","qnode_id":"Q59859916"}],[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q64705522","qnode_id":"Q64705522"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q277338","qnode_id":"Q277338"}],[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q7201933","qnode_id":"Q7201933"}]],[[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q61872390","qnode_id":"Q61872390"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q63024061","qnode_id":"Q63024061"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q59864574","qnode_id":"Q59864574"}],[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q64705521","qnode_id":"Q64705521"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q277338","qnode_id":"Q277338"}],[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q7201933","qnode_id":"Q7201933"}]],[[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q61872389","qnode_id":"Q61872389"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q63024061","qnode_id":"Q63024061"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q59868946","qnode_id":"Q59868946"}],[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q64705520","qnode_id":"Q64705520"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q277338","qnode_id":"Q277338"}],[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q7201933","qnode_id":"Q7201933"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q18973906","qnode_id":"Q18973906"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q61866965","qnode_id":"Q61866965"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q56745184","qnode_id":"Q56745184"}],[{"start":0,"end":57,"url":"http://www.wikidata.org/entity/Q64845974","qnode_id":"Q64845974"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q277338","qnode_id":"Q277338"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q61779043","qnode_id":"Q61779043"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q18972956","qnode_id":"Q18972956"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q61866941","qnode_id":"Q61866941"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q59747578","qnode_id":"Q59747578"}],[{"start":0,"end":50,"url":"http://www.wikidata.org/entity/Q64843272","qnode_id":"Q64843272"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q277338","qnode_id":"Q277338"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q61779043","qnode_id":"Q61779043"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q18973406","qnode_id":"Q18973406"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q61866941","qnode_id":"Q61866941"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q56738175","qnode_id":"Q56738175"}],[{"start":0,"end":39,"url":"http://www.wikidata.org/entity/Q64843243","qnode_id":"Q64843243"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q277338","qnode_id":"Q277338"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q61779043","qnode_id":"Q61779043"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q18973176","qnode_id":"Q18973176"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q61866898","qnode_id":"Q61866898"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q59760534","qnode_id":"Q59760534"}],[{"start":0,"end":58,"url":"http://www.wikidata.org/entity/Q64843002","qnode_id":"Q64843002"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q277338","qnode_id":"Q277338"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q61779043","qnode_id":"Q61779043"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q18969669","qnode_id":"Q18969669"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q61866791","qnode_id":"Q61866791"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q61927779","qnode_id":"Q61927779"}],[{"start":0,"end":51,"url":"http://www.wikidata.org/entity/Q64842264","qnode_id":"Q64842264"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q277338","qnode_id":"Q277338"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q61779043","qnode_id":"Q61779043"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q18969306","qnode_id":"Q18969306"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q61866689","qnode_id":"Q61866689"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q18969273","qnode_id":"Q18969273"}],[{"start":0,"end":71,"url":"http://www.wikidata.org/entity/Q64830947","qnode_id":"Q64830947"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q277338","qnode_id":"Q277338"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q61779043","qnode_id":"Q61779043"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q18969624","qnode_id":"Q18969624"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q61866689","qnode_id":"Q61866689"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q18969616","qnode_id":"Q18969616"}],[{"start":0,"end":64,"url":"http://www.wikidata.org/entity/Q64830920","qnode_id":"Q64830920"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q277338","qnode_id":"Q277338"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q61779043","qnode_id":"Q61779043"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q18968283","qnode_id":"Q18968283"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q61815105","qnode_id":"Q61815105"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q56709522","qnode_id":"Q56709522"}],[{"start":0,"end":34,"url":"http://www.wikidata.org/entity/Q64830451","qnode_id":"Q64830451"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q277338","qnode_id":"Q277338"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q61779043","qnode_id":"Q61779043"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q19047202","qnode_id":"Q19047202"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q61866508","qnode_id":"Q61866508"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q56711258","qnode_id":"Q56711258"}],[{"start":0,"end":59,"url":"http://www.wikidata.org/entity/Q64830751","qnode_id":"Q64830751"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q277338","qnode_id":"Q277338"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q61779043","qnode_id":"Q61779043"}]],[[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q18974751","qnode_id":"Q18974751"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q61866941","qnode_id":"Q61866941"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q19043380","qnode_id":"Q19043380"}],[{"start":0,"end":51,"url":"http://www.wikidata.org/entity/Q64843203","qnode_id":"Q64843203"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q277338","qnode_id":"Q277338"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q61779043","qnode_id":"Q61779043"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q18968356","qnode_id":"Q18968356"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q61815105","qnode_id":"Q61815105"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q61872289","qnode_id":"Q61872289"}],[{"start":0,"end":51,"url":"http://www.wikidata.org/entity/Q64830459","qnode_id":"Q64830459"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q277338","qnode_id":"Q277338"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q61779043","qnode_id":"Q61779043"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q19043380","qnode_id":"Q19043380"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q61815105","qnode_id":"Q61815105"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q61872292","qnode_id":"Q61872292"}],[{"start":0,"end":59,"url":"http://www.wikidata.org/entity/Q64829572","qnode_id":"Q64829572"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q277338","qnode_id":"Q277338"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q61779043","qnode_id":"Q61779043"}]],[[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q19043692","qnode_id":"Q19043692"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q61866524","qnode_id":"Q61866524"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q61927658","qnode_id":"Q61927658"}],[{"start":0,"end":51,"url":"http://www.wikidata.org/entity/Q64830806","qnode_id":"Q64830806"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q277338","qnode_id":"Q277338"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q61779043","qnode_id":"Q61779043"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q19043882","qnode_id":"Q19043882"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q61815105","qnode_id":"Q61815105"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q56708995","qnode_id":"Q56708995"}],[{"start":0,"end":54,"url":"http://www.wikidata.org/entity/Q64830447","qnode_id":"Q64830447"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q277338","qnode_id":"Q277338"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q61779043","qnode_id":"Q61779043"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q21823217","qnode_id":"Q21823217"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q15305594","qnode_id":"Q15305594"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q18037209","qnode_id":"Q18037209"}],[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q21991607","qnode_id":"Q21991607"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q83310","qnode_id":"Q83310"}]],[[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q18266450","qnode_id":"Q18266450"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q15305616","qnode_id":"Q15305616"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q24403514","qnode_id":"Q24403514"}],[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q21986254","qnode_id":"Q21986254"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q20747295","qnode_id":"Q20747295"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q83310","qnode_id":"Q83310"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q277338:0","abs_uri":"http://www.wikidata.org/entity/Q277338","rel_uri":"wd:Q277338","approximation":false,"readable_label":"pseudogene (Q277338)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"http://www.wikidata.org/entity/Q37748:0","abs_uri":"http://www.wikidata.org/entity/Q37748","rel_uri":"wd:Q37748","approximation":false,"readable_label":"chromosome (Q37748)"},{"id":"col-4","col_index":4,"label":"col4"},{"id":"http://www.wikidata.org/entity/Q8054:0","abs_uri":"http://www.wikidata.org/entity/Q8054","rel_uri":"wd:Q8054","approximation":false,"readable_label":"protein (Q8054)"},{"id":"col-6","col_index":6,"label":"col6"},{"id":"http://www.wikidata.org/entity/Q16521:0","abs_uri":"http://www.wikidata.org/entity/Q16521","rel_uri":"wd:Q16521","approximation":false,"readable_label":"taxon (Q16521)"},{"id":"col-2","col_index":2,"label":"col2"},{"id":"col-3","col_index":3,"label":"col3"},{"id":"col-5","col_index":5,"label":"col5"}],"edges":[{"source":"http://www.wikidata.org/entity/Q277338:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q277338:0","target":"http://www.wikidata.org/entity/Q37748:0","abs_uri":"http://www.wikidata.org/prop/P1057","rel_uri":"p:P1057","approximation":false,"readable_label":"chromosome (P1057)","id":2},{"source":"http://www.wikidata.org/entity/Q277338:0","target":"col-2","abs_uri":"http://www.wikidata.org/prop/P2548","rel_uri":"p:P2548","approximation":false,"readable_label":"strand orientation (P2548)","id":3},{"source":"http://www.wikidata.org/entity/Q277338:0","target":"col-3","abs_uri":"http://www.wikidata.org/prop/P684","rel_uri":"p:P684","approximation":false,"readable_label":"ortholog (P684)","id":4},{"source":"http://www.wikidata.org/entity/Q277338:0","target":"http://www.wikidata.org/entity/Q8054:0","abs_uri":"http://www.wikidata.org/prop/P688","rel_uri":"p:P688","approximation":false,"readable_label":"encodes (P688)","id":5},{"source":"http://www.wikidata.org/entity/Q277338:0","target":"col-5","abs_uri":"http://www.wikidata.org/prop/P279","rel_uri":"p:P279","approximation":false,"readable_label":"subclass of (P279)","id":6},{"source":"http://www.wikidata.org/entity/Q277338:0","target":"http://www.wikidata.org/entity/Q16521:0","abs_uri":"http://www.wikidata.org/prop/P703","rel_uri":"p:P703","approximation":false,"readable_label":"found in taxon (P703)","id":7},{"source":"http://www.wikidata.org/entity/Q37748:0","target":"col-1","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":8},{"source":"http://www.wikidata.org/entity/Q8054:0","target":"col-4","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":9},{"source":"http://www.wikidata.org/entity/Q16521:0","target":"col-6","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":10}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/XIL86R61.links.tsv b/examples/semtab2020_novartis/tables/XIL86R61.links.tsv
new file mode 100644
index 0000000..d5300ca
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/XIL86R61.links.tsv
@@ -0,0 +1,140 @@
+0 0 Q61872392
+0 1 Q63024061
+0 2 Q22809711
+0 3 Q59863437
+0 4 Q64705523
+0 5 Q277338
+0 6 Q7201933
+1 0 Q61872391
+1 1 Q63024061
+1 2 Q22809680
+1 3 Q59859916
+1 4 Q64705522
+1 5 Q277338
+1 6 Q7201933
+2 0 Q61872390
+2 1 Q63024061
+2 2 Q22809711
+2 3 Q59864574
+2 4 Q64705521
+2 5 Q277338
+2 6 Q7201933
+3 0 Q61872389
+3 1 Q63024061
+3 2 Q22809711
+3 3 Q59868946
+3 4 Q64705520
+3 5 Q277338
+3 6 Q7201933
+4 0 Q18973906
+4 1 Q61866965
+4 2 Q22809711
+4 3 Q56745184
+4 4 Q64845974
+4 5 Q277338
+4 6 Q61779043
+5 0 Q18972956
+5 1 Q61866941
+5 2 Q22809680
+5 3 Q59747578
+5 4 Q64843272
+5 5 Q277338
+5 6 Q61779043
+6 0 Q18973406
+6 1 Q61866941
+6 2 Q22809711
+6 3 Q56738175
+6 4 Q64843243
+6 5 Q277338
+6 6 Q61779043
+7 0 Q18973176
+7 1 Q61866898
+7 2 Q22809711
+7 3 Q59760534
+7 4 Q64843002
+7 5 Q277338
+7 6 Q61779043
+8 0 Q18969669
+8 1 Q61866791
+8 2 Q22809680
+8 3 Q61927779
+8 4 Q64842264
+8 5 Q277338
+8 6 Q61779043
+9 0 Q18969306
+9 1 Q61866689
+9 2 Q22809680
+9 3 Q18969273
+9 4 Q64830947
+9 5 Q277338
+9 6 Q61779043
+10 0 Q18969624
+10 1 Q61866689
+10 2 Q22809680
+10 3 Q18969616
+10 4 Q64830920
+10 5 Q277338
+10 6 Q61779043
+11 0 Q18968283
+11 1 Q61815105
+11 2 Q22809680
+11 3 Q56709522
+11 4 Q64830451
+11 5 Q277338
+11 6 Q61779043
+12 0 Q19047202
+12 1 Q61866508
+12 2 Q22809680
+12 3 Q56711258
+12 4 Q64830751
+12 5 Q277338
+12 6 Q61779043
+13 0 Q18974751
+13 1 Q61866941
+13 2 Q22809711
+13 3 Q19043380
+13 4 Q64843203
+13 5 Q277338
+13 6 Q61779043
+14 0 Q18968356
+14 1 Q61815105
+14 2 Q22809711
+14 3 Q61872289
+14 4 Q64830459
+14 5 Q277338
+14 6 Q61779043
+15 0 Q19043380
+15 1 Q61815105
+15 2 Q22809711
+15 3 Q61872292
+15 4 Q64829572
+15 5 Q277338
+15 6 Q61779043
+16 0 Q19043692
+16 1 Q61866524
+16 2 Q22809680
+16 3 Q61927658
+16 4 Q64830806
+16 5 Q277338
+16 6 Q61779043
+17 0 Q19043882
+17 1 Q61815105
+17 2 Q22809680
+17 3 Q56708995
+17 4 Q64830447
+17 5 Q277338
+17 6 Q61779043
+18 0 Q21823217
+18 1 Q15305594
+18 2 Q22809711
+18 3 Q18037209
+18 4 Q21991607
+18 5 Q20747295
+18 6 Q83310
+19 0 Q18266450
+19 1 Q15305616
+19 2 Q22809711
+19 3 Q24403514
+19 4 Q21986254
+19 5 Q20747295
+19 6 Q83310
diff --git a/examples/semtab2020_novartis/tables/YLSE3F5H.csv b/examples/semtab2020_novartis/tables/YLSE3F5H.csv
new file mode 100644
index 0000000..8b0a4e5
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/YLSE3F5H.csv
@@ -0,0 +1,32 @@
+col0,col1,col2
+CTSK,human chromosome 1,reverse strand
+CTSS,human chromosome 1,reverse strand
+SELL,human chromosome 1,reverse strand
+IRF6,human chromosome 1,reverse strand
+HSPG2,human chromosome 1,reverse strand
+AVP1B,human chromosome 1,reverse strand
+MUTYH,human chromosome 1,reverse strand
+SELP,human chromosome 1,reverse strand
+GBA,human chromosome 1,reverse strand
+STMN1,human chromosome 1,reverse strand
+RNSEL,human chromosome 1,reverse strand
+F3,human chromosome 1,reverse strand
+MTOR,human chromosome 1,reverse strand
+NGF,human chromosome 1,reverse strand
+CD34,human chromosome 1,reverse strand
+NID1,human chromosome 1,reverse strand
+NPPB,human chromosome 1,reverse strand
+F5,human chromosome 1,reverse strand
+MTHFR,human chromosome 1,reverse strand
+IL10,human chromosome 1,reverse strand
+SERPINC2,human chromosome 1,reverse strand
+NPPA,human chromosome 1,reverse strand
+ASPM,human chromosome 1,reverse strand
+HCRT,human chromosome 17,reverse strand
+C17orf313,human chromosome 17,reverse strand
+CD53,human chromosome 1,forward strand
+NR5A2,human chromosome 1,forward strand
+CDC42,human chromosome 1,forward strand
+BGLAP,human chromosome 1,forward strand
+FMO3,human chromosome 1,forward strand
+HDAC1,human chromosome 1,forward strand
diff --git a/examples/semtab2020_novartis/tables/YLSE3F5H.json b/examples/semtab2020_novartis/tables/YLSE3F5H.json
new file mode 100644
index 0000000..7b86a3f
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/YLSE3F5H.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["CTSK","CTSS","SELL","IRF6","HSPG2","AVP1B","MUTYH","SELP","GBA","STMN1","RNSEL","F3","MTOR","NGF","CD34","NID1","NPPB","F5","MTHFR","IL10","SERPINC2","NPPA","ASPM","HCRT","C17orf313","CD53","NR5A2","CDC42","BGLAP","FMO3","HDAC1"]},{"index":1,"name":"col1","values":["human chromosome 1","human chromosome 1","human chromosome 1","human chromosome 1","human chromosome 1","human chromosome 1","human chromosome 1","human chromosome 1","human chromosome 1","human chromosome 1","human chromosome 1","human chromosome 1","human chromosome 1","human chromosome 1","human chromosome 1","human chromosome 1","human chromosome 1","human chromosome 1","human chromosome 1","human chromosome 1","human chromosome 1","human chromosome 1","human chromosome 1","human chromosome 17","human chromosome 17","human chromosome 1","human chromosome 1","human chromosome 1","human chromosome 1","human chromosome 1","human chromosome 1"]},{"index":2,"name":"col2","values":["reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","reverse strand","forward strand","forward strand","forward strand","forward strand","forward strand","forward strand"]}],"metadata":{"table_id":"YLSE3F5H","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14902045","qnode_id":"Q14902045"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q430258","qnode_id":"Q430258"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14902024","qnode_id":"Q14902024"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q430258","qnode_id":"Q430258"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14901671","qnode_id":"Q14901671"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q430258","qnode_id":"Q430258"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14890118","qnode_id":"Q14890118"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q430258","qnode_id":"Q430258"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14890110","qnode_id":"Q14890110"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q430258","qnode_id":"Q430258"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14890030","qnode_id":"Q14890030"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q430258","qnode_id":"Q430258"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14883437","qnode_id":"Q14883437"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q430258","qnode_id":"Q430258"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14881072","qnode_id":"Q14881072"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q430258","qnode_id":"Q430258"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q14878770","qnode_id":"Q14878770"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q430258","qnode_id":"Q430258"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14878175","qnode_id":"Q14878175"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q430258","qnode_id":"Q430258"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14877422","qnode_id":"Q14877422"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q430258","qnode_id":"Q430258"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":2,"url":"http://www.wikidata.org/entity/Q14877233","qnode_id":"Q14877233"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q430258","qnode_id":"Q430258"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14876086","qnode_id":"Q14876086"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q430258","qnode_id":"Q430258"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q14873931","qnode_id":"Q14873931"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q430258","qnode_id":"Q430258"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14873747","qnode_id":"Q14873747"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q430258","qnode_id":"Q430258"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14866305","qnode_id":"Q14866305"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q430258","qnode_id":"Q430258"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14865348","qnode_id":"Q14865348"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q430258","qnode_id":"Q430258"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":2,"url":"http://www.wikidata.org/entity/Q14865116","qnode_id":"Q14865116"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q430258","qnode_id":"Q430258"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14864922","qnode_id":"Q14864922"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q430258","qnode_id":"Q430258"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14864464","qnode_id":"Q14864464"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q430258","qnode_id":"Q430258"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q14861061","qnode_id":"Q14861061"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q430258","qnode_id":"Q430258"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14860791","qnode_id":"Q14860791"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q430258","qnode_id":"Q430258"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14860484","qnode_id":"Q14860484"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q430258","qnode_id":"Q430258"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q60641010","qnode_id":"Q60641010"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q220677","qnode_id":"Q220677"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q38973527","qnode_id":"Q38973527"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q220677","qnode_id":"Q220677"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809711","qnode_id":"Q22809711"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q15315489","qnode_id":"Q15315489"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q430258","qnode_id":"Q430258"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q15312697","qnode_id":"Q15312697"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q430258","qnode_id":"Q430258"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q15311701","qnode_id":"Q15311701"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q430258","qnode_id":"Q430258"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14916342","qnode_id":"Q14916342"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q430258","qnode_id":"Q430258"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14914444","qnode_id":"Q14914444"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q430258","qnode_id":"Q430258"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]],[[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14914420","qnode_id":"Q14914420"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q430258","qnode_id":"Q430258"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q22809680","qnode_id":"Q22809680"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/entity/Q7187","rel_uri":"wd:Q7187","approximation":false,"readable_label":"gene (Q7187)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"http://www.wikidata.org/entity/Q37748:0","abs_uri":"http://www.wikidata.org/entity/Q37748","rel_uri":"wd:Q37748","approximation":false,"readable_label":"chromosome (Q37748)"},{"id":"col-2","col_index":2,"label":"col2"}],"edges":[{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"http://www.wikidata.org/entity/Q37748:0","abs_uri":"http://www.wikidata.org/prop/P1057","rel_uri":"p:P1057","approximation":false,"readable_label":"chromosome (P1057)","id":2},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-2","abs_uri":"http://www.wikidata.org/prop/P2548","rel_uri":"p:P2548","approximation":false,"readable_label":"strand orientation (P2548)","id":3},{"source":"http://www.wikidata.org/entity/Q37748:0","target":"col-1","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":4}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/YLSE3F5H.links.tsv b/examples/semtab2020_novartis/tables/YLSE3F5H.links.tsv
new file mode 100644
index 0000000..14fc9fa
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/YLSE3F5H.links.tsv
@@ -0,0 +1,93 @@
+0 0 Q14902045
+0 1 Q430258
+0 2 Q22809711
+1 0 Q14902024
+1 1 Q430258
+1 2 Q22809711
+2 0 Q14901671
+2 1 Q430258
+2 2 Q22809711
+3 0 Q14890118
+3 1 Q430258
+3 2 Q22809711
+4 0 Q14890110
+4 1 Q430258
+4 2 Q22809711
+5 0 Q14890030
+5 1 Q430258
+5 2 Q22809711
+6 0 Q14883437
+6 1 Q430258
+6 2 Q22809711
+7 0 Q14881072
+7 1 Q430258
+7 2 Q22809711
+8 0 Q14878770
+8 1 Q430258
+8 2 Q22809711
+9 0 Q14878175
+9 1 Q430258
+9 2 Q22809711
+10 0 Q14877422
+10 1 Q430258
+10 2 Q22809711
+11 0 Q14877233
+11 1 Q430258
+11 2 Q22809711
+12 0 Q14876086
+12 1 Q430258
+12 2 Q22809711
+13 0 Q14873931
+13 1 Q430258
+13 2 Q22809711
+14 0 Q14873747
+14 1 Q430258
+14 2 Q22809711
+15 0 Q14866305
+15 1 Q430258
+15 2 Q22809711
+16 0 Q14865348
+16 1 Q430258
+16 2 Q22809711
+17 0 Q14865116
+17 1 Q430258
+17 2 Q22809711
+18 0 Q14864922
+18 1 Q430258
+18 2 Q22809711
+19 0 Q14864464
+19 1 Q430258
+19 2 Q22809711
+20 0 Q14861061
+20 1 Q430258
+20 2 Q22809711
+21 0 Q14860791
+21 1 Q430258
+21 2 Q22809711
+22 0 Q14860484
+22 1 Q430258
+22 2 Q22809711
+23 0 Q60641010
+23 1 Q220677
+23 2 Q22809711
+24 0 Q38973527
+24 1 Q220677
+24 2 Q22809711
+25 0 Q15315489
+25 1 Q430258
+25 2 Q22809680
+26 0 Q15312697
+26 1 Q430258
+26 2 Q22809680
+27 0 Q15311701
+27 1 Q430258
+27 2 Q22809680
+28 0 Q14916342
+28 1 Q430258
+28 2 Q22809680
+29 0 Q14914444
+29 1 Q430258
+29 2 Q22809680
+30 0 Q14914420
+30 1 Q430258
+30 2 Q22809680
diff --git a/examples/semtab2020_novartis/tables/YY51Z3LY.csv b/examples/semtab2020_novartis/tables/YY51Z3LY.csv
new file mode 100644
index 0000000..747194c
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/YY51Z3LY.csv
@@ -0,0 +1,21 @@
+col0,col1,col2
+bacterial vaginosis,gynecology,acetic acid
+anogenital venereal wart,infectiology,Interferon alfa-n3
+dracunculiasis,infectiology,thiabendazole
+hepatitis E,infectiology,ribavirin
+dirofilariasis,infectiology,imidacloprid
+acariasis,infectiology,imidacloprid
+cysticercosis,infectiology,albendazole
+paracoccidioidomycosis,infectiology,Ketoconazole
+actinomycosis,infectiology,amoxicillin
+shigellosis,infectiology,bismuth
+acute hemorrhagic conjunctivitis,infectiology,emedastine
+amoebic dysentery,infectiology,iodoquinol
+fascioloidiasis,veterinary medicine,emetine
+intestinal schistosomiasis,infectiology,oxamniquine
+hymenolepiasis,infectiology,albendazole
+ancylostomiasis,infectiology,albendazole
+genital herpes,infectiology,aciclovir
+Pseudomonas infection,pulmonology,lomefloxacin
+dermatophytosis,infectiology,griseofulvin
+allergic bronchopulmonary aspergillosis,pulmonology,Itraconazole
diff --git a/examples/semtab2020_novartis/tables/YY51Z3LY.json b/examples/semtab2020_novartis/tables/YY51Z3LY.json
new file mode 100644
index 0000000..ea9681c
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/YY51Z3LY.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["bacterial vaginosis","anogenital venereal wart","dracunculiasis","hepatitis E","dirofilariasis","acariasis","cysticercosis","paracoccidioidomycosis","actinomycosis","shigellosis","acute hemorrhagic conjunctivitis","amoebic dysentery","fascioloidiasis","intestinal schistosomiasis","hymenolepiasis","ancylostomiasis","genital herpes","Pseudomonas infection","dermatophytosis","allergic bronchopulmonary aspergillosis"]},{"index":1,"name":"col1","values":["gynecology","infectiology","infectiology","infectiology","infectiology","infectiology","infectiology","infectiology","infectiology","infectiology","infectiology","infectiology","veterinary medicine","infectiology","infectiology","infectiology","infectiology","pulmonology","infectiology","pulmonology"]},{"index":2,"name":"col2","values":["acetic acid","Interferon alfa-n3","thiabendazole","ribavirin","imidacloprid","imidacloprid","albendazole","Ketoconazole","amoxicillin","bismuth","emedastine","iodoquinol","emetine","oxamniquine","albendazole","albendazole","aciclovir","lomefloxacin","griseofulvin","Itraconazole"]}],"metadata":{"table_id":"YY51Z3LY","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q804521","qnode_id":"Q804521"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q1221899","qnode_id":"Q1221899"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q47512","qnode_id":"Q47512"}]],[[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q208726","qnode_id":"Q208726"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":18,"url":"http://www.wikidata.org/entity/Q6046446","qnode_id":"Q6046446"}]],[[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q388646","qnode_id":"Q388646"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q424986","qnode_id":"Q424986"}]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q326643","qnode_id":"Q326643"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q421862","qnode_id":"Q421862"}]],[[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q443929","qnode_id":"Q443929"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q420098","qnode_id":"Q420098"}]],[[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q337998","qnode_id":"Q337998"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q420098","qnode_id":"Q420098"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q246068","qnode_id":"Q246068"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q411629","qnode_id":"Q411629"}]],[[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q247096","qnode_id":"Q247096"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q407883","qnode_id":"Q407883"}]],[[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q422268","qnode_id":"Q422268"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q201928","qnode_id":"Q201928"}]],[[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q327298","qnode_id":"Q327298"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q942","qnode_id":"Q942"}]],[[{"start":0,"end":32,"url":"http://www.wikidata.org/entity/Q11491562","qnode_id":"Q11491562"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q5370305","qnode_id":"Q5370305"}]],[[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q15460254","qnode_id":"Q15460254"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":10,"url":"http://www.wikidata.org/entity/Q5276473","qnode_id":"Q5276473"}]],[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q11996271","qnode_id":"Q11996271"}],[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q170201","qnode_id":"Q170201"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q3050386","qnode_id":"Q3050386"}]],[[{"start":0,"end":26,"url":"http://www.wikidata.org/entity/Q18553329","qnode_id":"Q18553329"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q682497","qnode_id":"Q682497"}]],[[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q12656487","qnode_id":"Q12656487"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q411629","qnode_id":"Q411629"}]],[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q11679861","qnode_id":"Q11679861"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q411629","qnode_id":"Q411629"}]],[[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q7476596","qnode_id":"Q7476596"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q147101","qnode_id":"Q147101"}]],[[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q7255052","qnode_id":"Q7255052"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q203337","qnode_id":"Q203337"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q203618","qnode_id":"Q203618"}]],[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q1909343","qnode_id":"Q1909343"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q788926","qnode_id":"Q788926"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q416096","qnode_id":"Q416096"}]],[[{"start":0,"end":39,"url":"http://www.wikidata.org/entity/Q1430844","qnode_id":"Q1430844"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q203337","qnode_id":"Q203337"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q411229","qnode_id":"Q411229"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q18123741:0","abs_uri":"http://www.wikidata.org/entity/Q18123741","rel_uri":"wd:Q18123741","approximation":false,"readable_label":"infectious disease (Q18123741)"},{"id":"col-1","col_index":1,"label":"col1"},{"id":"http://www.wikidata.org/entity/Q930752:0","abs_uri":"http://www.wikidata.org/entity/Q930752","rel_uri":"wd:Q930752","approximation":false,"readable_label":"medical specialty (Q930752)"},{"id":"col-2","col_index":2,"label":"col2"}],"edges":[{"source":"http://www.wikidata.org/entity/Q18123741:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q18123741:0","target":"http://www.wikidata.org/entity/Q930752:0","abs_uri":"http://www.wikidata.org/prop/P1995","rel_uri":"p:P1995","approximation":false,"readable_label":"health specialty (P1995)","id":2},{"source":"http://www.wikidata.org/entity/Q18123741:0","target":"col-2","abs_uri":"http://www.wikidata.org/prop/P2176","rel_uri":"p:P2176","approximation":false,"readable_label":"drug used for treatment (P2176)","id":3},{"source":"http://www.wikidata.org/entity/Q930752:0","target":"col-1","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":4}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/YY51Z3LY.links.tsv b/examples/semtab2020_novartis/tables/YY51Z3LY.links.tsv
new file mode 100644
index 0000000..f08a7b7
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/YY51Z3LY.links.tsv
@@ -0,0 +1,60 @@
+0 0 Q804521
+0 1 Q1221899
+0 2 Q47512
+1 0 Q208726
+1 1 Q788926
+1 2 Q6046446
+2 0 Q388646
+2 1 Q788926
+2 2 Q424986
+3 0 Q326643
+3 1 Q788926
+3 2 Q421862
+4 0 Q443929
+4 1 Q788926
+4 2 Q420098
+5 0 Q337998
+5 1 Q788926
+5 2 Q420098
+6 0 Q246068
+6 1 Q788926
+6 2 Q411629
+7 0 Q247096
+7 1 Q788926
+7 2 Q407883
+8 0 Q422268
+8 1 Q788926
+8 2 Q201928
+9 0 Q327298
+9 1 Q788926
+9 2 Q942
+10 0 Q11491562
+10 1 Q788926
+10 2 Q5370305
+11 0 Q15460254
+11 1 Q788926
+11 2 Q5276473
+12 0 Q11996271
+12 1 Q170201
+12 2 Q3050386
+13 0 Q18553329
+13 1 Q788926
+13 2 Q682497
+14 0 Q12656487
+14 1 Q788926
+14 2 Q411629
+15 0 Q11679861
+15 1 Q788926
+15 2 Q411629
+16 0 Q7476596
+16 1 Q788926
+16 2 Q147101
+17 0 Q7255052
+17 1 Q203337
+17 2 Q203618
+18 0 Q1909343
+18 1 Q788926
+18 2 Q416096
+19 0 Q1430844
+19 1 Q203337
+19 2 Q411229
diff --git a/examples/semtab2020_novartis/tables/YZPEH2QC.csv b/examples/semtab2020_novartis/tables/YZPEH2QC.csv
new file mode 100644
index 0000000..da938c6
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/YZPEH2QC.csv
@@ -0,0 +1,21 @@
+col0,col1,col2,col3
+Fraser syndrome,Cryptophthalmos,medical genetics,FRAS1
+Norrie disease,eye disease,medical genetics,NDP
+nasopharynx carcinoma,carcinoma,oncology,ITGA9
+Sturge–Weber syndrome,phakomatosis,medical genetics,GNAQ
+hypoparathyroidism-deafness-renal disease syndrome,chromosomal deletion syndrome,medical genetics,GATA3
+Pendred syndrome,Thyroid dyshormonogenesis,endocrinology,SLC26A4
+popliteal pterygium syndrome,IRF6-related disorders,medical genetics,IRF6
+Schimmelpenning syndrome,syndrome,medical genetics,HRAS
+prolactinoma,functioning pituitary adenoma,oncology,AIP
+Townes-Brocks syndrome,autosomal dominant disease,medical genetics,SALL1
+Simpson-Golabi-Behmel syndrome,macroglossia,medical genetics,GPC3
+Proteus syndrome,birth defect,medical genetics,AKT1
+CHARGE syndrome,syndrome,medical genetics,CHD7
+Muir-Torre syndrome,Lynch syndrome,oncology,MSH2
+von Hippel-Lindau disease,hemangioblastoma,medical genetics,VHL
+retinoblastoma,ocular cancer,oncology,RB1
+Carey Fineman Ziter syndrome,syndrome,medical genetics,MYMK
+CHILD syndrome,nevus,medical genetics,NSDHL
+rapadilino syndrome,autosomal recessive disease,medical genetics,RECQL4
+otopalatodigital syndrome type 1,otopalatodigital syndrome,medical genetics,FLNA
diff --git a/examples/semtab2020_novartis/tables/YZPEH2QC.json b/examples/semtab2020_novartis/tables/YZPEH2QC.json
new file mode 100644
index 0000000..5e993f4
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/YZPEH2QC.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["Fraser syndrome","Norrie disease","nasopharynx carcinoma","Sturge–Weber syndrome","hypoparathyroidism-deafness-renal disease syndrome","Pendred syndrome","popliteal pterygium syndrome","Schimmelpenning syndrome","prolactinoma","Townes-Brocks syndrome","Simpson-Golabi-Behmel syndrome","Proteus syndrome","CHARGE syndrome","Muir-Torre syndrome","von Hippel-Lindau disease","retinoblastoma","Carey Fineman Ziter syndrome","CHILD syndrome","rapadilino syndrome","otopalatodigital syndrome type 1"]},{"index":1,"name":"col1","values":["Cryptophthalmos","eye disease","carcinoma","phakomatosis","chromosomal deletion syndrome","Thyroid dyshormonogenesis","IRF6-related disorders","syndrome","functioning pituitary adenoma","autosomal dominant disease","macroglossia","birth defect","syndrome","Lynch syndrome","hemangioblastoma","ocular cancer","syndrome","nevus","autosomal recessive disease","otopalatodigital syndrome"]},{"index":2,"name":"col2","values":["medical genetics","medical genetics","oncology","medical genetics","medical genetics","endocrinology","medical genetics","medical genetics","oncology","medical genetics","medical genetics","medical genetics","medical genetics","oncology","medical genetics","oncology","medical genetics","medical genetics","medical genetics","medical genetics"]},{"index":3,"name":"col3","values":["FRAS1","NDP","ITGA9","GNAQ","GATA3","SLC26A4","IRF6","HRAS","AIP","SALL1","GPC3","AKT1","CHD7","MSH2","VHL","RB1","MYMK","NSDHL","RECQL4","FLNA"]}],"metadata":{"table_id":"YZPEH2QC","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q1425572","qnode_id":"Q1425572"}],[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q590324","qnode_id":"Q590324"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18046595","qnode_id":"Q18046595"}]],[[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q1415842","qnode_id":"Q1415842"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q3041498","qnode_id":"Q3041498"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q18029892","qnode_id":"Q18029892"}]],[[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q1693598","qnode_id":"Q1693598"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q33525","qnode_id":"Q33525"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q162555","qnode_id":"Q162555"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18028022","qnode_id":"Q18028022"}]],[[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q1886238","qnode_id":"Q1886238"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q748376","qnode_id":"Q748376"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18026236","qnode_id":"Q18026236"}]],[[{"start":0,"end":50,"url":"http://www.wikidata.org/entity/Q2027515","qnode_id":"Q2027515"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q16918398","qnode_id":"Q16918398"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18025464","qnode_id":"Q18025464"}]],[[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1707822","qnode_id":"Q1707822"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q7799748","qnode_id":"Q7799748"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q162606","qnode_id":"Q162606"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q14907462","qnode_id":"Q14907462"}]],[[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q1587881","qnode_id":"Q1587881"}],[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q3281419","qnode_id":"Q3281419"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14890118","qnode_id":"Q14890118"}]],[[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q1274018","qnode_id":"Q1274018"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q179630","qnode_id":"Q179630"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14884140","qnode_id":"Q14884140"}]],[[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q954831","qnode_id":"Q954831"}],[{"start":0,"end":29,"url":"http://www.wikidata.org/entity/Q18556775","qnode_id":"Q18556775"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q162555","qnode_id":"Q162555"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q18033785","qnode_id":"Q18033785"}]],[[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q385774","qnode_id":"Q385774"}],[{"start":0,"end":26,"url":"http://www.wikidata.org/entity/Q18553439","qnode_id":"Q18553439"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18031372","qnode_id":"Q18031372"}]],[[{"start":0,"end":30,"url":"http://www.wikidata.org/entity/Q478891","qnode_id":"Q478891"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q524095","qnode_id":"Q524095"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18026064","qnode_id":"Q18026064"}]],[[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q281115","qnode_id":"Q281115"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q727096","qnode_id":"Q727096"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q17816452","qnode_id":"Q17816452"}]],[[{"start":0,"end":15,"url":"http://www.wikidata.org/entity/Q1023604","qnode_id":"Q1023604"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q179630","qnode_id":"Q179630"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q15311448","qnode_id":"Q15311448"}]],[[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q827497","qnode_id":"Q827497"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q783644","qnode_id":"Q783644"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q162555","qnode_id":"Q162555"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q14911688","qnode_id":"Q14911688"}]],[[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q741315","qnode_id":"Q741315"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q460937","qnode_id":"Q460937"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q14905473","qnode_id":"Q14905473"}]],[[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q500695","qnode_id":"Q500695"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q2420648","qnode_id":"Q2420648"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q162555","qnode_id":"Q162555"}],[{"start":0,"end":3,"url":"http://www.wikidata.org/entity/Q40108","qnode_id":"Q40108"}]],[[{"start":0,"end":28,"url":"http://www.wikidata.org/entity/Q3508625","qnode_id":"Q3508625"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q179630","qnode_id":"Q179630"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18056721","qnode_id":"Q18056721"}]],[[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q3508568","qnode_id":"Q3508568"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q692077","qnode_id":"Q692077"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18039859","qnode_id":"Q18039859"}]],[[{"start":0,"end":19,"url":"http://www.wikidata.org/entity/Q3508578","qnode_id":"Q3508578"}],[],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}],[{"start":0,"end":6,"url":"http://www.wikidata.org/entity/Q18034064","qnode_id":"Q18034064"}]],[[{"start":0,"end":32,"url":"http://www.wikidata.org/entity/Q3508782","qnode_id":"Q3508782"}],[{"start":0,"end":25,"url":"http://www.wikidata.org/entity/Q56014249","qnode_id":"Q56014249"}],[{"start":0,"end":16,"url":"http://www.wikidata.org/entity/Q1071953","qnode_id":"Q1071953"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q17928815","qnode_id":"Q17928815"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q55789477:0","abs_uri":"http://www.wikidata.org/entity/Q55789477","rel_uri":"wd:Q55789477","approximation":false,"readable_label":"head and neck disease (Q55789477)"},{"id":"col-2","col_index":2,"label":"col2"},{"id":"http://www.wikidata.org/entity/Q930752:0","abs_uri":"http://www.wikidata.org/entity/Q930752","rel_uri":"wd:Q930752","approximation":false,"readable_label":"medical specialty (Q930752)"},{"id":"col-3","col_index":3,"label":"col3"},{"id":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/entity/Q7187","rel_uri":"wd:Q7187","approximation":false,"readable_label":"gene (Q7187)"},{"id":"col-1","col_index":1,"label":"col1"}],"edges":[{"source":"http://www.wikidata.org/entity/Q55789477:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q55789477:0","target":"col-1","abs_uri":"http://www.wikidata.org/prop/P279","rel_uri":"p:P279","approximation":false,"readable_label":"subclass of (P279)","id":2},{"source":"http://www.wikidata.org/entity/Q55789477:0","target":"http://www.wikidata.org/entity/Q930752:0","abs_uri":"http://www.wikidata.org/prop/P1995","rel_uri":"p:P1995","approximation":false,"readable_label":"health specialty (P1995)","id":3},{"source":"http://www.wikidata.org/entity/Q55789477:0","target":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/prop/P2293","rel_uri":"p:P2293","approximation":false,"readable_label":"genetic association (P2293)","id":4},{"source":"http://www.wikidata.org/entity/Q930752:0","target":"col-2","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":5},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-3","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":6}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/YZPEH2QC.links.tsv b/examples/semtab2020_novartis/tables/YZPEH2QC.links.tsv
new file mode 100644
index 0000000..4791cf0
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/YZPEH2QC.links.tsv
@@ -0,0 +1,79 @@
+0 0 Q1425572
+0 1 Q590324
+0 2 Q1071953
+0 3 Q18046595
+1 0 Q1415842
+1 1 Q3041498
+1 2 Q1071953
+1 3 Q18029892
+2 0 Q1693598
+2 1 Q33525
+2 2 Q162555
+2 3 Q18028022
+3 0 Q1886238
+3 1 Q748376
+3 2 Q1071953
+3 3 Q18026236
+4 0 Q2027515
+4 1 Q16918398
+4 2 Q1071953
+4 3 Q18025464
+5 0 Q1707822
+5 1 Q7799748
+5 2 Q162606
+5 3 Q14907462
+6 0 Q1587881
+6 1 Q3281419
+6 2 Q1071953
+6 3 Q14890118
+7 0 Q1274018
+7 1 Q179630
+7 2 Q1071953
+7 3 Q14884140
+8 0 Q954831
+8 1 Q18556775
+8 2 Q162555
+8 3 Q18033785
+9 0 Q385774
+9 1 Q18553439
+9 2 Q1071953
+9 3 Q18031372
+10 0 Q478891
+10 1 Q524095
+10 2 Q1071953
+10 3 Q18026064
+11 0 Q281115
+11 1 Q727096
+11 2 Q1071953
+11 3 Q17816452
+12 0 Q1023604
+12 1 Q179630
+12 2 Q1071953
+12 3 Q15311448
+13 0 Q827497
+13 1 Q783644
+13 2 Q162555
+13 3 Q14911688
+14 0 Q741315
+14 1 Q460937
+14 2 Q1071953
+14 3 Q14905473
+15 0 Q500695
+15 1 Q2420648
+15 2 Q162555
+15 3 Q40108
+16 0 Q3508625
+16 1 Q179630
+16 2 Q1071953
+16 3 Q18056721
+17 0 Q3508568
+17 1 Q692077
+17 2 Q1071953
+17 3 Q18039859
+18 0 Q3508578
+18 2 Q1071953
+18 3 Q18034064
+19 0 Q3508782
+19 1 Q56014249
+19 2 Q1071953
+19 3 Q17928815
diff --git a/examples/semtab2020_novartis/tables/ZVXL71YI.csv b/examples/semtab2020_novartis/tables/ZVXL71YI.csv
new file mode 100644
index 0000000..290c2df
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/ZVXL71YI.csv
@@ -0,0 +1,21 @@
+col0,col1,col2,col3
+Adenosine A2b receptor,membrane proteins,Adora2b,caffeine
+Adenosine A2b receptor,Adenosine A2B receptor,ADORA2B,caffeine
+Adenosine A2a receptor,G protein-coupled receptor,ADORA2A,caffeine
+Potassium sodium-activated channel subfamily T member 1,"Potassium channel, BK, alpha subunit",KCNT1,loxapine
+Dopamine receptor D3,G protein-coupled receptor,DRD3,loxapine
+5-hydroxytryptamine (serotonin) receptor 7,membrane proteins,Htr7,amitriptyline
+Dopamine receptor D5,Dopamine D5 receptor,DRD5,thioridazine
+Dopamine receptor D1,Dopamine D1 receptor,DRD1,thioridazine
+5-hydroxytryptamine receptor 1A,5-Hydroxytryptamine 1A receptor,HTR1A,thioridazine
+Prostaglandin-endoperoxide synthase 1,Haem peroxidase superfamily,PTGS1,acetaminophen
+Opioid receptor delta 1,Delta opioid receptor,OPRD1,pethidine
+Transient receptor potential cation channel subfamily M member 3,Transient receptor potential cation channel subfamily M member 3,TRPM3,mefenamic acid
+"Transient receptor potential cation channel, subfamily V, member 1",Transient receptor potential cation channel subfamily V member 1,Trpv1,capsaicin
+Androgen receptor,Androgen receptor,AR,bisphenol A
+Glucocorticoid receptor,Glucocorticoid receptor,Nr3c1,corticosterone
+Prostaglandin F receptor,Prostaglandin F receptor,PTGFR,dinoprost
+Transient receptor potential cation channel subfamily M member 8,Transient receptor potential cation channel subfamily M member 8,TRPM8,menthol
+"Transient receptor potential cation channel, subfamily A, member 1",Transient receptor potential cation channel subfamily A member 1,Trpa1,menthol
+Transient receptor potential cation channel subfamily A member 1,Transient receptor potential cation channel subfamily A member 1,TRPA1,oleocanthal
+Transient receptor potential cation channel subfamily M member 4,Transient receptor potential cation channel subfamily M member 4,TRPM4,adenosine diphosphate
diff --git a/examples/semtab2020_novartis/tables/ZVXL71YI.json b/examples/semtab2020_novartis/tables/ZVXL71YI.json
new file mode 100644
index 0000000..3ed6c2d
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/ZVXL71YI.json
@@ -0,0 +1 @@
+{"table":{"table":{"columns":[{"index":0,"name":"col0","values":["Adenosine A2b receptor","Adenosine A2b receptor","Adenosine A2a receptor","Potassium sodium-activated channel subfamily T member 1","Dopamine receptor D3","5-hydroxytryptamine (serotonin) receptor 7","Dopamine receptor D5","Dopamine receptor D1","5-hydroxytryptamine receptor 1A","Prostaglandin-endoperoxide synthase 1","Opioid receptor delta 1","Transient receptor potential cation channel subfamily M member 3","Transient receptor potential cation channel, subfamily V, member 1","Androgen receptor","Glucocorticoid receptor","Prostaglandin F receptor","Transient receptor potential cation channel subfamily M member 8","Transient receptor potential cation channel, subfamily A, member 1","Transient receptor potential cation channel subfamily A member 1","Transient receptor potential cation channel subfamily M member 4"]},{"index":1,"name":"col1","values":["membrane proteins","Adenosine A2B receptor","G protein-coupled receptor","Potassium channel, BK, alpha subunit","G protein-coupled receptor","membrane proteins","Dopamine D5 receptor","Dopamine D1 receptor","5-Hydroxytryptamine 1A receptor","Haem peroxidase superfamily","Delta opioid receptor","Transient receptor potential cation channel subfamily M member 3","Transient receptor potential cation channel subfamily V member 1","Androgen receptor","Glucocorticoid receptor","Prostaglandin F receptor","Transient receptor potential cation channel subfamily M member 8","Transient receptor potential cation channel subfamily A member 1","Transient receptor potential cation channel subfamily A member 1","Transient receptor potential cation channel subfamily M member 4"]},{"index":2,"name":"col2","values":["Adora2b","ADORA2B","ADORA2A","KCNT1","DRD3","Htr7","DRD5","DRD1","HTR1A","PTGS1","OPRD1","TRPM3","Trpv1","AR","Nr3c1","PTGFR","TRPM8","Trpa1","TRPA1","TRPM4"]},{"index":3,"name":"col3","values":["caffeine","caffeine","caffeine","loxapine","loxapine","amitriptyline","thioridazine","thioridazine","thioridazine","acetaminophen","pethidine","mefenamic acid","capsaicin","bisphenol A","corticosterone","dinoprost","menthol","menthol","oleocanthal","adenosine diphosphate"]}],"metadata":{"table_id":"ZVXL71YI","page_title":"","table_name":"","text_before":"","text_after":""}},"context":{"page_title":null,"page_url":null,"page_qnode":null},"links":[[[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q21498709","qnode_id":"Q21498709"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q423042","qnode_id":"Q423042"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q18247437","qnode_id":"Q18247437"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q60235","qnode_id":"Q60235"}]],[[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q21116164","qnode_id":"Q21116164"}],[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q24788201","qnode_id":"Q24788201"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q4682273","qnode_id":"Q4682273"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q60235","qnode_id":"Q60235"}]],[[{"start":0,"end":22,"url":"http://www.wikidata.org/entity/Q21116162","qnode_id":"Q21116162"}],[{"start":0,"end":26,"url":"http://www.wikidata.org/entity/Q38173","qnode_id":"Q38173"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q3063865","qnode_id":"Q3063865"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q60235","qnode_id":"Q60235"}]],[[{"start":0,"end":55,"url":"http://www.wikidata.org/entity/Q21118576","qnode_id":"Q21118576"}],[{"start":0,"end":36,"url":"http://www.wikidata.org/entity/Q24740685","qnode_id":"Q24740685"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18043170","qnode_id":"Q18043170"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q58614","qnode_id":"Q58614"}]],[[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q21110872","qnode_id":"Q21110872"}],[{"start":0,"end":26,"url":"http://www.wikidata.org/entity/Q38173","qnode_id":"Q38173"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q2338200","qnode_id":"Q2338200"}],[{"start":0,"end":8,"url":"http://www.wikidata.org/entity/Q58614","qnode_id":"Q58614"}]],[[{"start":0,"end":42,"url":"http://www.wikidata.org/entity/Q21497939","qnode_id":"Q21497939"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q423042","qnode_id":"Q423042"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q18251376","qnode_id":"Q18251376"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q58397","qnode_id":"Q58397"}]],[[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q21131500","qnode_id":"Q21131500"}],[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q24770874","qnode_id":"Q24770874"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q2338184","qnode_id":"Q2338184"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q58375","qnode_id":"Q58375"}]],[[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q21110867","qnode_id":"Q21110867"}],[{"start":0,"end":20,"url":"http://www.wikidata.org/entity/Q24720098","qnode_id":"Q24720098"}],[{"start":0,"end":4,"url":"http://www.wikidata.org/entity/Q2035393","qnode_id":"Q2035393"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q58375","qnode_id":"Q58375"}]],[[{"start":0,"end":31,"url":"http://www.wikidata.org/entity/Q21115043","qnode_id":"Q21115043"}],[{"start":0,"end":31,"url":"http://www.wikidata.org/entity/Q24775271","qnode_id":"Q24775271"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q238509","qnode_id":"Q238509"}],[{"start":0,"end":12,"url":"http://www.wikidata.org/entity/Q58375","qnode_id":"Q58375"}]],[[{"start":0,"end":37,"url":"http://www.wikidata.org/entity/Q21126810","qnode_id":"Q21126810"}],[{"start":0,"end":27,"url":"http://www.wikidata.org/entity/Q24773605","qnode_id":"Q24773605"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18030905","qnode_id":"Q18030905"}],[{"start":0,"end":13,"url":"http://www.wikidata.org/entity/Q57055","qnode_id":"Q57055"}]],[[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q21115334","qnode_id":"Q21115334"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q24725558","qnode_id":"Q24725558"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18030283","qnode_id":"Q18030283"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q55434","qnode_id":"Q55434"}]],[[{"start":0,"end":64,"url":"http://www.wikidata.org/entity/Q7671479","qnode_id":"Q7671479"}],[{"start":0,"end":64,"url":"http://www.wikidata.org/entity/Q24766885","qnode_id":"Q24766885"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q15324528","qnode_id":"Q15324528"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q284321","qnode_id":"Q284321"}]],[[{"start":0,"end":66,"url":"http://www.wikidata.org/entity/Q14908176","qnode_id":"Q14908176"}],[{"start":0,"end":64,"url":"http://www.wikidata.org/entity/Q24781365","qnode_id":"Q24781365"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14908165","qnode_id":"Q14908165"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q273169","qnode_id":"Q273169"}]],[[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q416601","qnode_id":"Q416601"}],[{"start":0,"end":17,"url":"http://www.wikidata.org/entity/Q24783766","qnode_id":"Q24783766"}],[{"start":0,"end":2,"url":"http://www.wikidata.org/entity/Q14873015","qnode_id":"Q14873015"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q271980","qnode_id":"Q271980"}]],[[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q21982871","qnode_id":"Q21982871"}],[{"start":0,"end":23,"url":"http://www.wikidata.org/entity/Q24742015","qnode_id":"Q24742015"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q14877412","qnode_id":"Q14877412"}],[{"start":0,"end":14,"url":"http://www.wikidata.org/entity/Q422543","qnode_id":"Q422543"}]],[[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q21122493","qnode_id":"Q21122493"}],[{"start":0,"end":24,"url":"http://www.wikidata.org/entity/Q24782238","qnode_id":"Q24782238"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q18030902","qnode_id":"Q18030902"}],[{"start":0,"end":9,"url":"http://www.wikidata.org/entity/Q421375","qnode_id":"Q421375"}]],[[{"start":0,"end":64,"url":"http://www.wikidata.org/entity/Q7671495","qnode_id":"Q7671495"}],[{"start":0,"end":64,"url":"http://www.wikidata.org/entity/Q24785144","qnode_id":"Q24785144"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q15324395","qnode_id":"Q15324395"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q407418","qnode_id":"Q407418"}]],[[{"start":0,"end":66,"url":"http://www.wikidata.org/entity/Q15323278","qnode_id":"Q15323278"}],[{"start":0,"end":64,"url":"http://www.wikidata.org/entity/Q24768973","qnode_id":"Q24768973"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q15323276","qnode_id":"Q15323276"}],[{"start":0,"end":7,"url":"http://www.wikidata.org/entity/Q407418","qnode_id":"Q407418"}]],[[{"start":0,"end":64,"url":"http://www.wikidata.org/entity/Q7834290","qnode_id":"Q7834290"}],[{"start":0,"end":64,"url":"http://www.wikidata.org/entity/Q24768973","qnode_id":"Q24768973"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q15323274","qnode_id":"Q15323274"}],[{"start":0,"end":11,"url":"http://www.wikidata.org/entity/Q402613","qnode_id":"Q402613"}]],[[{"start":0,"end":64,"url":"http://www.wikidata.org/entity/Q7671480","qnode_id":"Q7671480"}],[{"start":0,"end":64,"url":"http://www.wikidata.org/entity/Q24740597","qnode_id":"Q24740597"}],[{"start":0,"end":5,"url":"http://www.wikidata.org/entity/Q15323765","qnode_id":"Q15323765"}],[{"start":0,"end":21,"url":"http://www.wikidata.org/entity/Q185253","qnode_id":"Q185253"}]]]},"semantic_models":[{"version":1,"nodes":[{"id":"col-0","col_index":0,"label":"col0"},{"id":"http://www.wikidata.org/entity/Q8054:0","abs_uri":"http://www.wikidata.org/entity/Q8054","rel_uri":"wd:Q8054","approximation":false,"readable_label":"protein (Q8054)"},{"id":"col-2","col_index":2,"label":"col2"},{"id":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/entity/Q7187","rel_uri":"wd:Q7187","approximation":false,"readable_label":"gene (Q7187)"},{"id":"col-3","col_index":3,"label":"col3"},{"id":"http://www.wikidata.org/entity/Q11173:0","abs_uri":"http://www.wikidata.org/entity/Q11173","rel_uri":"wd:Q11173","approximation":false,"readable_label":"chemical compound (Q11173)"},{"id":"col-1","col_index":1,"label":"col1"}],"edges":[{"source":"http://www.wikidata.org/entity/Q8054:0","target":"col-0","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":1},{"source":"http://www.wikidata.org/entity/Q8054:0","target":"col-1","abs_uri":"http://www.wikidata.org/prop/P361","rel_uri":"p:P361","approximation":false,"readable_label":"part of (P361)","id":2},{"source":"http://www.wikidata.org/entity/Q8054:0","target":"http://www.wikidata.org/entity/Q7187:0","abs_uri":"http://www.wikidata.org/prop/P702","rel_uri":"p:P702","approximation":false,"readable_label":"encoded by (P702)","id":3},{"source":"http://www.wikidata.org/entity/Q8054:0","target":"http://www.wikidata.org/entity/Q11173:0","abs_uri":"http://www.wikidata.org/prop/P129","rel_uri":"p:P129","approximation":false,"readable_label":"physically interacts with (P129)","id":4},{"source":"http://www.wikidata.org/entity/Q7187:0","target":"col-2","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":5},{"source":"http://www.wikidata.org/entity/Q11173:0","target":"col-3","abs_uri":"http://www.w3.org/2000/01/rdf-schema#label","rel_uri":"rdfs:label","approximation":false,"readable_label":null,"id":6}]}]}
\ No newline at end of file
diff --git a/examples/semtab2020_novartis/tables/ZVXL71YI.links.tsv b/examples/semtab2020_novartis/tables/ZVXL71YI.links.tsv
new file mode 100644
index 0000000..82c20b9
--- /dev/null
+++ b/examples/semtab2020_novartis/tables/ZVXL71YI.links.tsv
@@ -0,0 +1,80 @@
+0 0 Q21498709
+0 1 Q423042
+0 2 Q18247437
+0 3 Q60235
+1 0 Q21116164
+1 1 Q24788201
+1 2 Q4682273
+1 3 Q60235
+2 0 Q21116162
+2 1 Q38173
+2 2 Q3063865
+2 3 Q60235
+3 0 Q21118576
+3 1 Q24740685
+3 2 Q18043170
+3 3 Q58614
+4 0 Q21110872
+4 1 Q38173
+4 2 Q2338200
+4 3 Q58614
+5 0 Q21497939
+5 1 Q423042
+5 2 Q18251376
+5 3 Q58397
+6 0 Q21131500
+6 1 Q24770874
+6 2 Q2338184
+6 3 Q58375
+7 0 Q21110867
+7 1 Q24720098
+7 2 Q2035393
+7 3 Q58375
+8 0 Q21115043
+8 1 Q24775271
+8 2 Q238509
+8 3 Q58375
+9 0 Q21126810
+9 1 Q24773605
+9 2 Q18030905
+9 3 Q57055
+10 0 Q21115334
+10 1 Q24725558
+10 2 Q18030283
+10 3 Q55434
+11 0 Q7671479
+11 1 Q24766885
+11 2 Q15324528
+11 3 Q284321
+12 0 Q14908176
+12 1 Q24781365
+12 2 Q14908165
+12 3 Q273169
+13 0 Q416601
+13 1 Q24783766
+13 2 Q14873015
+13 3 Q271980
+14 0 Q21982871
+14 1 Q24742015
+14 2 Q14877412
+14 3 Q422543
+15 0 Q21122493
+15 1 Q24782238
+15 2 Q18030902
+15 3 Q421375
+16 0 Q7671495
+16 1 Q24785144
+16 2 Q15324395
+16 3 Q407418
+17 0 Q15323278
+17 1 Q24768973
+17 2 Q15323276
+17 3 Q407418
+18 0 Q7834290
+18 1 Q24768973
+18 2 Q15323274
+18 3 Q402613
+19 0 Q7671480
+19 1 Q24740597
+19 2 Q15323765
+19 3 Q185253
diff --git a/examples/t2dv2/ground-truth/29414811_2_4773219892816395776/version.01.json b/examples/t2dv2/ground-truth/29414811_2_4773219892816395776/version.01.json
new file mode 100644
index 0000000..cfb613e
--- /dev/null
+++ b/examples/t2dv2/ground-truth/29414811_2_4773219892816395776/version.01.json
@@ -0,0 +1,92 @@
+{
+ "version": 2,
+ "table_id": "29414811_2_4773219892816395776",
+ "semantic_models": [
+ {
+ "version": 1,
+ "nodes": [
+ {
+ "id": "wd:Q7889:0",
+ "abs_uri": "http://www.wikidata.org/entity/Q7889",
+ "rel_uri": "wd:Q7889",
+ "approximation": false,
+ "readable_label": null
+ },
+ {
+ "id": "column-2",
+ "col_index": 2,
+ "label": "Genre"
+ },
+ {
+ "id": "column-1",
+ "col_index": 1,
+ "label": "Game"
+ },
+ {
+ "id": "column-4",
+ "col_index": 4,
+ "label": "Developer(s)"
+ },
+ {
+ "id": "column-0",
+ "col_index": 0,
+ "label": "Year"
+ },
+ {
+ "id": "column-3",
+ "col_index": 3,
+ "label": "Platform(s)"
+ }
+ ],
+ "edges": [
+ {
+ "source": "wd:Q7889:0",
+ "target": "column-2",
+ "abs_uri": "http://www.wikidata.org/prop/P136",
+ "rel_uri": "p:P136",
+ "approximation": false,
+ "readable_label": null,
+ "id": 1
+ },
+ {
+ "source": "wd:Q7889:0",
+ "target": "column-1",
+ "abs_uri": "http://www.w3.org/2000/01/rdf-schema#label",
+ "rel_uri": "rdfs:label",
+ "approximation": false,
+ "readable_label": null,
+ "id": 2
+ },
+ {
+ "source": "wd:Q7889:0",
+ "target": "column-4",
+ "abs_uri": "http://www.wikidata.org/prop/P178",
+ "rel_uri": "p:P178",
+ "approximation": false,
+ "readable_label": null,
+ "id": 3
+ },
+ {
+ "source": "wd:Q7889:0",
+ "target": "column-0",
+ "abs_uri": "http://www.wikidata.org/prop/P577",
+ "rel_uri": "p:P577",
+ "approximation": false,
+ "readable_label": null,
+ "id": 4
+ },
+ {
+ "source": "wd:Q7889:0",
+ "target": "column-3",
+ "abs_uri": "http://www.wikidata.org/prop/P400",
+ "rel_uri": "p:P400",
+ "approximation": false,
+ "readable_label": null,
+ "id": 5
+ }
+ ]
+ }
+ ],
+ "is_curated": true,
+ "note": ""
+}
\ No newline at end of file
diff --git a/examples/t2dv2/main.ipynb b/examples/t2dv2/main.ipynb
new file mode 100644
index 0000000..dc3303d
--- /dev/null
+++ b/examples/t2dv2/main.ipynb
@@ -0,0 +1,148 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import glob, sys\n",
+ "\n",
+ "from omegaconf import OmegaConf\n",
+ "from tqdm.auto import tqdm\n",
+ "from grams.prelude import GRAMS, ROOT_DIR, DATA_DIR, I, WikidataSemanticModelHelper\n",
+ "from sm.prelude import M, O"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "cfg = OmegaConf.load(ROOT_DIR / \"grams.yaml\")\n",
+ "grams = GRAMS(DATA_DIR, cfg)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "pycharm": {
+ "name": "#%% Run Novartis related tables and show the results\n"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "cwd = ROOT_DIR / \"examples/t2dv2\"\n",
+ "tables = [I.LinkedTable.from_csv_file(infile) for infile in glob.glob(str(cwd / \"tables/*.csv\")) if infile.find(\"table_linker\") == -1]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "pycharm": {
+ "name": "#%%\n"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "d5da58ce703d43b7bcc8c63ba66b860b",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ " 0%| | 0/1 [00:00, ?it/s]"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "annotations = [grams.annotate(table, verbose=True) for table in tqdm(tables)]"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "metadata": {
+ "pycharm": {
+ "name": "#%% To get the performance, use the\n"
+ }
+ },
+ "source": [
+ "from sm_annotator.prelude import Annotator, BatchAnnotator, widgets, GRAMSAnnotatorAssistant, SliderApp\n",
+ "\n",
+ "qnodes = grams.qnodes.cache_dict()\n",
+ "wdprops = grams.wdprops\n",
+ "wdclasses = grams.wdclasses.cache_dict()\n",
+ "\n",
+ "assistant = GRAMSAnnotatorAssistant([\n",
+ " dict(table=table, sg=annotation.sg, dg=annotation.dg)\n",
+ " for annotation, table in zip(annotations, tables)\n",
+ "], qnodes, wdprops)\n",
+ "annotator = Annotator(qnodes, wdclasses, wdprops, cwd / \"ground-truth\", \n",
+ " eshost='http://mira.isi.edu:9200', username='', password='', dev=False, assistant=assistant)\n",
+ "batch_annotator = BatchAnnotator(annotator)\n",
+ "batch_annotator.render(same_tab=True)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "batch_annotator.batch_annotate([\n",
+ " (table.get_friendly_fs_id(), \"\", table)\n",
+ " for table in tables\n",
+ "], start_index=0)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "29414811_2_4773219892816395776.json\n"
+ ]
+ }
+ ],
+ "source": [
+ "!ls \"{cwd}/ground-truth\""
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "grams",
+ "language": "python",
+ "name": "grams"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.8.5"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/examples/t2dv2/tables/29414811_2_4773219892816395776.candidates.tsv b/examples/t2dv2/tables/29414811_2_4773219892816395776.candidates.tsv
new file mode 100644
index 0000000..4a856e1
--- /dev/null
+++ b/examples/t2dv2/tables/29414811_2_4773219892816395776.candidates.tsv
@@ -0,0 +1,22 @@
+0 1 Q548203 Q548203:1.0 Q3931421:0.9999998807907104 Q27438121:0.9999997615814209 Q641697:0.9999995231628418 Q1030206:0.9999963045120239
+1 1 Q17146 Q17146:1.0 Q20082627:0.9999998807907104 Q1064135:0.9999877214431763 Q17138:0.9999722242355347 Q11233573:0.9998165965080261
+10 1 Q1538485 Q21772565:1.0 Q1538485:1.0 Q2757933:0.9999998807907104 Q94587084:0.9999998807907104 Q1512956:0.9999998807907104
+11 1 Q1207356 Q1060715:0.9999995231628418 Q1194617:0.999996542930603 Q2501650:0.9999961853027344 Q1207356:0.9999958276748657 Q2067832:0.9999957084655762
+12 1 Q213911 Q213911:1.0 Q735613:1.0 Q861205:1.0 Q12395:1.0 Q2714265:0.9999998807907104
+13 1 Q369086 Q2708114:1.0 Q369086:1.0 Q19089:0.9999959468841553 Q3772920:0.9999943971633911 Q60786878:0.9999831914901733
+14 1 Q216995 Q216995:1.0 Q1142888:1.0 Q1046604:0.9999997615814209 Q17630799:0.9999995231628418 Q1063119:0.9999994039535522
+15 1 Q2995061 Q2466962:1.0 Q2995061:1.0 Q2982122:1.0 Q2978434:1.0 Q1967293:1.0
+16 1 Q518987 Q2564684:1.0 Q518987:0.9999998807907104 Q585939:0.9999997615814209 Q16201230:0.9999996423721313 Q936413:0.9999995231628418
+17 1 Q6498489 Q1209867:1.0 Q60743293:0.9999997615814209 Q3122108:0.9999996423721313 Q3282619:0.9999996423721313 Q3045091:0.9999995231628418
+18 1 Q1133204 Q5575485:0.9999998807907104 Q1133204:0.9999997615814209 Q196255:0.9999997615814209 Q605180:0.9999995231628418 Q16256689:0.9999995231628418
+19 1 Q37123 Q65239343:1.0 Q37115:1.0 Q37123:1.0 Q1068669:1.0 Q1046812:0.9999998807907104
+2 1 Q94797 Q94797:1.0 Q648099:0.9999998807907104 Q105265216:0.9999998807907104 Q149106:0.9999998807907104 Q94501:0.9999998807907104
+20 1 Q1780344 Q3500399:1.0 Q14928351:0.9999998807907104 Q3500405:0.9999998807907104 Q1780344:0.9999977350234985 Q3500403:0.9999964237213135
+21 1 Q1315653 Q1315653:1.0 Q275122:0.9999980926513672 Q5557386:0.9999974966049194 Q5557490:0.9999960660934448 Q956303:0.9999860525131226
+3 1 Q57270 Q57270:1.0 Q4914658:0.9999995231628418 Q2074746:0.9999992847442627 Q302839:0.9999991655349731 Q4914660:0.999994158744812
+4 1 Q735613 Q735613:1.0 Q12395:1.0 Q213911:0.9999998807907104 Q14915024:0.9999997615814209 Q861205:0.9999997615814209
+5 1 Q275950 Q622336:1.0 Q275950:1.0 Q1248594:0.9999996423721313 Q78059950:0.9999995231628418 Q1138864:0.9999995231628418
+6 1 Q252804 Q252804:1.0 Q20831013:0.9999984502792358 Q11861246:0.9999960660934448 Q45028:0.9999805688858032 Q54913666:0.9999655485153198
+7 1 Q715292 Q715292:1.0 Q99309488:1.0 Q852583:0.9999998807907104 Q245172:0.9999997615814209 Q917588:0.9999996423721313
+8 1 Q1765733 Q1765733:1.0 Q2089035:0.9999998807907104 Q2530723:0.9999998807907104 Q1139244:0.9999998807907104 Q2539933:0.9999992847442627
+9 1 Q276217 Q276217:1.0 Q61721730:1.0 Q2642466:1.0 Q5643304:0.9999980926513672 Q1747150:0.999983549118042
diff --git a/examples/t2dv2/tables/29414811_2_4773219892816395776.csv b/examples/t2dv2/tables/29414811_2_4773219892816395776.csv
new file mode 100644
index 0000000..965904b
--- /dev/null
+++ b/examples/t2dv2/tables/29414811_2_4773219892816395776.csv
@@ -0,0 +1,23 @@
+Year,Game,Genre,Platform(s),Developer(s)
+2010,Red Dead Redemption,Open World: (Third-Person) Shooter,"PlayStation 3, Xbox 360",Rockstar San Diego
+2009,Uncharted 2: Among Thieves,"Action-adventure, Third-person shooter",PlayStation 3,Naughty Dog
+2008,Grand Theft Auto IV,"Open World, Action","Windows, PlayStation 3, Xbox 360",Rockstar North
+2007,BioShock,Shooter,"Windows , Xbox 360, Mac OS X, PlayStation 3",2K Boston/2K Australia
+2006,The Legend of Zelda: Twilight Princess,Action-adventure,"Wii, Nintendo GameCube",Nintendo
+2005,Resident Evil 4,Survival Horror: (Third-Person) Shooter,"GameCube, PlayStation 2, Windows, Wii",Capcom
+2004,Halo 2,Shooter,"Xbox, Windows,",Bungie
+2003,Prince of Persia: Sands of Time,Action-adventure,"Xbox, GameCube, PlayStation 2, Windows",Ubisoft Montreal
+2002,Metroid Prime,Action-adventure,Nintendo GameCube,Nintendo
+2001,Halo Combat Evolved,Shooter,"Xbox, Windows, Macintosh",Bungie
+2000,Tony Hawk's Pro Skater 2,Sports,"PC, Nintendo 64, PlayStation, Dreamcast",Activision
+1999,Soul Calibur,Fighting,Sega Dreamcast,Namco
+1998,The Legend of Zelda: Ocarina of Time,Action-adventure,Nintendo 64,Nintendo
+1997,Goldeneye 007,Shooter,Nintendo 64,Rare
+1996,Super Mario 64,Action-platformer,Nintendo 64,Nintendo
+1995,Twisted Metal,Action-driving game,PlayStation,SingleTrac
+1994,Donkey Kong Country,Action-platformer,Super Nintendo,Rare
+1993,Samurai Shodown,Fighting,Neo-Geo MVS,SNK
+1992,Street Fighter II,Fighting,Super Nintendo,Capcom
+1991,Sonic the Hedgehog,Action-platformer,Sega Genesis,Sega
+1990,Strider,Action-adventure,Sega Genesis,Capcom
+1989,Ghouls 'n Ghosts,Action-adventure,Sega Genesis,Capcom
diff --git a/examples/t2dv2/tables/29414811_2_4773219892816395776.links.tsv b/examples/t2dv2/tables/29414811_2_4773219892816395776.links.tsv
new file mode 100644
index 0000000..316757e
--- /dev/null
+++ b/examples/t2dv2/tables/29414811_2_4773219892816395776.links.tsv
@@ -0,0 +1,22 @@
+0 1 Q548203
+1 1 Q17146
+10 1 Q1538485
+11 1 Q1207356
+12 1 Q213911
+13 1 Q369086
+14 1 Q216995
+15 1 Q2995061
+16 1 Q518987
+17 1 Q6498489
+18 1 Q1133204
+19 1 Q37123
+2 1 Q94797
+20 1 Q1780344
+21 1 Q1315653
+3 1 Q57270
+4 1 Q735613
+5 1 Q275950
+6 1 Q252804
+7 1 Q715292
+8 1 Q1765733
+9 1 Q276217
diff --git a/examples/t2dv2/tables/29414811_2_4773219892816395776.table_linker.csv b/examples/t2dv2/tables/29414811_2_4773219892816395776.table_linker.csv
new file mode 100644
index 0000000..fb72baa
--- /dev/null
+++ b/examples/t2dv2/tables/29414811_2_4773219892816395776.table_linker.csv
@@ -0,0 +1,111 @@
+column,row,label,context,label_clean,kg_id,kg_labels,kg_aliases,method,kg_descriptions,pagerank,retrieval_score,GT_kg_id,GT_kg_label,evaluation_label,monge_elkan,des_cont_jaccard,jaro_winkler,graph-embedding-score,singleton,reciprocal_rank,num_char,num_tokens,siamese_pred,correct
+1,0,Red Dead Redemption,"2010|Open World: (Third-Person) Shooter|PlayStation 3, Xbox 360|Rockstar San Diego",Red Dead Redemption,Q548203,Red Dead Redemption,,exact-match,2010 action-adventure video game,1.3892879715013064e-06,0.22088423997519224,Q548203,Red Dead Redemption,1,1.0,0.25,1.0,0.8315580140952763,1.0,0.3306451612903225,0.07692307692307693,0.07894736842105263,1.0,1
+1,0,Red Dead Redemption,"2010|Open World: (Third-Person) Shooter|PlayStation 3, Xbox 360|Rockstar San Diego",Red Dead Redemption,Q3931421,Red Dead Redemption: The Man from Blackwater,,fuzzy-augmented,2010 film,3.6629798362729013e-07,0.18446561638289138,Q548203,Red Dead Redemption,-1,0.8599567099567099,0.5,0.8863636363636364,0.6736793350596454,0.235480627909748,0.07963709677419355,0.17813765182186236,0.18421052631578946,0.9999998807907104,1
+1,0,Red Dead Redemption,"2010|Open World: (Third-Person) Shooter|PlayStation 3, Xbox 360|Rockstar San Diego",Red Dead Redemption,Q27438121,Red Dead Redemption 2,RDR2|RDR 2|Red Dead Redemption II|RDR II|Red Dead 2,fuzzy-augmented,2018 western-themed action-adventure video game,9.092834502763557e-07,0.29575078655715914,Q548203,Red Dead Redemption,-1,0.875,0.0,0.9809523809523808,0.8300056138836477,0.235480627909748,0.24697580645161288,0.08502024291497975,0.10526315789473684,0.9999997615814209,1
+1,0,Red Dead Redemption,"2010|Open World: (Third-Person) Shooter|PlayStation 3, Xbox 360|Rockstar San Diego",Red Dead Redemption,Q641697,Red Dead Revolver,,fuzzy-augmented,2004 third-person shooter video game,6.214642911791822e-07,0.1989903395472015,Q548203,Red Dead Redemption,-1,0.9125925925925926,0.125,0.8743986663491308,0.8222897272515239,0.235480627909748,0.1967741935483871,0.06882591093117409,0.07894736842105263,0.9999995231628418,1
+1,0,Red Dead Redemption,"2010|Open World: (Third-Person) Shooter|PlayStation 3, Xbox 360|Rockstar San Diego",Red Dead Redemption,Q1030206,Undead Nightmare,Red Dead Redemption: Undead Nightmare,fuzzy-augmented,2010 video game downloadable content,3.955041957433531e-07,0.26057247930952093,Q548203,Red Dead Redemption,-1,0.5879629629629629,0.2,0.5837719298245614,0.8475312357178982,0.235480627909748,0.9999999999999999,0.06477732793522267,0.05263157894736842,0.9999963045120239,1
+1,1,Uncharted 2: Among Thieves,"2009|Action-adventure, Third-person shooter|PlayStation 3|Naughty Dog",Uncharted 2 Among Thieves,Q17146,Uncharted 2: Among Thieves,,fuzzy-augmented,2009 video game,5.868211585736621e-07,0.4495307283326596,Q17146,Uncharted 2: Among Thieves,1,0.86625,0.3333333333333333,0.9606153846153846,0.7859793231009721,0.235480627909748,0.24697580645161288,0.10526315789473684,0.10526315789473684,1.0,1
+1,1,Uncharted 2: Among Thieves,"2009|Action-adventure, Third-person shooter|PlayStation 3|Naughty Dog",Uncharted 2 Among Thieves,Q20082627,Uncharted,,fuzzy-augmented,2009 film,3.436024992699295e-07,0.18455266345036797,Q17146,Uncharted 2: Among Thieves,-1,0.693968253968254,0.5,0.8692307692307693,0.5145544885111359,0.235480627909748,0.013278642936596231,0.03643724696356275,0.02631578947368421,0.9999998807907104,1
+1,1,Uncharted 2: Among Thieves,"2009|Action-adventure, Third-person shooter|PlayStation 3|Naughty Dog",Uncharted 2 Among Thieves,Q1064135,Uncharted,Uncharted Series,fuzzy-augmented,video game series,1.085918686290058e-06,0.1852686304168764,Q17146,Uncharted 2: Among Thieves,-1,0.693968253968254,0.0,0.8692307692307693,0.7763721369961689,0.235480627909748,0.1633064516129032,0.03643724696356275,0.02631578947368421,0.9999877214431763,1
+1,1,Uncharted 2: Among Thieves,"2009|Action-adventure, Third-person shooter|PlayStation 3|Naughty Dog",Uncharted 2 Among Thieves,Q17138,Uncharted 3: Drake\s Deception,,fuzzy-augmented,2011 action-adventure third-person shooter platform video game,4.005768447375376e-07,0.16961516673728821,Q17146,Uncharted 2: Among Thieves,-1,0.4504497354497354,0.25,0.8471794871794872,0.751984406251555,0.235480627909748,0.10752688172043011,0.12145748987854252,0.10526315789473684,0.9999722242355347,1
+1,1,Uncharted 2: Among Thieves,"2009|Action-adventure, Third-person shooter|PlayStation 3|Naughty Dog",Uncharted 2 Among Thieves,Q11233573,Uncharted Waters,,fuzzy-augmented,video game released in 1990,3.436024992699295e-07,0.15697413844760222,Q17146,Uncharted 2: Among Thieves,-1,0.6205555555555555,0.0,0.8423076923076923,0.7798581151994779,0.235480627909748,0.1967741935483871,0.06477732793522267,0.05263157894736842,0.9998165965080261,1
+1,10,Tony Hawk's Pro Skater 2,"2000|Sports|PC, Nintendo 64, PlayStation, Dreamcast|Activision",Tony Hawk's Pro Skater 2,Q21772565,Tony Hawk\s Pro Skater 2x,,fuzzy-augmented,skateboarding video game,3.436024992699295e-07,0.39925092073223595,Q1538485,Tony Hawk\\'s Pro Skater 2,-1,0.9566666666666668,0.0,0.9756666666666668,0.8095880906244562,0.235480627909748,0.1967741935483871,0.10121457489878542,0.13157894736842105,1.0,0
+1,10,Tony Hawk's Pro Skater 2,"2000|Sports|PC, Nintendo 64, PlayStation, Dreamcast|Activision",Tony Hawk's Pro Skater 2,Q1538485,Tony Hawk\s Pro Skater 2,,fuzzy-augmented,video game,4.2597613343463215e-07,0.42138437623760655,Q1538485,Tony Hawk\\'s Pro Skater 2,1,0.9866666666666668,0.0,0.9833333333333334,0.8083483035262229,0.235480627909748,0.1633064516129032,0.09716599190283401,0.13157894736842105,1.0,0
+1,10,Tony Hawk's Pro Skater 2,"2000|Sports|PC, Nintendo 64, PlayStation, Dreamcast|Activision",Tony Hawk's Pro Skater 2,Q2757933,Tony Hawk\s Pro Skater HD,,fuzzy-augmented,2012 video game,3.436024992699295e-07,0.37882539609648525,Q1538485,Tony Hawk\\'s Pro Skater 2,-1,0.8422222222222222,0.0,0.9593333333333334,0.8306384955146178,0.235480627909748,0.9999999999999999,0.10121457489878542,0.13157894736842105,0.9999998807907104,0
+1,10,Tony Hawk's Pro Skater 2,"2000|Sports|PC, Nintendo 64, PlayStation, Dreamcast|Activision",Tony Hawk's Pro Skater 2,Q94587084,Tony Hawk\s Pro Skater 1 + 2,Tony Hawk\s Pro Skater 1+2|THPS 1 + 2|Pro Skater 1+2|THPS 1+2|Pro Skater 1 and 2|Pro Skater 1 + 2|THPS 1 and 2|Tony Hawk\s Pro Skater 1 and 2,fuzzy-augmented,2020 skateboarding video game developed by Vicarious Visions,3.436024992699295e-07,0.38371309555368077,Q1538485,Tony Hawk\\'s Pro Skater 2,-1,0.8457142857142858,0.0,0.9559523809523808,0.8143923864394651,0.235480627909748,0.3306451612903225,0.11336032388663968,0.18421052631578946,0.9999998807907104,0
+1,10,Tony Hawk's Pro Skater 2,"2000|Sports|PC, Nintendo 64, PlayStation, Dreamcast|Activision",Tony Hawk's Pro Skater 2,Q1512956,Tony Hawk\s Pro Skater 4,,fuzzy-augmented,video game,3.436024992699295e-07,0.37310222196065446,Q1538485,Tony Hawk\\'s Pro Skater 2,-1,0.7866666666666667,0.0,0.9666666666666666,0.8276169873333099,0.235480627909748,0.4979838709677419,0.09716599190283401,0.13157894736842105,0.9999998807907104,0
+1,11,Soul Calibur,1999|Fighting|Sega Dreamcast|Namco,Soul Calibur,Q1060715,Soulcalibur,,fuzzy-augmented,video game,3.78962598500862e-07,0.32295300023464724,Q1207356,Soulcalibur,-1,0.7351731601731601,0.0,0.9833333333333332,0.759990926013955,0.235480627909748,0.1633064516129032,0.04453441295546559,0.02631578947368421,0.9999995231628418,0
+1,11,Soul Calibur,1999|Fighting|Sega Dreamcast|Namco,Soul Calibur,Q1194617,Soulcalibur Legends,,fuzzy-augmented,2007 video game,3.436024992699295e-07,0.26333458692378725,Q1207356,Soulcalibur,-1,0.6506493506493507,0.0,0.8991228070175439,0.8218443468997617,0.235480627909748,0.9999999999999999,0.07692307692307693,0.05263157894736842,0.999996542930603,0
+1,11,Soul Calibur,1999|Fighting|Sega Dreamcast|Namco,Soul Calibur,Q2501650,Soulcalibur V,,fuzzy-augmented,2012 video game,3.609451924260139e-07,0.2580016532731718,Q1207356,Soulcalibur,-1,0.516991341991342,0.0,0.9525641025641024,0.8135514338309179,0.235480627909748,0.4979838709677419,0.05263157894736842,0.05263157894736842,0.9999961853027344,0
+1,11,Soul Calibur,1999|Fighting|Sega Dreamcast|Namco,Soul Calibur,Q1207356,Soulcalibur,Soul series|Soul,fuzzy-augmented,video game series,9.03753728375121e-06,0.2644991481880866,Q1207356,Soulcalibur,1,0.7351731601731601,0.0,0.9833333333333332,0.6561340258266938,0.235480627909748,0.07963709677419355,0.04453441295546559,0.02631578947368421,0.9999958276748657,0
+1,11,Soul Calibur,1999|Fighting|Sega Dreamcast|Namco,Soul Calibur,Q2067832,Soulcalibur II,,fuzzy-augmented,2002 video game,5.379107121847507e-07,0.2806139541978063,Q1207356,Soulcalibur,-1,0.516991341991342,0.0,0.9404761904761904,0.7604366878208862,0.235480627909748,0.1967741935483871,0.05668016194331984,0.05263157894736842,0.9999957084655762,0
+1,12,The Legend of Zelda: Ocarina of Time,1998|Action-adventure|Nintendo 64|Nintendo,The Legend of Zelda Ocarina of Time,Q213911,The Legend of Zelda: Ocarina of Time,OoT|Zelda 64|Zelda OoT|Ocarina of Time|Zelda Ocarina of Time|Zelda: OoT|Zelda: Ocarina of Time|TloZ: OoT,fuzzy-augmented,1998 video game for the Nintendo 64,1.1911033254103161e-06,0.6109133848077564,Q213911,The Legend of Zelda: Ocarina of Time,1,0.9330357142857144,0.2857142857142857,0.9603174603174603,0.8196125762481342,0.235480627909748,0.3306451612903225,0.145748987854251,0.18421052631578946,1.0,1
+1,12,The Legend of Zelda: Ocarina of Time,1998|Action-adventure|Nintendo 64|Nintendo,The Legend of Zelda Ocarina of Time,Q735613,The Legend of Zelda: Twilight Princess,TLoZ: TP|Zelda: Twilight Princess|TLOZ: TP|TLOZ: Twilight Princess|Zelda: TP|TLoZ: Twilight Princess|Twilight Princess,fuzzy-augmented,2006 action-adventure video game,6.033000517170999e-07,0.33694788585033064,Q213911,The Legend of Zelda: Ocarina of Time,-1,0.8192956349206348,0.25,0.8814954051796157,0.8093092824184346,0.235480627909748,0.1633064516129032,0.15384615384615385,0.15789473684210525,1.0,1
+1,12,The Legend of Zelda: Ocarina of Time,1998|Action-adventure|Nintendo 64|Nintendo,The Legend of Zelda Ocarina of Time,Q861205,The Legend of Zelda: Skyward Sword,,fuzzy-augmented,2011 video game on the Nintendo Wii,6.300602614114573e-07,0.36766852590744836,Q213911,The Legend of Zelda: Ocarina of Time,-1,0.8134424603174604,0.14285714285714285,0.8578431372549019,0.8276802534227766,0.235480627909748,0.4979838709677419,0.13765182186234817,0.15789473684210525,1.0,1
+1,12,The Legend of Zelda: Ocarina of Time,1998|Action-adventure|Nintendo 64|Nintendo,The Legend of Zelda Ocarina of Time,Q12395,The Legend of Zelda,The Legend of Zelda (video game)|LoZ: The Hyrule Fantasy|THE HYRULE FANTASY|The Hyrule Fantasy|Legend of Zelda: The Hyrule Fantasy,fuzzy-augmented,1986 action-adventure video game,6.4025013664677e-07,0.34433714228316137,Q213911,The Legend of Zelda: Ocarina of Time,-1,0.8936011904761905,0.25,0.9055555555555556,0.8004017852047628,0.235480627909748,0.07963709677419355,0.07692307692307693,0.10526315789473684,1.0,1
+1,12,The Legend of Zelda: Ocarina of Time,1998|Action-adventure|Nintendo 64|Nintendo,The Legend of Zelda Ocarina of Time,Q2714265,The Legend of Zelda: Ocarina of Time Master Quest,,fuzzy-augmented,2002 video game,3.729296867799899e-07,0.447305646853947,Q213911,The Legend of Zelda: Ocarina of Time,-1,0.8847993827160494,0.0,0.9024943310657596,0.8160468436427546,0.235480627909748,0.24697580645161288,0.19838056680161945,0.23684210526315788,0.9999998807907104,1
+1,13,Goldeneye 007,1997|Shooter|Nintendo 64|Rare,Goldeneye 007,Q2708114,GoldenEye 007,,fuzzy-augmented,2010 video game,4.160386046672296e-07,0.3237374726017853,Q369086,GoldenEye 007,-1,1.0,0.0,1.0,0.7933255855121767,0.235480627909748,0.3306451612903225,0.05263157894736842,0.05263157894736842,1.0,0
+1,13,Goldeneye 007,1997|Shooter|Nintendo 64|Rare,Goldeneye 007,Q369086,GoldenEye 007,,fuzzy-augmented,1997 video game,5.49001905766426e-07,0.29873011982794023,Q369086,GoldenEye 007,1,1.0,0.3333333333333333,1.0,0.7851563864424013,0.235480627909748,0.1967741935483871,0.05263157894736842,0.05263157894736842,1.0,0
+1,13,Goldeneye 007,1997|Shooter|Nintendo 64|Rare,Goldeneye 007,Q19089,GoldenEye,,fuzzy-augmented,1995 James Bond film by Martin Campbell,1.1668916068573583e-06,0.3049012162406877,Q369086,GoldenEye 007,-1,0.75,0.0,0.9384615384615383,0.5861095098284791,0.235480627909748,0.04616935483870969,0.03643724696356275,0.02631578947368421,0.9999959468841553,0
+1,13,Goldeneye 007,1997|Shooter|Nintendo 64|Rare,Goldeneye 007,Q3772920,GoldenEye Reloaded 007,,fuzzy-augmented,,3.436024992699295e-07,0.25989317820919366,Q369086,GoldenEye 007,-1,0.9433641975308642,0.0,0.8447552447552448,0.7080752937194119,0.235480627909748,0.0963709677419355,0.08906882591093118,0.07894736842105263,0.9999943971633911,0
+1,13,Goldeneye 007,1997|Shooter|Nintendo 64|Rare,Goldeneye 007,Q60786878,GoldenEye,,fuzzy-augmented,,3.6481393628607503e-07,0.1944977883640298,Q369086,GoldenEye 007,-1,0.75,0.0,0.9384615384615383,0.7061306043058955,0.235480627909748,0.08724340175953081,0.03643724696356275,0.02631578947368421,0.9999831914901733,0
+1,14,Super Mario 64,1996|Action-platformer|Nintendo 64|Nintendo,Super Mario 64,Q216995,Super Mario 64,SM 64|スーパーマリオ64|M64|SM64|M 64|Mario 64,exact-match,1996 platform video game developed by Nintendo,9.897859797844363e-07,0.2143052981667525,Q216995,Super Mario 64,1,1.0,0.14285714285714285,1.0,0.8228675225888695,0.235480627909748,0.4979838709677419,0.05668016194331984,0.07894736842105263,1.0,1
+1,14,Super Mario 64,1996|Action-platformer|Nintendo 64|Nintendo,Super Mario 64,Q1142888,Super Mario 64 DS,,fuzzy-augmented,2004 platform video game developed by Nintendo,7.541714243767726e-07,0.2909678307171272,Q216995,Super Mario 64,-1,0.9458333333333332,0.14285714285714285,0.9647058823529412,0.8073256529883932,0.235480627909748,0.3306451612903225,0.06882591093117409,0.10526315789473684,1.0,1
+1,14,Super Mario 64,1996|Action-platformer|Nintendo 64|Nintendo,Super Mario 64,Q1046604,Mario Kart 64,MK64|MK 64,fuzzy-augmented,1996 racing video game,1.5750578225461464e-06,0.24247673904110867,Q216995,Super Mario 64,-1,0.8527777777777779,0.25,0.7278388278388279,0.8000339942056202,0.235480627909748,0.1967741935483871,0.05263157894736842,0.07894736842105263,0.9999997615814209,1
+1,14,Super Mario 64,1996|Action-platformer|Nintendo 64|Nintendo,Super Mario 64,Q17630799,Super Mario 64 2,,fuzzy-augmented,rumoured sequel to Super Mario 64,3.9177943696566764e-07,0.29065803927625994,Q216995,Super Mario 64,-1,0.875,0.14285714285714285,0.975,0.6721479057880145,0.235480627909748,0.011904761904761915,0.06477732793522267,0.10526315789473684,0.9999995231628418,1
+1,14,Super Mario 64,1996|Action-platformer|Nintendo 64|Nintendo,Super Mario 64,Q1063119,Super Mario RPG,Mario RPG: LotSS|Mario RPG: Legend of the Seven Stars|SMRPG: Legend of the Seven Stars|SMRPG: LotSS|LotSS|Legend of the Seven Stars|Super Mario RPG: LotSS|Super Mario RPG|SMRPG,fuzzy-augmented,1996 action role-playing game,1.1445431738335514e-06,0.19847545955409276,Q216995,Super Mario 64,-1,0.7518518518518518,0.25,0.9314285714285714,0.7555664054389551,0.235480627909748,0.037802419354838704,0.06072874493927126,0.07894736842105263,0.9999994039535522,1
+1,15,Twisted Metal,1995|Action-driving game|PlayStation|SingleTrac,Twisted Metal,Q2466962,Twisted Metal 4,,fuzzy-augmented,1999 video game,3.436024992699295e-07,0.2449926222799255,Q2995061,Twisted Metal,-1,0.8333333333333333,0.25,0.9733333333333334,0.8364952687400237,0.235480627909748,0.9999999999999999,0.06072874493927126,0.07894736842105263,1.0,0
+1,15,Twisted Metal,1995|Action-driving game|PlayStation|SingleTrac,Twisted Metal,Q2995061,Twisted Metal,,exact-match,video game series,1.0349354965613845e-06,0.20997183265234368,Q2995061,Twisted Metal,1,1.0,0.25,1.0,0.7073920338217183,0.235480627909748,0.05871975806451613,0.05263157894736842,0.05263157894736842,1.0,0
+1,15,Twisted Metal,1995|Action-driving game|PlayStation|SingleTrac,Twisted Metal,Q2982122,Twisted Metal,,fuzzy-augmented,2012 video game,3.436024992699295e-07,0.26304440627388503,Q2995061,Twisted Metal,-1,1.0,0.25,1.0,0.8348543144356954,0.235480627909748,0.4979838709677419,0.05263157894736842,0.05263157894736842,1.0,0
+1,15,Twisted Metal,1995|Action-driving game|PlayStation|SingleTrac,Twisted Metal,Q2978434,Twisted Metal: Black,,fuzzy-augmented,2001 video game,3.436024992699295e-07,0.2564460573171694,Q2995061,Twisted Metal,-1,0.8972222222222224,0.25,0.93,0.8203394720134353,0.235480627909748,0.24697580645161288,0.08097165991902834,0.07894736842105263,1.0,0
+1,15,Twisted Metal,1995|Action-driving game|PlayStation|SingleTrac,Twisted Metal,Q1967293,Twisted Metal,,fuzzy-augmented,1995 video game,4.898712797469079e-07,0.27144808839844037,Q2995061,Twisted Metal,-1,1.0,0.3333333333333333,1.0,0.8017671310802987,0.235480627909748,0.1633064516129032,0.05263157894736842,0.05263157894736842,1.0,0
+1,16,Donkey Kong Country,1994|Action-platformer|Super Nintendo|Rare,Donkey Kong Country,Q2564684,Donkey Kong,,fuzzy-augmented,1994 platform game for the Game Boy,1.0937574664597787e-06,0.2553198826997746,Q518987,Donkey Kong Country,-1,0.9404761904761904,0.16666666666666666,0.9157894736842104,0.8161425378459277,0.235480627909748,0.3306451612903225,0.04453441295546559,0.05263157894736842,1.0,0
+1,16,Donkey Kong Country,1994|Action-platformer|Super Nintendo|Rare,Donkey Kong Country,Q518987,Donkey Kong Country,DKC,fuzzy-augmented,1994 side-scroller platform video game for the SNES,8.153468474664555e-07,0.3093443637074719,Q518987,Donkey Kong Country,1,1.0,0.125,1.0,0.7751266278403274,0.235480627909748,0.055028462998102476,0.07692307692307693,0.07894736842105263,0.9999998807907104,0
+1,16,Donkey Kong Country,1994|Action-platformer|Super Nintendo|Rare,Donkey Kong Country,Q585939,Donkey Kong Country Returns,Donkey Kong Country Returns 3D|DKCR,fuzzy-augmented,2D platformer video game developed by Retro Studios,4.0592479245364506e-07,0.31565001702707435,Q518987,Donkey Kong Country,-1,0.9434523809523808,0.0,0.9407407407407408,0.806535084408002,0.235480627909748,0.1967741935483871,0.10931174089068826,0.10526315789473684,0.9999997615814209,0
+1,16,Donkey Kong Country,1994|Action-platformer|Super Nintendo|Rare,Donkey Kong Country,Q16201230,Donkey Kong Country,Super Donkey Kong,fuzzy-augmented,video game series,9.531648353980995e-07,0.3496739247806017,Q518987,Donkey Kong Country,-1,1.0,0.0,1.0,0.7209301444431921,0.235480627909748,0.02734375000000001,0.07692307692307693,0.07894736842105263,0.9999996423721313,0
+1,16,Donkey Kong Country,1994|Action-platformer|Super Nintendo|Rare,Donkey Kong Country,Q936413,Donkey Kong Jungle Beat,,fuzzy-augmented,2004 Nintendo GameCube Game,3.436024992699295e-07,0.21037122687643625,Q518987,Donkey Kong Country,-1,0.8249007936507937,0.2,0.8883295194508009,0.8008638178212414,0.235480627909748,0.10752688172043011,0.0931174089068826,0.10526315789473684,0.9999995231628418,0
+1,17,Samurai Shodown,1993|Fighting|Neo-Geo MVS|SNK,Samurai Shodown,Q1209867,Samurai Shodown,,fuzzy-augmented,1993 video game,1.0039189081094595e-06,0.33742346280443375,Q6498489,Samurai Shodown,-1,1.0,0.3333333333333333,1.0,0.751086668042069,0.235480627909748,0.13940092165898615,0.06072874493927126,0.05263157894736842,1.0,-1
+1,17,Samurai Shodown,1993|Fighting|Neo-Geo MVS|SNK,Samurai Shodown,Q60743293,Samurai Shodown,Samurai Spirits,fuzzy-augmented,2019 video game,3.5313277164304526e-07,0.3372937933221184,Q6498489,Samurai Shodown,-1,1.0,0.0,1.0,0.7208904104618954,0.235480627909748,0.05871975806451613,0.06072874493927126,0.05263157894736842,0.9999997615814209,-1
+1,17,Samurai Shodown,1993|Fighting|Neo-Geo MVS|SNK,Samurai Shodown,Q3122108,Samurai Shodown Anthology,,fuzzy-augmented,,3.436024992699295e-07,0.2680698903745585,Q6498489,Samurai Shodown,-1,0.9312169312169312,0.0,0.92,0.7884404063290137,0.235480627909748,0.4979838709677419,0.10121457489878542,0.07894736842105263,0.9999996423721313,-1
+1,17,Samurai Shodown,1993|Fighting|Neo-Geo MVS|SNK,Samurai Shodown,Q3282619,Samurai Shodown! 2,,fuzzy-augmented,1999 video game,3.5313277164304526e-07,0.2680698903745585,Q6498489,Samurai Shodown,-1,0.8229166666666667,0.0,0.9666666666666668,0.778252372829115,0.235480627909748,0.3306451612903225,0.0728744939271255,0.07894736842105263,0.9999996423721313,-1
+1,17,Samurai Shodown,1993|Fighting|Neo-Geo MVS|SNK,Samurai Shodown,Q3045091,Samurai Shodown VI,,fuzzy-augmented,2005 video game,3.5313277164304526e-07,0.2977365675118534,Q6498489,Samurai Shodown,-1,0.8333333333333333,0.0,0.9666666666666668,0.7562904507283651,0.235480627909748,0.1967741935483871,0.0728744939271255,0.07894736842105263,0.9999995231628418,-1
+1,18,Street Fighter II,1992|Fighting|Super Nintendo|Capcom,Street Fighter II,Q5575485,Street Fighter II: Champion Edition,Street Fighter II: Champion Edition|Street Fighter II\: Champion Edition,fuzzy-augmented,1992 arcade game,3.436024992699295e-07,0.2582904389706534,Q1133204,Street Fighter II: The World Warrior,-1,0.8733201058201058,0.3333333333333333,0.8971428571428571,0.7282154629197294,0.235480627909748,0.02943548387096775,0.1417004048582996,0.13157894736842105,0.9999998807907104,0
+1,18,Street Fighter II,1992|Fighting|Super Nintendo|Capcom,Street Fighter II,Q1133204,Street Fighter II: The World Warrior,SF 2|SF2|SFII|Street Fighter 2|Street Fighter II,fuzzy-augmented,1991 video game developed by Capcom,1.4822290021808571e-06,0.3082285952314926,Q1133204,Street Fighter II: The World Warrior,1,0.8576719576719576,0.16666666666666666,0.8944444444444445,0.761393107934501,0.235480627909748,0.07963709677419355,0.145748987854251,0.15789473684210525,0.9999997615814209,0
+1,18,Street Fighter II,1992|Fighting|Super Nintendo|Capcom,Street Fighter II,Q196255,Street Fighter II Turbo: Hyper Fighting,Street Fighter II: Hyper Fighting,fuzzy-augmented,1992 arcade video game,3.6446407935283173e-07,0.27970659364990164,Q1133204,Street Fighter II: The World Warrior,-1,0.9154431216931216,0.25,0.8871794871794872,0.7350658587821585,0.235480627909748,0.03315412186379929,0.15789473684210525,0.15789473684210525,0.9999997615814209,0
+1,18,Street Fighter II,1992|Fighting|Super Nintendo|Capcom,Street Fighter II,Q605180,Street Fighter: The Movie,STREET FIGHTER REAL BATTLE ON FILM|Street Fighter: Real Battle on Film,fuzzy-augmented,1995 console fighting game,3.76053846065555e-07,0.18197536157650393,Q1133204,Street Fighter II: The World Warrior,-1,0.8198908730158729,0.25,0.9162352941176469,0.7802288617099994,0.235480627909748,0.24697580645161288,0.10121457489878542,0.10526315789473684,0.9999995231628418,0
+1,18,Street Fighter II,1992|Fighting|Super Nintendo|Capcom,Street Fighter II,Q16256689,Street Fighter,,fuzzy-augmented,1992 Korean animated film,3.436024992699295e-07,0.18407989676537517,Q1133204,Street Fighter II: The World Warrior,-1,0.9246031746031746,0.25,0.9647058823529412,0.5866278290175062,0.235480627909748,0.013278642936596231,0.05668016194331984,0.05263157894736842,0.9999995231628418,0
+1,19,Sonic the Hedgehog,1991|Action-platformer|Sega Genesis|Sega,Sonic the Hedgehog,Q65239343,Sonic the Hedgehog,,fuzzy-augmented,Sega video game series,4.086218865079982e-07,0.2701536132805987,Q37123,Sonic the Hedgehog,-1,1.0,0.25,1.0,0.6734091634927117,0.235480627909748,0.018786656891495612,0.0728744939271255,0.07894736842105263,1.0,0
+1,19,Sonic the Hedgehog,1991|Action-platformer|Sega Genesis|Sega,Sonic the Hedgehog,Q37115,Sonic the Hedgehog,Sonic the Hedgehog (Sega Master System)|Sonic 8-bit|Sonic the Hedgehog for Sega Master System|Sonic the Hedgehog for Sega Game Gear|Sonic the Hedgehog for the Sega Game Gear|Sonic the Hedgehog 8-bit|Sonic the Hedgehog (Sega Game Gear)|Sonic the Hedgehog for the Sega Master System,fuzzy-augmented,8-bit 1991 platform video game,4.4549746445456735e-07,0.28419595782498813,Q37123,Sonic the Hedgehog,-1,1.0,0.2,1.0,0.7970957171176447,0.235480627909748,0.1633064516129032,0.0728744939271255,0.07894736842105263,1.0,0
+1,19,Sonic the Hedgehog,1991|Action-platformer|Sega Genesis|Sega,Sonic the Hedgehog,Q37123,Sonic the Hedgehog,Sonic 1|Sonic the Hedgehog I|Sonic the Hedgehog 1|Sonic I,fuzzy-augmented,1991 video game,5.723249172263516e-07,0.3024181194748238,Q37123,Sonic the Hedgehog,1,1.0,0.3333333333333333,1.0,0.7712357167251184,0.235480627909748,0.041605571847507336,0.0728744939271255,0.07894736842105263,1.0,0
+1,19,Sonic the Hedgehog,1991|Action-platformer|Sega Genesis|Sega,Sonic the Hedgehog,Q1068669,Sonic the Hedgehog,Sonic the Hedgehog \06|Sonic \06|Sonic the Hedgehog 2006|Sonic 2006|Sonic 06|Sonic the Hedgehog 06|Sonic (2006)|Sonic the Hedgehog (2006),fuzzy-augmented,2006 video game by Sega,8.870639509976738e-07,0.3039760878681548,Q37123,Sonic the Hedgehog,-1,1.0,0.2,1.0,0.7865488979825909,0.235480627909748,0.06768433179723503,0.0728744939271255,0.07894736842105263,1.0,0
+1,19,Sonic the Hedgehog,1991|Action-platformer|Sega Genesis|Sega,Sonic the Hedgehog,Q1046812,Sonic the Hedgehog,Sonic the Hedgehog series|Sonic|Sonic series,fuzzy-augmented,Sega video game series and associated media franchise,7.802327017496567e-06,0.2764158703431435,Q37123,Sonic the Hedgehog,-1,1.0,0.125,1.0,0.7328580291224558,0.235480627909748,0.02639296187683286,0.0728744939271255,0.07894736842105263,0.9999998807907104,0
+1,2,Grand Theft Auto IV,"2008|Open World, Action|Windows, PlayStation 3, Xbox 360|Rockstar North",Grand Theft Auto IV,Q94797,Grand Theft Auto IV,GTA 4|GTA IV,fuzzy-augmented,2008 open world action-adventure video game,1.0174159037900636e-05,0.41715895335192554,Q94797,Grand Theft Auto IV,1,1.0,0.16666666666666666,1.0,0.7942129864406107,0.235480627909748,0.1967741935483871,0.07692307692307693,0.10526315789473684,1.0,1
+1,2,Grand Theft Auto IV,"2008|Open World, Action|Windows, PlayStation 3, Xbox 360|Rockstar North",Grand Theft Auto IV,Q648099,Grand Theft Auto Advance,Grand Theft Auto|GTA Advance,fuzzy-augmented,handheld console game,1.3756920740087038e-06,0.32184975578405056,Q94797,Grand Theft Auto IV,-1,0.8886904761904761,0.0,0.9394736842105262,0.8318932082289215,0.235480627909748,0.9999999999999999,0.09716599190283401,0.10526315789473684,0.9999998807907104,1
+1,2,Grand Theft Auto IV,"2008|Open World, Action|Windows, PlayStation 3, Xbox 360|Rockstar North",Grand Theft Auto IV,Q105265216,Grand Theft Auto,,fuzzy-augmented,Game Boy Color port of Grand Theft Auto,3.436024992699295e-07,0.2974686144614717,Q94797,Grand Theft Auto IV,-1,0.875,0.0,0.9684210526315791,0.8099353705292418,0.235480627909748,0.3306451612903225,0.06477732793522267,0.07894736842105263,0.9999998807907104,1
+1,2,Grand Theft Auto IV,"2008|Open World, Action|Windows, PlayStation 3, Xbox 360|Rockstar North",Grand Theft Auto IV,Q149106,Grand Theft Auto III,GTA 3|GTA III,fuzzy-augmented,2001 open world action-adventure video game,3.0712677577479953e-06,0.2900404724571481,Q94797,Grand Theft Auto IV,-1,0.9027777777777778,0.125,0.9694736842105264,0.7861826670562574,0.235480627909748,0.13940092165898615,0.08097165991902834,0.10526315789473684,0.9999998807907104,1
+1,2,Grand Theft Auto IV,"2008|Open World, Action|Windows, PlayStation 3, Xbox 360|Rockstar North",Grand Theft Auto IV,Q94501,Grand Theft Auto,Grand Theft Auto 1|GTA 1|GTA,fuzzy-augmented,1997 action-adventure open world video game,2.0002494174700363e-06,0.3065888133877962,Q94797,Grand Theft Auto IV,-1,0.875,0.125,0.9684210526315791,0.7670938407629115,0.235480627909748,0.05871975806451613,0.06477732793522267,0.07894736842105263,0.9999998807907104,1
+1,20,Strider,1990|Action-adventure|Sega Genesis|Capcom,Strider,Q3500399,Strider,,fuzzy-augmented,side-scrolling action-adventure game,3.436024992699295e-07,0.19490363806136843,Q1780344,Strider,-1,1.0,0.3333333333333333,1.0,0.7735951045607506,0.235480627909748,0.13940092165898615,0.02834008097165992,0.02631578947368421,1.0,0
+1,20,Strider,1990|Action-adventure|Sega Genesis|Capcom,Strider,Q14928351,Strider,,exact-match,video game from 2014,3.436024992699295e-07,0.1985442195080838,Q1780344,Strider,-1,1.0,0.0,1.0,0.8474321585702475,0.235480627909748,0.4979838709677419,0.02834008097165992,0.02631578947368421,0.9999998807907104,0
+1,20,Strider,1990|Action-adventure|Sega Genesis|Capcom,Strider,Q3500405,Strider II,,fuzzy-augmented,1990 video game,3.436024992699295e-07,0.17812369430035166,Q1780344,Strider,-1,0.75,0.3333333333333333,0.94,0.7647194652533015,0.235480627909748,0.10752688172043011,0.04048582995951417,0.05263157894736842,0.9999998807907104,0
+1,20,Strider,1990|Action-adventure|Sega Genesis|Capcom,Strider,Q1780344,Strider,Strider Hiryū|Strider Hiryu,fuzzy-augmented,1989 arcade video game,3.436024992699295e-07,0.2059917584785488,Q1780344,Strider,1,1.0,0.0,1.0,0.7112429408839829,0.235480627909748,0.06290322580645163,0.02834008097165992,0.02631578947368421,0.9999977350234985,0
+1,20,Strider,1990|Action-adventure|Sega Genesis|Capcom,Strider,Q3500403,Strider 2,,fuzzy-augmented,1999 video game,3.436024992699295e-07,0.19717091430527267,Q1780344,Strider,-1,0.75,0.0,0.9555555555555556,0.7851682847910001,0.235480627909748,0.1967741935483871,0.03643724696356275,0.05263157894736842,0.9999964237213135,0
+1,21,Ghouls 'n Ghosts,1989|Action-adventure|Sega Genesis|Capcom,Ghouls 'n Ghosts,Q1315653,Ghouls \n Ghosts,,exact-match,video game,3.436024992699295e-07,0.22088423997519224,Q1315653,Ghouls \\'n Ghosts,1,0.8888888888888888,0.0,0.975,0.7844414051576102,1.0,0.1967741935483871,0.06477732793522267,0.07894736842105263,1.0,1
+1,21,Ghouls 'n Ghosts,1989|Action-adventure|Sega Genesis|Capcom,Ghouls 'n Ghosts,Q275122,Super Ghouls \n Ghosts,,fuzzy-augmented,1991 video game,3.436024992699295e-07,0.31127589424977703,Q1315653,Ghouls \\'n Ghosts,-1,0.8347222222222221,0.0,0.717550505050505,0.838053052910042,0.235480627909748,0.9999999999999999,0.08906882591093118,0.10526315789473684,0.9999980926513672,1
+1,21,Ghouls 'n Ghosts,1989|Action-adventure|Sega Genesis|Capcom,Ghouls 'n Ghosts,Q5557386,Ghosts\n Goblins,,fuzzy-augmented,video game series by Capcom,6.725292389898464e-07,0.1949591816048337,Q1315653,Ghouls \\'n Ghosts,-1,0.7229497354497354,0.2,0.8117424242424243,0.6602737361554458,0.235480627909748,0.08724340175953081,0.06477732793522267,0.05263157894736842,0.9999974966049194,1
+1,21,Ghouls 'n Ghosts,1989|Action-adventure|Sega Genesis|Capcom,Ghouls 'n Ghosts,Q5557490,Ghouls,,fuzzy-augmented,1984 video game,3.436024992699295e-07,0.2037169576372805,Q1315653,Ghouls \\'n Ghosts,-1,0.8074074074074074,0.0,0.875,0.7610546479336281,0.235480627909748,0.1633064516129032,0.024291497975708502,0.02631578947368421,0.9999960660934448,1
+1,21,Ghouls 'n Ghosts,1989|Action-adventure|Sega Genesis|Capcom,Ghouls 'n Ghosts,Q956303,Ghosts \n Goblins,Makaimura|魔界村,fuzzy-augmented,1985 video game,1.510545555458901e-06,0.2142642030720823,Q1315653,Ghouls \\'n Ghosts,-1,0.8248677248677247,0.0,0.6872771836007129,0.7881779607763056,0.235480627909748,0.3306451612903225,0.06882591093117409,0.07894736842105263,0.9999860525131226,1
+1,3,BioShock,"2007|Shooter|Windows , Xbox 360, Mac OS X, PlayStation 3|2K Boston/2K Australia",BioShock,Q57270,BioShock,,exact-match,2007 video game,5.482136872964143e-07,0.2143052981667525,Q57270,BioShock,1,1.0,0.3333333333333333,1.0,0.7823420590208403,0.235480627909748,0.3306451612903225,0.032388663967611336,0.02631578947368421,1.0,1
+1,3,BioShock,"2007|Shooter|Windows , Xbox 360, Mac OS X, PlayStation 3|2K Boston/2K Australia",BioShock,Q4914658,BioShock series,,fuzzy-augmented,First-person shooter video game series developed by Irrational Games,8.943469960023595e-07,0.22989175321947636,Q57270,BioShock,-1,0.8402777777777778,0.1111111111111111,0.9066666666666666,0.7357901390863495,0.235480627909748,0.1633064516129032,0.06072874493927126,0.05263157894736842,0.9999995231628418,1
+1,3,BioShock,"2007|Shooter|Windows , Xbox 360, Mac OS X, PlayStation 3|2K Boston/2K Australia",BioShock,Q2074746,BioShock Infinite,,fuzzy-augmented,first-person shooter video game and the third installment in the BioShock series,5.77142081791242e-07,0.20754432015848287,Q57270,BioShock,-1,0.8541666666666667,0.09090909090909093,0.8941176470588236,0.8431016165703551,0.235480627909748,0.9999999999999999,0.06882591093117409,0.05263157894736842,0.9999992847442627,1
+1,3,BioShock,"2007|Shooter|Windows , Xbox 360, Mac OS X, PlayStation 3|2K Boston/2K Australia",BioShock,Q302839,BioShock 2,,fuzzy-augmented,2010 first-person shooter video game developed by 2K Marin,4.66336342878804e-07,0.19767317863378867,Q57270,BioShock,-1,0.75,0.1111111111111111,0.96,0.7713035835776569,0.235480627909748,0.24697580645161288,0.04048582995951417,0.05263157894736842,0.9999991655349731,1
+1,3,BioShock,"2007|Shooter|Windows , Xbox 360, Mac OS X, PlayStation 3|2K Boston/2K Australia",BioShock,Q4914660,BioShock,,exact-match,extended play,3.436024992699295e-07,0.2143052981667525,Q57270,BioShock,-1,1.0,0.0,1.0,0.4817453684503325,0.235480627909748,0.05174731182795699,0.032388663967611336,0.02631578947368421,0.999994158744812,1
+1,4,The Legend of Zelda: Twilight Princess,"2006|Action-adventure|Wii, Nintendo GameCube|Nintendo",The Legend of Zelda Twilight Princess,Q735613,The Legend of Zelda: Twilight Princess,TLoZ: TP|Zelda: Twilight Princess|TLOZ: TP|TLOZ: Twilight Princess|Zelda: TP|TLoZ: Twilight Princess|Twilight Princess,fuzzy-augmented,2006 action-adventure video game,6.033000517170999e-07,0.4917646540133621,Q735613,The Legend of Zelda: Twilight Princess,1,0.9234126984126984,0.25,0.9678520625889048,0.8093092824184346,0.235480627909748,0.12147177419354839,0.15384615384615385,0.15789473684210525,1.0,1
+1,4,The Legend of Zelda: Twilight Princess,"2006|Action-adventure|Wii, Nintendo GameCube|Nintendo",The Legend of Zelda Twilight Princess,Q12395,The Legend of Zelda,The Legend of Zelda (video game)|LoZ: The Hyrule Fantasy|THE HYRULE FANTASY|The Hyrule Fantasy|Legend of Zelda: The Hyrule Fantasy,fuzzy-augmented,1986 action-adventure video game,6.4025013664677e-07,0.3137054737169327,Q735613,The Legend of Zelda: Twilight Princess,-1,0.8492063492063492,0.25,0.9,0.8004017852047628,0.235480627909748,0.07320099255583128,0.07692307692307693,0.10526315789473684,1.0,1
+1,4,The Legend of Zelda: Twilight Princess,"2006|Action-adventure|Wii, Nintendo GameCube|Nintendo",The Legend of Zelda Twilight Princess,Q213911,The Legend of Zelda: Ocarina of Time,OoT|Zelda 64|Zelda OoT|Ocarina of Time|Zelda Ocarina of Time|Zelda: OoT|Zelda: Ocarina of Time|TloZ: OoT,fuzzy-augmented,1998 video game for the Nintendo 64,1.1911033254103161e-06,0.32167830900405636,Q735613,The Legend of Zelda: Twilight Princess,-1,0.8141156462585033,0.14285714285714285,0.8930530348860658,0.8196125762481342,0.235480627909748,0.24697580645161288,0.145748987854251,0.18421052631578946,0.9999998807907104,1
+1,4,The Legend of Zelda: Twilight Princess,"2006|Action-adventure|Wii, Nintendo GameCube|Nintendo",The Legend of Zelda Twilight Princess,Q14915024,The Legend of Zelda: Hyrule Historia,Hyrule Historia,fuzzy-augmented,book by Nintendo,3.436024992699295e-07,0.2852090191106954,Q735613,The Legend of Zelda: Twilight Princess,-1,0.773776455026455,0.3333333333333333,0.8861564831619277,0.5888483677755219,0.235480627909748,0.015654648956356747,0.145748987854251,0.15789473684210525,0.9999997615814209,1
+1,4,The Legend of Zelda: Twilight Princess,"2006|Action-adventure|Wii, Nintendo GameCube|Nintendo",The Legend of Zelda Twilight Princess,Q861205,The Legend of Zelda: Skyward Sword,,fuzzy-augmented,2011 video game on the Nintendo Wii,6.300602614114573e-07,0.31303477652022194,Q735613,The Legend of Zelda: Twilight Princess,-1,0.7593253968253968,0.14285714285714285,0.8706377708978328,0.8276802534227766,0.235480627909748,0.4979838709677419,0.13765182186234817,0.15789473684210525,0.9999997615814209,1
+1,5,Resident Evil 4,"2005|Survival Horror: (Third-Person) Shooter|GameCube, PlayStation 2, Windows, Wii|Capcom",Resident Evil 4,Q622336,Resident Evil,REmake|Biohazard|Resident Evil (2002 video game),fuzzy-augmented,2002 survival horror video game remake,4.7462893025352463e-07,0.28112236853553463,Q275950,Resident Evil 4,-1,0.8333333333333333,0.1111111111111111,0.9733333333333334,0.8508612759610004,0.235480627909748,0.9999999999999999,0.05263157894736842,0.05263157894736842,1.0,0
+1,5,Resident Evil 4,"2005|Survival Horror: (Third-Person) Shooter|GameCube, PlayStation 2, Windows, Wii|Capcom",Resident Evil 4,Q275950,Resident Evil 4,,exact-match,2005 survival horror video game,7.0089136981933e-07,0.22088423997519224,Q275950,Resident Evil 4,1,1.0,0.2,1.0,0.850687195995076,1.0,0.3306451612903225,0.06072874493927126,0.07894736842105263,1.0,0
+1,5,Resident Evil 4,"2005|Survival Horror: (Third-Person) Shooter|GameCube, PlayStation 2, Windows, Wii|Capcom",Resident Evil 4,Q1248594,Resident Evil,Resident Evil 1|Bio Hazard,fuzzy-augmented,1996 survival horror video game,6.701034537683159e-07,0.2528697658674183,Q275950,Resident Evil 4,-1,0.8333333333333333,0.125,0.9733333333333334,0.8094112132885524,0.235480627909748,0.05174731182795699,0.05263157894736842,0.05263157894736842,0.9999996423721313,0
+1,5,Resident Evil 4,"2005|Survival Horror: (Third-Person) Shooter|GameCube, PlayStation 2, Windows, Wii|Capcom",Resident Evil 4,Q78059950,Resident Evil 3,RE 3|RE3|Resident Evil 3 Remake|RE3 Remake|RE 3 Remake|Biohazard RE:3,fuzzy-augmented,2020 survival horror video game remake developed by Capcom,7.614116916995016e-07,0.2670042078179752,Q275950,Resident Evil 4,-1,0.6666666666666666,0.1111111111111111,0.9733333333333334,0.8314550837543635,0.235480627909748,0.1633064516129032,0.06072874493927126,0.07894736842105263,0.9999995231628418,0
+1,5,Resident Evil 4,"2005|Survival Horror: (Third-Person) Shooter|GameCube, PlayStation 2, Windows, Wii|Capcom",Resident Evil 4,Q1138864,Resident Evil: Revelations,Resident Evil Revelations,fuzzy-augmented,2012 survival horror video game,5.634452669889955e-07,0.2750247769514353,Q275950,Resident Evil 4,-1,0.7657070707070708,0.125,0.8943589743589744,0.8254751015120989,0.235480627909748,0.12147177419354839,0.10526315789473684,0.07894736842105263,0.9999995231628418,0
+1,6,Halo 2,"2004|Shooter|Xbox, Windows,|Bungie",Halo 2,Q252804,Halo 2,,fuzzy-augmented,2004 video game,1.527512829871732e-06,0.21771898092921851,Q252804,Halo 2,1,1.0,0.3333333333333333,1.0,0.7771577921252035,0.235480627909748,0.13940092165898615,0.024291497975708502,0.05263157894736842,1.0,1
+1,6,Halo 2,"2004|Shooter|Xbox, Windows,|Bungie",Halo 2,Q20831013,Halo Wars 2,,fuzzy-augmented,video game,4.212621672773282e-07,0.21123500788886324,Q252804,Halo 2,-1,0.9166666666666669,0.0,0.8575757575757577,0.8019742008739553,0.235480627909748,0.4979838709677419,0.04453441295546559,0.07894736842105263,0.9999984502792358,1
+1,6,Halo 2,"2004|Shooter|Xbox, Windows,|Bungie",Halo 2,Q11861246,Halo,Halo (album)|Halo (Jonna Tervomaa),fuzzy-augmented,2004 album by Jonna Tervomaa,3.9105907845573427e-07,0.16193267501421804,Q252804,Halo 2,-1,0.75,0.2,0.9333333333333332,0.5454840252957401,0.235480627909748,0.018279569892473133,0.016194331983805668,0.02631578947368421,0.9999960660934448,1
+1,6,Halo 2,"2004|Shooter|Xbox, Windows,|Bungie",Halo 2,Q45028,Halo 4,,fuzzy-augmented,2012 first-person shooter video game developed by 343 Industries,6.649802428981021e-07,0.14464235176352278,Q252804,Halo 2,-1,0.5,0.1111111111111111,0.9333333333333332,0.8002327775780934,0.235480627909748,0.3306451612903225,0.024291497975708502,0.05263157894736842,0.9999805688858032,1
+1,6,Halo 2,"2004|Shooter|Xbox, Windows,|Bungie",Halo 2,Q54913666,Halo Infinite,,fuzzy-augmented,upcoming 2021 first-person shooter video game developed by 343 Industries,4.912315934178926e-07,0.16796074298424207,Q252804,Halo 2,-1,0.5,0.1,0.8435897435897436,0.79860591040155,0.235480627909748,0.24697580645161288,0.05263157894736842,0.05263157894736842,0.9999655485153198,1
+1,7,Prince of Persia: Sands of Time,"2003|Action-adventure|Xbox, GameCube, PlayStation 2, Windows|Ubisoft Montreal",Prince of Persia Sands of Time,Q715292,Prince of Persia: The Sands of Time,,fuzzy-augmented,2003 video game,5.968285394314384e-07,0.49473015994555,Q715292,Prince of Persia: The Sands of Time,1,0.9066326530612244,0.3333333333333333,0.9319815668202764,0.7384459109300655,0.235480627909748,0.05871975806451613,0.1417004048582996,0.18421052631578946,1.0,1
+1,7,Prince of Persia: Sands of Time,"2003|Action-adventure|Xbox, GameCube, PlayStation 2, Windows|Ubisoft Montreal",Prince of Persia Sands of Time,Q99309488,Prince of Persia: The Sands of Time,,fuzzy-augmented,2003 handheld video game,3.562851055816787e-07,0.31378036331586706,Q715292,Prince of Persia: The Sands of Time,-1,0.9066326530612244,0.25,0.9319815668202764,0.7682427287245636,0.235480627909748,0.12147177419354839,0.1417004048582996,0.18421052631578946,1.0,1
+1,7,Prince of Persia: Sands of Time,"2003|Action-adventure|Xbox, GameCube, PlayStation 2, Windows|Ubisoft Montreal",Prince of Persia Sands of Time,Q852583,Prince of Persia,,fuzzy-augmented,2008 video game,3.810075310579019e-07,0.33263175032876835,Q715292,Prince of Persia: The Sands of Time,-1,0.8619047619047621,0.0,0.9032258064516128,0.8166024195219284,0.235480627909748,0.4979838709677419,0.06477732793522267,0.07894736842105263,0.9999998807907104,1
+1,7,Prince of Persia: Sands of Time,"2003|Action-adventure|Xbox, GameCube, PlayStation 2, Windows|Ubisoft Montreal",Prince of Persia Sands of Time,Q245172,Prince of Persia: The Sands of Time,,fuzzy-augmented,2010 American action fantasy film,5.162126926142e-07,0.49035703501742955,Q715292,Prince of Persia: The Sands of Time,-1,0.9066326530612244,0.0,0.9319815668202764,0.6517256976717211,0.235480627909748,0.02943548387096775,0.1417004048582996,0.18421052631578946,0.9999997615814209,1
+1,7,Prince of Persia: Sands of Time,"2003|Action-adventure|Xbox, GameCube, PlayStation 2, Windows|Ubisoft Montreal",Prince of Persia Sands of Time,Q917588,Prince of Persia: The Forgotten Sands,,fuzzy-augmented,"video game for PC, Xbox 360 and PS3",4.303892120501475e-07,0.37551772971798997,Q715292,Prince of Persia: The Sands of Time,-1,0.8499653565129757,0.0,0.8962822269273882,0.7855799239374176,0.235480627909748,0.1967741935483871,0.14979757085020243,0.15789473684210525,0.9999996423721313,1
+1,8,Metroid Prime,2002|Action-adventure|Nintendo GameCube|Nintendo,Metroid Prime,Q1765733,Metroid Prime,,fuzzy-augmented,2002 video game,7.165095244019769e-07,0.27684512876759976,Q1765733,Metroid Prime,1,1.0,0.3333333333333333,1.0,0.8335772468056301,0.235480627909748,0.9999999999999999,0.05263157894736842,0.05263157894736842,1.0,1
+1,8,Metroid Prime,2002|Action-adventure|Nintendo GameCube|Nintendo,Metroid Prime,Q2089035,Metroid Prime 2: Echoes,Dark Echoes,fuzzy-augmented,2004 action-adventure first person-shooter video game,4.6079875398212504e-07,0.2693034457819413,Q1765733,Metroid Prime,-1,0.8174603174603174,0.16666666666666666,0.9130434782608696,0.8185354687122393,0.235480627909748,0.1967741935483871,0.0931174089068826,0.10526315789473684,0.9999998807907104,1
+1,8,Metroid Prime,2002|Action-adventure|Nintendo GameCube|Nintendo,Metroid Prime,Q2530723,Metroid,,fuzzy-augmented,1986 action-adventure shooter video game,5.198599115103781e-07,0.1981146556195938,Q1765733,Metroid Prime,-1,0.8619047619047621,0.2,0.9076923076923076,0.7938635052883305,0.235480627909748,0.12147177419354839,0.02834008097165992,0.02631578947368421,0.9999998807907104,1
+1,8,Metroid Prime,2002|Action-adventure|Nintendo GameCube|Nintendo,Metroid Prime,Q1139244,Metroid Prime Hunters,,fuzzy-augmented,2006 action-adventure first-person shooter video game,4.40233055095872e-07,0.2767318830681642,Q1765733,Metroid Prime,-1,0.917989417989418,0.16666666666666666,0.923809523809524,0.7894086654684779,0.235480627909748,0.0963709677419355,0.08502024291497975,0.07894736842105263,0.9999998807907104,1
+1,8,Metroid Prime,2002|Action-adventure|Nintendo GameCube|Nintendo,Metroid Prime,Q2539933,Metroid Prime: Trilogy,,fuzzy-augmented,video game compilation,4.597038631732474e-07,0.2750209077403415,Q1765733,Metroid Prime,-1,0.9246031746031746,0.0,0.9181818181818182,0.8073841853476738,0.235480627909748,0.1633064516129032,0.08906882591093118,0.07894736842105263,0.9999992847442627,1
+1,9,Halo Combat Evolved,"2001|Shooter|Xbox, Windows, Macintosh|Bungie",Halo Combat Evolved,Q276217,Halo: Combat Evolved,Halo: CE|Halo 1|Halo PC|Halo,fuzzy-augmented,2001 video game,8.678430707937966e-07,0.41718007109879024,Q276217,Halo: Combat Evolved,1,0.9866666666666668,0.3333333333333333,0.99,0.8186399381948972,0.235480627909748,0.9999999999999999,0.08097165991902834,0.07894736842105263,1.0,1
+1,9,Halo Combat Evolved,"2001|Shooter|Xbox, Windows, Macintosh|Bungie",Halo Combat Evolved,Q61721730,Halo Combat Evolved,,fuzzy-augmented,video game owned by Stanford University Libraries,3.436024992699295e-07,0.3611925254783732,Q276217,Halo: Combat Evolved,-1,1.0,0.0,1.0,0.7464747133995474,0.235480627909748,0.24697580645161288,0.07692307692307693,0.07894736842105263,1.0,1
+1,9,Halo Combat Evolved,"2001|Shooter|Xbox, Windows, Macintosh|Bungie",Halo Combat Evolved,Q2642466,Halo: Combat Evolved Anniversary,,fuzzy-augmented,2011 first-person shooter video game,4.980884952911457e-07,0.3835414858595353,Q276217,Halo: Combat Evolved,-1,0.9244805194805196,0.2,0.91875,0.7815973692999048,0.235480627909748,0.3306451612903225,0.12955465587044535,0.10526315789473684,1.0,1
+1,9,Halo Combat Evolved,"2001|Shooter|Xbox, Windows, Macintosh|Bungie",Halo Combat Evolved,Q5643304,Halo,,fuzzy-augmented,2001 single by Soil,3.436024992699295e-07,0.1633425544440002,Q276217,Halo: Combat Evolved,-1,0.8167989417989419,0.25,0.8421052631578947,0.5364873799333227,0.235480627909748,0.02639296187683286,0.016194331983805668,0.02631578947368421,0.9999980926513672,1
+1,9,Halo Combat Evolved,"2001|Shooter|Xbox, Windows, Macintosh|Bungie",Halo Combat Evolved,Q1747150,Halo,,fuzzy-augmented,series of video games,3.076723863799841e-06,0.1643785255322367,Q276217,Halo: Combat Evolved,-1,0.8167989417989419,0.0,0.8421052631578947,0.7332843568368913,0.235480627909748,0.13940092165898615,0.016194331983805668,0.02631578947368421,0.999983549118042,1
diff --git a/grams.yaml b/grams.yaml
new file mode 100644
index 0000000..64fd363
--- /dev/null
+++ b/grams.yaml
@@ -0,0 +1,21 @@
+data_graph:
+ max_n_hop: 1
+ options:
+ - DISABLE_SAME_ENT_SEARCH
+ - PRUNING_REDUNDANT_ENT
+ # - NO_KG_INDEX
+# possible values: psl, oracle, steiner_tree
+st_method: psl
+steiner_tree:
+ # algo: bank or maximum_arborescence
+ algo: semtab2020 # maximum_arborescence
+psl:
+ enable_logging: false
+ threshold: 0.5
+ # select_simplepath, steiner_tree, external:semtab2020
+ postprocessing: steiner_tree
+ disable_rules: # []
+ - HeaderSimimilarity
+ - DataTypeMismatch
+hydra.run.dir: .
+hydra.output_subdir: null
\ No newline at end of file
diff --git a/grams/__init__.py b/grams/__init__.py
new file mode 100644
index 0000000..4cd7536
--- /dev/null
+++ b/grams/__init__.py
@@ -0,0 +1 @@
+from typing import List, Dict, Tuple, Callable, Any, Optional
diff --git a/grams/algorithm/__init__.py b/grams/algorithm/__init__.py
new file mode 100644
index 0000000..4cd7536
--- /dev/null
+++ b/grams/algorithm/__init__.py
@@ -0,0 +1 @@
+from typing import List, Dict, Tuple, Callable, Any, Optional
diff --git a/grams/algorithm/bank_solver.py b/grams/algorithm/bank_solver.py
new file mode 100644
index 0000000..0ed74d0
--- /dev/null
+++ b/grams/algorithm/bank_solver.py
@@ -0,0 +1,491 @@
+import copy
+from dataclasses import dataclass
+from operator import attrgetter, itemgetter
+
+import networkx as nx
+import matplotlib.pyplot as plt
+from typing import Set, List, Dict, Tuple, Callable, FrozenSet, Optional, Any
+
+"""
+Find a top-k approximate minimum steiner arborescence using the BANK algorithm
+"""
+
+
+@dataclass
+class Edge:
+ __slots__ = ("id", "source_id", "target_id", "edge_key", "weight", "n_edges")
+ id: str
+ source_id: str
+ target_id: str
+ edge_key: str
+ weight: float
+ n_edges: int
+
+
+@dataclass
+class Solution:
+ id: FrozenSet[str]
+ graph: nx.MultiDiGraph
+ weight: float
+
+ def get_n_edges(self):
+ if not hasattr(self, "n_edges"):
+ self.n_edges = sum(e.n_edges for _, _, e in self.graph.edges(data='data'))
+ return self.n_edges
+
+
+class NoSingleRootException(Exception):
+ pass
+
+
+# noinspection PyMethodMayBeStatic
+class SteinerTreeBankSolver:
+
+ def __init__(self,
+ original_graph: nx.MultiDiGraph,
+ terminal_nodes: Set[str],
+ weight_fn: Callable[[dict], float],
+ solution_cmp_fn: Optional[Callable[[Solution], Any]]=None,
+ top_k_st: int = 10,
+ top_k_path: int = 10,
+ allow_shorten_graph: bool = True):
+ # original graph
+ self.original_graph = original_graph
+ # function that extract weights
+ self.weight_fn = weight_fn
+ # function to compare & sort solutions
+ self.solution_cmp_fn = solution_cmp_fn or attrgetter('weight')
+ # graph that the bank algorithm will work with
+ self.graph: nx.MultiDiGraph = None
+ # output graphs
+ self.solutions: List[Solution] = []
+ self.terminal_nodes = terminal_nodes
+ self.top_k_st = top_k_st
+ self.top_k_path = top_k_path
+ self.allow_shorten_graph = allow_shorten_graph
+
+ def run(self):
+ self.graph = self._preprocessing(self.original_graph, self.weight_fn)
+
+ if nx.is_weakly_connected(self.graph):
+ self.solutions = self._solve(self.graph, self.terminal_nodes, self.top_k_st, self.top_k_path)
+ else:
+ graphs = self._split_connected_components(self.graph)
+ final_solutions = None
+ for g in graphs:
+ terminal_nodes = self.terminal_nodes.intersection(list(g.nodes))
+ solutions = self._solve(g, terminal_nodes, self.top_k_st, self.top_k_path)
+ if final_solutions is None:
+ final_solutions = solutions
+ else:
+ next_solutions = []
+ for current_sol in final_solutions:
+ for sol in solutions:
+ next_solutions.append(self._merge_graph(current_sol.graph, sol.graph))
+ final_solutions = self._sort_solutions(next_solutions, return_solution=True)[:self.top_k_st]
+
+ self.solutions = final_solutions
+
+ # [self._get_roots(sol.graph) for sol in self.solutions]
+ # [sol.weight for sol in self.solutions]
+ return [self._postprocessing(self.original_graph, self.graph, sol.graph) for sol in self.solutions], self.solutions
+
+ def _preprocessing(self, g: nx.MultiDiGraph, weight_fn: Callable[[dict], float]):
+ ng = nx.MultiDiGraph()
+
+ # convert edges
+ edge_id_count = 0
+ for uid, vid, eid, edata in g.edges(data=True, keys=True):
+ ng.add_edge(uid, vid, key=eid, data=Edge(id=str(edge_id_count), source_id=uid, target_id=vid, edge_key=eid,
+ weight=weight_fn(edata), n_edges=1))
+ edge_id_count += 1
+
+ if self.allow_shorten_graph:
+ # shorten path using the following heuristic
+ # for a node that only connect two nodes (indegree & outdegree = 1) and not terminal nodes, we replace the node by one edge
+ # map from the replaced edge to the removed nodes
+ removed_nodes = {}
+ for uid in list(ng.nodes):
+ if uid not in self.terminal_nodes and g.in_degree(uid) == 1 and g.out_degree(uid) == 1:
+ inedge: Edge = next(iter(ng.in_edges(uid, data='data')))[-1]
+ outedge: Edge = next(iter(ng.out_edges(uid, data='data')))[-1]
+ if inedge.edge_key == outedge.edge_key:
+ new_edge_key = inedge.edge_key
+ else:
+ new_edge_key = f"{inedge.edge_key}-{outedge.edge_key}"
+ if not ng.has_edge(inedge.source_id, outedge.target_id, key=new_edge_key):
+ # replace it only if we don't have that link before
+ ng.remove_node(uid)
+ ng.add_edge(inedge.source_id, outedge.target_id, key=new_edge_key, data=Edge(id=str(edge_id_count),
+ source_id=inedge.source_id,
+ target_id=outedge.target_id,
+ edge_key=new_edge_key,
+ weight=inedge.weight + outedge.weight,
+ n_edges=2))
+ removed_nodes[str(edge_id_count)] = (uid, inedge, outedge)
+ edge_id_count += 1
+
+ # store the removed nodes to restore it later
+ ng.graph['removed_nodes'] = removed_nodes
+ return ng
+
+ def _postprocessing(self, origin_graph: nx.MultiDiGraph, prep_graph: nx.MultiDiGraph, out_graph: nx.MultiDiGraph):
+ if self.allow_shorten_graph:
+ removed_nodes = prep_graph.graph['removed_nodes']
+ else:
+ removed_nodes = set()
+
+ g = origin_graph.copy()
+ selected_edges = set()
+ for uid, vid, edge in out_graph.edges(data='data'):
+ if edge.id in removed_nodes:
+ removed_node, inedge, outedge = removed_nodes[edge.id]
+ selected_edges.add((inedge.source_id, inedge.target_id, inedge.edge_key))
+ selected_edges.add((outedge.source_id, outedge.target_id, outedge.edge_key))
+ else:
+ selected_edges.add((uid, vid, edge.edge_key))
+
+ for uid, vid, eid in list(g.edges(keys=True)):
+ if (uid, vid, eid) not in selected_edges:
+ g.remove_edge(uid, vid, eid)
+ for uid in list(g.nodes):
+ if g.in_degree(uid) == 0 and g.out_degree(uid) == 0:
+ g.remove_node(uid)
+ return g
+
+ def _merge_graph(self, g1: nx.MultiDiGraph, g2: nx.MultiDiGraph) -> nx.MultiDiGraph:
+ g = g1.copy()
+ for uid, vid, eid, edata in g2.edges(keys=True, data=True):
+ g.add_edge(uid, vid, key=eid, **edata)
+ return g
+
+ def _split_connected_components(self, g: nx.MultiDiGraph):
+ connected_comps = [
+ comp
+ for comp in nx.weakly_connected_components(g)
+ # must have at least two terminal nodes (to form a graph)
+ if len(self.terminal_nodes.intersection(comp)) > 1
+ ]
+
+ node2comp = {}
+ for i, comp in enumerate(connected_comps):
+ for uid in comp:
+ node2comp[uid] = i
+
+ subgs = [nx.MultiDiGraph() for _ in range(len(connected_comps))]
+ for uid, vid, eid, edata in g.edges(keys=True, data=True):
+ if uid not in node2comp:
+ continue
+ subgs[node2comp[uid]].add_edge(uid, vid, key=eid, **edata)
+ return subgs
+
+ def _solve(self, g: nx.MultiDiGraph, terminal_nodes: Set[str], top_k_st: int, top_k_path: int):
+ """Despite the name, this is finding steiner tree. Assuming their is a root node that connects all
+ terminal nodes together.
+ """
+ roots = set(g.nodes.keys())
+
+ attr_visit_hists = []
+ # to ensure the order
+ for uid in list(sorted(terminal_nodes)):
+ visit_hist = UpwardTraversal.top_k_beamsearch(g, uid, top_k_path)
+ roots = roots.intersection(visit_hist.paths.keys())
+ attr_visit_hists.append((uid, visit_hist))
+
+ if len(roots) == 0:
+ # there is no nodes that can connect to all terminal nodes either this are disconnected
+ # components or you pass a directed graph with weakly connected components (a -> b <- c)
+ if nx.is_weakly_connected(g):
+ # perhaps, we can break the weakly connected components by breaking one of the link (a -> b <- c)
+ raise NoSingleRootException(
+ "You pass a weakly connected component and there are parts of the graph like this (a -> b <- c). Fix it before running this algorithm")
+ raise Exception("Your graph is disconnected. Consider splitting them before calling bank solver")
+
+ # to ensure the order again & remove randomness
+ roots = sorted(roots)
+
+ # merge the paths using beam search
+ results = []
+ for root in roots:
+ current_states = []
+ uid, visit_hist = attr_visit_hists[0]
+ for path in visit_hist.paths[root]:
+ pg = nx.MultiDiGraph()
+ if len(path.path) > 0:
+ assert uid == path.path[0].target_id
+ pg.add_node(uid)
+ for e in path.path:
+ pg.add_node(e.source_id)
+ pg.add_edge(e.source_id, e.target_id, e.edge_key, weight=e.weight, data=e)
+ current_states.append(pg)
+
+ if len(current_states) > top_k_st:
+ current_states = self._sort_solutions(current_states)[:top_k_st]
+
+ for uid, visit_hist in attr_visit_hists[1:]:
+ next_states = []
+ for state in current_states:
+ for path in visit_hist.paths[root]:
+ pg = state.copy()
+ if len(path.path) > 0:
+ assert uid == path.path[0].target_id
+ pg.add_node(uid)
+ for e in path.path:
+ if e.source_id not in pg.nodes:
+ pg.add_node(e.source_id)
+ # TODO: here we don't check by edge_key because we may create another edge of different key
+ # hope this new path has been exploited before.
+ if not pg.has_edge(e.source_id, e.target_id):
+ pg.add_edge(e.source_id, e.target_id, key=e.edge_key, weight=e.weight, data=e)
+
+ # after add a path to the graph, it can create new cycle, detect and fix it
+ try:
+ # note that the function I'm using doesn't return parallel edges
+ cycles_iter = nx.find_cycle(pg, uid, orientation='reverse')
+ # we can show that if the graph contain cycle, there is a better path
+ #
+ # cycles_iter = [(uid, vid) for uid, vid, eid, orien in cycles_iter]
+ # for _g in self._break_cycles(root, pg, cycles_iter):
+ # next_states.append(_g)
+ except nx.NetworkXNoCycle:
+ next_states.append(pg)
+ if len(list(pg.edges)) != len({(uid, vid) for uid, vid, eid in pg.edges(keys=True)}):
+ assert False
+ if len(next_states) > top_k_st:
+ next_states = self._sort_solutions(next_states)[:top_k_st]
+ current_states = next_states
+ # cgs = [g for g in next_states if len(list(nx.simple_cycles(g))) > 0]
+ # nx.draw_networkx(cgs[0]); plt.show()
+ # nx.draw(cgs[0]); plt.show()
+ results += current_states
+ return self._sort_solutions(results, return_solution=True)
+
+ def _break_cycles(self, root: str, g: nx.MultiDiGraph, cycles_iter: List[Tuple[str, str]]):
+ # g = current_states[0]; g = self.output_graphs[0]
+ # pos = nx.kamada_kawai_layout(g); nx.draw_networkx(g, pos); nx.draw_networkx_edge_labels(g, pos, edge_labels={(u, v): d for u, v, d in g.edges(keys=True)}); plt.show()
+ # nx.draw(g); plt.show()
+ return self._break_cycles_brute_force(root, g, cycles_iter)
+
+ def _break_cycles_brute_force(self, root: str, g: nx.MultiDiGraph, cycles_iter: List[Tuple[str, str]]):
+ # one side effect of this approach is that it may separate the graph
+ # currently we say it only happen when the target node of the remove edge only has one incoming edge
+ # if it has two edges, then the other source (not of the remove edge) must have a path from root -> itself
+ # now, since we have cascade removing, if the path doesn't go through the removed node, then it's okay,
+ # if the path goes through the remove node, it's impossible since we only remove node that indegree == 0 or
+ # outdegree == 0
+ parallel_edges = []
+ for uid, vid in cycles_iter:
+ if g.in_degree(vid) == 1:
+ # we can't remove this edge, as removing it will make it's unreachable from the root
+ # so just skip edge
+ continue
+ edges = [edata['data'] for eid, edata in g[uid][vid].items()]
+ edge_weight = min(edges, key=attrgetter('weight')).weight
+ parallel_edges.append((uid, vid, edges, edge_weight))
+
+ # not comparing based on weight anymore since psl can be quite difficult to select correct edge
+ # min_edge_weight = min(parallel_edges, key=itemgetter(3))[3]
+ # unbreakable = {i for i, x in enumerate(parallel_edges) if x[3] == min_edge_weight}
+ # new_graphs = []
+ # if len(unbreakable) < len(parallel_edges):
+ # # it's great! we have some edge to break!
+ # # for each breakable edge, we will have a new graph
+ # for i, item in enumerate(parallel_edges):
+ # if i in unbreakable:
+ # continue
+ # ng: nx.MultiDiGraph = g.copy()
+ # ng.remove_edge(item[0], item[1])
+ # ng = self._remove_redundant_nodes(root, ng)
+ # new_graphs.append(ng)
+ # else:
+ # so bad, we have to try one by one
+ new_graphs = []
+ for uid, vid, edges, edge_weight in parallel_edges:
+ ng: nx.MultiDiGraph = g.copy()
+ ng.remove_edge(uid, vid)
+ # self._draw(ng); self._draw(g)
+ ng = self._remove_redundant_nodes(root, ng)
+ new_graphs.append(ng)
+
+ # just assert if it works as expected
+ for ng in new_graphs:
+ assert len(list(nx.simple_cycles(ng))) == 0
+ return new_graphs
+
+ def _sort_solutions(self, graphs: List[nx.MultiDiGraph], return_solution: bool = False):
+ """Sort the solution by th"""
+ solutions = {}
+ for g in graphs:
+ id = frozenset((e.id for _, _, e in g.edges(data='data')))
+ if id in solutions:
+ continue
+
+ weight = sum(e.weight for _, _, e in g.edges(data='data'))
+ solutions[id] = Solution(id, g, weight)
+
+ solutions = sorted(solutions.values(), key=self.solution_cmp_fn)
+ if return_solution:
+ return solutions
+ return [sol.graph for sol in solutions]
+
+ def _remove_redundant_nodes(self, root: str, g: nx.MultiDiGraph):
+ # remove nodes in the graph that shouldn't be in a steiner tree
+ while True:
+ removed_nodes = []
+ for uid in g.nodes:
+ if uid == root or uid in self.terminal_nodes:
+ continue
+ if g.in_degree(uid) == 0 or g.out_degree(uid) == 0:
+ removed_nodes.append(uid)
+ if len(removed_nodes) == 0:
+ break
+ for uid in removed_nodes:
+ g.remove_node(uid)
+ return g
+
+ def _get_roots(self, g: nx.MultiDiGraph):
+ """This function is mainly used for debugging"""
+ return [uid for uid in g if g.in_degree(uid) == 0]
+
+ @staticmethod
+ def _draw(g: nx.MultiDiGraph):
+ """This function is mainly used for debugging"""
+ pos = nx.kamada_kawai_layout(g)
+ nx.draw_networkx(g, pos)
+ nx.draw_networkx_edge_labels(g, pos, edge_labels={(u, v): d for u, v, d in g.edges(keys=True)})
+ plt.show()
+
+
+class CycleBreaker:
+ def spanning_arborescence(self, g: nx.MultiDiGraph, root: str, terminal_nodes: Set[str]):
+ resp = nx.algorithms.tree.branchings.minimum_spanning_arborescence(g, attr='weight', default=None,
+ preserve_attrs=True)
+ ng = nx.MultiDiGraph()
+ for uid, vid, edge in resp.edges(data='data'):
+ ng.add_edge(uid, vid, key=edge.edge_key, weight=edge.weight, data=edge)
+ # after breaking we may have multiple root nodes or leaf so we just need to clean them
+ return [self._truncate_redundant_nodes(ng, root, terminal_nodes)]
+
+ def one_edge_at_a_time(self, og: nx.MultiDiGraph, root: str, terminal_nodes: Set[str]):
+ """This approach doesn't allow us to select the graph that has shorter length due to multiple cycles in the graph"""
+ current_graphs = [og]
+ has_cycle = True
+ while has_cycle:
+ # loop until we don't have any cycle left
+ new_graphs = []
+ for g in current_graphs:
+ for cycle in nx.simple_cycles(g):
+ cycle.append(cycle[0])
+ edge_groups = []
+ for i in range(len(cycle) - 1):
+ uid = cycle[i]
+ vid = cycle[i+1]
+ # we may have more than one edge between two nodes
+ edges = [edata['data'] for eid, edata in g[uid][vid].items()]
+ edge_weight = min(edges, key=attrgetter('weight'))
+ edge_groups.append((uid, vid, edges, edge_weight))
+
+ min_edge_weight = min(edge_groups, itemgetter(3))
+ unbreakable = {i for i, x in enumerate(edge_groups) if x[3] == min_edge_weight}
+ if len(unbreakable) < len(edge_groups):
+ # it's great! we have some edge to break!
+ # for each breakable edge, we will have a new graph
+ for i, item in enumerate(edge_groups):
+ if i in unbreakable:
+ continue
+ ng: nx.MultiDiGraph = g.copy()
+ # we remove all instead of just one
+ ng.remove_edge(item[0], item[1])
+ ng = self._truncate_redundant_nodes(ng, root, terminal_nodes)
+ new_graphs.append(ng)
+ else:
+ # so bad, we have to try one by one
+ tmp = []
+ for uid, vid, edges, edge_weight in edge_groups:
+ ng: nx.MultiDiGraph = g.copy()
+ ng.remove_edge(uid, vid)
+ ng = self._truncate_redundant_nodes(ng, root, terminal_nodes)
+ # given a score for each split, we prefer the one that is smaller?
+ # however, we can't calculate the depth since the graph may contain multiple cycles
+ # this make this approach not good.
+ tmp.append(ng)
+
+ # find which one is better
+
+ pass
+
+ def _truncate_redundant_nodes(self, g: nx.MultiDiGraph, root: str, terminal_nodes: Set[str]):
+ while True:
+ removed_nodes = []
+ for uid in g.nodes:
+ if uid == root or uid in terminal_nodes:
+ continue
+ if g.in_degree(uid) == 0 or g.out_degree(uid) == 0:
+ removed_nodes.append(uid)
+ if len(removed_nodes) == 0:
+ break
+ for uid in removed_nodes:
+ g.remove_node(uid)
+ return g
+
+
+@dataclass
+class UpwardPath:
+ __slots__ = ("visited_nodes", "path", "weight")
+
+ visited_nodes: Set[str]
+ path: List[Edge]
+ weight: float
+
+ @staticmethod
+ def empty(start_node_id: str):
+ return UpwardPath({start_node_id}, [], 0.0)
+
+ def push(self, edge: Edge) -> 'UpwardPath':
+ c = self.clone()
+ c.path.append(edge)
+ c.visited_nodes.add(edge.source_id)
+ c.weight += edge.weight
+ return c
+
+ def clone(self):
+ return UpwardPath(copy.copy(self.visited_nodes), copy.copy(self.path), self.weight)
+
+
+@dataclass
+class UpwardTraversal:
+ __slots__ = ("source_id", "paths")
+
+ source_id: str
+
+ # storing that we can reach this node through those list of paths
+ paths: Dict[str, List[UpwardPath]]
+
+ @staticmethod
+ def top_k_beamsearch(g: nx.MultiDiGraph, start_node_id: str, top_k_path: int):
+ travel_hist = UpwardTraversal(start_node_id, dict())
+ travel_hist.paths[start_node_id] = [UpwardPath.empty(start_node_id)]
+ for source_id, target_id, edge_id, orientation in nx.edge_bfs(g, start_node_id, orientation='reverse'):
+ if source_id not in travel_hist.paths:
+ travel_hist.paths[source_id] = []
+
+ edge: Edge = g.edges[source_id, target_id, edge_id]['data']
+ #
+ # for edata in g[source_id][target_id].values():
+ # edge: Edge = edata['data']
+ for path in travel_hist.paths[target_id]:
+ if source_id in path.visited_nodes:
+ # path will become loopy, which we don't want to do
+ continue
+ path = path.push(edge)
+ travel_hist.paths[source_id].append(path)
+
+ # we trim the number of paths in here
+ if len(travel_hist.paths[source_id]) > top_k_path:
+ # calculate the score of each path, and then select the best one
+ travel_hist.sort_paths(source_id)
+ travel_hist.paths[source_id] = travel_hist.paths[source_id][:top_k_path]
+ return travel_hist
+
+ def sort_paths(self, node_id: str):
+ self.paths[node_id] = sorted(self.paths[node_id], key=attrgetter('weight'))
diff --git a/grams/algorithm/data_graph.py b/grams/algorithm/data_graph.py
new file mode 100644
index 0000000..2d2d77f
--- /dev/null
+++ b/grams/algorithm/data_graph.py
@@ -0,0 +1,1764 @@
+import copy
+import re
+from collections import defaultdict
+from dataclasses import dataclass, field
+from enum import Enum, Flag, auto
+from functools import cmp_to_key
+from itertools import chain
+from typing import Dict, Optional, Union, List, Set, Tuple, NamedTuple, Any
+
+import networkx as nx
+from loguru import logger
+from tqdm import tqdm
+from typing_extensions import TypedDict
+
+from grams.algorithm.kg_index import KGObjectIndex
+from grams.algorithm.literal_match import WikidataValueType, TextParser, LiteralMatcher
+from grams.inputs.linked_table import LinkedTable
+from kgdata.wikidata.models import QNode, DataValue, WDProperty, WDClass
+import sm.misc as M
+
+
+@dataclass(frozen=True)
+class Span:
+ start: int
+ end: int
+
+ @property
+ def length(self):
+ return self.end - self.start
+
+
+@dataclass
+class CellNode:
+ id: str
+ value: str
+ column: int
+ row: int
+ qnode_ids: List[str]
+ qnodes_span: Dict[str, List[Span]]
+
+ @property
+ def is_cell(self):
+ return True
+
+ @property
+ def is_literal_value(self):
+ return False
+
+ @property
+ def is_entity_value(self):
+ return False
+
+ @property
+ def is_statement(self):
+ return False
+
+
+@dataclass
+class ContextSpan:
+ # the text of the context
+ text: str
+ span: Span
+
+ def get_text_span(self):
+ return self.text[self.span.start:self.span.end]
+
+
+@dataclass
+class LiteralValueNode:
+ id: str
+ value: DataValue
+ # not none if it is appear in the context
+ context_span: Optional[ContextSpan]
+
+ @property
+ def is_cell(self):
+ return False
+
+ @property
+ def is_literal_value(self):
+ return True
+
+ @property
+ def is_entity_value(self):
+ return False
+
+ @property
+ def is_statement(self):
+ return False
+
+ @property
+ def is_context(self):
+ return self.context_span is not None
+
+
+@dataclass
+class EntityValueNode:
+ id: str
+ qnode_id: str
+ # not none if it is appear in the context
+ context_span: Optional[ContextSpan]
+
+ @property
+ def is_cell(self):
+ return False
+
+ @property
+ def is_literal_value(self):
+ return False
+
+ @property
+ def is_entity_value(self):
+ return True
+
+ @property
+ def is_statement(self):
+ return False
+
+ @property
+ def is_context(self):
+ return self.context_span is not None
+
+
+EdgeFlowSource = NamedTuple("EdgeFlowSource", [("source_id", str), ("edge_id", str)])
+EdgeFlowTarget = NamedTuple("EdgeFlowTarget", [("target_id", str), ("edge_id", str)])
+
+
+class LinkGenMethod(Enum):
+ """Methods to generate a link"""
+ # this statement is generated by exact matching from the link
+ FromWikidataLink = "from_wikidata_link"
+ FromLiteralMatchingFunc = "from_literal_matching_function"
+
+
+@dataclass
+class FlowProvenance:
+ """Contain information regarding how this relationship/flow has been generated (typically coming from the matching algorithm)
+ """
+ # method that
+ gen_method: LinkGenMethod
+ gen_method_arg: Any
+ prob: float
+
+ def merge(self, another: 'FlowProvenance') -> Union['FlowProvenance', None]:
+ """Try to merge the provenance, if we cannot merge them, just return None"""
+ if self.gen_method != another.gen_method:
+ return None
+ if self.gen_method == LinkGenMethod.FromWikidataLink:
+ return self
+ if self.gen_method == LinkGenMethod.FromLiteralMatchingFunc:
+ if self.gen_method_arg == another.gen_method_arg:
+ if self.prob > another.prob:
+ return self
+ return another
+ return None
+ raise M.UnreachableError()
+
+ @staticmethod
+ def merge_lst(lst1: List['FlowProvenance'], lst2: List['FlowProvenance']) -> List['FlowProvenance']:
+ """Assume that items within each list (lst1 & lst2) are not mergeable"""
+ if len(lst1) == 0:
+ return lst2
+ elif len(lst2) == 0:
+ return lst1
+
+ lst = copy.copy(lst1)
+ for item in lst2:
+ for i in range(len(lst1)):
+ resp = lst[i].merge(item)
+ if resp is None:
+ # cannot merge them
+ lst.append(item)
+ break
+ else:
+ # merge lst[i] & item
+ lst[i] = resp
+ break
+ return lst
+
+
+@dataclass
+class StatementNode:
+ id: str
+ # id of the qnode that contains the statement
+ qnode_id: str
+ # predicate of the statement
+ predicate: str
+ # whether this statement actually exist in KG
+ is_in_kg: bool
+
+ # recording which link in the source is connected to the target.
+ forward_flow: Dict[EdgeFlowSource, Set[EdgeFlowTarget]] = field(default_factory=dict)
+ reversed_flow: Dict[EdgeFlowTarget, Set[EdgeFlowSource]] = field(default_factory=dict)
+ flow: Dict[Tuple[EdgeFlowSource, EdgeFlowTarget], List[FlowProvenance]] = field(default_factory=dict)
+
+ @property
+ def value(self):
+ return self.id
+
+ @property
+ def is_cell(self):
+ return False
+
+ @property
+ def is_literal_value(self):
+ return False
+
+ @property
+ def is_entity_value(self):
+ return False
+
+ @property
+ def is_statement(self):
+ return True
+
+ def track_provenance(self, source_flow: EdgeFlowSource, target_flow: EdgeFlowTarget, provenances: List[FlowProvenance]):
+ ptr = self.forward_flow
+ if source_flow not in ptr:
+ ptr[source_flow] = set()
+ ptr[source_flow].add(target_flow)
+
+ ptr = self.reversed_flow
+ if target_flow not in ptr:
+ ptr[target_flow] = set()
+ ptr[target_flow].add(source_flow)
+
+ # TODO: merge the provenance if we can
+ if (source_flow, target_flow) not in self.flow:
+ self.flow[source_flow, target_flow] = provenances
+ else:
+ self.flow[source_flow, target_flow] = FlowProvenance.merge_lst(self.flow[source_flow, target_flow], provenances)
+
+ def untrack_source_flow(self, source_flow: EdgeFlowSource):
+ for target_flow in self.forward_flow.pop(source_flow):
+ self.flow.pop((source_flow, target_flow))
+ self.reversed_flow[target_flow].remove(source_flow)
+ if len(self.reversed_flow[target_flow]) == 0:
+ self.reversed_flow.pop(target_flow)
+
+ def untrack_target_flow(self, target_flow: EdgeFlowTarget):
+ for source_flow in self.reversed_flow.pop(target_flow):
+ self.flow.pop((source_flow, target_flow))
+ self.forward_flow[source_flow].remove(target_flow)
+ if len(self.forward_flow[source_flow]) == 0:
+ self.forward_flow.pop(source_flow)
+
+ def untrack_flow(self, source_flow: EdgeFlowSource, target_flow: EdgeFlowTarget):
+ self.flow.pop((source_flow, target_flow))
+ self.forward_flow[source_flow].remove(target_flow)
+ self.reversed_flow[target_flow].remove(source_flow)
+
+ if len(self.forward_flow[source_flow]) == 0:
+ self.forward_flow.pop(source_flow)
+ if len(self.reversed_flow[target_flow]) == 0:
+ self.reversed_flow.pop(target_flow)
+
+ def has_source_flow(self, source_flow):
+ return source_flow in self.forward_flow
+
+ def has_target_flow(self, target_flow):
+ return target_flow in self.reversed_flow
+
+ def has_flow(self, source_flow, target_flow):
+ return (source_flow, target_flow) in self.flow
+
+ def iter_source_flow(self, target_flow: EdgeFlowTarget):
+ for source_flow in self.reversed_flow[target_flow]:
+ yield source_flow, self.flow[source_flow, target_flow]
+
+ def iter_target_flow(self, source_flow: EdgeFlowSource):
+ for target_flow in self.forward_flow[source_flow]:
+ yield target_flow, self.flow[source_flow, target_flow]
+
+ def get_provenance(self, source_flow: EdgeFlowSource, target_flow: EdgeFlowTarget):
+ return self.flow[source_flow, target_flow]
+
+ def get_provenance_by_edge(self, inedge: 'DGEdge', outedge: 'DGEdge'):
+ return self.flow[EdgeFlowSource(inedge.source, inedge.predicate), EdgeFlowTarget(outedge.target, outedge.predicate)]
+
+ def is_same_flow(self, inedge: 'DGEdge', outedge: 'DGEdge') -> bool:
+ return (EdgeFlowSource(inedge.source, inedge.predicate), EdgeFlowTarget(outedge.target, outedge.predicate)) in self.flow
+
+
+DGNode = Union[CellNode, LiteralValueNode, EntityValueNode, StatementNode]
+
+
+@dataclass
+class DGStatementID:
+ qnode_id: str
+ predicate: str
+ statement_index: int
+
+ def get_id(self):
+ return f"stmt:{self.qnode_id}-{self.predicate}-{self.statement_index}"
+
+ @staticmethod
+ def parse_id(id: str) -> 'DGStatementID':
+ m = re.match(r'stmt:([^-]+)-([^-]+)-([^-]+)', id)
+ assert m is not None
+ qnode_id, predicate, stmt_index = [m.group(i) for i in range(1, 4)]
+ stmt_index = int(stmt_index)
+ return DGStatementID(qnode_id, predicate, stmt_index)
+
+
+@dataclass
+class DGPathNodeStatement:
+ qnode_id: str
+ predicate: str
+ statement_index: int
+ # how this DGPath statement has been matched
+ provenance: FlowProvenance
+
+ def get_id(self):
+ return DGStatementID(self.qnode_id, self.predicate, self.statement_index).get_id()
+
+ # ############# METHODs to construct DGPathNodeStatement from provenance ##############
+ @staticmethod
+ def from_FromWikidataLink(qnode_id, predicate, stmt_index):
+ return DGPathNodeStatement(qnode_id, predicate, stmt_index, FlowProvenance(LinkGenMethod.FromWikidataLink, None, 1.0))
+
+ @staticmethod
+ def from_FromLiteralMatchingFunc(qnode_id, predicate, stmt_index, fn_args, prob):
+ return DGPathNodeStatement(qnode_id, predicate, stmt_index,
+ FlowProvenance(LinkGenMethod.FromLiteralMatchingFunc, fn_args, prob))
+
+
+@dataclass
+class DGPathNodeQNode:
+ qnode_id: str
+
+ def get_id(self):
+ return f"ent:{self.qnode_id}"
+
+
+@dataclass
+class DGPathNodeLiteralValue:
+ value: DataValue
+
+ def get_id(self):
+ return f"val:{self.value.to_string_repr()}"
+
+
+@dataclass
+class DGPathExistingNode:
+ id: str
+
+ def get_id(self):
+ return self.id
+
+
+DGPathNode = Union[DGPathNodeStatement, DGPathNodeQNode, DGPathNodeLiteralValue, DGPathExistingNode]
+
+
+@dataclass
+class DGPathEdge:
+ value: str
+ is_qualifier: bool
+
+ @staticmethod
+ def p(value: str):
+ return DGPathEdge(value, is_qualifier=False)
+
+ @staticmethod
+ def q(value: str):
+ return DGPathEdge(value, is_qualifier=True)
+
+
+@dataclass
+class DGPath:
+ # a sequence of path, is always
+ sequence: List[Union[DGPathEdge, DGPathNode]] = field(default_factory=list)
+
+
+@dataclass
+class DGEdge:
+ source: str
+ target: str
+ predicate: str
+ is_qualifier: bool
+ # deprecated, will be replaced by the information stored directly in the statement edge flow
+ # paths: List[DGPath]
+ is_inferred: Optional[bool] = None
+
+ @staticmethod
+ def can_link(source: DGNode, target: DGNode):
+ """Test if there can be two links between the two nodes. Basically, it just enforce the constraint that no link
+ cross different rows
+ """
+ if not source.is_cell or not target.is_cell:
+ return True
+ return source.row == target.row
+
+
+@dataclass
+class InferredNewProp:
+ # information to identify the statement for the new prop (whether to reuse it, or create a new one)
+ # the qnode of the new statement (the one will have this property)
+ qnode_id: str
+ # the new property we inferred
+ new_prop: str
+ # the value associated with the property (use it to compare if the statement exists) - the reason we use
+ # the value instead of the whole statement is that sometimes, desire them to match the qualifier does not
+ # sense, if the value exist, then some how the algorithm is already match it so it should be okay
+ value: DataValue
+
+ # the source nodes in the data graph
+ source_id: str
+ # the target node in the data graph that will contain the value of the prop, not the statement node
+ target_id: str
+
+ # edges that contain the qualifiers that we want to be copied to the new statement as well
+ # the flow of the qualifier can be retrieve
+ qualifier_edges: List[DGEdge]
+
+ # flow provenance of the property.
+ flow_provenances: List[FlowProvenance]
+
+
+@dataclass
+class InferredNewQualifier:
+ # the statement node in the data graph that we will add the qualifier to
+ statement_id: str
+ # the qualifier we are going to add
+ new_qualifier: str
+
+ # source id (node that has the statement id)
+ source_id: str
+ # the property of the statement (the one that connect source and statement)
+ property: str
+ # the target node of the qualifier
+ target_id: str
+
+ # flow provenance of the qualifier
+ flow_provenances: List[FlowProvenance]
+
+
+class BuildDGOption(Flag):
+ # disable kg search for the same entity
+ DISABLE_SAME_ENT_SEARCH = auto()
+ # removing unnecessary entities to keep the graph size reasonable
+ PRUNING_REDUNDANT_ENT = auto()
+ # disable path discovering using index
+ NO_KG_INDEX = auto()
+
+
+def kg_path_discovering(options: BuildDGOption, qnodes: Dict[str, QNode], kg_object_index: KGObjectIndex, dg: nx.MultiDiGraph, u: DGNode, v: DGNode, max_n_hop: int=2, bidirection: bool = True):
+ """Find all paths between two nodes in the graph.
+
+ Parameters
+ ----------
+ options: options when building DG
+ qnodes: dictionary of qnodes
+ dg: the input data graph
+ u: node
+ v: node
+ max_n_hop: hop start at 1 (current node), n_hop = 2 mean the path can go through an hidden node
+ bidirection: set to false telling if we want to just find paths going from u -> v.
+ This parameter just for re-using code, as we will call the function changing the order of u & v
+
+ Returns
+ -------
+ """
+
+ # noinspection PyShadowingNames
+ def _path_object_search_v1(qnodes: Dict[str, QNode], kg_object_index: KGObjectIndex, source: QNode, target: QNode, max_n_hop=2):
+ # max_n_hop = 2 mean we will find a path that go from source to target through an hidden node
+ # hop start at 1 (current node)
+
+ if options & BuildDGOption.DISABLE_SAME_ENT_SEARCH:
+ if source.id == target.id:
+ return []
+
+ matches = []
+ iter = source.props.items()
+
+ for p, stmts in iter:
+ if p == 'P31':
+ # no need to search in the instanceOf property, as the ontology is removed from the databased as they are huge
+ continue
+
+ for stmt_i, stmt in enumerate(stmts):
+ # add the cell id so that we have different statement for different cell.
+ has_stmt_value = False
+
+ # to simplify the design, we do not consider a statement that its value do not exist in KQ
+ # due to an error on KG
+ if stmt.value.is_qnode():
+ if stmt.value.as_qnode_id() not in qnodes:
+ # this can happen due to some of the qnodes is in the link, but is missing in the KG
+ # this is very rare so we can employ some check to make sure this is not due to
+ # our wikidata subset
+ is_error_in_kg = any(
+ any(_s.value.is_qnode() and _s.value.as_qnode_id() in qnodes for _s in _stmts)
+ for _p, _stmts in source.props.items()
+ ) or stmt.value.as_qnode_id().startswith("L")
+ if not is_error_in_kg:
+ raise Exception(f"Missing qnodes in your KG subset: {stmt.value.as_qnode_id()}")
+ continue
+
+ if stmt.value.is_qnode():
+ # found by match entity
+ if stmt.value.as_qnode_id() == target.id:
+ matches.append(DGPath(sequence=[DGPathEdge.p(p),
+ DGPathNodeStatement.from_FromWikidataLink(source.id, p, stmt_i),
+ DGPathEdge.p(p)]))
+ has_stmt_value = True
+ elif max_n_hop > 1:
+ stmt_value_qnode_id = stmt.value.as_qnode_id()
+ if stmt_value_qnode_id.startswith("L"):
+ assert stmt_value_qnode_id not in qnodes, "The L nodes (lexical) is not in the Wikidata dump"
+ continue
+
+ for nextpath in _path_object_search(qnodes, kg_object_index, qnodes[stmt_value_qnode_id], target, max_n_hop - 1):
+ matches.append(DGPath(
+ sequence=[DGPathEdge.p(p),
+ DGPathNodeStatement.from_FromWikidataLink(source.id, p, stmt_i),
+ DGPathEdge.p(p),
+ DGPathNodeQNode(stmt.value.as_qnode_id())] + nextpath.sequence
+ ))
+ has_stmt_value = True
+
+ for q, qvals in stmt.qualifiers.items():
+ for qval in qvals:
+ if qval.is_qnode():
+ if qval.as_qnode_id() == target.id:
+ if not has_stmt_value:
+ if stmt.value.is_qnode():
+ pn_stmt_value = DGPathNodeQNode(stmt.value.as_qnode_id())
+ else:
+ pn_stmt_value = DGPathNodeLiteralValue(stmt.value)
+ matches.append(DGPath(sequence=[DGPathEdge.p(p),
+ DGPathNodeStatement.from_FromWikidataLink(source.id, p, stmt_i),
+ DGPathEdge.p(p),
+ pn_stmt_value]))
+ has_stmt_value = True
+
+ matches.append(DGPath(sequence=[DGPathEdge.p(p),
+ DGPathNodeStatement.from_FromWikidataLink(source.id, p, stmt_i),
+ DGPathEdge.q(q)]))
+ elif max_n_hop > 1:
+ qval_qnode_id = qval.as_qnode_id()
+ if qval_qnode_id.startswith("L"):
+ assert qval_qnode_id not in qnodes, "The L nodes (lexical) is not in the Wikidata dump"
+ continue
+
+ if qval_qnode_id not in qnodes:
+ # this can happen due to some of the qnodes is in the link, but is missing in the KG
+ # this is very rare so we can employ some check to make sure this is not due to
+ # our wikidata subset
+ is_error_in_kg = any(
+ any(_s.value.is_qnode() and _s.value.as_qnode_id() in qnodes for _s in _stmts)
+ for _p, _stmts in source.props.items()
+ ) or qval_qnode_id.startswith("L")
+ if not is_error_in_kg:
+ raise Exception(f"Missing qnodes in your KG subset: {qval_qnode_id}")
+ continue
+
+ _n_matches = len(matches)
+ for nextpath in _path_object_search(qnodes, kg_object_index, qnodes[qval_qnode_id], target, max_n_hop - 1):
+ matches.append(DGPath(
+ sequence=[DGPathEdge.p(p),
+ DGPathNodeStatement.from_FromWikidataLink(source.id, p, stmt_i),
+ DGPathEdge.q(q),
+ DGPathNodeQNode(qval.as_qnode_id())] + nextpath.sequence
+ ))
+
+ if len(matches) > _n_matches and not has_stmt_value:
+ if stmt.value.is_qnode():
+ pn_stmt_value = DGPathNodeQNode(stmt.value.as_qnode_id())
+ else:
+ pn_stmt_value = DGPathNodeLiteralValue(stmt.value)
+ matches.append(DGPath(
+ sequence=[DGPathEdge.p(p),
+ DGPathNodeStatement.from_FromWikidataLink(source.id, p, stmt_i),
+ DGPathEdge.p(p),
+ pn_stmt_value]))
+ has_stmt_value = True
+
+ return matches
+
+ def _path_object_search_v2(qnode: Dict[str, QNode], kg_object_index: KGObjectIndex, source: QNode, target: QNode, max_n_hop=2):
+ # max_n_hop = 2 mean we will find a path that go from source to target through an hidden node
+ # hop start at 1 (current node)
+
+ if options & BuildDGOption.DISABLE_SAME_ENT_SEARCH:
+ if source.id == target.id:
+ return []
+
+ matches = []
+ for hop1_path in kg_object_index.iter_hop1_props(source.id, target.id):
+ stmt_i = hop1_path.statement_index
+ rel = hop1_path.relationship
+ stmt = source.props[rel.prop][stmt_i]
+
+ if len(rel.quals) > 0 and not rel.both:
+ # the prop doesn't match, have to add it
+ if stmt.value.is_qnode():
+ pn_stmt_value = DGPathNodeQNode(stmt.value.as_qnode_id())
+ else:
+ pn_stmt_value = DGPathNodeLiteralValue(stmt.value)
+ matches.append(DGPath(sequence=[DGPathEdge.p(rel.prop),
+ DGPathNodeStatement.from_FromWikidataLink(source.id, rel.prop, stmt_i),
+ DGPathEdge.p(rel.prop),
+ pn_stmt_value]))
+ else:
+ # the prop match
+ matches.append(DGPath(sequence=[DGPathEdge.p(rel.prop),
+ DGPathNodeStatement.from_FromWikidataLink(source.id, rel.prop, stmt_i),
+ DGPathEdge.p(rel.prop)]))
+ for qual in rel.quals:
+ matches.append(DGPath(sequence=[DGPathEdge.p(rel.prop),
+ DGPathNodeStatement.from_FromWikidataLink(source.id, rel.prop, stmt_i),
+ DGPathEdge.q(qual)]))
+
+ if max_n_hop > 1:
+ # TODO: need to refactor the current method of searching for paths
+ # as we may generate many duplicated segments
+ for match_item in kg_object_index.iter_hop2_props(source.id, target.id):
+ if match_item.qnode == target.id:
+ continue
+ hop1_paths = match_item.hop1
+ hop2_paths = match_item.hop2
+ middle_qnode = qnodes[match_item.qnode]
+
+ # we don't care about transitive here, so we do a cross product
+ hop1_seqs = []
+ for hop1_path in hop1_paths:
+ rel = hop1_path.relationship
+ stmt_i = hop1_path.statement_index
+ stmt = source.props[rel.prop][stmt_i]
+
+ if len(rel.quals) > 0 and not rel.both:
+ # we don't have the statement value yet, add it to the matches
+ # the prop doesn't match, have to add it, we don't worry about duplication
+ # as it is resolve during the merge provenance phase
+ if stmt.value.is_qnode():
+ pn_stmt_value = DGPathNodeQNode(stmt.value.as_qnode_id())
+ else:
+ pn_stmt_value = DGPathNodeLiteralValue(stmt.value)
+ matches.append(DGPath(sequence=[DGPathEdge.p(rel.prop),
+ DGPathNodeStatement.from_FromWikidataLink(source.id, rel.prop, stmt_i),
+ DGPathEdge.p(rel.prop),
+ pn_stmt_value]))
+ else:
+ # add prop to the seqs that we need to expand next, and so stmt.value must be a qnode
+ # as it is the middle qnode
+ assert stmt.value.as_qnode_id() == middle_qnode.id
+ hop1_seqs.append([
+ DGPathEdge.p(rel.prop),
+ DGPathNodeStatement.from_FromWikidataLink(source.id, rel.prop, stmt_i),
+ DGPathEdge.p(rel.prop),
+ DGPathNodeQNode(middle_qnode.id)
+ ])
+
+ for qual in rel.quals:
+ hop1_seqs.append([
+ DGPathEdge.p(rel.prop),
+ DGPathNodeStatement.from_FromWikidataLink(source.id, rel.prop, stmt_i),
+ DGPathEdge.p(qual),
+ DGPathNodeQNode(middle_qnode.id)
+ ])
+
+ for hop2_path in hop2_paths:
+ rel = hop2_path.relationship
+ stmt_i = hop2_path.statement_index
+ stmt = middle_qnode.props[rel.prop][stmt_i]
+
+ if len(rel.quals) > 0 and not rel.both:
+ if stmt.value.is_qnode():
+ pn_stmt_value = DGPathNodeQNode(stmt.value.as_qnode_id())
+ else:
+ pn_stmt_value = DGPathNodeLiteralValue(stmt.value)
+
+ hop2_seq = [
+ DGPathEdge.p(rel.prop),
+ DGPathNodeStatement.from_FromWikidataLink(middle_qnode.id, rel.prop, stmt_i),
+ DGPathEdge.p(rel.prop),
+ pn_stmt_value
+ ]
+ for hop1_seq in hop1_seqs:
+ matches.append(DGPath(sequence=hop1_seq + hop2_seq))
+ else:
+ hop2_seq = [
+ DGPathEdge.p(rel.prop),
+ DGPathNodeStatement.from_FromWikidataLink(middle_qnode.id, rel.prop, stmt_i),
+ DGPathEdge.p(rel.prop),
+ ]
+ for hop1_seq in hop1_seqs:
+ matches.append(DGPath(sequence=hop1_seq + hop2_seq))
+
+ for qual in rel.quals:
+ hop2_seq = [
+ DGPathEdge.p(rel.prop),
+ DGPathNodeStatement.from_FromWikidataLink(middle_qnode.id, rel.prop, stmt_i),
+ DGPathEdge.p(qual)
+ ]
+ for hop1_seq in hop1_seqs:
+ matches.append(DGPath(sequence=hop1_seq + hop2_seq))
+
+ return matches
+
+ if options & BuildDGOption.NO_KG_INDEX:
+ _path_object_search = _path_object_search_v1
+ else:
+ _path_object_search = _path_object_search_v2
+
+ textparser = TextParser()
+ funcs = {
+ WikidataValueType.string.value: [LiteralMatcher.string_test_exact],
+ WikidataValueType.globe_coordinate.value: [LiteralMatcher.globe_coordinate_test],
+ WikidataValueType.time.value: [LiteralMatcher.time_test],
+ WikidataValueType.quantity.value: [LiteralMatcher.quantity_test],
+ WikidataValueType.mono_lingual_text.value: [LiteralMatcher.mono_lingual_text_test_exact],
+ WikidataValueType.entity_id.value: []#[LiteralMatcher.entity_id_test_fuzzy]
+ }
+
+ # noinspection PyUnusedLocal,PyShadowingNames
+ def _path_value_search(qnodes: Dict[str, QNode], source: QNode, value):
+ matches = []
+ for p, stmts in source.props.items():
+ if p == 'P31':
+ # no need to search in the instanceOf property, as the ontology is removed from the databased as they are huge
+ continue
+
+ for stmt_i, stmt in enumerate(stmts):
+ has_stmt_value = False
+ if stmt.value.type in LiteralMatcher.literal_types:
+ stmt_value = stmt.value
+ else:
+ assert stmt.value.type in LiteralMatcher.non_literal_types
+ if stmt.value.as_qnode_id() not in qnodes:
+ # this can happen due to some of the qnodes is in the link, but is missing in the KG
+ # this is very rare so we can employ some check to make sure this is not due to
+ # our wikidata subset
+ is_error_in_kg = any(
+ any(_s.value.is_qnode() and _s.value.as_qnode_id() in qnodes for _s in _stmts)
+ for _p, _stmts in source.props.items()
+ ) or stmt.value.as_qnode_id().startswith("L")
+ if not is_error_in_kg:
+ raise Exception(f"Missing qnodes in your KG subset: {stmt.value.as_qnode_id()}")
+ continue
+ stmt_value = qnodes[stmt.value.as_qnode_id()]
+ for fn in funcs[stmt.value.type]:
+ match, confidence = fn(stmt_value, value)
+ if match:
+ matches.append(DGPath(
+ sequence=[DGPathEdge.p(p),
+ DGPathNodeStatement.from_FromLiteralMatchingFunc(source.id, p, stmt_i,
+ {"func": fn.__name__,
+ "value": stmt.value},
+ confidence),
+ DGPathEdge.p(p)]))
+ has_stmt_value = True
+
+ for q, qvals in stmt.qualifiers.items():
+ for qval in qvals:
+ if qval.type in LiteralMatcher.literal_types:
+ qval_value = qval
+ else:
+ assert qval.type in LiteralMatcher.non_literal_types
+ if qval.as_qnode_id() not in qnodes:
+ # this can happen due to some of the qnodes is in the link, but is missing in the KG
+ # this is very rare so we can employ some check to make sure this is not due to
+ # our wikidata subset
+ is_error_in_kg = any(
+ any(_s.value.is_qnode() and _s.value.as_qnode_id() in qnodes for _s in _stmts)
+ for _p, _stmts in source.props.items()
+ ) or not qval.as_qnode_id().startswith("L")
+ if not is_error_in_kg:
+ raise Exception(f"Missing qnodes in your KG subset: {qval.as_qnode_id()}")
+ continue
+ qval_value = qnodes[qval.as_qnode_id()]
+ for fn in funcs[qval.type]:
+ match, confidence = fn(qval_value, value)
+ if match:
+ if not has_stmt_value:
+ if stmt.value.is_qnode():
+ pn_stmt_value = DGPathNodeQNode(stmt.value.as_qnode_id())
+ else:
+ pn_stmt_value = DGPathNodeLiteralValue(stmt.value)
+ matches.append(
+ DGPath(sequence=[DGPathEdge.p(p),
+ DGPathNodeStatement.from_FromWikidataLink(source.id, p, stmt_i),
+ DGPathEdge.p(p), pn_stmt_value]))
+ has_stmt_value = True
+
+ matches.append(DGPath(
+ sequence=[
+ DGPathEdge.p(p),
+ DGPathNodeStatement.from_FromLiteralMatchingFunc(source.id, p, stmt_i, {"func": fn.__name__, "value": qval}, confidence),
+ DGPathEdge.q(q)]))
+ return matches
+
+ paths = []
+
+ # no qnode in the source
+ u_qnodes = [qnodes[u.qnode_id]] if u.is_entity_value else [qnodes[qnode_id] for qnode_id in u.qnode_ids]
+ if len(u_qnodes) == 0:
+ if bidirection:
+ return kg_path_discovering(options, qnodes, kg_object_index, dg, v, u, max_n_hop=max_n_hop, bidirection=False)
+ return []
+
+ v_qnodes = [qnodes[v.qnode_id]] if v.is_entity_value else [qnodes[qnode_id] for qnode_id in v.qnode_ids]
+ v_value = v.context_span.get_text_span() if v.is_entity_value else v.value
+ for n1 in u_qnodes:
+ # count = timer.start("object discovery")
+ for n2 in v_qnodes:
+ for newpath in _path_object_search(qnodes, kg_object_index, n1, n2, max_n_hop=max_n_hop):
+ newpath.sequence.insert(0, DGPathExistingNode(u.id))
+ if isinstance(newpath.sequence[-1], DGPathEdge):
+ newpath.sequence.append(DGPathExistingNode(v.id))
+ paths.append(newpath)
+ # count.stop()
+
+ # count = timer.start("literal discovery")
+ for newpath in _path_value_search(qnodes, n1, textparser.parse(v_value)):
+ newpath.sequence.insert(0, DGPathExistingNode(u.id))
+ if isinstance(newpath.sequence[-1], DGPathEdge):
+ newpath.sequence.append(DGPathExistingNode(v.id))
+ paths.append(newpath)
+ # count.stop()
+
+ if bidirection:
+ paths += kg_path_discovering(options, qnodes, kg_object_index, dg, v, u, max_n_hop=max_n_hop, bidirection=False)
+
+ return paths
+
+
+class KGInference:
+ def __init__(self, dg: nx.MultiDiGraph, qnodes: Dict[str, QNode], wdprops: Dict[str, WDProperty]):
+ # mapping from qnode id, and property to a list of statement values (with the corresponding DG statement node if exist)
+ # the reason we want to include all is that we want to know if we need to add new statement value or reuse existing value
+ self.subkg: Dict[Tuple[str, str], List[Tuple[Optional[StatementNode], DataValue]]] = {}
+ self.qnodes = qnodes
+ self.wdprops = wdprops
+ self.dg = dg
+
+ for sid, sdata in dg.nodes(data=True):
+ s: StatementNode = sdata['data']
+ if not s.is_statement:
+ continue
+
+ dgsid = DGStatementID.parse_id(sid)
+ self._set_stmt_node(s.qnode_id, dgsid.predicate, dgsid.statement_index, s)
+
+ def infer_subproperty(self):
+ """Infer new properties via sub-property of (inheritance)"""
+ properties = set()
+ qualifiers = set()
+
+ for uid, udata in self.dg.nodes(data=True):
+ u = udata['data']
+ if u.is_statement:
+ continue
+
+ for sid, us_edges in self.dg[u.id].items():
+ # add all links to the list of properties
+ properties.update(us_edges.keys())
+
+ for vid, sv_edges in self.dg[sid].items():
+ qualifiers.update((v_eid for v_eid in sv_edges.keys() if v_eid not in us_edges))
+
+ parent_props = self._build_parent_map(properties)
+ parent_qualifiers = self._build_parent_map(qualifiers)
+
+ # list of new properties and qualifiers that we will infer
+ new_props = []
+ new_qualifiers = []
+
+ for sid, sdata in self.dg.nodes(data=True):
+ s: StatementNode = sdata['data']
+ if not s.is_statement:
+ continue
+
+ # parents of the statement
+ parents = set()
+ prop = s.predicate
+ for uid, _, eid, edata in self.dg.in_edges(sid, data=True, keys=True):
+ parents.add(uid)
+ assert prop == edata['data'].predicate
+
+ # children that are properties of the statement
+ prop_children = set()
+ # children that are qualifiers of the statement
+ qualifier2children = defaultdict(set)
+ for _, vid, eid, edata in self.dg.out_edges(sid, data=True, keys=True):
+ e: DGEdge = edata['data']
+ if e.predicate == prop:
+ prop_children.add(vid)
+ else:
+ qualifier2children[e.predicate].add(vid)
+
+ stmt_index = DGStatementID.parse_id(sid).statement_index
+ if prop in parent_props:
+ for parent_prop in parent_props[prop]:
+ for vid in prop_children:
+ for source_flow, flow_provenances in s.iter_source_flow(EdgeFlowTarget(vid, prop)):
+ new_prop = InferredNewProp(qnode_id=s.qnode_id, new_prop=parent_prop,
+ value=self._get_stmt_value(s.qnode_id, prop, stmt_index),
+ source_id=source_flow.source_id,
+ target_id=vid, qualifier_edges=[],
+ flow_provenances=flow_provenances)
+ new_props.append(new_prop)
+
+ for q, children in qualifier2children.items():
+ for pq in parent_qualifiers.get(q, []):
+ for vid in children:
+ for source_flow, provenance in s.iter_source_flow(EdgeFlowTarget(vid, q)):
+ new_qualifiers.append(InferredNewQualifier(statement_id=sid, new_qualifier=pq,
+ source_id=source_flow.source_id,
+ property=source_flow.edge_id,
+ target_id=vid, flow_provenances=provenance))
+
+ self.add_inference(new_props, new_qualifiers)
+ return self
+
+ def kg_transitive_inference(self):
+ """Infer new relationship based on the transitive property: a -> b -> c => a -> c"""
+
+ # find the list of transitive properties in the graph
+ transitive_props = set()
+ for uid, vid, eid, edata in self.dg.edges(data=True, keys=True):
+ prop = self.wdprops[eid]
+ # transitive class
+ if 'Q18647515' in prop.instanceof:
+ transitive_props.add(eid)
+
+ # now start from node u, we find if there is another v connect to u via a transitive property, and another p connect
+ # to v with the same property, we don't need to keep the chain going as even if it's longer, we will eventually loop
+ # through all item in the chain by looping through nodes in the graph
+
+ chains = []
+ for uid, udata in self.dg.nodes(data=True):
+ u: DGNode = udata['data']
+ if u.is_statement:
+ continue
+
+ for sid, us_edges in self.dg[uid].items():
+ stmt: StatementNode = self.dg.nodes[sid]['data']
+ for trans_prop in transitive_props:
+ if trans_prop not in us_edges:
+ continue
+ us_edge: DGEdge = us_edges[trans_prop]['data']
+ for vid, sv_edges in self.dg[sid].items():
+ if trans_prop not in sv_edges:
+ continue
+ sv_edge: DGEdge = sv_edges[trans_prop]['data']
+ if not stmt.is_same_flow(us_edge, sv_edge):
+ # don't allow infer new link across rows
+ continue
+
+ for s2id, vs2_edges in self.dg[vid].items():
+ if trans_prop not in vs2_edges:
+ continue
+ vs2_edge: DGEdge = vs2_edges[trans_prop]['data']
+ stmt2: StatementNode = self.dg.nodes[s2id]['data']
+ for v2id, s2v2_edges in self.dg[s2id].items():
+ if trans_prop not in s2v2_edges:
+ continue
+ s2v2_edge: DGEdge = s2v2_edges[trans_prop]['data']
+ if not stmt2.is_same_flow(vs2_edge, s2v2_edge):
+ # don't allow infer new link across rows
+ continue
+
+ # we now record the chain
+ chains.append((us_edge, sv_edge, vs2_edge, s2v2_edge))
+
+ # make sure that there is no duplication in the chains
+ assert len(chains) == len({
+ (
+ us_edge.source, us_edge.predicate, us_edge.target,
+ sv_edge.source, sv_edge.predicate, sv_edge.target,
+ vs2_edge.source, vs2_edge.predicate, vs2_edge.target,
+ s2v2_edge.source, s2v2_edge.predicate, s2v2_edge.target,
+ )
+ for us_edge, sv_edge, vs2_edge, s2v2_edge in chains})
+
+ # generate new property, but qualifiers cannot inherit via transitive inference
+ new_props = []
+ for us_edge, sv_edge, vs2_edge, s2v2_edge in chains:
+ trans_prop = us_edge.predicate
+ stmt: StatementNode = self.dg.nodes[us_edge.target]['data']
+ stmt2: StatementNode = self.dg.nodes[vs2_edge.target]['data']
+
+ prop_value = self._get_stmt_value(stmt2.qnode_id, trans_prop, DGStatementID.parse_id(stmt2.id).statement_index)
+ # prop_value = stmt2.qnode.props[trans_prop][DGStatementID.parse_id(stmt2.id).statement_index].value
+
+ # calculating the provenance of the new transitive link. however, I don't know in case we have multiple
+ # provenance since it depends on both first leg and second leg. for now, we put an assertion to handle
+ # only the case where we have two legs has the same provenance, which make the new transitive link has
+ # the same provenance too.
+ first_leg_provenances = stmt.get_provenance_by_edge(us_edge, sv_edge)
+ second_leg_provenances = stmt2.get_provenance_by_edge(vs2_edge, s2v2_edge)
+ assert len(first_leg_provenances) == 1 and len(second_leg_provenances) == 1 and first_leg_provenances[0] == \
+ second_leg_provenances[0]
+
+ provenances = first_leg_provenances
+ new_props.append(InferredNewProp(
+ qnode_id=stmt.qnode_id, new_prop=trans_prop, value=prop_value,
+ source_id=us_edge.source, target_id=s2v2_edge.target,
+ qualifier_edges=[], flow_provenances=provenances
+ ))
+
+ self.add_inference(new_props, [])
+ return self
+
+ def add_inference(self, new_props: List[InferredNewProp], new_qualifiers: List[InferredNewQualifier]):
+ """After we run inference, we got a list of new properties and new qualifiers that can update using this function
+ """
+ new_nodes = []
+ new_edges = []
+
+ # here we enforce the constraint that there is no cross links
+ # between nodes in different rows, this happen because transitive
+ # inference generate links cross rows
+ new_props = [
+ new_prop for new_prop in new_props
+ if DGEdge.can_link(self.dg.nodes[new_prop.source_id]['data'], self.dg.nodes[new_prop.target_id]['data'])
+ ]
+ new_qualifiers = [
+ new_qualifier for new_qualifier in new_qualifiers
+ if DGEdge.can_link(self.dg.nodes[new_qualifier.source_id]['data'], self.dg.nodes[new_qualifier.target_id]['data'])
+ ]
+
+ for new_prop in new_props:
+ stmt_exist = None
+ prop = new_prop.new_prop
+
+ # search for existing statement in the KG
+ self._track_property(new_prop.qnode_id, prop)
+ for sprime, value in self.subkg[new_prop.qnode_id, prop]:
+ if sprime is not None and new_prop.value == value:
+ stmt_exist = sprime
+ break
+
+ # if the statement exist, re-use it
+ if stmt_exist is not None:
+ sprime = stmt_exist
+ else:
+ for stmt_index, (sprime, value) in enumerate(self.subkg[new_prop.qnode_id, prop]):
+ if new_prop.value == value:
+ stmt_id = DGStatementID(new_prop.qnode_id, prop, stmt_index).get_id()
+ sprime = StatementNode(stmt_id, new_prop.qnode_id, prop, is_in_kg=True)
+ self._set_stmt_node(new_prop.qnode_id, prop, stmt_index, sprime)
+ new_nodes.append(sprime)
+ break
+ else:
+ stmt_index = self.get_next_available_stmt_index(new_prop.qnode_id, prop)
+ stmt_id = DGStatementID(new_prop.qnode_id, prop, stmt_index).get_id()
+ sprime = StatementNode(stmt_id, new_prop.qnode_id, prop, is_in_kg=False)
+ self._add_stmt_value(new_prop.qnode_id, prop, stmt_index, sprime, new_prop.value)
+ new_nodes.append(sprime)
+
+ if not self.dg.has_edge(new_prop.source_id, sprime.id, key=prop):
+ new_edges.append(
+ DGEdge(source=new_prop.source_id, target=sprime.id, predicate=prop, is_qualifier=False,
+ is_inferred=True))
+ if not self.dg.has_edge(sprime.id, new_prop.target_id, key=prop):
+ new_edges.append(
+ DGEdge(source=sprime.id, target=new_prop.target_id, predicate=prop, is_qualifier=False,
+ is_inferred=True))
+
+ sprime.track_provenance(EdgeFlowSource(new_prop.source_id, prop), EdgeFlowTarget(new_prop.target_id, prop),
+ new_prop.flow_provenances)
+
+ # TODO: we haven't add to add add qualifiers, so we assert we don't have any. fix me!
+ assert len(new_prop.qualifier_edges) == 0
+ # for qual_edge in new_prop.qualifier_edges:
+ # new_edges.append(DGEdge(source=stmt_id, target=qual_edge.target, predicate=qual_edge.predicate, is_qualifier=True, paths=[], is_inferred=True))
+
+ for new_qual in new_qualifiers:
+ stmt: StatementNode = self.dg.nodes[new_qual.statement_id]['data']
+ new_edges.append(
+ DGEdge(source=new_qual.statement_id, target=new_qual.target_id, predicate=new_qual.new_qualifier,
+ is_qualifier=True, is_inferred=True))
+ stmt.track_provenance(EdgeFlowSource(new_qual.source_id, new_qual.property),
+ EdgeFlowTarget(new_qual.target_id, new_qual.new_qualifier), new_qual.flow_provenances)
+
+ for node in new_nodes:
+ self.dg.add_node(node.id, data=node)
+ for edge in new_edges:
+ self.dg.add_edge(edge.source, edge.target, key=edge.predicate, data=edge)
+
+ def _track_property(self, qnode_id: str, prop: str):
+ """Ensure that the subkg has values of the qnode's property"""
+ if (qnode_id, prop) not in self.subkg:
+ lst = []
+ for stmt_i, stmt in enumerate(self.qnodes[qnode_id].props.get(prop, [])):
+ lst.append((None, stmt.value))
+ self.subkg[qnode_id, prop] = lst
+
+ def _get_stmt_value(self, qnode_id: str, prop: str, stmt_index: int):
+ self._track_property(qnode_id, prop)
+ return self.subkg[qnode_id, prop][stmt_index][1]
+
+ def _set_stmt_node(self, qnode_id: str, prop: str, stmt_index: int, stmt: StatementNode):
+ self._track_property(qnode_id, prop)
+ assert self.subkg[qnode_id, prop][stmt_index][0] is None, "Cannot override existing value in the KG"
+ self.subkg[qnode_id, prop][stmt_index] = (stmt, self.subkg[qnode_id, prop][stmt_index][1])
+
+ def _add_stmt_value(self, qnode_id: str, prop: str, stmt_index: int, stmt: StatementNode, value: DataValue):
+ self._track_property(qnode_id, prop)
+ assert stmt_index == len(self.subkg[qnode_id, prop]), "Can only add new value"
+ self.subkg[qnode_id, prop].append((stmt, value))
+
+ def get_next_available_stmt_index(self, qnode_id: str, prop: str):
+ return len(self.subkg[qnode_id, prop])
+
+ def _build_parent_map(self, props: Set[str]):
+ """Build a map from a property to its parents in the same list"""
+ parent_props: Dict[str, List[str]] = {}
+ for p1 in props:
+ parent_props[p1] = []
+ for p2 in props:
+ if p1 == p2:
+ continue
+ if p2 in self.wdprops[p1].parents_closure:
+ parent_props[p1].append(p2)
+ if len(parent_props[p1]) == 0:
+ parent_props.pop(p1)
+ return parent_props
+
+
+class DGPruning:
+ NxDGEdgeAttr = TypedDict('NxDGEdgeAttr', data=DGEdge)
+ NxDGEdge = Tuple[str, str, str, NxDGEdgeAttr]
+
+ def __init__(self, dg: nx.MultiDiGraph):
+ self.dg = dg
+
+ def prune_hidden_entities(self):
+ """Prune redundant KG entities, which added to the graph via KG discovering and from the context.
+
+ **Step 1:**
+ Let:
+ - n be an entity node in DG.
+ - v is a node connected from n via a property: LEG2: n -> p -> s -> p' -> v, and s does not have other property/qualifier rather than p'
+
+ We made the following heuristics:
+ * If there is no other node connect to n, then n is a root node and is from the context. We should not
+ prune this node, so just skip it.
+ * For all another node ui \in U that connects to n via the path: LEG1: ui -> pi -> s' -> pi' -> n, if there is always a better
+ path LEG* between ui and v, then we can remove the path LEG2. U contains nodes in cells or context, otherwise
+ ui will be an entity to entity that won't be in the final model anyway.
+ Note: LEG* is better than LEG2 when it's shorter, also from wikidata link or if not, it must have better match confidence (i.e., better provenance)
+
+ **Step 2:**
+ Let n' be an entity node in DG that do not link to other nodes (v doesn't exist).
+ We have the following heuristics:
+ * If there is no other node connect to it, this is a standable node and should be removed
+ * For any node ui that connects to n via the path: LEG1: ui -> pi -> s' -> pi' -> n. If s' doesn't have other properties/qualifiers,
+ then we can remove LEG1.
+
+ **Step 3:**
+ Finally, if a node is standable, we should remove it.
+ """
+ # step 1: prune the second leg paths
+ legprime: Dict[Tuple[str, str], FlowProvenance] = {}
+ rm_legs: List[Tuple[str, EdgeFlowSource, EdgeFlowTarget]] = []
+ for nid, ndata in self.dg.nodes(data=True):
+ n: EntityValueNode = ndata['data']
+ if not n.is_entity_value:
+ continue
+
+ if self.dg.in_degree(nid) == 0:
+ # no other node connect to it
+ continue
+
+ # get list of grandpa ui (only cells or values in the context), along with their paths to node n.
+ grandpa = set()
+ for gpid in self.iter_grand_parents(nid):
+ gp = self.dg.nodes[gpid]['data']
+ if gp.is_cell or gp.is_context:
+ grandpa.add(gpid)
+
+ for _, sid, ns_eid in self.dg.out_edges(nid, keys=True):
+ stmt: StatementNode = self.dg.nodes[sid]['data']
+ stmt_outedges = self.out_edges(sid)
+ if len(stmt_outedges) > 1:
+ # this stmt has other qualifiers, so it's not what we are looking for
+ continue
+
+ for sv_outedge in next(iter(stmt_outedges.values())):
+ v = self.dg.nodes[sv_outedge.target]['data']
+ # got leg 2, now looks for all incoming
+ leg2 = (EdgeFlowSource(nid, ns_eid), EdgeFlowTarget(v.id, sv_outedge.predicate))
+ if not stmt.has_flow(*leg2):
+ continue
+ leg2_provenance = stmt.get_provenance(*leg2)
+
+ has_better_paths = True
+ for gpid in grandpa:
+ if (gpid, v.id) not in legprime:
+ paths = [
+ (self.dg.nodes[path[0][1]]['data'], EdgeFlowSource(path[0][0], path[0][2]), EdgeFlowTarget(path[1][1], path[1][2]))
+ for path in nx.all_simple_edge_paths(self.dg, gpid, v.id, cutoff=2)
+ ]
+ provs = [
+ prov
+ for s, sf, tf in paths
+ if s.has_flow(sf, tf)
+ for prov in s.get_provenance(sf, tf)
+ ]
+ if len(provs) == 0:
+ legprime[gpid, v.id] = None
+ else:
+ legprime[gpid, v.id] = max(provs, key=cmp_to_key(self.specific_pruning_provenance_cmp))
+ best_prov = legprime[gpid, v.id]
+ if best_prov is None or \
+ max(self.specific_pruning_provenance_cmp(best_prov, leg2_prov)
+ for leg2_prov in leg2_provenance) < 0:
+ # no better path
+ has_better_paths = False
+ break
+
+ if has_better_paths:
+ rm_legs.append((sid, leg2[0], leg2[1]))
+
+ # logger.info("#legs: {}", len(rm_legs))
+ self.remove_flow(rm_legs)
+ # logger.info("# 0-indegree: {}", sum(self.dg.in_degree(uid) == 0 for uid in self.dg.nodes))
+ # logger.info("# 0-outdegree: {}", sum(self.dg.out_degree(uid) == 0 for uid in self.dg.nodes))
+ # logger.info("# 0-standalone: {}",
+ # sum(self.dg.out_degree(uid) + self.dg.in_degree(uid) == 0 for uid in self.dg.nodes))
+
+ # step 2: prune the first leg paths
+ rm_legs: List[Tuple[str, EdgeFlowSource, EdgeFlowTarget]] = []
+ for nid, ndata in self.dg.nodes(data=True):
+ n: EntityValueNode = ndata['data']
+ if not n.is_entity_value or self.dg.out_degree(nid) > 0:
+ continue
+
+ for sid, _, sn_eid, sn_edata in self.dg.in_edges(nid, data=True, keys=True):
+ stmt_outedges = self.out_edges(sid)
+ if len(stmt_outedges) == 1:
+ # stmt does not have other property/qualifier
+ target_flow = EdgeFlowTarget(nid, sn_eid)
+ stmt: StatementNode = self.dg.nodes[sid]['data']
+ for source_flow, _ in stmt.iter_source_flow(target_flow):
+ rm_legs.append((sid, source_flow, target_flow))
+
+ # logger.info("#legs: {}", len(rm_legs))
+ self.remove_flow(rm_legs)
+ # logger.info("# 0-indegree: {}", sum(self.dg.in_degree(uid) == 0 for uid in self.dg.nodes))
+ # logger.info("# 0-outdegree: {}", sum(self.dg.out_degree(uid) == 0 for uid in self.dg.nodes))
+ # logger.info("# 0-standalone: {}",
+ # sum(self.dg.out_degree(uid) + self.dg.in_degree(uid) == 0 for uid in self.dg.nodes))
+ self.prune_disconnected_nodes()
+
+ def prune_disconnected_nodes(self):
+ """This function prune out disconnected nodes that are:
+ 1. nodes without incoming edges and outgoing edges
+ 2. statement nodes with no incoming edges or no outgoing edges
+
+ Returns
+ -------
+ """
+ rm_nodes = set()
+ for uid, udata in self.dg.nodes(data=True):
+ u: DGNode = udata['data']
+ if u.is_entity_value:
+ if self.dg.in_degree(uid) == 0 and self.dg.out_degree(uid) == 0:
+ rm_nodes.add(uid)
+ elif u.is_statement:
+ if self.dg.in_degree(uid) == 0 or self.dg.out_degree(uid) == 0:
+ rm_nodes.add(uid)
+ for uid in rm_nodes:
+ self.dg.remove_node(uid)
+
+ def remove_flow(self, flows: List[Tuple[str, EdgeFlowSource, EdgeFlowTarget]]):
+ for sid, source_flow, target_flow in flows:
+ stmt: StatementNode = self.dg.nodes[sid]['data']
+ stmt.untrack_flow(source_flow, target_flow)
+ if not stmt.has_source_flow(source_flow):
+ self.dg.remove_edge(source_flow.source_id, sid, source_flow.edge_id)
+ if not stmt.has_target_flow(target_flow):
+ self.dg.remove_edge(sid, target_flow.target_id, target_flow.edge_id)
+
+ def specific_pruning_provenance_cmp(self, prov0: FlowProvenance, prov1: FlowProvenance):
+ # compare provenance, this function only accept
+ if prov0.gen_method == LinkGenMethod.FromWikidataLink:
+ # always favour from wikidata link
+ return 1
+ if prov1.gen_method == LinkGenMethod.FromWikidataLink:
+ return -1
+ # assert prov0.gen_method == prov1.gen_method and prov0.gen_method_arg == prov1.gen_method_arg
+ # do not need to check if the two gen method and args are equal, as even if we select the incorrect one
+ # we only truncate when the other leg worst than it
+ return prov0.prob - prov1.prob
+
+ def iter_grand_parents(self, nid: str):
+ for pid, _ in self.dg.in_edges(nid):
+ for ppid, _ in self.dg.in_edges(pid):
+ yield ppid
+
+ def out_edges(self, uid: str) -> Dict[str, List[DGEdge]]:
+ label2edges = defaultdict(list)
+ for _, vid, eid, edata in self.dg.out_edges(uid, data=True, keys=True):
+ label2edges[eid].append(edata['data'])
+ return label2edges
+
+
+def prune_data_graph_deprecated(dg: nx.MultiDiGraph):
+ """This algorithm prunes:
+
+ * Redundant hidden entities added to the graph from KG discovering. This can also include entities in the context.
+
+
+ 0. A second leg of a path between two nodes that goes through an entity node f1:
+ (f0 -> p1 -> s1 -> p2 -> f1) (-> p3 -> s2 -> p3 -> f3). This removal is triggered when
+ s2 doesn't have other qualifiers map to other nodes
+ there is another better path between f1 and f3 (shorter, also from wikidatalink), s2 must not have any
+
+ 1. An entity that between all sources and targets that it connects exist a better path in the graph connecting them,
+ and it is not used as a mean (should we consider it to be the only mean?) to connect the other columns/context as property & qualifiers (i.e., property value of statement). for example,
+ an award is needed to connect the year column in the model. Note: A path is consider better when it is shorter,
+ and also generated from WikidataLink rather than literal matching.
+ 2. Statements that belong to the same qnode and same property, have the same value and qualifiers matched in the data graph.
+ The reason we may have multiple statements like this, is because a property may have multiple statements of the same values in
+ KG, usually different with each other by the qualifiers
+
+ Finally, after applying those pruning rules, if any dangling nodes will be removed too (e.g., a statement node that
+ doesn't have any value, or an entity node that doesn't link to any node)
+
+ Parameters
+ ----------
+ dg
+
+ Returns
+ -------
+ """
+ def iter_grand_parents(edges: List[DGPruning.NxDGEdge]):
+ for f2, f3, eid, edge in edges:
+ for f1, _f2 in dg.in_edges(f2):
+ yield dg.nodes[f1]['data']
+
+ def iter_grand_children(edges: List[DGPruning.NxDGEdge]):
+ """Iterate through children of children of a given node"""
+ for f1, f2, eid, edge in edges:
+ for f3, f23_edges in dg[f2].items():
+ yield dg.nodes[f3]['data']
+
+ def iter_sibling_edges(edges: List[DGPruning.NxDGEdge]):
+ """Iter through out sibling nodes"""
+ for f1, f2, eid, edge in edges:
+ for f2_sib, sib_edges in dg[f1].items():
+ for sib_eid, sib_edata in sib_edges.items():
+ yield sib_edata['data']
+
+ def remove_nodes(node_ids: Set[str], remove_dangling_statements: bool=True):
+ for nid in node_ids:
+ node = dg.nodes[nid]['data']
+ if node.is_statement:
+ # if it is statements, just remove it
+ dg.remove_node(nid)
+ else:
+ # when delete a node, we need to remove its flow also
+ for sid, _, eid in dg.in_edges(nid, keys=True):
+ s: StatementNode = dg.nodes[sid]['data']
+ s.untrack_target_flow(EdgeFlowTarget(nid, eid))
+ for _, sid, eid in dg.out_edges(nid, keys=True):
+ s: StatementNode = dg.nodes[sid]['data']
+ s.untrack_source_flow(EdgeFlowSource(nid, eid))
+ dg.remove_node(nid)
+
+ if not remove_dangling_statements or len(node_ids) == 0:
+ return
+
+ # remove dangling statements
+ dangling_statements = set()
+ for nid in dg.nodes:
+ node = dg.nodes[nid]['data']
+ if node.is_statement and (dg.out_degree(nid) == 0 or dg.in_degree(nid) == 0):
+ dangling_statements.add(nid)
+ for nid in dangling_statements:
+ dg.remove_node(nid)
+
+ def extract_stmt_signature(stmt_id: str):
+ # to compare if two statements are equal, we only need to compare if the property & qualifiers values are
+ # the same. We define that the value is the same by their node id in the graph (so that cell need to be the exact
+ # cell, while literal or entity will be the value)
+ signature = set()
+ for uid, edges in dg[stmt_id].items():
+ for eid in edges:
+ signature.add(f"({eid}-{uid})")
+ signature = sorted(list(signature))
+ return "-".join(signature)
+
+ has_direct_kglink_paths = {}
+
+ rm_node_ids = set()
+ # remove entities first.
+ for uid, udata in dg.nodes(data=True):
+ u: EntityValueNode = udata['data']
+ if not u.is_entity_value:
+ continue
+
+ # find entity node that only connected by two distinct qnodes
+ inedges: List[DGPruning.NxDGEdge] = list(dg.in_edges(uid, data=True, keys=True))
+ outedges: List[DGPruning.NxDGEdge] = list(dg.out_edges(uid, data=True, keys=True))
+
+ if len(inedges) == 0:
+ # this happen when this entity is needed to links between columns or column and context as property & qualifiers
+ # we can't prune this node, but we want to make sure this is actually the case
+ assert len({
+ v.column if v.is_cell else v.id
+ for v in iter_grand_children(outedges)
+ if v.is_cell or ((v.is_literal_value or v.is_entity_value) and v.is_context)
+ }) >= 2, "Should have at least two columns or one column one context"
+ continue
+
+ if len(outedges) == 0:
+ # this happen when the real value of statement is missing, we can't prune this node
+ # but we want to make sure there is actually another qualifiers are there
+ assert len({sib_edge.predicate for sib_edge in iter_sibling_edges(inedges)}) >= 2, "The statement should have at least two distinct predicates"
+ continue
+
+ # check if it is used to connect other columns via property & qualifiers
+
+ # check if this entity only connect two qnodes and no literal values
+ source_qnodes = {dg.nodes[sid]['data'].qnode_id for _1, sid, _2, _3 in inedges}
+ if len(source_qnodes) != 1:
+ continue
+ # TODO: in order to get correct target qnodes, we need to examine the flow of the statements, the current finding
+ # target qnodes will not work as expected when the target cell have more than one qnode
+ target_qnodes = set()
+ for v in iter_grand_children(outedges):
+ if v.is_cell:
+ target_qnodes = target_qnodes.union(v.qnode_ids)
+ elif v.is_entity_value:
+ target_qnodes.add(v.qnode_id)
+ else:
+ assert v.is_literal_value
+ target_qnodes = set()
+ break
+ if len(target_qnodes) != 1:
+ continue
+
+ # now they connect only two distinct qnodes and no literal, so we know this path is through wikidata links
+ # find out if all grand parents and grand children have better paths
+ # first retrieve the list of grant parent and grant child we need to test.
+ # instead of finding pairs of parents and children through flows, we do it here..?
+ grant_parents = list(iter_grand_parents(inedges))
+ grand_children = list(iter_grand_children(outedges))
+
+ grand_child_cells = defaultdict(list)
+ grand_child_ents = []
+ for gc in grand_children:
+ if gc.is_cell:
+ grand_child_cells[gc.row].append(gc)
+ else:
+ assert gc.is_entity_value
+ grand_child_ents.append(gc)
+
+ all_paths_are_better = True
+ for gp in grant_parents:
+ if gp.is_cell:
+ corresponding_gcs = chain(grand_child_cells[gp.row], grand_child_ents)
+ else:
+ corresponding_gcs = grand_child_ents
+ for gc in corresponding_gcs:
+ # identify if there is other better path is shorter and directly connect the two nodes
+ if (gp.id, gc.id) not in has_direct_kglink_paths:
+ has_better_path = False
+ has_better_path = any((
+ any((
+ provenance.gen_method == LinkGenMethod.FromWikidataLink
+ for provenance in dg.nodes[path[0][1]]['data'].get_provenance(EdgeFlowSource(path[0][0], path[0][2]), EdgeFlowTarget(path[1][1], path[1][2]))
+ ))
+ for path in nx.all_simple_edge_paths(dg, gp.id, gc.id, cutoff=2)
+ ))
+ has_direct_kglink_paths[gp.id, gc.id] = has_better_path
+ else:
+ has_better_path = has_direct_kglink_paths[gp.id, gc.id]
+ if not has_better_path:
+ all_paths_are_better = False
+ break
+ if not all_paths_are_better:
+ break
+
+ if all_paths_are_better:
+ rm_node_ids.add(uid)
+
+ remove_nodes(rm_node_ids)
+
+ rm_node_ids = set()
+ # remove duplicated statements
+ for uid, udata in dg.nodes(data=True):
+ u: Union[CellNode, EntityValueNode] = udata['data']
+ if not (u.is_cell or u.is_entity_value):
+ continue
+
+ # we are looking at statements of the same edges
+ prop2stmts = defaultdict(list)
+ for sid, us_edges in dg[uid].items():
+ for eid, edata in us_edges.items():
+ prop2stmts[eid].append(sid)
+
+ for stmts in prop2stmts.values():
+ # remove duplicated statements
+ if len(stmts) > 1:
+ stmt_sig = {}
+ for si, sid in enumerate(stmts):
+ sig = extract_stmt_signature(sid)
+ if sig in stmt_sig:
+ continue
+ stmt_sig[sig] = si
+ if len(stmt_sig) < len(stmts):
+ for si in set(range(len(stmts))).difference(stmt_sig.values()):
+ rm_node_ids.add(stmts[si])
+
+ remove_nodes(rm_node_ids)
+
+
+def build_data_graph(table: LinkedTable,
+ qnodes: Dict[str, QNode],
+ wdprops: Dict[str, WDProperty],
+ kg_object_index: KGObjectIndex,
+ ignore_columns: Set[int] = None,
+ max_n_hop: int = 2,
+ options: BuildDGOption = BuildDGOption.DISABLE_SAME_ENT_SEARCH | BuildDGOption.PRUNING_REDUNDANT_ENT,
+ verbose: bool = False):
+ dg = nx.MultiDiGraph()
+ context_node_id = None
+
+ for ci, col in enumerate(table.table.columns):
+ for ri, val in enumerate(col.values):
+ cell_qnodes = set()
+ cell_qnode_spans = {}
+
+ for link in table.links[ri][ci]:
+ if link.qnode_id is not None:
+ cell_qnodes.add(link.qnode_id)
+ if link.qnode_id not in cell_qnode_spans:
+ cell_qnode_spans[link.qnode_id] = []
+ cell_qnode_spans[link.qnode_id].append(Span(link.start, link.end))
+
+ assert all(len(spans) == len(set(spans)) for spans in cell_qnode_spans.values())
+ node = CellNode(id=f"{ri}-{ci}", value=val, column=ci, row=ri, qnode_ids=list(cell_qnodes),
+ qnodes_span=cell_qnode_spans)
+ dg.add_node(node.id, data=node)
+
+ if table.context.page_qnode is not None:
+ context_node_id = DGPathNodeQNode(table.context.page_qnode).get_id()
+ node = EntityValueNode(id=context_node_id, qnode_id=table.context.page_qnode,
+ context_span=ContextSpan(text=table.context.page_title,
+ span=Span(0, len(table.context.page_title))))
+ dg.add_node(node.id, data=node)
+
+ # find all paths
+ n_rows = len(table.table.columns[0].values)
+ kg_path_discovering_tasks = []
+ new_paths = []
+ for ri in range(n_rows):
+ for ci, col in enumerate(table.table.columns):
+ if ignore_columns is not None and ci in ignore_columns:
+ continue
+
+ u = dg.nodes[f"{ri}-{ci}"]['data']
+ for cj in range(ci+1, len(table.table.columns)):
+ if ignore_columns is not None and cj in ignore_columns:
+ continue
+
+ v = dg.nodes[f"{ri}-{cj}"]['data']
+ kg_path_discovering_tasks.append((u, v))
+ if context_node_id in dg:
+ kg_path_discovering_tasks.append((u, dg.nodes[context_node_id]['data']))
+
+ for u, v in tqdm(kg_path_discovering_tasks, desc="KG searching") if verbose else kg_path_discovering_tasks:
+ new_paths += kg_path_discovering(options, qnodes, kg_object_index, dg, u, v, max_n_hop=max_n_hop)
+
+ # add paths to the graph
+ for path in new_paths:
+ curr_nodeid = path.sequence[0].id
+ tmp_path = [curr_nodeid]
+ for i in range(1, len(path.sequence), 2):
+ prop: DGPathEdge = path.sequence[i]
+ value: DGPathNode = path.sequence[i+1]
+
+ if isinstance(value, DGPathExistingNode):
+ nodeid = value.id
+ else:
+ nodeid = value.get_id()
+ if nodeid not in dg.nodes:
+ if isinstance(value, DGPathNodeStatement):
+ dg.add_node(nodeid, data=StatementNode(nodeid, qnode_id=value.qnode_id, predicate=value.predicate, is_in_kg=True))
+ elif isinstance(value, DGPathNodeQNode):
+ dg.add_node(nodeid, data=EntityValueNode(nodeid, qnode_id=value.qnode_id, context_span=None))
+ else:
+ dg.add_node(nodeid, data=LiteralValueNode(nodeid, value=value.value, context_span=None))
+
+ if dg.has_edge(curr_nodeid, nodeid, prop.value):
+ edge: DGEdge = dg.edges[curr_nodeid, nodeid, prop.value]['data']
+ else:
+ edge = DGEdge(source=curr_nodeid, target=nodeid, predicate=prop.value, is_qualifier=prop.is_qualifier)
+ dg.add_edge(curr_nodeid, nodeid, key=prop.value, data=edge)
+
+ tmp_path.append(edge)
+ tmp_path.append(nodeid)
+ curr_nodeid = nodeid
+
+ # path format: u - e - (s) - e - v - e - (s2) - e - v2
+ for i in range(2, len(tmp_path), 4):
+ u_node_id = tmp_path[i-2]
+ u_edge = tmp_path[i-1]
+ snode: StatementNode = dg.nodes[tmp_path[i]]['data']
+ v_edge = tmp_path[i+1]
+ v_node_id = tmp_path[i+2]
+
+ edge_source = EdgeFlowSource(u_node_id, u_edge.predicate)
+ edge_target = EdgeFlowTarget(v_node_id, v_edge.predicate)
+ snode.track_provenance(edge_source, edge_target, [path.sequence[i].provenance])
+
+ # DEBUG code
+ # for uid, udata in dg.nodes(data=True):
+ # u = udata['data']
+ # if not u.is_cell:
+ # continue
+ # if u.column == 0:
+ # for _, sid, eid, edata in dg.out_edges(uid, data=True, keys=True):
+ # for _1, vid, e2, e2data in dg.out_edges(sid, data=True, keys=True):
+ # v = dg.nodes[vid]['data']
+ # if v.is_cell and v.column == 1:
+ # print(uid, sid, vid, eid, e2)
+
+ KGInference(dg, qnodes, wdprops) \
+ .infer_subproperty() \
+ .kg_transitive_inference()
+
+ # pruning unnecessary paths
+ if options & BuildDGOption.PRUNING_REDUNDANT_ENT:
+ DGPruning(dg).prune_hidden_entities()
+
+ return dg
+
+
+def viz_dg(dg: nx.MultiDiGraph, qnodes: Dict[str, QNode], wdprops: Dict[str, WDProperty], outdir, graph_id):
+ colors = {
+ "context": dict(fill="#C6E5FF", stroke="#5B8FF9"),
+ "statement": dict(fill="#d9d9d9", stroke="#434343"),
+ "kg": dict(fill="#b7eb8f", stroke="#135200"),
+ "cell": dict(fill='#ffd666', stroke='#874d00')
+ }
+
+ def node_fn(uid, udata):
+ u: DGNode = udata['data']
+ html = ""
+ if u.is_cell:
+ label = u.value
+ nodetype = 'cell'
+ for qnode_id in u.qnode_ids:
+ qnode = qnodes[qnode_id]
+ html += f"""{qnode.label} ({qnode.id})
"""
+
+ elif u.is_literal_value:
+ label = u.value.to_string_repr()
+ nodetype = 'context' if u.is_context else 'kg'
+ elif u.is_entity_value:
+ qnode = qnodes[u.qnode_id]
+ label = f"{qnode.label} ({qnode.id})"
+ nodetype = 'context' if u.is_context else 'kg'
+ html = f"""{label}"""
+ else:
+ label = ""
+ nodetype = 'statement'
+
+ return {
+ "label": label,
+ "style": colors[nodetype],
+ "html": html
+ }
+
+ def edge_fn(eid, edata):
+ e: DGEdge = edata['data']
+ return {
+ "label": f"{wdprops[e.predicate].label} ({e.predicate})"
+ }
+
+ # viz_graph(dg, node_fn, edge_fn, outdir, graph_id)
+
+
+# if __name__ == '__main__':
+# from sm_unk.dev.wikitable2wikidata.sxx_evaluation import get_input_data
+#
+# table_index = 34
+#
+# max_n_hop = 2
+# dataset_dir = HOME_DIR / "wikitable2wikidata/250tables"
+# gold_models = get_input_data(dataset_dir, dataset_dir.name, only_curated=True)
+#
+# REDIS_CACHE_URL = 'redis://localhost:6379/8'
+# qnodes = get_qnodes(dataset_dir, n_hop=max_n_hop+1, test=True)
+# wdclasses = WDClass.from_file(dataset_dir / "ontology", load_parent_closure=True)
+# wdprops = WDProperty.from_file(load_parent_closure=True)
+#
+# kg_index_file = dataset_dir / "kg_index" / "object_index.2hop_transitive.pkl.gz"
+# if kg_index_file.exists():
+# kg_object_index = KGObjectIndex.deserialize(kg_index_file, verbose=True)
+# else:
+# index_qnode_ids = list(get_qnodes(dataset_dir, n_hop=1, no_wdclass=True).keys())
+# kg_object_index = KGObjectIndex.from_qnodes(index_qnode_ids, qnodes, wdprops,
+# n_hop=2,
+# verbose=True)
+# kg_object_index.serialize(kg_index_file)
+#
+# info = lambda x: f"{qnodes[x].label} ({x}): {qnodes[x].description}" if x[0] == 'Q' else f"{wdprops[x].label} ({x})"
+#
+# logger.info("Finish setup working environment")
+#
+# table = gold_models[table_index][1]
+# cache_fn = lambda fn: M.redis_cache_func(REDIS_CACHE_URL, namespace=f"{dataset_dir.name}.{fn.__name__}")(fn)
+#
+# # @cache_fn
+# def wrapper(table_index):
+# return build_data_graph(table, qnodes, wdprops, kg_object_index, max_n_hop=max_n_hop, verbose=True)
+#
+# timer = M.Timer()
+# dg = wrapper(table_index)
+# timer.report()
+# exit(0)
+# logger.info("Pruning graph")
+#
+# logger.info("dg #nodes: {}", len(dg.nodes))
+# logger.info("dg #edges: {}", len(dg.edges))
+# logger.info("dg #cells: {}", sum((int(udata['data'].is_cell) for uid, udata in dg.nodes.items())))
+# logger.info("dg #entities: {}", sum((int(udata['data'].is_entity_value) for uid, udata in dg.nodes.items())))
+# logger.info("dg #literals: {}", sum((int(udata['data'].is_literal_value) for uid, udata in dg.nodes.items())))
+# logger.info("dg #statements: {}", sum((int(udata['data'].is_statement) for uid, udata in dg.nodes.items())))
+#
+# DGPruning(dg).prune_hidden_entities()
+#
+# # dg = build_data_graph(table, qnodes, wdprops)
+#
+# logger.info("dg #nodes: {}", len(dg.nodes))
+# logger.info("dg #edges: {}", len(dg.edges))
+# logger.info("dg #cells: {}", sum((int(udata['data'].is_cell) for uid, udata in dg.nodes.items())))
+# logger.info("dg #entities: {}", sum((int(udata['data'].is_entity_value) for uid, udata in dg.nodes.items())))
+# logger.info("dg #literals: {}", sum((int(udata['data'].is_literal_value) for uid, udata in dg.nodes.items())))
+# logger.info("dg #statements: {}", sum((int(udata['data'].is_statement) for uid, udata in dg.nodes.items())))
+#
+# exit(0)
+# info('P527')
+# uid = '0-0'
+# dict(dg[uid])
+# dict(dg['stmt:Q12589-P150-241'])
+# dict(dg['stmt:Q12589-P150-242'])
+# dict(dg['stmt:Q12589-P527-430'])
+# dict(dg['stmt:Q1063324-P421-0'])
+# dict(dg['ent:Q1063324'])
+#
+# table.table.as_dataframe()
+#
+# from sm_unk.dev.wikitable2wikidata.s31_graph_v2_semantic_graph import SemanticGraphConstructor
+# constructor = SemanticGraphConstructor([
+# SemanticGraphConstructor.init_sg,
+# ], qnodes, wdclasses, wdprops)
+# args = constructor.run(table, dg)
+# len(args.sg)
+# constructor.calculate_link_frequency(args)
+# constructor.prune_sg_redundant_entity(args)
+# len(args.sg)
+# sg = args.sg
+# dict(sg['column-0'])
+# dict(sg['stmt:column-0-P150-ent:Q205584'])
diff --git a/grams/algorithm/helpers.py b/grams/algorithm/helpers.py
new file mode 100644
index 0000000..c253ce7
--- /dev/null
+++ b/grams/algorithm/helpers.py
@@ -0,0 +1,172 @@
+from dataclasses import dataclass
+from enum import Enum
+from operator import attrgetter
+from typing import Dict, Set, List, Optional, Callable, Tuple, Union, Any
+
+import networkx as nx
+import orjson
+from loguru import logger
+
+import grams.misc as M
+
+
+@dataclass
+class Tree:
+ @dataclass
+ class HierarchyRecord:
+ id: str
+ duplicated: bool
+ depth: int
+
+ id: str
+ depth: int
+ children: List['Tree']
+ score: Optional[float] = None
+
+ def get_flatten_hierarchy(self, dedup: bool = False) -> List[HierarchyRecord]:
+ """Flatten the tree into a flatten hierarchy"""
+ stack = [self]
+ output = []
+ seen_ids = set()
+ while len(stack) > 0:
+ node = stack.pop()
+ dup = node.id in seen_ids
+ output.append(Tree.HierarchyRecord(node.id, dup, node.depth))
+ seen_ids.add(node.id)
+
+ if not dedup or not dup:
+ # do not travel the children
+ for c in reversed(node.children):
+ stack.append(c)
+ return output
+
+ def sort(self, key=None, reverse: bool = False):
+ self.children.sort(key=key or attrgetter('score'), reverse=reverse)
+ for c in self.children:
+ c.sort(key, reverse)
+
+ def adjust_depth(self, depth: int):
+ self.depth = depth
+ for c in self.children:
+ c.adjust_depth(depth + 1)
+ return self
+
+ def update_score(self, score_fn: Callable[['Tree'], float]):
+ """Use to adjust score of a node in the tree based on its children. The score is going to be used for sorting"""
+ for c in self.children:
+ c.update_score(score_fn)
+ self.score = score_fn(self)
+ return self
+
+ def preorder(self, fn: Callable[['Tree', List['Tree']], None], path: List['Tree'] = None):
+ if path is None:
+ path = []
+ fn(self, path)
+ path.append(self)
+ for child in self.children:
+ child.preorder(fn, path)
+ path.pop()
+
+ def clone(self):
+ return Tree(self.id, self.depth, [c.clone() for c in self.children], self.score)
+
+
+@dataclass
+class Forest:
+ trees: List[Tree]
+
+ def get_flatten_hierarchy(self, dedup: bool = False) -> List[Tree.HierarchyRecord]:
+ return Tree(None, -1, self.trees).get_flatten_hierarchy(dedup)[1:]
+
+ def sort(self, key=None, reverse: bool = False):
+ for tree in self.trees:
+ tree.sort(key, reverse)
+ self.trees.sort(key=key or attrgetter('score'), reverse=reverse)
+ return self
+
+ def update_score(self, score_fn: Callable[['Tree'], float]):
+ for tree in self.trees:
+ tree.update_score(score_fn)
+ return self
+
+ def preorder(self, fn: Callable[['Tree'], None]):
+ for tree in self.trees:
+ tree.preorder(fn)
+
+
+class IndirectDictAccess:
+
+ def __init__(self, odict, access):
+ self.odict = odict
+ self.access = access
+
+ def __getitem__(self, item):
+ return self.access(self.odict[item])
+
+ def __contains__(self, item):
+ return item in self.odict
+
+
+def reorder2tree(lst: List[str], super_relationships: Dict[str, Set[str]]) -> Forest:
+ """This function is very forgiving as it doesn't throw exception
+ when the qnode is not in the super_relationship dictionary
+ """
+ if len(lst) == 0:
+ return Forest([])
+
+ roots = []
+ graph: Dict[str, Tree] = {}
+
+ # unknown class; we do have this case in some dumps (tag to wrong qnodes)
+ lst = [u for u in lst if u in super_relationships]
+
+ for i, u in enumerate(lst):
+ parents = set()
+ for j, v in enumerate(lst):
+ if i == j:
+ continue
+ if v in super_relationships[u]:
+ parents.add(v)
+
+ if len(parents) > 1:
+ # remove grand parents
+ dparents = set()
+ for v1 in parents:
+ if all(v1 == v2 or v1 not in super_relationships[v2] for v2 in parents):
+ dparents.add(v1)
+ parents = dparents
+
+ if len(parents) == 0:
+ roots.append(u)
+
+ if u not in graph:
+ graph[u] = Tree(u, -1, [])
+ for v in parents:
+ if v not in graph:
+ graph[v] = Tree(v, -1, [])
+ graph[v].children.append(graph[u])
+
+ if len(roots) == 0:
+ # we have cycle.. try to break it, or we can just throw exception
+ # this is going to be very rare based on previous analysis (6 cycles found)
+ raise Exception("Found cycles")
+
+ # want to make sure that we have is not a graph
+ visited = set()
+
+ def is_acyclic(uid):
+ if uid in visited:
+ return False
+ visited.add(uid)
+ for c in graph[uid].children:
+ if not is_acyclic(c.id):
+ return False
+ visited.remove(uid)
+ return True
+
+ for root in roots:
+ if not is_acyclic(root):
+ raise Exception("Found cycles")
+
+ # clone so that we can get correct depth
+ return Forest([graph[root].clone().adjust_depth(0) for root in roots])
diff --git a/grams/algorithm/kg_index.py b/grams/algorithm/kg_index.py
new file mode 100644
index 0000000..935061a
--- /dev/null
+++ b/grams/algorithm/kg_index.py
@@ -0,0 +1,145 @@
+import enum
+import time
+from collections import defaultdict
+from pathlib import Path
+from typing import Dict, Iterable, Set, Union, List, NamedTuple, Optional
+
+import sm.misc as M
+from loguru import logger
+from tqdm.auto import tqdm
+from kgdata.wikidata.models import QNode, WDProperty
+
+Relationship = NamedTuple('Relationship', [('prop', str), ('quals', List[str]), ('both', bool)])
+OneHopIndexPath = NamedTuple('OneHopIndexPath', [('relationship', Relationship), ('statement_index', int)])
+OneHopIndex = Dict[str, Dict[str, List[OneHopIndexPath]]]
+
+# to find all paths between hop1 & hop2, you need to do cross product, but if you consider only transitive props, then
+# you need to filter the second hop to be only equal
+TwoHopIndexPath = NamedTuple('TwoHopIndexPath', [('qnode', str), ('hop1', List[OneHopIndexPath]), ('hop2', List[OneHopIndexPath])])
+TwoHopIndex = Dict[str, Dict[str, List[TwoHopIndexPath]]]
+
+
+class TraversalOption(enum.Enum):
+ NoConstraint = "no_constraint"
+ TransitiveOnly = "transitive_only"
+
+
+class KGObjectIndex:
+ """An index in KG that help speed up searching for entities
+ """
+ def __init__(self, one_hop_index: OneHopIndex, two_hop_index: TwoHopIndex, n_hop: int, traversal_option: TraversalOption):
+ self.one_hop_index = one_hop_index
+ self.two_hop_index = two_hop_index
+ self.n_hop = n_hop
+ self.traversal_option = traversal_option
+
+ @staticmethod
+ def from_qnodes(index_qnode_ids: List[str], qnodes: Dict[str, QNode], wdprops: Dict[str, WDProperty],
+ n_hop: int = 1, traversal_option: TraversalOption = TraversalOption.TransitiveOnly, verbose: bool = False):
+ # one hop index
+ hop1_index: OneHopIndex = {}
+ hop2_index: TwoHopIndex = {}
+
+ assert 1 <= n_hop <= 2
+ if n_hop > 1 and traversal_option == TraversalOption.TransitiveOnly:
+ transitive_props = {pid for pid, p in wdprops.items() if p.is_transitive()}
+ else:
+ transitive_props = None
+
+ if verbose:
+ pbar = tqdm(total=len(index_qnode_ids) * (1 + int(n_hop > 1)), desc=f'build kg object index for {len(index_qnode_ids)} qnodes')
+ else:
+ pbar = M.FakeTQDM()
+
+ for index_qnode_id in index_qnode_ids:
+ qnode = qnodes[index_qnode_id]
+ hop1_index[qnode.id] = dict(build_one_hop_index(qnode))
+ pbar.update(1)
+
+ if n_hop > 1:
+ for index_qnode_id in index_qnode_ids:
+ qnode_hop2_index = defaultdict(list)
+ for uid, hop1_paths in hop1_index[index_qnode_id].items():
+ # TODO: ignore P31 and not discovering outside of it until to fix the method that discovers incoming&outgoing edges to include classes
+ # which currently has scalability issue since they have too many edges.
+ hop1_paths = [
+ hop1_path for hop1_path in hop1_paths
+ if hop1_path.relationship.prop != 'P31' and (transitive_props is None or hop1_path.relationship.prop in transitive_props)
+ ]
+ if len(hop1_paths) == 0:
+ continue
+ if transitive_props is not None:
+ hop1_props = {path.relationship.prop for path in hop1_paths}
+
+ if uid not in qnodes:
+ # this can happen due to some of the qnodes is in the link, but is missing in the KG
+ # this is very rare so we can employ some check to make sure this is not due to
+ # our wikidata subset
+ is_error_in_kg = any(
+ any(_s.value.is_qnode() and _s.value.as_qnode_id() in qnodes for _s in _stmts)
+ for _p, _stmts in qnodes[index_qnode_id].props.items()
+ )
+ if not is_error_in_kg:
+ raise Exception(f"Missing qnodes in your KG subset: {uid}")
+ continue
+
+ for vid, hop2_paths in build_one_hop_index(qnodes[uid], transitive_props).items():
+ hop2_paths = [
+ hop2_path for hop2_path in hop2_paths
+ if transitive_props is None or hop2_path.relationship.prop in hop1_props
+ ]
+ if len(hop2_paths) > 0:
+ qnode_hop2_index[vid].append(TwoHopIndexPath(uid, hop1_paths, hop2_paths))
+ pbar.update(1)
+ hop2_index[index_qnode_id] = dict(qnode_hop2_index)
+ pbar.close()
+ return KGObjectIndex(hop1_index, hop2_index, n_hop, traversal_option)
+
+ @staticmethod
+ def deserialize(infile: Union[Path, str], verbose: bool = False):
+ start = time.time()
+ index = M.deserialize_pkl(infile)
+ if verbose:
+ logger.info("Deserialize KG object index takes {} seconds", f"{time.time() - start:.3f}")
+ return index
+
+ def serialize(self, outfile: Union[Path, str]):
+ Path(outfile).parent.mkdir(exist_ok=True, parents=True)
+ M.serialize_pkl(self, outfile)
+
+ def iter_hop1_props(self, source_qnode_id: str, target_qnode_id: str) -> Iterable[OneHopIndexPath]:
+ return self.one_hop_index[source_qnode_id].get(target_qnode_id, [])
+
+ def iter_hop2_props(self, source_qnode_id: str, target_qnode_id: str) -> Iterable[TwoHopIndexPath]:
+ return self.two_hop_index[source_qnode_id].get(target_qnode_id, [])
+
+
+def build_one_hop_index(qnode: QNode, filter_props: Set[str]=None):
+ hop1_index = defaultdict(list)
+ if filter_props is None:
+ iter = qnode.props.items()
+ else:
+ iter = [(p, qnode.props.get(p, [])) for p in filter_props]
+
+ for p, stmts in iter:
+ for stmt_i, stmt in enumerate(stmts):
+ lst = defaultdict(list)
+ if stmt.value.is_qnode():
+ target_qnode_id = stmt.value.as_qnode_id()
+ lst[target_qnode_id].append(None)
+
+ for q, qvals in stmt.qualifiers.items():
+ for target_qnode_id in {qval.as_qnode_id() for qval in qvals if qval.is_qnode()}:
+ lst[target_qnode_id].append(q)
+
+ for target_qnode_id, rels in lst.items():
+ assert all(x is not None for x in rels[1:])
+ # (p & q) is guarantee to be unique
+ if rels[0] is not None or len(rels) > 1:
+ # we have qualifiers
+ qs = rels if rels[0] is not None else rels[1:]
+ else:
+ qs = []
+ rel = Relationship(p, qs, both=rels[0] is None and len(rels) > 1)
+ hop1_index[target_qnode_id].append(OneHopIndexPath(rel, stmt_i))
+ return hop1_index
diff --git a/grams/algorithm/link_feature.py b/grams/algorithm/link_feature.py
new file mode 100644
index 0000000..e2b2320
--- /dev/null
+++ b/grams/algorithm/link_feature.py
@@ -0,0 +1,493 @@
+import functools
+from collections import defaultdict
+from enum import Enum
+from operator import xor, attrgetter
+
+import networkx as nx
+from typing import Dict, Iterable, Tuple, Set, List, Optional, Callable
+
+from kgdata.wikidata.models import QNode, WDProperty, WDQuantityPropertyStats
+from grams.inputs.linked_table import LinkedTable
+from grams.algorithm.data_graph import DGNode
+from grams.algorithm.semantic_graph import SGNode, SGStatementNode, SGEdge
+from grams.algorithm.literal_match import TextParser
+
+
+class QuantityType(Enum):
+ Integer = "int"
+ Float = "float"
+
+
+class LinkFeatureExtraction:
+ FreqOverRow = "FrequencyOfLinkOverRow"
+ FreqOverEntRow = "FrequencyOfLinkOverEntRow"
+ FreqOverPossibleLink = "FrequencyOfLinkOverPossibleLink"
+ FreqUnmatchOverEntRow = "FrequencyOfUnmatchLinkOverEntRow"
+ FreqUnmatchOverPossibleLink = "FrequencyOfUnmatchLinkOverPossibleLink"
+ GTE5Link = "GTE5Link"
+ GTE10Link = "GTE10Link"
+ GTE15Link = "GTE15Link"
+ GTE20Link = "GTE20Link"
+ GTE30Link = "GTE30Link"
+ GTE40Link = "GTE40Link"
+ GTE50Link = "GTE50Link"
+ NotFuncDep = "NotFuncDep"
+ DataTypeMismatch = "DataTypeMismatch"
+ HeaderSimilarity = "HeaderSimimilarity"
+
+
+ def __init__(self,
+ table: LinkedTable, sg: nx.MultiDiGraph, dg: nx.MultiDiGraph,
+ qnodes: Dict[str, QNode], wdprops: Dict[str, WDProperty],
+ wd_num_prop_stats: Dict[str, WDQuantityPropertyStats],
+ sim_fn: Optional[Callable[[str, str], float]] = None):
+ self.table = table
+ self.sg = sg
+ self.dg = dg
+ self.qnodes = qnodes
+ self.wdprops = wdprops
+ self.wd_num_prop_stats = wd_num_prop_stats
+ self.text_parser = TextParser()
+ self.sim_fn = sim_fn
+
+ def extract_features(self):
+ n_rows = self.table.size()
+ freq_over_row = {}
+ freq_over_ent_row = {}
+ freq_over_pos_link = {}
+ freq_unmatch_over_ent_row = {}
+ freq_unmatch_over_pos_link = {}
+ not_fd_links = {}
+ dtype_mismatch_links = {}
+ # make none to force setting sim_fn to not None when we want to use the features
+ header_sim_links = None if self.sim_fn is None else {}
+
+ gte_link_thresholds = [5, 10, 15, 20, 30, 40, 50]
+ gte_link_bins = {k: {} for k in gte_link_thresholds}
+ pair2max_possible_link = defaultdict(int)
+
+ for sid, sdata in self.sg.nodes(data=True):
+ stmt: SGStatementNode = self.sg.nodes[sid]['data']
+ if not stmt.is_statement:
+ continue
+
+ in_edge: SGEdge
+ out_edge: SGEdge
+
+ (uid, _, in_edge), = list(self.sg.in_edges(stmt.id, data='data'))
+ unode: SGNode = self.sg.nodes[uid]['data']
+ for _, vid, out_edge in self.sg.out_edges(stmt.id, data='data'):
+ vnode: SGNode = self.sg.nodes[vid]['data']
+ dg_flows = stmt.get_edges_provenance([out_edge])
+ dg_links: Set[Tuple[str, str]] = set()
+
+ sum_prob = 0.0
+ for source_flow, target_flows in dg_flows.items():
+ assert source_flow.sg_source_id == uid
+ if unode.is_column:
+ # we should only have one target flow as we consider row by row only
+ target_flow, provenances = self._unpack_size1_dict(target_flows)
+ dg_links.add((source_flow.dg_source_id, target_flow.dg_target_id))
+ sum_prob += max(provenances, key=attrgetter('prob')).prob
+ else:
+ # this is entity and we may have more than one target flow (to different row)
+ assert unode.is_entity_value
+ for target_flow, provenances in target_flows.items():
+ dg_links.add((source_flow.dg_source_id, target_flow.dg_target_id))
+ sum_prob += max(provenances, key=attrgetter('prob')).prob
+
+ # what is the maximum possible links we can have? this ignore the the link so this is used to calculate FreqOverEntRow
+ max_possible_ent_rows = self._get_maximum_possible_ent_links_between_two_nodes(
+ uid, vid, self.wdprops[out_edge.predicate].is_data_property())
+
+ # calculate the number of links that the source node doesn't have the values as in the target nodes
+ # we should consider on what kind of missing. e.g., object missing is more obvious than value mis-match
+ # in value mis-match: datetime mis-match is more profound than area/population mis-match
+ n_unmatch_links = self._get_n_unmatch_discovered_links(
+ uid, vid, in_edge.predicate, out_edge.predicate,
+ dg_links, self.wdprops[out_edge.predicate].is_data_property())
+ n_possible_links = n_unmatch_links + len(dg_links)
+ pair2max_possible_link[uid, vid] = max(n_possible_links, pair2max_possible_link[uid, vid])
+
+ # compute the features
+ pair_freq_over_row = sum_prob / n_rows
+ pair_freq_over_ent_row = (sum_prob / max_possible_ent_rows) if max_possible_ent_rows > 0 else 0
+ # re-calculate it later
+ pair_freq_over_pos_link = (sum_prob, (uid, vid))
+ pair_freq_unmatch_over_ent_row = (n_unmatch_links / max_possible_ent_rows) if max_possible_ent_rows > 0 else 0
+ # re-calculate it later
+ pair_freq_unmatch_over_pos_link = (n_unmatch_links, (uid, vid))
+ dtype_mismatch = None
+ header_sim = None
+
+ if vnode.is_column:
+ assert out_edge.predicate[0] == 'P', 'Sanity Check'
+ if in_edge.predicate in self.wd_num_prop_stats and (in_edge.predicate == out_edge.predicate or out_edge.predicate in self.wd_num_prop_stats[in_edge.predicate].qualifiers):
+ if in_edge.predicate == out_edge.predicate:
+ stat = self.wd_num_prop_stats[in_edge.predicate].value
+ else:
+ stat = self.wd_num_prop_stats[in_edge.predicate].qualifiers[out_edge.predicate]
+
+ dtype = self._estimate_col_quantity_type(vnode.column)
+ if dtype is not None and stat.size > 0:
+ # if the property is not quantity, their size is 0
+ is_prop_int = (stat.int_size / stat.size) > 0.99
+ if dtype == QuantityType.Integer:
+ dtype_mismatch = int(not is_prop_int)
+ else:
+ dtype_mismatch = int(is_prop_int)
+ if self.sim_fn is not None:
+ header_sim = self.sim_fn(self.wdprops[out_edge.predicate].label, vnode.label)
+
+ if in_edge.predicate == out_edge.predicate:
+ # copy to the parent property as well
+ key = (uid, sid, in_edge.predicate)
+ assert key not in freq_over_row
+ freq_over_row[key] = pair_freq_over_row
+ freq_over_ent_row[key] = pair_freq_over_ent_row
+ freq_over_pos_link[key] = pair_freq_over_pos_link
+ freq_unmatch_over_ent_row[key] = pair_freq_unmatch_over_ent_row
+ freq_unmatch_over_pos_link[key] = pair_freq_unmatch_over_pos_link
+
+ if unode.is_column and vnode.is_column:
+ not_fd_links[key] = int(not self._functional_dependency_test(unode.column, vnode.column))
+
+ for threshold, indicator in zip(gte_link_thresholds, self._categorize_num_gte(len(dg_links), gte_link_thresholds)):
+ gte_link_bins[threshold][key] = indicator
+
+ if dtype_mismatch is not None:
+ dtype_mismatch_links[key] = dtype_mismatch
+
+ if header_sim is not None:
+ header_sim_links[key] = header_sim
+
+ key = (sid, vid, out_edge.predicate)
+ assert key not in freq_over_row
+ freq_over_row[key] = pair_freq_over_row
+ freq_over_ent_row[key] = pair_freq_over_ent_row
+ freq_over_pos_link[key] = pair_freq_over_pos_link
+ freq_unmatch_over_ent_row[key] = pair_freq_unmatch_over_ent_row
+ freq_unmatch_over_pos_link[key] = pair_freq_unmatch_over_pos_link
+
+ for threshold, indicator in zip(gte_link_thresholds,
+ self._categorize_num_gte(len(dg_links), gte_link_thresholds)):
+ gte_link_bins[threshold][key] = indicator
+
+ if unode.is_column and vnode.is_column:
+ not_fd_links[key] = int(not self._functional_dependency_test(unode.column, vnode.column))
+
+ if dtype_mismatch is not None:
+ dtype_mismatch_links[key] = dtype_mismatch
+
+ if header_sim is not None:
+ header_sim_links[key] = header_sim
+
+ # re-adjust the feature over possible links
+ for key, (freq, pair) in freq_over_pos_link.items():
+ if pair2max_possible_link[pair] == 0:
+ freq_over_pos_link[key] = 0
+ else:
+ freq_over_pos_link[key] = freq / pair2max_possible_link[pair]
+ for key, (freq, pair) in freq_unmatch_over_pos_link.items():
+ if pair2max_possible_link[pair] == 0:
+ freq_unmatch_over_pos_link[key] = 0
+ else:
+ freq_unmatch_over_pos_link[key] = freq / pair2max_possible_link[pair]
+
+ return {
+ self.FreqOverRow: freq_over_row,
+ self.FreqOverEntRow: freq_over_ent_row,
+ self.FreqOverPossibleLink: freq_over_pos_link,
+ self.FreqUnmatchOverEntRow: freq_unmatch_over_ent_row,
+ self.FreqUnmatchOverPossibleLink: freq_unmatch_over_pos_link,
+ self.GTE5Link: gte_link_bins[5],
+ self.GTE10Link: gte_link_bins[10],
+ self.GTE15Link: gte_link_bins[15],
+ self.GTE20Link: gte_link_bins[20],
+ self.GTE30Link: gte_link_bins[30],
+ self.GTE40Link: gte_link_bins[40],
+ self.GTE50Link: gte_link_bins[50],
+ self.NotFuncDep: not_fd_links,
+ self.DataTypeMismatch: dtype_mismatch_links,
+ self.HeaderSimilarity: header_sim_links
+ }
+
+ def add_debug_info(self, features: dict, sg=None):
+ if sg is None:
+ sg = self.sg
+ """Add features to edges in the graph for debugging. Features are from the extract_features function"""
+ # add the features to edges for debugging purpose
+ for feat, feat_data in features.items():
+ if feat_data is None:
+ continue
+ for (uid, vid, eid), val in feat_data.items():
+ if sg.has_edge(uid, vid, eid):
+ edge: SGEdge = sg.edges[uid, vid, eid]['data']
+ assert feat not in edge.features
+ edge.features[feat] = val
+
+ def _unpack_size1_dict(self, odict: dict):
+ """Unpack a dictionary of size 1"""
+ lst = list(odict.items())
+ assert len(lst) == 1, lst
+ return lst[0]
+
+ def _categorize_num_gte(self, num: int, thresholds: List[int]):
+ feats = []
+ for i in range(len(thresholds)):
+ if num >= thresholds[i]:
+ feats.append(1)
+ else:
+ feats.append(0)
+ return feats
+
+ def _iter_dg_pair(self, uid: str, vid: str) -> Iterable[Tuple[DGNode, DGNode]]:
+ """This function iterate through each pair of data graph nodes between two semantic graph nodes.
+
+ If both sg nodes are entities, we only have one pair.
+ If one or all of them are columns, the number of pairs will be the size of the table.
+ Otherwise, not support iterating between nodes & statements
+ """
+ u: SGNode = self.sg.nodes[uid]['data']
+ v: SGNode = self.sg.nodes[vid]['data']
+
+ if u.is_column and v.is_column:
+ uci = u.column
+ vci = v.column
+ for ri in range(self.table.size()):
+ ucell = self.dg.nodes[f"{ri}-{uci}"]['data']
+ vcell = self.dg.nodes[f"{ri}-{vci}"]['data']
+ yield ucell, vcell
+ elif u.is_column:
+ assert v.is_value
+ uci = u.column
+ vcell = self.dg.nodes[v.id]['data']
+ for ri in range(self.table.size()):
+ ucell = self.dg.nodes[f"{ri}-{uci}"]['data']
+ yield ucell, vcell
+ elif v.is_column:
+ assert u.is_value
+ vci = v.column
+ ucell = self.dg.nodes[u.id]['data']
+ for ri in range(self.table.size()):
+ vcell = self.dg.nodes[f"{ri}-{vci}"]['data']
+ yield ucell, vcell
+ else:
+ assert not u.is_column and not v.is_column
+ yield self.dg.nodes[u.id]['data'], self.dg.nodes[v.id]['data']
+
+ def _get_maximum_possible_ent_links_between_two_nodes(self, uid: str, vid: str, is_data_predicate: bool):
+ """Find the maximum possible links between two nodes (ignore the possible predicates):
+
+ Let M be the maximum possible links we want to find, N is the number of rows in the table.
+ 1. If two nodes are not columns, M is 1 because it's entity to entity link.
+ 2. If one node is a column, M = N - U, where U is the number of pairs that cannot have KG discovered links. A
+ pair that cannot have KG discovered links is:
+ a. If both nodes are columns, and the link is
+ * data predicate: the source cell links to no entity.
+ * object predicate: the source or target cell link to no entity
+ b. If only one node is column, and the link is
+ * data predicate:
+ - if the source node must be an entity, then the target must be a column. U is always 0
+ - else then the source node must be is a column and target is a literal value: a cell in the column links to no entity
+ * object predicate: a cell in the column links to no entity.
+ """
+ u: SGNode = self.sg.nodes[uid]['data']
+ v: SGNode = self.sg.nodes[vid]['data']
+
+ if not u.is_column and not v.is_column:
+ return 1
+
+ # instead of going through each node attach to the node in the semantic graph, we cheat by directly generating
+ # the data node ID
+ n_rows = self.table.size()
+ n_null_entities = 0
+ if xor(u.is_column, v.is_column):
+ if is_data_predicate:
+ if u.is_value:
+ assert u.is_entity_value and v.is_column
+ return n_rows
+
+ assert u.is_column and v.is_value
+
+ ci = u.column if u.is_column else v.column
+ for ri in range(n_rows):
+ if len(self.dg.nodes[f"{ri}-{ci}"]['data'].qnode_ids) == 0:
+ n_null_entities += 1
+ else:
+ uci = u.column
+ vci = v.column
+
+ for ri in range(n_rows):
+ ucell_unk = len(self.dg.nodes[f"{ri}-{uci}"]['data'].qnode_ids) == 0
+ vcell_unk = len(self.dg.nodes[f"{ri}-{vci}"]['data'].qnode_ids) == 0
+
+ if is_data_predicate:
+ if ucell_unk:
+ n_null_entities += 1
+ elif ucell_unk or vcell_unk:
+ n_null_entities += 1
+
+ return n_rows - n_null_entities
+
+ def _dg_pair_has_possible_ent_links(self, dgu: DGNode, dgv: DGNode, is_data_predicate: bool):
+ if dgu.is_cell and dgv.is_cell:
+ # both are cells
+ if is_data_predicate:
+ # data predicate: source cell must link to some entities to have possible links
+ return len(dgu.qnode_ids) > 0
+ else:
+ # object predicate: source cell and target cell must link to some entities to have possible links
+ return len(dgu.qnode_ids) > 0 and len(dgv.qnode_ids) > 0
+ elif dgu.is_cell:
+ # the source is cell, the target will be literal/entity value
+ # we have link when source cell link to some entities, doesn't depend on type of predicate
+ return len(dgu.qnode_ids) > 0
+ elif dgv.is_cell:
+ # the target is cell, the source will be literal/entity value
+ if is_data_predicate:
+ # data predicate: always has possibe links
+ return True
+ else:
+ # object predicate: have link when the target cell link to some entities
+ return len(dgv.qnode_ids) > 0
+ else:
+ # all cells are values, always have link due to how the link is generated in the first place
+ return True
+
+ def _get_n_unmatch_discovered_links(self, uid: str, vid: str, inpred: str, outpred: str, uv_links: Set[Tuple[str, str]],
+ is_outpred_data_predicate: bool):
+ """Get number of discovered links that don't match due to value differences. This function do not count if:
+ * the link between two DG nodes is impossible
+ * the property/qualifier do not exist in the QNode
+ """
+ u: SGNode = self.sg.nodes[uid]['data']
+ v: SGNode = self.sg.nodes[vid]['data']
+ is_outpred_qualifier = inpred != outpred
+
+ n_unmatch_links = 0
+ for dgu, dgv in self._iter_dg_pair(uid, vid):
+ # if has link, then we don't have to count
+ if (dgu.id, dgv.id) in uv_links:
+ continue
+
+ # ignore pairs that can't have any links
+ if not self._dg_pair_has_possible_ent_links(dgu, dgv, is_outpred_data_predicate):
+ continue
+
+ if dgu.is_cell:
+ # the source is cell node
+ # property doesn't exist in any qnode
+ dgu_qnodes = [self.qnodes[qnode_id] for qnode_id in dgu.qnode_ids]
+ if all(inpred not in qnode.props for qnode in dgu_qnodes):
+ continue
+
+ if is_outpred_qualifier:
+ # qualifier doesn't exist in any qnode
+ has_qual = False
+ for qnode in dgu_qnodes:
+ for stmt in qnode.props.get(inpred, []):
+ if outpred in stmt.qualifiers:
+ has_qual = True
+ if not has_qual:
+ continue
+
+ # #########################
+ # TODO: remove me, modify on Apr 29, should have a better solution
+ # this is to say like if that properties have multiple values, then it's more likely to miss
+ if all(len(qnode.props.get(inpred, [])) > 2 for qnode in dgu_qnodes):
+ continue
+ # #########################
+ else:
+ # the source is entity
+ assert dgu.is_entity_value
+ dgu_qnode = self.qnodes[dgu.qnode_id]
+ if inpred not in dgu_qnode.props:
+ continue
+ if is_outpred_qualifier:
+ if all(outpred not in stmt.qualifiers for stmt in dgu_qnode.props[inpred]):
+ continue
+ # #########################
+ # TODO: remove me, modify on Apr 29, should have a better solution
+ # this is to say like if that properties have multiple values, then it's more likely to miss
+ if len(dgu_qnode.props.get(inpred, [])) > 2:
+ continue
+ # #########################
+ n_unmatch_links += 1
+ return n_unmatch_links
+
+ def _functional_dependency_test(self, source_column_index: int, target_column_index: int):
+ """Test whether values in the target column is uniquely determiend by the values in the source column. True if
+ it's FD.
+
+ Parameters
+ ----------
+ source_column_index
+ target_column_index
+
+ Returns
+ -------
+ """
+ sci = source_column_index
+ tci = target_column_index
+
+ # find a mapping from
+ source_map = self._get_value_map(source_column_index)
+ target_map = {
+ ri: key
+ for key, rows in self._get_value_map(target_column_index).items()
+ for ri in rows
+ }
+
+ n_violate_fd = 0
+ for key, rows in source_map.items():
+ target_keys = {target_map[ri] for ri in rows}
+ if len(target_keys) > 1:
+ n_violate_fd += 1
+
+ if len(source_map) == 0:
+ return True
+
+ if n_violate_fd / len(source_map) > 0.01:
+ return False
+ return True
+
+ def _estimate_col_quantity_type(self, column_index: int) -> Optional[QuantityType]:
+ """These function should be replaced by a ML method"""
+ col = self.table.table.get_column_by_index(column_index)
+ n_int, n_float = 0, 0
+ n_values = 0
+ for val in col.values:
+ nval = self.text_parser.parse(val)
+ if nval.number is not None:
+ if isinstance(nval.number, int):
+ n_int += 1
+ else:
+ n_float += 1
+ if nval.normed_string.lower() not in {"", "na", "n/a"}:
+ n_values += 1
+ # more than 80% is considered to be the numeric column
+ if n_values > 0 and (n_int + n_float) / n_values > 0.95:
+ if n_int / (n_int + n_float) > 0.95:
+ return QuantityType.Integer
+ return QuantityType.Float
+ return None
+
+ @functools.lru_cache(maxsize=None)
+ def _get_value_map(self, column_index: int):
+ """Get a map of values in a column to its row numbers (possible duplication).
+ This function is not perfect now. The value of the column is considered to be list of entities (if exist) or
+ just the value of the cell
+ """
+ map = defaultdict(list)
+ col = self.table.table.get_column_by_index(column_index)
+ for ri in range(self.table.size()):
+ links = self.table.links[ri][column_index]
+ ents = [link.qnode_id for link in links if link.qnode_id is not None]
+ if len(ents) > 0:
+ key = tuple(ents)
+ else:
+ key = col.values[ri].strip()
+ map[key].append(ri)
+ return dict(map)
+
diff --git a/grams/algorithm/literal_match.py b/grams/algorithm/literal_match.py
new file mode 100644
index 0000000..e83f116
--- /dev/null
+++ b/grams/algorithm/literal_match.py
@@ -0,0 +1,234 @@
+import re
+import rltk
+from dataclasses import dataclass
+from datetime import datetime, MINYEAR
+from enum import Enum
+from typing import Tuple, Optional
+
+import fastnumbers
+import ftfy
+from dateutil.parser import parse as dt_parse, ParserError
+
+from kgdata.wikidata.models import DataValue, QNode
+
+
+class WikidataValueType(Enum):
+ string = "string"
+ time = "time"
+ quantity = "quantity"
+ mono_lingual_text = "monolingualtext"
+ globe_coordinate = "globecoordinate"
+ entity_id = "wikibase-entityid"
+
+
+@dataclass
+class DatetimeParsedRepr:
+ year: Optional[int] = None
+ month: Optional[int] = None
+ day: Optional[int] = None
+ hour: Optional[int] = None
+ minute: Optional[int] = None
+ second: Optional[int] = None
+
+ def has_only_year(self):
+ return self.year is not None and \
+ self.month is None and \
+ self.day is None and \
+ self.hour is None and \
+ self.minute is None and \
+ self.second is None
+
+ def first_day_of_year(self):
+ return self.year is not None and \
+ self.month == 1 and \
+ self.day == 1 and \
+ self.hour is None and \
+ self.minute is None and \
+ self.second is None
+
+
+@dataclass
+class ParsedTextRepr:
+ origin: str
+ normed_string: str
+ number_string: str
+ number: Optional[float]
+ datetime: Optional[DatetimeParsedRepr]
+
+
+class TextParser:
+ def __init__(self):
+ self.cache = {}
+ self.default_dt = datetime(MINYEAR, 1, 1)
+ self.default_dt2 = datetime(MINYEAR+3, 2, 28)
+ self.number_chars = re.compile("[^0-9\.+-]")
+
+ def parse(self, text: str) -> ParsedTextRepr:
+ if text not in self.cache:
+ self.cache[text] = self._parse(text)
+ return self.cache[text]
+
+ def _parse(self, text: str):
+ try:
+ dt = dt_parse(text, default=self.default_dt)
+ year = dt.year
+ month = dt.month
+ day = dt.day
+
+ if dt.year == self.default_dt.year or dt.month == self.default_dt.month or dt.day == self.default_dt.day:
+ dt2 = dt_parse(text, default=self.default_dt2)
+
+ if dt.year == self.default_dt.year and dt2.year == self.default_dt2.year:
+ year = None
+ if dt.month == self.default_dt.month and dt2.month == self.default_dt2.month:
+ month = None
+ if dt.day == self.default_dt.day and dt2.day == self.default_dt2.day:
+ day = None
+
+ dt = DatetimeParsedRepr(
+ year=year,
+ month=month,
+ day=day
+ )
+ except (ParserError, TypeError, OverflowError):
+ dt = None
+
+ number_string = self._parse_number_string(text)
+ normed_string = self._norm_string(text)
+ if fastnumbers.isfloat(number_string) and (len(number_string) / min(1, len(normed_string))) > 0.95:
+ number = fastnumbers.fast_real(number_string, coerce=False)
+ else:
+ number = None
+ return ParsedTextRepr(
+ origin=text,
+ normed_string=normed_string,
+ number_string=number_string,
+ number=number,
+ datetime=dt
+ )
+
+ def _parse_number_string(self, text: str):
+ num_string = self.number_chars.sub("", text)
+ return num_string
+
+ def _norm_string(self, text: str):
+ return ftfy.fix_text(text).replace("\xa0", " ").strip()
+
+
+class LiteralMatcher:
+ literal_types = {
+ WikidataValueType.string.value, WikidataValueType.time.value,
+ WikidataValueType.quantity.value, WikidataValueType.mono_lingual_text.value,
+ WikidataValueType.globe_coordinate.value}
+ non_literal_types = {WikidataValueType.entity_id.value}
+
+ @classmethod
+ def string_test_exact(cls, p_val: DataValue, val: ParsedTextRepr) -> Tuple[bool, float]:
+ if val.normed_string == p_val.value:
+ return True, 1.0
+ return False, 0.0
+
+ @classmethod
+ def string_test_fuzzy(cls, p_val: DataValue, val: ParsedTextRepr) -> Tuple[bool, float]:
+ return cls.match_string(val.normed_string, p_val.value)
+
+ @classmethod
+ def globe_coordinate_test(cls, p_val: DataValue, val: ParsedTextRepr) -> Tuple[bool, float]:
+ return False, 0.0
+
+ @classmethod
+ def time_test(cls, p_val: DataValue, val: ParsedTextRepr) -> Tuple[bool, float]:
+ if val.datetime is None:
+ return False, 0.0
+ timestr = p_val.value['time']
+ # there are two calendar:
+ # gregorian calendar, and julian calendar (just 13 days behind gregorian)
+ # https://www.timeanddate.com/calendar/julian-gregorian-switch.html#:~:text=13%20Days%20Behind%20Today,days%20behind%20the%20Gregorian%20calendar.
+ # TODO: we need to consider a range for julian calendar
+ assert p_val.value['calendarmodel'] in {'http://www.wikidata.org/entity/Q1985786',
+ 'http://www.wikidata.org/entity/Q1985727'}, p_val.value['calendarmodel']
+ # TODO: handle timezone, before/after and precision
+ # pass
+
+ # parse timestring
+ match = re.match(r"([-+]\d+)-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z", timestr)
+
+ target_val = DatetimeParsedRepr(
+ year=int(match.group(1)),
+ month=int(match.group(2)),
+ day=int(match.group(3)),
+ hour=int(match.group(4)),
+ minute=int(match.group(5)),
+ second=int(match.group(6)),
+ )
+ if timestr[0] == '-':
+ target_val.year = -target_val.year
+ else:
+ assert timestr[0] == '+'
+ for p in ['year', 'month', 'day', 'hour', 'minute', 'second']:
+ if getattr(target_val, p) == 0:
+ setattr(target_val, p, None)
+
+ if target_val == val.datetime:
+ return True, 1.0
+ if val.datetime.has_only_year() and target_val.year == val.datetime.year:
+ return True, 0.8
+ if val.datetime.first_day_of_year() and target_val.year == val.datetime.year:
+ return True, 0.75
+ return False, 0.0
+
+ @classmethod
+ def quantity_test(cls, p_val: DataValue, val: ParsedTextRepr) -> Tuple[bool, float]:
+ if val.number is None:
+ return False, 0.0
+
+ target_val = float(p_val.value["amount"])
+ if abs(target_val - val.number) < 1e-5:
+ return True, 1.0
+ if target_val == 0:
+ diff_percentage = abs(target_val - val.number) / 1e-7
+ else:
+ diff_percentage = abs((target_val - val.number) / target_val)
+
+ if diff_percentage < 0.05:
+ # within 5%
+ return True, 0.95 - diff_percentage
+ # if diff_percentage < 0.1:
+ # return True, 0.9 - diff_percentage
+ return False, 0.0
+
+ @classmethod
+ def mono_lingual_text_test_exact(cls, p_val: DataValue, val: ParsedTextRepr) -> Tuple[bool, float]:
+ target_val = p_val.value['text']
+ if val.normed_string == target_val:
+ return True, 1.0
+ return False, 0.0
+
+ @classmethod
+ def mono_lingual_text_test_fuzzy(cls, p_val: DataValue, val: ParsedTextRepr) -> Tuple[bool, float]:
+ target_val = p_val.value['text']
+ return cls.match_string(val.normed_string, target_val)
+
+ @classmethod
+ def entity_id_test_fuzzy(cls, qnode: QNode, val: ParsedTextRepr) -> Tuple[bool, float]:
+ string = val.normed_string
+ lst = [x.strip() for x in string.split(",")]
+ for label in [qnode.label] + qnode.aliases:
+ for item in lst:
+ result = cls.match_string(label, item)
+ if result[0] is True:
+ return result
+ return False, 0.0
+
+ @classmethod
+ def match_string(cls, s1: str, s2: str):
+ if s1 == s2:
+ return True, 1.0
+ # calculate edit distance
+ distance = rltk.levenshtein_distance(s1, s2)
+ if distance <= 1:
+ return True, 0.95
+ elif distance > 1 and distance / max(1, min(len(s1), len(s2))) < 0.03:
+ return True, 0.85
+ return False, 0.0
+
diff --git a/grams/algorithm/psl_solver.py b/grams/algorithm/psl_solver.py
new file mode 100644
index 0000000..56a36c5
--- /dev/null
+++ b/grams/algorithm/psl_solver.py
@@ -0,0 +1,872 @@
+import copy
+import math
+import os
+from pathlib import Path
+import sm.misc as M
+from grams.algorithm.semtab2020 import SemTab2020PostProcessing
+from typing import Optional, Dict, List, Set, Tuple
+import networkx as nx
+
+from grams.inputs.linked_table import LinkedTable
+from grams.algorithm.type_feature import TypeFeatureExtraction
+import time
+from functools import cmp_to_key
+
+from grams.algorithm.link_feature import LinkFeatureExtraction
+from grams.algorithm.bank_solver import SteinerTreeBankSolver, Solution, \
+ NoSingleRootException
+import uuid
+from itertools import chain
+from multiprocessing import Pool
+from operator import itemgetter, attrgetter, xor
+
+from loguru import logger
+from pslpython.model import Model
+from pslpython.predicate import Predicate
+from pslpython.partition import Partition
+from pslpython.rule import Rule
+from typing import Set, Iterable, TypedDict, Union, Any, Callable
+
+from grams.algorithm.data_graph import DGNode
+from grams.algorithm.semantic_graph import SGNode, SGStatementNode, SGColumnNode, \
+ SemanticGraphConstructorArgs, SGEdge, SemanticGraphConstructor
+from kgdata.wikidata.models import QNode, WDProperty, WDClass, WDQuantityPropertyStats
+from tqdm.auto import tqdm
+
+
+global_objects = {}
+
+
+class IDMap:
+ def __init__(self, counter: int = 0):
+ self.counter = counter
+ self.map = {}
+ self.invert_map = {}
+
+ def add_keys(self, keys: Iterable[Any]):
+ for key in keys:
+ new_key = f"i-{self.counter}"
+ self.map[key] = new_key
+ self.invert_map[new_key] = key
+ self.counter += 1
+ return self
+
+ def m(self, key):
+ """Get a new key from old key"""
+ return self.map[key]
+
+ def im(self, new_key):
+ """Get the old key from the new key"""
+ return self.invert_map[new_key]
+
+
+class FakeIDMap(IDMap):
+ def m(self, key):
+ return key
+ def im(self, new_key):
+ return new_key
+
+
+PSLRunParallelArgs = TypedDict("PSL.RunParallelArgs", table=LinkedTable, datagraph=nx.MultiDiGraph, semanticgraph=nx.MultiDiGraph)
+
+
+class PSLSteinerTreeSolver:
+ LinkNegPrior = "NegPrior"
+ LinkNegParentPropPrior = "NegParentPropPrior"
+ CascadingError = "CascadingError"
+ FreqLinkOverRow = LinkFeatureExtraction.FreqOverRow
+ FreqLinkOverEntRow = LinkFeatureExtraction.FreqOverEntRow
+ FreqLinkOverPosLink = LinkFeatureExtraction.FreqOverPossibleLink
+ FreqLinkUnmatchOverEntRow = LinkFeatureExtraction.FreqUnmatchOverEntRow
+ FreqLinkUnmatchOverPossibleLink = LinkFeatureExtraction.FreqUnmatchOverPossibleLink
+ LinkDataTypeMismatch = LinkFeatureExtraction.DataTypeMismatch
+ LinkHeaderSimilarity = LinkFeatureExtraction.HeaderSimilarity
+ LinkNotFuncDep = LinkFeatureExtraction.NotFuncDep
+
+ TypeNegPrior = "TypeNegPrior"
+ FreqTypeOverRow = TypeFeatureExtraction.FreqOverRow
+ TypeMustInPropRange = "TypeMustInPropRange"
+ TypeOnlyOneConstraint = "TypeOnlyOneConstraint"
+
+ def __init__(self,
+ qnodes: Dict[str, QNode],
+ wdclasses: Dict[str, WDClass],
+ wdprops: Dict[str, WDProperty],
+ wd_numprop_stats: Dict[str, WDQuantityPropertyStats],
+ disable_rules: Set[str] = None,
+ sim_fn: Optional[Callable[[str, str], float]]=None,
+ cache_dir: Optional[str] = None,
+ postprocessing_method: str = None,
+ enable_logging: bool = False):
+ self.wdclasses = wdclasses
+ self.wdprops = wdprops
+ self.wd_numprop_stats = wd_numprop_stats
+ self.qnodes = qnodes
+ self.temp_dir = f"/tmp/psl-python-{str(uuid.uuid4()).replace('-', '')}"
+ self.sim_fn = sim_fn
+ # use this cache dir to catch the extraction result
+ self.cache_dir = Path(cache_dir) if cache_dir is not None else cache_dir
+ self.postprocessing_method = postprocessing_method
+ assert self.postprocessing_method in {'select_simplepath', 'steiner_tree', 'external:semtab2020'}, self.postprocessing_method
+ # if self.postprocessing_method == 'external:semtab2020':
+ # self.postprocessing_fn = SemTab2020PostProcessing()
+
+ # blacklist some rules if they do not apply to particular domain (synthetic dataset)
+ all_rules = {
+ self.LinkNegPrior,
+ self.TypeNegPrior,
+ self.LinkNegParentPropPrior,
+ self.FreqLinkOverRow,
+ self.FreqLinkOverEntRow,
+ self.FreqLinkOverPosLink,
+ self.FreqLinkUnmatchOverEntRow,
+ self.FreqLinkUnmatchOverPossibleLink,
+ self.LinkHeaderSimilarity,
+ self.LinkDataTypeMismatch,
+ self.FreqTypeOverRow,
+ self.LinkNotFuncDep,
+ }
+ self.disable_rules = set(disable_rules) if disable_rules is not None else set()
+ assert all(r in all_rules for r in self.disable_rules)
+
+ self.enable_logging = enable_logging
+
+ # load the model
+ self.get_model()
+
+ def get_model(self):
+ """Construct the PSL model"""
+ model = Model("st-solver")
+
+ # ##################################################################
+ # add predicates
+ self.link_pos_feats = [
+ r for r in [self.FreqLinkOverRow, self.FreqLinkOverEntRow, self.FreqLinkOverPosLink, self.LinkHeaderSimilarity]
+ if r not in self.disable_rules
+ ]
+ self.link_neg_feats = [
+ r for r in [self.FreqLinkUnmatchOverEntRow, self.FreqLinkUnmatchOverPossibleLink, self.LinkDataTypeMismatch]
+ if r not in self.disable_rules
+ ]
+ self.link_structure_feats = [
+ r for r in [self.LinkNotFuncDep]
+ if r not in self.disable_rules
+ ]
+ self.link_all_feats = self.link_pos_feats + self.link_neg_feats + self.link_structure_feats
+ self.type_pos_feats = [
+ r for r in [self.FreqTypeOverRow]
+ if r not in self.disable_rules
+ ]
+ self.type_all_feats = self.type_pos_feats
+
+ model.add_predicate(Predicate("CanRel", closed=True, size=3))
+ model.add_predicate(Predicate("CanType", closed=True, size=2))
+ model.add_predicate(Predicate("Rel", closed=False, size=3))
+ model.add_predicate(Predicate("Type", closed=False, size=2))
+ model.add_predicate(Predicate("SubProp", closed=True, size=2))
+ model.add_predicate(Predicate("NotRange", closed=True, size=2))
+ model.add_predicate(Predicate("NotStatement", closed=True, size=1))
+ model.add_predicate(Predicate("Statement", closed=True, size=1))
+
+ for feat in self.link_all_feats:
+ model.add_predicate(Predicate(f"RelFeature_{feat}", closed=True, size=3))
+ for feat in self.type_all_feats:
+ model.add_predicate(Predicate(f"TypeFeature_{feat}", closed=True, size=2))
+
+ # ##################################################################
+ # add rules
+ feat_weights = {
+ self.LinkNegPrior: 2,
+ self.TypeNegPrior: 2,
+ self.LinkNegParentPropPrior: 0.1,
+ self.CascadingError: 2,
+ self.TypeMustInPropRange: 2,
+ self.FreqLinkOverRow: 3,
+ self.FreqLinkOverEntRow: 4,
+ self.FreqLinkOverPosLink: 5,
+ self.FreqLinkUnmatchOverEntRow: 4,
+ self.FreqLinkUnmatchOverPossibleLink: 5,
+ self.LinkHeaderSimilarity: 2,
+ self.LinkDataTypeMismatch: 100,
+ self.FreqTypeOverRow: 2,
+ self.LinkNotFuncDep: 100
+ }
+ self.rules = {}
+
+ # rules apply for only link
+ for feat in self.link_pos_feats:
+ self.rules[feat] = Rule(
+ f"CanRel(N1, N2, P) & RelFeature_{feat}(N1, N2, P) -> Rel(N1, N2, P)",
+ weighted=True, squared=True, weight=feat_weights[feat])
+ for feat in self.link_neg_feats:
+ self.rules[feat] = Rule(
+ f"CanRel(N1, N2, P) & RelFeature_{feat}(N1, N2, P) -> ~Rel(N1, N2, P)",
+ weighted=True, weight=feat_weights[feat], squared=True)
+
+ self.rules[self.LinkNotFuncDep] = Rule(
+ f"CanRel(N1, N2, P) & CanRel(N2, N3, P2) & NotStatement(N2) & Rel(N1, N2, P) & RelFeature_{self.LinkNotFuncDep}(N2, N3, P2) -> ~Rel(N2, N3, P2)",
+ weighted=True, weight=feat_weights[self.LinkNotFuncDep], squared=True)
+
+ # give a small negative weight to the parent property if there is a more specific child property
+ # some how use CanRel is a much better option than Rel, perhaps this reflect how the parameters is set
+ self.rules[self.LinkNegParentPropPrior] = [
+ Rule(
+ f"CanRel(N1, S, P) & Statement(S) & CanRel(S, N2, P) & CanRel(N1, S2, PP) & Statement(S2) & CanRel(S2, N2, PP) & SubProp(P, PP) -> ~Rel(N1, S2, PP)",
+ weighted=True, weight=feat_weights[self.LinkNegParentPropPrior], squared=True),
+ Rule(
+ f"CanRel(N1, S, P) & Statement(S) & CanRel(S, N2, P) & CanRel(N1, S2, PP) & Statement(S2) & CanRel(S2, N2, PP) & SubProp(P, PP) -> ~Rel(S2, N2, PP)",
+ weighted=True, weight=feat_weights[self.LinkNegParentPropPrior], squared=True),
+ ]
+
+ # default negative prior
+ self.rules[self.LinkNegPrior] = Rule("~Rel(N1, N2, P)", weighted=True, weight=feat_weights[self.LinkNegPrior], squared=True)
+ self.rules[self.TypeNegPrior] = Rule("~Type(N, P)", weighted=True, weight=feat_weights[self.TypeNegPrior], squared=True)
+ self.rules[self.CascadingError] = [
+ Rule(
+ f"CanRel(N0, S, P) & Statement(S) & CanRel(S, N1, P) & CanRel(S, N2, Q) & N1 != N2 & ~Rel(S, N1, P) -> ~Rel(S, N2, Q)",
+ weighted = True, weight = feat_weights[self.CascadingError], squared=True),
+ Rule(
+ f"CanRel(N0, S, P) & Statement(S) & CanRel(S, N1, P) & ~Rel(S, N1, P) -> ~Rel(N0, S, P)",
+ weighted=True, weight=feat_weights[self.CascadingError], squared=True),
+ Rule(
+ f"CanRel(N0, S, P) & Statement(S) & CanRel(S, N1, P) & ~Rel(N0, S, P) -> ~Rel(S, N1, P)",
+ weighted=True, weight=feat_weights[self.CascadingError], squared=True),
+ ]
+
+ # self.rules[self.TypeMustInPropRange] = Rule(
+ # "CanRel(S, N1, P) & Statement(S) & CanType(N1, T) & NotRange(P, T) & Rel(S, N1, P) -> ~Type(N1, T)",
+ # weighted=True, weight=feat_weights[self.TypeMustInPropRange], squared=True)
+ self.rules[self.FreqTypeOverRow] = Rule(f"CanType(N, T) & TypeFeature_{self.FreqTypeOverRow}(N, T) -> Type(N, T)",
+ weighted=True, weight=feat_weights[self.FreqTypeOverRow], squared=True)
+
+ for rule_id, rules in self.rules.items():
+ if rule_id in self.disable_rules:
+ continue
+ if isinstance(rules, list):
+ for rule in rules:
+ model.add_rule(rule)
+ else:
+ model.add_rule(rules)
+
+ # set the model and done
+ self.model = model
+
+ def infer(self, data: Dict[str, list]):
+ """Run inference and get back the result.
+ Note: Check the model to see the list of predicate we need to pass the data to.
+ """
+ if len(self.model.get_predicates()) != len(data) + 2:
+ preds = {x.upper() for x in data.keys()}
+ miss_preds = [p for p in self.model.get_predicates() if p not in preds and p not in {'REL', 'TYPE'}]
+ raise Exception(f"Data in all predicates must be set. Missing {','.join(miss_preds)} predicates")
+ # TODO: fix me! temporary allow cantype to be empty
+ can_be_empty_predicates = {"SubProp", f"RelFeature_{self.LinkDataTypeMismatch}", "NotRange", "CanType", f"TypeFeature_{self.FreqTypeOverRow}"}
+ RelPredicate = self.model.get_predicate("Rel").clear_data().add_data(Partition.TARGETS, data['CanRel'])
+ if len(data['CanType']) == 0:
+ TypePredicate = self.model.get_predicate("Type").clear_data()
+ else:
+ TypePredicate = self.model.get_predicate("Type").clear_data().add_data(Partition.TARGETS, data['CanType'])
+ for pred, pred_data in data.items():
+ if pred in can_be_empty_predicates and len(pred_data) == 0:
+ self.model.get_predicate(pred).clear_data()
+ else:
+ self.model.get_predicate(pred).clear_data().add_data(Partition.OBSERVATIONS, pred_data)
+
+ infer_resp = self.model.infer(logger=None if self.enable_logging else False,
+ additional_cli_optons=[
+ "--h2path",
+ os.path.join(self.temp_dir, "h2")
+ ],
+ temp_dir=str(self.temp_dir))#, cleanup_temp=False)
+ return {
+ "links": {
+ (r[0], r[1], r[2]): r['truth']
+ for ri, r in infer_resp[RelPredicate].iterrows()
+ },
+ "types": {
+ (r[0], r[1]): r['truth']
+ for ri, r in infer_resp[TypePredicate].iterrows()
+ }
+ }
+
+ def run(self, r: PSLRunParallelArgs, threshold: float=0.5):
+ table, sg, dg = r['table'], r['semanticgraph'], r['datagraph']
+ pred_with_probs = self.solve(table, sg, dg)
+ sg = self.solve_post_process(table, sg, dg, pred_with_probs['links'], threshold)
+ cta = pred_with_probs['types']
+ return sg, cta
+
+ def run_with_parallel(self, inputs: List[PSLRunParallelArgs], threshold: float=0.5, batch_size: Optional[int]=None, show_progress: bool=False):
+ global global_objects
+ predictions = self.solve_parallel(
+ [(r['table'], r['semanticgraph'], r['datagraph']) for r in inputs],
+ batch_size=batch_size, show_progress=show_progress)
+
+ global_objects['PSLSteinerTreeSolver'] = self
+ # DO this cause we don't know why semtab postprocessing create that issue...
+ is_parallel = self.postprocessing_method != 'external:semtab2020'
+ sgs = M.parallel_map(PSLSteinerTreeSolver._solve_post_process_wrapper, [
+ (r['table'], r['semanticgraph'], r['datagraph'], predwprobs['links'], threshold)
+ for r, predwprobs in zip(inputs, predictions)
+ ], show_progress=show_progress, progress_desc='psl: post-process', is_parallel=is_parallel)
+ cta = [predwprobs['types'] for r, predwprobs in zip(inputs, predictions)]
+ return sgs, cta
+
+ def solve_parallel(self, inputs: List[Tuple[LinkedTable, nx.MultiDiGraph, nx.MultiDiGraph]], batch_size: Optional[int]=None, show_progress: bool=False):
+ global global_objects
+ global_objects['cache_dir'] = self.cache_dir
+ global_objects['wdprops'] = self.wdprops
+ global_objects['qnodes'] = self.qnodes
+ global_objects['wd_numprop_stats'] = self.wd_numprop_stats
+ global_objects['sim_fn'] = self.sim_fn
+
+ # create a mapping from id to index so that we can re-assign the result later.
+ results = M.parallel_map(PSLSteinerTreeSolver._extract_features_wrapper, list(enumerate(inputs)), show_progress=show_progress,
+ progress_desc='psl: extract features')
+
+ if batch_size is None:
+ batch_size = len(inputs)
+ iter = range(1)
+ else:
+ iter = range(0, len(inputs), batch_size)
+ if show_progress:
+ iter = tqdm(iter, total=math.ceil(len(inputs) / batch_size), desc='psl: inference')
+
+ if self.cache_dir is not None:
+ (self.cache_dir / "inferences").mkdir(exist_ok=True, parents=True)
+ get_cache_file = lambda tbl, index: (self.cache_dir / f"inferences/a{index:03d}_{tbl.get_friendly_fs_id()}.pkl")
+ else:
+ get_cache_file = lambda tbl, index: None
+
+ predictions = []
+ for i in iter:
+ input_batch = inputs[i:i+batch_size]
+ feature_batch = results[i:i+batch_size]
+ cache_files = [
+ get_cache_file(table, j)
+ for j, (table, sg, dg) in enumerate(inputs[i:i+batch_size], start=i)
+ ]
+ if not all(x is not None and x.exists() for x in cache_files):
+ data, idmap = self.extract_predicate_data(input_batch, feature_batch, is_parallel=True)
+ try:
+ infer_resp = self.infer(data)
+ except:
+ logger.exception("Error while processing batch: {}", i)
+ raise
+ table_infer_link_resp = {}
+ table_infer_type_resp = {}
+ for (uid, vid, eid), prob in infer_resp['links'].items():
+ table_id, uid = idmap.im(uid)
+ assert table_id == idmap.im(vid)[0]
+ vid = idmap.im(vid)[1]
+
+ if table_id not in table_infer_link_resp:
+ table_infer_link_resp[table_id] = {}
+ table_infer_link_resp[table_id][uid, vid, eid] = prob
+ for (uid, classid), prob in infer_resp['types'].items():
+ table_id, uid = idmap.im(uid)
+ if table_id not in table_infer_type_resp:
+ table_infer_type_resp[table_id] = {}
+ if uid not in table_infer_type_resp[table_id]:
+ table_infer_type_resp[table_id][uid] = {}
+ table_infer_type_resp[table_id][uid][classid] = prob
+
+ batch_predictions = []
+ for (table, sg, dg), cache_file in zip(input_batch, cache_files):
+ pred = {
+ "id": table.id,
+ "links": table_infer_link_resp.get(table.id, {}),
+ "types": table_infer_type_resp.get(table.id, {}),
+ }
+ if cache_file is not None:
+ M.serialize_pkl(pred, cache_file)
+ batch_predictions.append(pred)
+ else:
+ batch_predictions = [M.deserialize_pkl(cache_file) for cache_file in cache_files]
+
+ # set the prob. for debugging purpose
+ for (table, sg, dg), pred in zip(input_batch, batch_predictions):
+ assert table.id == pred['id']
+ for k, v in pred['links'].items():
+ sg.edges[k]['data'].features['prob'] = v
+ predictions += batch_predictions
+
+ return predictions
+
+ def solve(self, table, sg, dg):
+ if len(sg.edges) == 0:
+ return {}
+
+ data, idmap = self.extract_predicate_data([(table, sg, dg)], is_parallel=False, show_progress=False)
+
+ def unpack_mappedid(key):
+ table_id, real_key = idmap.im(key)
+ assert table_id == table.id
+ return real_key
+
+ infer_resp = self.infer(data)
+ infer_resp_types = {}
+ for (uid, classid), prob in infer_resp['types'].items():
+ _ouid = unpack_mappedid(uid)
+ if _ouid not in infer_resp_types:
+ infer_resp_types[_ouid] = {}
+ infer_resp_types[_ouid][classid] = prob
+
+ infer_resp['links'] = {
+ (unpack_mappedid(uid), unpack_mappedid(vid), eid): prob
+ for (uid, vid, eid), prob in infer_resp['links'].items()
+ }
+ infer_resp['types'] = infer_resp_types
+
+ # set the prob. for debugging purpose
+ link_feat_extractor = LinkFeatureExtraction(table, sg, dg, self.qnodes, self.wdprops, self.wd_numprop_stats, self.sim_fn)
+ link_feat_extractor.add_debug_info(link_feat_extractor.extract_features())
+ for k, prob in infer_resp['links'].items():
+ sg.edges[k]['data'].features['prob'] = prob
+
+ return infer_resp
+
+ def solve_post_process(self, table, sg, dg, pred_with_probs: Dict[Tuple[str, str, str], float], threshold):
+ if self.postprocessing_method == 'select_simplepath':
+ return self.postprocessing_select_simplepath(table, sg, dg, pred_with_probs, threshold)
+ if self.postprocessing_method == 'steiner_tree':
+ return self.postprocessing_steiner_tree(table, sg, dg, pred_with_probs, threshold)
+ if self.postprocessing_method == 'external:semtab2020':
+ # return self.postprocessing_fn.solve_post_process(table, sg, dg, pred_with_probs, threshold)
+ return SemTab2020PostProcessing.get_instance().solve_post_process(table, sg, dg, pred_with_probs, threshold)
+ assert False, self.postprocessing_method
+
+ def remove_dangling_statement(self, sg: nx.MultiDiGraph):
+ ids = set()
+ for sid, s in list(sg.nodes(data='data')):
+ if s.is_statement and (sg.in_degree(sid) == 0 or sg.out_degree(sid) == 0):
+ ids.add(sid)
+ for id in ids:
+ sg.remove_node(id)
+
+ def postprocessing_select_simplepath(self, table, sg, dg, pred_with_probs: Dict[Tuple[str, str, str], float], threshold):
+ def select_shorter_path(paths: List[List[Tuple[str, str, str]]]):
+ """Prefer shorter path. When there are multiple shorter path, select the one with higher prob"""
+ paths = sorted(paths, key=len)
+ paths = [path for path in paths if len(path) == len(paths[0])]
+ if len(paths) == 1:
+ return paths[0]
+
+ # multiple shorter paths
+ path_probs = []
+ for path in paths:
+ path_prob = 0
+ for uid, vid, eid in path:
+ edge: SGEdge = sg.edges[uid, vid, eid]['data']
+ path_prob += pred_with_probs[uid, vid, eid]
+ path_probs.append(path_prob)
+ path, path_prob = max(zip(paths, path_probs), key=itemgetter(1))
+ return path
+
+ pred_edges = [k for k, v in pred_with_probs.items() if v >= threshold]
+ steiner_tree = SemanticGraphConstructor.get_sg_subgraph(sg, pred_edges)
+ self.remove_dangling_statement(steiner_tree)
+
+ if len(steiner_tree.edges) == 0:
+ # empty graph
+ return steiner_tree
+
+ return SemanticGraphConstructor.keep_one_simple_path_between_important_nodes(
+ steiner_tree, select_shorter_path,
+ both_direction=True)
+
+ def postprocessing_steiner_tree(self, table, sg, dg, pred_with_probs: Dict[Tuple[str, str, str], float], threshold):
+ pred_edges = [k for k, v in pred_with_probs.items() if v >= threshold]
+ steiner_tree = SemanticGraphConstructor.get_sg_subgraph(sg, pred_edges)
+ self.remove_dangling_statement(steiner_tree)
+
+ if len(steiner_tree.edges) == 0:
+ # empty graph
+ return steiner_tree
+
+ # normalizing the prob score so that we can compare the weight between graph accurately
+ norm_pred_probs = {}
+ lst = sorted((x for x in pred_with_probs.items() if x[1] >= threshold), key=itemgetter(1))
+ eps = 0.001
+ clusters = []
+ pivot = 1
+ clusters = [[lst[0]]]
+ while pivot < len(lst):
+ x = lst[pivot-1][1]
+ y = lst[pivot][1]
+ if (y - x) <= eps:
+ # same clusters
+ clusters[-1].append(lst[pivot])
+ else:
+ # different clusters
+ clusters.append([lst[pivot]])
+ pivot += 1
+ for cluster in clusters:
+ avg_prob = sum([x[1] for x in cluster]) / len(cluster)
+ for k, prob in cluster:
+ norm_pred_probs[k] = avg_prob
+
+ # print(lst)
+ # print(norm_pred_probs)
+
+ def get_solution_weight(sol: Solution):
+ if sol.get_n_edges() == 0:
+ return 0.0
+ return sol.weight / sol.get_n_edges()
+
+ def cmp_bank_solution(sol_a: Solution, sol_b: Solution):
+ # since PSL inference is not exact but optimization, there is always a delta different in their final
+ # calculation, which we need to compensate for.
+ # Empirically, the number is very small ~0.001 in a range of [0 - 1] for each edge
+ # we need to count the number of edge, but have to do it this way since bank solver
+ # do some optimization to reduce the number of nodes & edges
+ # diff = abs((sol_a.weight / sol_a.get_n_edges()) - (sol_b.weight / sol_b.get_n_edges()))
+ # if diff > 0.002:
+ sol_a_weight = get_solution_weight(sol_a)
+ sol_b_weight = get_solution_weight(sol_b)
+
+ if sol_a_weight < sol_b_weight:
+ return -1
+ if sol_a_weight > sol_b_weight:
+ return 1
+
+ if not hasattr(sol_a, "depth"):
+ sol_a.depth = len(nx.algorithms.dag.dag_longest_path(sol_a.graph))
+ if not hasattr(sol_b, "depth"):
+ sol_b.depth = len(nx.algorithms.dag.dag_longest_path(sol_b.graph))
+ return sol_a.depth - sol_b.depth
+
+ terminal_nodes = SemanticGraphConstructor.st_terminal_nodes(steiner_tree)
+ bank_solver = SteinerTreeBankSolver(
+ steiner_tree, terminal_nodes, top_k_st=50, top_k_path=50,
+ weight_fn=lambda e: 1.0 / max(1e-7, norm_pred_probs[e['data'].source_id, e['data'].target_id, e['data'].predicate]),
+ solution_cmp_fn=cmp_to_key(cmp_bank_solution)
+ )
+ try:
+ candidate_sts, solutions = bank_solver.run()
+ except NoSingleRootException:
+ # fallback
+ return self.postprocessing_select_simplepath(table,sg, dg, pred_with_probs, threshold)
+
+ # TODO: add back the context node or entity that are statements
+ pred_tree: nx.MultiDiGraph = candidate_sts[0]
+ remove_nodes = set()
+ for sid, s in pred_tree.nodes(data='data'):
+ if not s.is_statement:
+ continue
+ if pred_tree.in_degree(s.id) == 0:
+ remove_nodes.add(s.id)
+ for sid in remove_nodes:
+ pred_tree.remove_node(sid)
+ for sid, s in list(pred_tree.nodes(data='data')):
+ if not s.is_statement:
+ continue
+ assert pred_tree.in_degree(s.id) == 1
+ in_edge, = pred_tree.in_edges(s.id, keys=True)
+ inedge_predicate = in_edge[-1]
+ for _sid, _vid, e in steiner_tree.out_edges(s.id, data='data'):
+ if e.predicate == inedge_predicate:
+ # statement prop
+ if not pred_tree.has_node(e.target_id):
+ ent = steiner_tree.nodes[e.target_id]['data']
+ # assert ent.is_value and ent.is_entity_value, f"The only reason why we don't have statement value is it is entity: {ent}"
+ assert ent.is_value, f"The only reason why we don't have statement value is it is an literal"
+ pred_tree.add_node(ent.id, data=copy.deepcopy(ent))
+ pred_tree.add_edge(s.id, ent.id, key=e.predicate, data=copy.deepcopy(e))
+ else:
+ v = steiner_tree.nodes[e.target_id]['data']
+ if v.is_value and v.is_in_context:
+ # we should have this as PSL think it's correct
+ assert not pred_tree.has_node(v.id)
+ pred_tree.add_node(v.id, data=copy.deepcopy(v))
+ pred_tree.add_edge(s.id, v.id, key=e.predicate, data=copy.deepcopy(e))
+
+ # TODO: uncomment for debugging
+ # print(candidate_sts)
+ # if there are more than one candidate steiner tree of same weight, select the one with shorter height
+ # candidate_sts = [item for item in candidate_sts if item[1] == candidate_sts[0][1]]
+ # candidate_st = sorted(candidate_sts, key=lambda g: len(nx.algorithms.dag.dag_longest_path(g[0])))[0][0]
+
+ # env.viz_sg(steiner_tree, "after_PSL")
+ # for i, (g, sol) in enumerate(zip(candidate_sts, solutions)):
+ # # g = SemanticGraphConstructor.get_sg_subgraph(sg, list(g.edges(keys=True)))
+ # (precision, recall, f1), oracle_sg = env.eval_steiner_tree(table_index, g)
+ # print(f"\t i={i:02d} precision={precision:.4f} recall={recall:.4f} f1={f1:.4f} score={get_solution_weight(sol)} depth={len(nx.algorithms.dag.dag_longest_path(g))}")
+ # env.viz_sg(g, f"st_{i:02d}")
+ # # viz = lambda x, y: env.viz_sg(SemanticGraphConstructor.get_sg_subgraph(sg, list(x.edges(keys=True))), y)
+ # for i in range(len(candidate_sts)):
+ # env.viz_sg(candidate_sts[i][0], f"st_{i:02d}")
+
+ # bank_solver._get_graph_weight(candidate_sts[0][0])
+ return pred_tree
+
+ def train_setup(self, inputs: List[Tuple[LinkedTable, nx.MultiDiGraph, nx.MultiDiGraph, dict]]):
+ features = [x[3] for x in inputs]
+ inputs = [(x[0], x[1], x[2]) for x in inputs]
+ data, idmap = self.extract_predicate_data(inputs, features=features)
+
+ assert len(self.model.get_predicates()) == len(data) + 2, "Data in all predicates must be set"
+ can_be_empty_predicates = {"SubProp", f"RelFeature_{self.LinkDataTypeMismatch}"}
+ self.model.get_predicate("Rel").clear_data().add_data(Partition.TARGETS, data['CanRel'])
+ self.model.get_predicate("Type").clear_data().add_data(Partition.TARGETS, data['CanType'])
+ for pred, pred_data in data.items():
+ if pred in can_be_empty_predicates and len(pred_data) == 0:
+ self.model.get_predicate(pred).clear_data()
+ else:
+ self.model.get_predicate(pred).clear_data().add_data(Partition.OBSERVATIONS, pred_data)
+
+ # clean up the temporary directory first. this code is takening from the PSL model.infer method
+ if Path(self.temp_dir).exists():
+ self.model._cleanup_temp(str(self.temp_dir))
+ # this write out the data file and rules_file
+ logger, temp_dir, data_file_path, rules_file_path = self.model._prep_run(logger=None if self.enable_logging else False, temp_dir=str(self.temp_dir))
+ cli_options = []
+ cli_options.append('--infer')
+ inferred_dir = os.path.join(temp_dir, Model.CLI_INFERRED_OUTPUT_DIR)
+ cli_options.append('--output')
+ cli_options.append(inferred_dir)
+
+ self.train_args = dict(
+ data_file_path=data_file_path,
+ rules_file_path=rules_file_path,
+ cli_options=cli_options,
+ psl_config={}, jvm_options=[], logger=logger,
+ inferred_dir=inferred_dir, temp_dir=temp_dir)
+ return idmap
+
+ def train_set_parameters(self, rules: Dict[str, float]):
+ for rule_id, rule_weight in rules.items():
+ if isinstance(self.rules[rule_id], list):
+ for rule in self.rules[rule_id]:
+ rule.set_weight(rule_weight)
+ else:
+ self.rules[rule_id].set_weight(rule_weight)
+ self.model._write_rules(self.train_args['temp_dir'])
+
+ def train_eval(self, idmap: IDMap) -> Dict[str, Dict[Tuple[str, str, str], float]]:
+ table_infer_resp = {}
+ RelPredicate = self.model.get_predicate("Rel")
+
+ self.model._run_psl(
+ self.train_args['data_file_path'], self.train_args['rules_file_path'], self.train_args['cli_options'],
+ self.train_args['psl_config'],
+ self.train_args['jvm_options'],
+ self.train_args['logger'])
+ infer_resp = self.model._collect_inference_results(self.train_args['inferred_dir'])
+
+ for ri, r in infer_resp[RelPredicate].iterrows():
+ uid, vid, eid, prob = r[0], r[1], r[2], r['truth']
+ table_id, uid = idmap.im(uid)
+ assert table_id == idmap.im(vid)[0]
+ vid = idmap.im(vid)[1]
+
+ if table_id not in table_infer_resp:
+ table_infer_resp[table_id] = {}
+ table_infer_resp[table_id][uid, vid, eid] = prob
+ return table_infer_resp
+
+ def extract_predicate_data(self,
+ inputs: List[Tuple[LinkedTable, nx.MultiDiGraph, nx.MultiDiGraph]],
+ features: Optional[List[dict]]=None,
+ idmap=None,
+ is_parallel: Union[str, bool]='auto',
+ show_progress: bool=False):
+ """Extract predicates data from the list of tables, sgs, and dgs. If the features do not provided, it will
+ be created automatically.
+
+ If parallel is 'auto' we run parallel when length of inputs > 1
+ """
+ global global_objects
+ if idmap is None:
+ idmap = IDMap()
+
+ if features is None:
+ is_parallel = len(inputs) > 1 if is_parallel == 'auto' else is_parallel
+ global_objects['wdprops'] = self.wdprops
+ global_objects['qnodes'] = self.qnodes
+ global_objects['wd_numprop_stats'] = self.wd_numprop_stats
+ global_objects['sim_fn'] = self.sim_fn
+
+ if is_parallel:
+ global_objects['cache_dir'] = self.cache_dir
+ else:
+ global_objects['cache_dir'] = None
+ features = M.parallel_map(PSLSteinerTreeSolver._extract_features_wrapper, list(enumerate(inputs)),
+ show_progress=show_progress, progress_desc='psl: extract features', is_parallel=is_parallel)
+ data = {
+ "CanRel": [],
+ "CanType": [],
+ "NotStatement": [],
+ "Statement": [],
+ "SubProp": set(),
+ "NotRange": set(),
+ }
+ for feat in self.link_all_feats:
+ data[f'RelFeature_{feat}'] = []
+ for feat in self.type_all_feats:
+ data[f"TypeFeature_{feat}"] = []
+ for (table, sg, dg), result in zip(inputs, features):
+ table_id = table.id
+ idmap.add_keys([(table_id, uid) for uid in sg.nodes.keys()])
+ data['CanRel'] += [(idmap.m((table_id, uid)), idmap.m((table_id, vid)), eid) for uid, vid, eid, in
+ sg.edges(keys=True)]
+ data[f"CanType"] += [(idmap.m((table_id, uid)), classid) for uid, classid in
+ result['type_feats'][TypeFeatureExtraction.Freq]]
+ data['NotStatement'] += [(idmap.m((table_id, uid)),) for uid, u in sg.nodes(data='data') if
+ not u.is_statement]
+ data['Statement'] += [(idmap.m((table_id, uid)),) for uid, u in sg.nodes(data='data') if
+ u.is_statement]
+
+ props = {eid for uid, vid, eid, in sg.edges(keys=True)}
+ for p in props:
+ for pp in props:
+ if p != pp and pp in self.wdprops[p].parents_closure:
+ data["SubProp"].add((p, pp))
+
+ for uid, class_ids in result['type_feats']['_column_to_types'].items():
+ # find list of incoming edges
+ incoming_props = {eid for _, _, eid in sg.in_edges(uid, keys=True)}
+ incoming_props = [self.wdprops[eid] for eid in incoming_props]
+ for class_id in class_ids:
+ if class_id not in self.wdclasses:
+ continue
+ parents_closure = self.wdclasses[class_id].parents_closure
+ for p in incoming_props:
+ if len(p.subjects) > 0 and not any(class_id == subj or subj in parents_closure for subj in p.subjects):
+ data['NotRange'].add((p.id, class_id))
+
+ for feat in self.link_all_feats:
+ data[f"RelFeature_{feat}"] += [
+ (idmap.m((table_id, uid)), idmap.m((table_id, vid)), eid, prob)
+ for (uid, vid, eid), prob in result['link_feats'][feat].items()
+ ]
+ for feat in self.type_all_feats:
+ data[f"TypeFeature_{feat}"] += [
+ (idmap.m((table_id, uid)), classid, prob)
+ for (uid, classid), prob in result['type_feats'][feat].items()
+ ]
+ return data, idmap
+
+ @staticmethod
+ def _solve_post_process_wrapper(args):
+ global global_objects
+ table, sg, dg, pred_with_probs, threshold = args
+ return global_objects['PSLSteinerTreeSolver'].solve_post_process(table, sg, dg, pred_with_probs, threshold)
+
+ @staticmethod
+ def _update_global_objects(**kwargs):
+ global global_objects
+ for k, v in kwargs.items():
+ global_objects[k] = v
+
+ @staticmethod
+ def _extract_features_wrapper(args):
+ """Wrap the function that need to pass some big objects through a global objects"""
+ global global_objects
+ wdprops = global_objects['wdprops']
+ wd_numprop_stats = global_objects['wd_numprop_stats']
+ qnodes = global_objects['qnodes']
+ cache_dir = global_objects['cache_dir']
+ sim_fn = global_objects['sim_fn']
+
+ index, (table, sg, dg) = args
+
+ if cache_dir is not None:
+ (cache_dir / "features").mkdir(exist_ok=True, parents=True)
+ filename = table.get_friendly_fs_id()
+ filepath = cache_dir / "features" / f"a{index:03d}_{filename}.pkl"
+ if filepath.exists():
+ return M.deserialize_pkl(filepath)
+ else:
+ filepath = None
+
+ start = time.time()
+ link_feat_extractor = LinkFeatureExtraction(table, sg, dg, qnodes, wdprops, wd_numprop_stats, sim_fn)
+ type_feat_extractor = TypeFeatureExtraction(table, sg, dg, qnodes, wdprops, wd_numprop_stats)
+
+ link_feats = link_feat_extractor.extract_features()
+ type_feats = type_feat_extractor.extract_features()
+
+ exectime = time.time() - start
+ props = {eid for uid, vid, eid, in sg.edges(keys=True)}
+ sub_props = set()
+ for p in props:
+ for pp in props:
+ if p != pp and pp in wdprops[p].parents_closure:
+ sub_props.add((p, pp))
+ prop_ranges = set()
+
+ cache_content = {
+ "link_feats": link_feats,
+ "type_feats": type_feats,
+ "sub_props": sub_props,
+ "exec_time": exectime}
+ if filepath is not None:
+ M.serialize_pkl(cache_content, filepath)
+ return cache_content
+
+
+if __name__ == '__main__':
+ import hydra, itertools
+ from omegaconf import DictConfig, OmegaConf
+ from grams.config import ROOT_DIR
+
+ cfg = OmegaConf.load(ROOT_DIR / "grasd.yaml")
+
+ table_index = 24
+ logger.info("Creating environment...")
+ # dataset = "semtab2020_subset"
+ # dataset = "semtab2020"
+ dataset = "250tables"
+ env = Env(cfg=cfg, dataset_dir=HOME_DIR / f"wikitable2wikidata/{dataset}", n_tables=None, load_qnode_test=True)
+ globals()['env'] = env
+ globals()['table_index'] = table_index
+ # env.clear_cache()
+ env.clear_viz_dir()
+
+ logger.info("Creating initial semantic graph & get correct semantic models")
+ args = env.get_init_sg(table_index)
+ sms = env.get_semantic_models(table_index)
+
+ logger.info("Creating PSL solver and solve it")
+ print(args.table.id, len(args.sg), len(args.dg))
+ psl_solver = PSLSteinerTreeSolver(env.qnodes, env.wdclasses, env.wdprops, env.wd_numprop_stats,
+ disable_rules=set(cfg.psl.disable_rules), sim_fn=sim_fn)
+ # pred_sg, = psl_solver.run_with_parallel([(args.table, args.sg, args.dg)], 0.5)
+
+ # @env.cache_psfn
+ def solve(idx):
+ return psl_solver.solve(args.table, args.sg, args.dg)
+
+ sg = args.sg
+ # trim it so we can debug easier
+ # include_nodes = ['column-0', 'column-1', 'column-2', 'column-3']
+ # sg = sg.subgraph({
+ # s for uid, vid in itertools.combinations(include_nodes, 2)
+ # for ((x, s, p), (s2, y, p2)) in itertools.chain(nx.all_simple_edge_paths(sg, uid, vid, cutoff=2), nx.all_simple_edge_paths(sg, vid, uid, cutoff=2))
+ # if p == p2}.union(include_nodes))
+ pred_with_probs = psl_solver.solve(args.table, sg, args.dg)
+
+ logger.info("Run PSL post process")
+ for k, v in sorted(pred_with_probs['links'].items(), key=lambda x: x[0][0] + x[0][1][-5:]):
+ # if k[0].startswith("column-0") or k[0].startswith("stmt:column-0"):
+ print(k, ":", v)
+ pred_sg = psl_solver.solve_post_process(args.table, args.sg, args.dg, pred_with_probs['links'], 0.51)
+
+ (precision, recall, f1), oracle_sg = env.eval_steiner_tree(table_index, pred_sg)
+ # print result and plot the graph
+ logger.info(f"Steiner Tree - precision={precision:.2f} recall={recall:.2f} f1={f1:.2f}")
+
+ (precision, recall, f1), cpa_sm = env.eval_cpa(table_index, pred_sg)
+ logger.info(f"CPA - precision={precision:.2f} recall={recall:.2f} f1={f1:.2f}")
+
+ env.viz_dg(args.dg, "dg")
+ env.viz_sg(args.sg, "full_sg")
+ env.viz_sm(env.get_best_semantic_model(table_index), "gold_sm")
+ # copy features from pred_sg to oracle for debugging purpose
+ for k in oracle_sg.edges(keys=True):
+ assert args.sg.has_edge(*k)
+ oracle_sg.edges[k]['data'].features = args.sg.edges[k]['data'].features
+
+ env.viz_sg(oracle_sg, "oracle_sg")
+ env.viz_sg(pred_sg, "pred_sg")
diff --git a/grams/algorithm/semantic_graph.py b/grams/algorithm/semantic_graph.py
new file mode 100644
index 0000000..e12ab21
--- /dev/null
+++ b/grams/algorithm/semantic_graph.py
@@ -0,0 +1,980 @@
+import copy
+import numpy as np
+from collections import Counter
+from dataclasses import dataclass
+from itertools import chain, combinations
+from operator import itemgetter
+from typing import Dict, Optional, Union, List, Set, Tuple, NamedTuple, Any, Callable
+from uuid import uuid4
+
+import networkx as nx
+from rdflib import RDFS
+from typing_extensions import TypedDict
+from grams.algorithm.sm_wikidata import WDOnt
+from kgdata.wikidata.models import QNode, DataValue, WDProperty, WDClass
+from sm.misc.graph import viz_graph
+from grams.inputs.linked_table import LinkedTable
+from grams.algorithm.data_graph import build_data_graph, DGNode, StatementNode, CellNode, \
+ ContextSpan, EdgeFlowSource, EdgeFlowTarget, \
+ FlowProvenance
+import sm.outputs as O
+
+
+@dataclass
+class SGColumnNode:
+ id: str
+ # column name
+ label: str
+ # column index
+ column: int
+ # list nodes' id in the data graph
+ nodes: Set[str]
+
+ @property
+ def is_column(self):
+ return True
+
+ @property
+ def is_value(self):
+ return False
+
+ @property
+ def is_statement(self):
+ return False
+
+ def clone(self):
+ return copy.deepcopy(self)
+
+
+@dataclass
+class SGEntityValueNode:
+ id: str
+ label: str
+ qnode_id: str
+ context_span: Optional[ContextSpan]
+
+ @property
+ def is_column(self):
+ return False
+
+ @property
+ def is_value(self):
+ return True
+
+ @property
+ def is_entity_value(self):
+ return True
+
+ @property
+ def is_literal_value(self):
+ return False
+
+ @property
+ def is_in_context(self):
+ return self.context_span is not None
+
+ @property
+ def is_statement(self):
+ return False
+
+ def clone(self):
+ return copy.deepcopy(self)
+
+
+@dataclass
+class SGLiteralValueNode:
+ id: str
+ label: str
+ value: DataValue
+ context_span: Optional[ContextSpan]
+
+ @property
+ def is_column(self):
+ return False
+
+ @property
+ def is_value(self):
+ return True
+
+ @property
+ def is_entity_value(self):
+ return False
+
+ @property
+ def is_literal_value(self):
+ return True
+
+ @property
+ def is_in_context(self):
+ return self.context_span is not None
+
+ @property
+ def is_statement(self):
+ return False
+
+ def clone(self):
+ return copy.deepcopy(self)
+
+
+SGEdgeFlowTarget = NamedTuple("SGEdgeFlowTarget", [("dg_target_id", str), ("sg_target_id", str), ("edge_id", str)])
+# the source of the flow is not needed but still there for what reason? consistent?
+SGEdgeFlowSource = NamedTuple("SGEdgeFlowSource", [("dg_source_id", str), ("sg_source_id", str), ("edge_id", str)])
+
+
+@dataclass
+class SGStatementNode:
+ id: str
+ # (source flow, target_flow) => dg statement id => provenance (we have multiple statement id because
+ # for one qnode one property, we may have multiple matches statement
+ forward_flow: Dict[SGEdgeFlowSource, Dict[SGEdgeFlowTarget, Set[str]]]
+ reversed_flow: Dict[SGEdgeFlowTarget, Dict[SGEdgeFlowSource, Set[str]]]
+ flow: Dict[Tuple[SGEdgeFlowSource, SGEdgeFlowTarget], Dict[str, List[FlowProvenance]]]
+
+ @staticmethod
+ def get_id(source_id: str, source_predicate: str, target_id: str):
+ return f"stmt:{source_id}-{source_predicate}-{target_id}"
+
+ @staticmethod
+ def new(id: str):
+ return SGStatementNode(id, {}, {}, {})
+
+ @property
+ def is_column(self):
+ return False
+
+ @property
+ def is_value(self):
+ return False
+
+ @property
+ def is_statement(self):
+ return True
+
+ def clone(self):
+ return copy.deepcopy(self)
+
+ def track_provenance(self, source_flow: SGEdgeFlowSource, sid: str, target_flow: SGEdgeFlowTarget, provenances: List[FlowProvenance]):
+ """Track the provenance of which edges goes through the node"""
+ if source_flow not in self.forward_flow:
+ self.forward_flow[source_flow] = {}
+ if target_flow not in self.forward_flow[source_flow]:
+ self.forward_flow[source_flow][target_flow] = set()
+ self.forward_flow[source_flow][target_flow].add(sid)
+
+ if target_flow not in self.reversed_flow:
+ self.reversed_flow[target_flow] = {}
+ if source_flow not in self.reversed_flow[target_flow]:
+ self.reversed_flow[target_flow][source_flow] = set()
+ self.reversed_flow[target_flow][source_flow].add(sid)
+
+ if (source_flow, target_flow) not in self.flow:
+ self.flow[source_flow, target_flow] = {}
+ assert sid not in self.flow[source_flow, target_flow], "Gate to make sure the assumption (source_flow, target_flow, dg statement id) is unique correct"
+ self.flow[source_flow, target_flow][sid] = provenances
+
+ def compute_freq(self, _sg: nx.MultiDiGraph, dg: nx.MultiDiGraph, edge: 'SGEdge', is_unique_freq: bool):
+ """Compute the frequency of data edges between """
+ if not is_unique_freq:
+ freq = 0
+ if edge.target_id == self.id:
+ # from node to statement, we only consider the target that is property of the statement
+ # since we always create new statement based on the value
+ for (source_flow, target_flow), stmt2prov in self.flow.items():
+ if source_flow.edge_id != edge.predicate or source_flow.sg_source_id != edge.source_id:
+ continue
+ if target_flow.edge_id != edge.predicate:
+ continue
+ # same_stmt = False
+ # for stmt_id, prov in stmt2prov.items():
+ # stmt: StatementNode = dg.nodes[stmt_id]['data']
+ # if (EdgeFlowSource(source_flow.dg_source_id, source_flow.edge_id),
+ # EdgeFlowTarget(target_flow.dg_target_id, target_flow.edge_id)) in stmt.flow:
+ # same_stmt = True
+ # break
+ #
+ # if same_stmt:
+ freq += 1
+ else:
+ for (source_flow, target_flow), stmt2prov in self.flow.items():
+ if target_flow.sg_target_id == edge.target_id and target_flow.edge_id == edge.predicate:
+ freq += 1
+ return freq
+
+ unique_pairs = set()
+ if edge.target_id == self.id:
+ for (source_flow, target_flow), stmt2prov in self.flow.items():
+ if source_flow.sg_source_id != edge.source_id or source_flow.edge_id != edge.predicate:
+ continue
+
+ # the target is the one contain value of the statement property, which we should have
+ # only one
+ if target_flow.edge_id != edge.predicate:
+ continue
+
+ dg_source = dg.nodes[source_flow.dg_source_id]['data']
+ if dg_source.is_cell:
+ source_val = dg_source.value
+ else:
+ assert dg_source.is_entity_value
+ source_val = dg_source.qnode_id
+
+ dg_target = dg.nodes[target_flow.dg_target_id]['data']
+ if dg_target.is_cell:
+ target_val = dg_target.value
+ elif dg_target.is_literal_value:
+ target_val = dg_target.value.to_string_repr()
+ else:
+ assert dg_target.is_entity_value
+ target_val = dg_target.qnode_id
+
+ unique_pairs.add((source_val, target_val))
+ else:
+ for source_flow, target_flows in self.forward_flow.items():
+ dg_source = dg.nodes[source_flow.dg_source_id]['data']
+ if dg_source.is_cell:
+ source_val = dg_source.value
+ else:
+ assert dg_source.is_entity_value
+ source_val = dg_source.qnode_id
+ for target_flow in target_flows.keys():
+ if target_flow.sg_target_id == edge.target_id and target_flow.edge_id == edge.predicate:
+ dg_target = dg.nodes[target_flow.dg_target_id]['data']
+ if dg_target.is_cell:
+ target_val = dg_target.value
+ elif dg_target.is_literal_value:
+ target_val = dg_target.value.to_string_repr()
+ else:
+ assert dg_target.is_entity_value
+ target_val = dg_target.qnode_id
+ unique_pairs.add((source_val, target_val))
+
+ return len(unique_pairs)
+
+ def get_edges_provenance(self, edges: List['SGEdge']):
+ """Retrieve all flow provenances that connects these edges.
+
+ Note that in case we have more than one statement per flow, we merge the statements (i.e., merge
+ the provenances) to return the best statements (since the provenance store how the link is generated,
+ merged the provenance will select the best one).
+ """
+ select_target_flows = {(e.target_id, e.predicate) for e in edges}
+ links = {}
+ for source_flow, target_flows in self.forward_flow.items():
+ # source_flow.predicate is always the same, just change to different data node
+ # get list of statements that contains all edges
+ stmts: Dict[str, Dict[SGEdgeFlowTarget, List[FlowProvenance]]] = None
+ for target_flow in target_flows:
+ if (target_flow.sg_target_id, target_flow.edge_id) not in select_target_flows:
+ continue
+ if stmts is None:
+ stmts = {}
+ for stmt_id, provs in self.flow[source_flow, target_flow].items():
+ stmts[stmt_id] = {}
+ stmts[stmt_id][target_flow] = provs
+ else:
+ # do intersection because we want to find the stmt contain all edges
+ stmt2provenances = self.flow[source_flow, target_flow]
+ remove_stmt_ids = set(stmts.keys()).difference(stmt2provenances.keys())
+ for stmt_id in remove_stmt_ids:
+ stmts.pop(stmt_id)
+ for stmt_id, provenances in stmt2provenances.items():
+ if stmt_id not in stmts:
+ continue
+ # no merge since the target flow is unique
+ stmts[stmt_id][target_flow] = provenances
+
+ if stmts is not None and len(stmts) > 0:
+ # do a merge to get the provenances (this is the strategy to return the best statement)
+ # note that all target flows will be appear, so the first step is to reverse target flow with statement id
+ swap_stmts: Dict[SGEdgeFlowTarget, List[FlowProvenance]] = {
+ target_flow: []
+ for target_flow in next(iter(stmts.values()))
+ }
+ for stmt_id, _target_flows in stmts.items():
+ for target_flow, provenances in _target_flows.items():
+ swap_stmts[target_flow] = FlowProvenance.merge_lst(swap_stmts[target_flow], provenances)
+ links[source_flow] = swap_stmts
+ return links
+
+
+@dataclass
+class SGEdge:
+ source_id: str
+ target_id: str
+ predicate: str
+ features: Dict[str, Any]
+
+ def clone(self):
+ return copy.deepcopy(self)
+
+
+SGNode = Union[SGColumnNode, SGEntityValueNode, SGLiteralValueNode, SGStatementNode]
+NxSGNodeAttr = TypedDict('NxSGNodeAttr', data=SGNode)
+NxSGEdgeAttr = TypedDict('NxSGEdgeAttr', data=SGEdge)
+NxSGEdge = Tuple[str, str, str, NxSGEdgeAttr]
+
+
+def get_label(porq: str, qnodes: Dict[str, QNode], wdclasses: Dict[str, WDClass], wdprops: Dict[str, WDProperty]):
+ if porq.startswith("Q"):
+ if porq in wdclasses:
+ item = wdclasses[porq]
+ elif porq in qnodes:
+ item = qnodes[porq]
+ else:
+ item = wdprops[porq]
+
+ return f"{item.label} ({item.id})"
+
+
+def merge_connected_components(graphs: List[nx.MultiDiGraph]):
+ g = nx.MultiDiGraph()
+ for gi in graphs:
+ for uid, udata in gi.nodes.items():
+ assert uid not in g.nodes
+ g.add_node(uid, **udata)
+
+ for uid, vid, eid, edge_data in gi.edges(data=True, keys=True):
+ g.add_edge(uid, vid, key=eid, **edge_data)
+ return g
+
+
+@dataclass
+class SemanticGraphConstructorArgs:
+ table: LinkedTable
+ dg: nx.MultiDiGraph
+ sg: nx.MultiDiGraph
+ # column type assignment, from column index (must stored as string) => QNode
+ cta: Optional[Dict[str, str]] = None
+ # semantic model
+ sm: Optional[O.SemanticModel] = None
+
+
+# noinspection PyMethodMayBeStatic
+class SemanticGraphConstructor:
+ STATEMENT_URI = WDOnt.STATEMENT_URI
+ STATEMENT_REL_URI = WDOnt.STATEMENT_REL_URI
+
+ def __init__(self, steps: List, qnodes: Dict[str, QNode], wdclasses: Dict[str, WDClass], wdprops: Dict[str, WDProperty]):
+ self.steps = steps
+ self.qnodes = qnodes
+ self.wdclasses = wdclasses
+ self.wdprops = wdprops
+
+ def run(self, table: LinkedTable, dg: nx.MultiDiGraph, debug=False):
+ args = SemanticGraphConstructorArgs(table, dg, None)
+ for i, step in enumerate(self.steps):
+ step(self, args)
+ if debug:
+ viz_sg(args.sg, self.qnodes, self.wdclasses, self.wdprops, HOME_DIR / "graph_viz" / "debug", f"g_{i:02}")
+ return args
+
+ @staticmethod
+ def get_sg_node_id(u: DGNode):
+ if u.is_cell:
+ return f"column-{u.column}"
+ return u.id
+
+ def get_sg_node_label(self, args: SemanticGraphConstructorArgs, u: DGNode):
+ if u.is_cell:
+ return args.table.table.columns[u.column].name
+ if u.is_entity_value:
+ qnode = self.qnodes[u.qnode_id]
+ return f"{qnode.label} ({qnode.id})"
+ if u.is_literal_value:
+ return u.value.to_string_repr()
+ raise M.UnreachableError("This function doesn't supposed to be called with statement node")
+
+ @staticmethod
+ def get_sg_subgraph(sg: nx.MultiDiGraph, edges: List[Tuple[str, str, str]]):
+ """Get a subgraph of the semantic graph containing a set of edges"""
+ sg_prime = nx.MultiDiGraph()
+ for uid, vid, eid in edges:
+ if uid not in sg_prime.nodes:
+ sg_prime.add_node(uid, data=sg.nodes[uid]['data'])
+ if vid not in sg_prime.nodes:
+ sg_prime.add_node(vid, data=sg.nodes[vid]['data'])
+ sg_prime.add_edge(uid, vid, key=eid, data=sg.edges[uid, vid, eid]['data'])
+ return sg_prime
+
+ @staticmethod
+ def keep_one_simple_path_between_important_nodes(sg: nx.MultiDiGraph,
+ ranking_fn: Optional[Callable[[List[List[Tuple[str, str, str]]]], List[Tuple[str, str, str]]]] = None,
+ both_direction: bool = True):
+ """
+ Let important nodes be columns and context values. If there is more than one path between the important nodes,
+ we only keep one. Note that this function doesn't remove a literal value if it is value of a property of a statement
+ that appear in the chosen path.
+
+ The default ranking function is select the path with shorter length. Supply your own function for better result.
+ The both direction function will select the correct one
+ """
+ # figure out the important nodes
+ important_nodes = set()
+ for uid, udata in sg.nodes(data=True):
+ u: SGNode = udata['data']
+ if u.is_column or (u.is_value and u.is_in_context):
+ important_nodes.add(uid)
+
+ if ranking_fn is None:
+ ranking_fn = lambda paths: sorted(paths, key=lambda path: len(path))[0]
+
+ # select one path if there are multiple edges
+ selected_edges = set()
+ for uid, vid in combinations(important_nodes, 2):
+ forward_paths = [
+ path
+ for path in nx.all_simple_edge_paths(sg, uid, vid, cutoff=6)
+ if not any(edge[0] in important_nodes for edge in path[1:])
+ ]
+ backward_paths = [
+ path
+ for path in nx.all_simple_edge_paths(sg, vid, uid, cutoff=6)
+ if not any(edge[0] in important_nodes for edge in path[1:])
+ ]
+ if both_direction:
+ if len(forward_paths) + len(backward_paths) > 0:
+ selected_edges = selected_edges.union(ranking_fn(forward_paths + backward_paths))
+ else:
+ if len(forward_paths) > 0:
+ selected_edges = selected_edges.union(ranking_fn(forward_paths))
+ if len(backward_paths) > 0:
+ selected_edges = selected_edges.union(ranking_fn(backward_paths))
+
+ new_sg = SemanticGraphConstructor.get_sg_subgraph(sg, selected_edges)
+
+ # add missing statement values
+ for sid, sdata in list(new_sg.nodes(data=True)):
+ stmt: SGStatementNode = sdata['data']
+ if not stmt.is_statement:
+ continue
+
+ (uid, _, eid), = list(new_sg.in_edges(sid, keys=True))
+ for vid, us_edges in new_sg[sid].items():
+ if eid in us_edges:
+ # we have the statement property in the graph
+ break
+ else:
+ # we do not have the statement property in the graph. get it from the original graph
+ for vid, us_edges in sg[sid].items():
+ if eid in us_edges:
+ if vid not in new_sg.nodes:
+ new_sg.add_node(vid, data=sg.nodes[vid]['data'])
+ new_sg.add_edge(sid, vid, key=eid, data=us_edges[eid]['data'])
+
+ return new_sg
+
+ @staticmethod
+ def st_terminal_nodes(sg: nx.MultiDiGraph):
+ terminal_nodes = set()
+ for uid, udata in sg.nodes(data=True):
+ u: SGNode = udata['data']
+ if u.is_column:
+ terminal_nodes.add(uid)
+ return terminal_nodes
+
+ # ##################################################################################################################
+ # CODE FOR PIPELINE
+ # ##################################################################################################################
+ def init_sg(self, args: SemanticGraphConstructorArgs):
+ sg = nx.MultiDiGraph()
+ dg = args.dg
+
+ # first step: add node to the graph
+ for uid, udata in dg.nodes(data=True):
+ u: DGNode = udata['data']
+ if u.is_statement:
+ # we can have more than one predicates per entity column, these are represented in the statement
+ continue
+
+ sgi = self.get_sg_node_id(u)
+ if not sg.has_node(sgi):
+ label = self.get_sg_node_label(args, u)
+ if u.is_cell:
+ sgu = SGColumnNode(id=sgi, label=label, column=u.column, nodes=set())
+ elif u.is_entity_value:
+ sgu = SGEntityValueNode(id=sgi, label=label, qnode_id=u.qnode_id, context_span=u.context_span)
+ else:
+ assert u.is_literal_value
+ sgu = SGLiteralValueNode(id=sgi, label=label, value=u.value, context_span=u.context_span)
+ sg.add_node(sgi, data=sgu)
+
+ if sg.nodes[sgi]['data'].is_column:
+ sg.nodes[sgi]['data'].nodes.add(uid)
+
+ # second step: add link
+ for uid, udata in dg.nodes(data=True):
+ u: DGNode = udata['data']
+ if u.is_statement:
+ continue
+
+ # add statement
+ p2stmts: Dict[str, List[StatementNode]] = {}
+ for _, sid, us_eid, us_edata in dg.out_edges(uid, data=True, keys=True):
+ if us_eid not in p2stmts:
+ p2stmts[us_eid] = []
+ p2stmts[us_eid].append(dg.nodes[sid]['data'])
+
+ sgi = self.get_sg_node_id(u)
+ for p, stmts in p2stmts.items():
+ for stmt in stmts:
+ # we duplicate based on SG column node, not by DG node
+ children_values = {}
+ children_non_values = []
+
+ for _, vid, sv_eid, sv_edata in dg.out_edges(stmt.id, data=True, keys=True):
+ v: DGNode = dg.nodes[vid]['data']
+ if (vid, sv_eid) not in stmt.forward_flow[uid, p]:
+ # because of re-use statement, we need to only consider the children of this u node only
+ continue
+
+ if sv_eid == p:
+ tgi = self.get_sg_node_id(v)
+ if tgi not in children_values:
+ children_values[tgi] = []
+ children_values[tgi].append(v)
+ else:
+ children_non_values.append((sv_eid, v))
+
+ for tgi, vals in children_values.items():
+ stmt_id = SGStatementNode.get_id(sgi, p, tgi)
+ if stmt_id not in sg.nodes:
+ sg.add_node(stmt_id, data=SGStatementNode.new(stmt_id))
+
+ if (sgi, stmt_id, p) not in sg.edges:
+ sg.add_edge(sgi, stmt_id, key=p, data=SGEdge(sgi, stmt_id, p, {}))
+ if (stmt_id, tgi, p) not in sg.edges:
+ sg.add_edge(stmt_id, tgi, key=p, data=SGEdge(stmt_id, tgi, p, {}))
+
+ sg_stmt: SGStatementNode = sg.nodes[stmt_id]['data']
+ for v in vals:
+ sg_stmt.track_provenance(SGEdgeFlowSource(uid, sgi, p), stmt.id, SGEdgeFlowTarget(v.id, tgi, p), stmt.get_provenance(EdgeFlowSource(uid, p), EdgeFlowTarget(v.id, p)))
+
+ for qp, qv in children_non_values:
+ qtgi = self.get_sg_node_id(qv)
+ if (stmt_id, qtgi, qp) not in dg.edges:
+ sg.add_edge(stmt_id, qtgi, key=qp, data=SGEdge(stmt_id, qtgi, qp, {}))
+
+ sg_stmt.track_provenance(SGEdgeFlowSource(uid, sgi, p), stmt.id, SGEdgeFlowTarget(qv.id, qtgi, qp), stmt.get_provenance(EdgeFlowSource(uid, p), EdgeFlowTarget(qv.id, qp)))
+ args.sg = sg
+
+ def cta_majority_vote(self, args: SemanticGraphConstructorArgs):
+ # temporary before we should refactor the whole s*
+ """Predict the column types for each column in the semantic graph"""
+ def add_merge_qnodes(cell: CellNode, cell2qnodes: Dict[str, List[QNode]]):
+ # attempt to merge qnodes (spatial) if they are contained in each other
+ # we should go even higher order
+ if len(cell.qnode_ids) > 1:
+ # attempt to merge qnodes (spatial) if they are contained in each other
+ # we should go even higher order
+ ignore_qnodes = set()
+ for q0_id in cell.qnode_ids:
+ q0 = self.qnodes[q0_id]
+ # location or located in the administrative entity
+ vals = {
+ stmt.value.as_qnode_id()
+ for stmt in chain(q0.props.get("P131", []), q0.props.get("P276", []))
+ }
+ for q1_id in cell.qnode_ids:
+ if q0_id == q1_id:
+ continue
+ if q1_id in vals:
+ # q0 is inside q1, ignore q1
+ ignore_qnodes.add(q1_id)
+ cell2qnodes[cell.id] = [self.qnodes[q_id] for q_id in cell.qnode_ids if q_id not in ignore_qnodes]
+ elif len(cell.qnode_ids) > 0:
+ cell2qnodes[cell.id] = [self.qnodes[cell.qnode_ids[0]]]
+ else:
+ cell2qnodes[cell.id] = []
+
+ args.cta = {}
+ sg = args.sg
+ dg = args.dg
+ cell2qnodes = {}
+ for uid, udata in sg.nodes(data=True):
+ u: SGColumnNode = udata['data']
+ if not u.is_column:
+ continue
+
+ # cells in this column
+ cells: List[CellNode] = [dg.nodes[cid]['data'] for cid in u.nodes]
+ covered_fractions = [
+ sum(span.length for spans in cell.qnodes_span.values() for span in spans) / max(len(cell.value), 1)
+ for cell in cells
+ if len(cell.qnode_ids) >= 1
+ ]
+ if len(covered_fractions) == 0:
+ continue
+ avg_covered_fractions = np.mean(covered_fractions)
+ if avg_covered_fractions < 0.8:
+ continue
+
+ for cell in cells:
+ add_merge_qnodes(cell, cell2qnodes)
+ merged_class_freq = dict(Counter([
+ c
+ for cell in cells
+ for c in (stmt.value.as_qnode_id()
+ for qnode in cell2qnodes[cell.id]
+ for stmt in qnode.props.get("P31", [])
+ )
+ ]).items())
+ if len(merged_class_freq) == 0:
+ continue
+
+ best_class, count = max(merged_class_freq.items(), key=itemgetter(1))
+ args.cta[str(u.column)] = WDOnt.get_qnode_uri(best_class)
+
+ return args
+
+ def construct_sm(self, args: SemanticGraphConstructorArgs):
+ """Get the final semantic model by combining relationships in SG and CTA"""
+ sm = O.SemanticModel()
+ readable_label_fn = lambda qnode_id: get_label(qnode_id, self.qnodes, self.wdclasses, self.wdprops)
+ # a mapping from data node to class node for each data node that has a type
+ dnode2cnode = {}
+ for uid, udata in args.sg.nodes(data=True):
+ u: SGNode = udata['data']
+ if u.is_column:
+ sm.add_node(O.DataNode(u.id, u.column, u.label))
+ if u.column in args.cta:
+ qnode = self.wdclasses[args.cta[u.column]]
+ _cid = str(uuid4())
+ sm.add_node(O.ClassNode(_cid, qnode.get_uri(), f"wd:{qnode.id}", readable_label=readable_label_fn(qnode.id)))
+ dnode2cnode[u.id] = _cid
+ sm.add_edge(O.Edge(_cid, u.id, str(RDFS.label), "rdfs:label"))
+ elif u.is_statement:
+ sm.add_node(O.ClassNode(u.id, self.STATEMENT_URI, self.STATEMENT_REL_URI))
+ elif u.is_value:
+ if u.is_literal_value:
+ sm.add_node(O.LiteralNode(u.id, u.value.to_string_repr()))
+ else:
+ sm.add_node(O.LiteralNode(u.id, u.value.as_qnode_id(), readable_label=readable_label_fn(u.value.as_qnode_id())))
+
+ for uid, vid, eid, edata in args.sg.edges(data=True, keys=True):
+ if uid in dnode2cnode:
+ uid = dnode2cnode[uid]
+ if vid in dnode2cnode:
+ vid = dnode2cnode[vid]
+ porq = self.wdprops[eid]
+ sm.add_edge(O.Edge(uid, vid, porq.get_uri(), f"p:{porq.id}", readable_label=readable_label_fn(porq.id)))
+ args.sm = sm
+
+ def prune_sg_redundant_entity(self, args: SemanticGraphConstructorArgs):
+ """Remove entity & context that is:
+ - just an intermediate nodes that the parents only has one edge and the children has only one edge too
+ - don't remove entity what is the main value of the statement
+ """
+ sm_g = args.sg
+
+ # we are going to remove entity & context that is:
+ # just a intermediate nodes that the parents only one edges and the children too
+ stop = False
+ while not stop:
+ stop = True
+ # for nid, ndata in list(sm_g.nodes(data=True)):
+ # n: SGNode = ndata['data']
+ # if n.is_statement:
+ # inedges: List[NxSGEdge] = list(sm_g.in_edges(nid, data=True, keys=True))
+ # outedges: List[NxSGEdge] = list(sm_g.out_edges(nid, data=True, keys=True))
+ #
+ # if len(inedges) != 1:
+ # continue
+ # uid, _, seid, sedata = inedges[0]
+ # max_weight = -1
+ # for _, vid, teid, tedata in outedges:
+ # v: SGNode = sm_g.nodes[vid]['data']
+ # if teid != seid:
+ # # we only want to consider value of the statement
+ # continue
+ #
+ # if v.is_column:
+ # if tedata['data'].features['freq'] > max_weight:
+ # max_weight = tedata['data'].features['freq']
+ # for _, vid, teid, tedata in outedges:
+ # v: SGNode = sm_g.nodes[vid]['data']
+ # if teid != seid:
+ # # we only want to consider value of the statement
+ # continue
+ #
+ # if v.is_value:
+ # if tedata['data'].features['freq'] < max_weight:
+ # assert False
+ # sm_g.remove_node(vid)
+ # stop = False
+
+ for nid, ndata in list(sm_g.nodes(data=True)):
+ n: SGNode = ndata['data']
+ if n.is_value and n.is_entity_value:
+ inedges: List[NxSGEdge] = list(sm_g.in_edges(nid, data=True, keys=True))
+ outedges: List[NxSGEdge] = list(sm_g.out_edges(nid, data=True, keys=True))
+
+ if len(inedges) == 0:
+ if all(sm_g.out_degree(vid) == 1 for _, vid, teid, tedata in outedges):
+ sm_g.remove_node(nid)
+ stop = False
+ continue
+
+ """
+ Remove the entity when it is not doing
+ """
+ if len(inedges) > 1 or len(outedges) > 1:
+ continue
+
+ if len(inedges) > 0:
+ uid, _, seid, sedata = inedges[0]
+ if not all(sm_g.nodes[sib_nid]['data'].is_value for
+ _, sib_nid in sm_g.out_edges(uid)):
+ continue
+
+ if len(outedges) > 0:
+ _, vid, teid, tedata = outedges[0]
+ if sm_g.out_degree(vid) != 1:
+ continue
+
+ # remove that node has only
+ sm_g.remove_node(nid)
+ stop = False
+
+ for nid, ndata in list(sm_g.nodes(data=True)):
+ n = ndata['data']
+ if n.is_statement and (sm_g.in_degree(nid) == 0 or sm_g.out_degree(nid) == 0):
+ sm_g.remove_node(nid)
+ stop = False
+
+ # ##################################################################################################################
+ # CODE FOR COMPUTING FEATURES
+ # ##################################################################################################################
+ def calculate_link_frequency(self, args: SemanticGraphConstructorArgs):
+ def get_in_edges(g, uid) -> List[SGEdge]:
+ return [edata['data'] for _, _, eid, edata in g.in_edges(uid, data=True, keys=True)]
+
+ sg = args.sg
+ for sid, sdata in sg.nodes(data=True):
+ s: SGStatementNode = sdata['data']
+ if not s.is_statement:
+ continue
+
+ in_edge, = get_in_edges(sg, sid)
+ in_edge.features['freq'] = s.compute_freq(sg, args.dg, in_edge, is_unique_freq=False)
+ in_edge.features['unique_freq'] = s.compute_freq(sg, args.dg, in_edge, is_unique_freq=True)
+
+ for _, vid, sv_eid, sv_edata in sg.out_edges(sid, data=True, keys=True):
+ # count the frequency of the sv_eid by counting the number of times it is between specific instance
+ sv_e: SGEdge = sv_edata['data']
+ sv_e.features['freq'] = s.compute_freq(sg, args.dg, sv_e, is_unique_freq=False)
+ sv_e.features['unique_freq'] = s.compute_freq(sg, args.dg, sv_e, is_unique_freq=True)
+
+ # ##################################################################################################################
+ # CODE FOR SOLVING THE STEINER TREE PROBLEM
+ # ##################################################################################################################
+ def st_maximum_arborescence_solver(self, args: SemanticGraphConstructorArgs):
+ sm_g = nx.MultiDiGraph()
+ for uid, vid, eid, edata in args.sg.edges(data=True, keys=True):
+ if args.sg.nodes[vid]['data'].is_statement:
+ # freq. for source -> statement
+ # use a constant value as this link depends on the link coming out from the statement
+ edata['weight'] = 0.1
+ else:
+ edata['weight'] = edata['data'].features['freq']
+
+ # find the tree
+ resp = nx.algorithms.tree.branchings.maximum_branching(args.sg, attr='weight', preserve_attrs=True)
+ for s, t, e, edata in resp.edges(data=True, keys=True):
+ edge = edata['data']
+ if s not in sm_g:
+ sm_g.add_node(s, **args.sg.nodes[s])
+ if t not in sm_g:
+ sm_g.add_node(t, **args.sg.nodes[t])
+ sm_g.add_edge(s, t, key=edge.predicate, data=edge)
+
+ # remove redundant nodes that is not relevant to the terminals
+ # expand the statement with
+ # for nid, ndata in list(sm_g.nodes(data=True)):
+ # n: SGNode = ndata['data']
+ # if n.is_statement:
+ # edges = list(sm_g.in_edges(nid, data=True, keys=True))
+ # if len(edges) == 0:
+ # continue
+ # assert len(edges) == 1
+ # uid, _, seid, edata = edges[0]
+ # source_e: SGEdge = edata['data']
+ #
+ # # split when all props of statement is the same as the value of the statement
+ # need_split_stmt = True
+ # for _, vid, _, edata in sm_g.out_edges(nid, data=True, keys=True):
+ # if edata['data'].predicate != source_e.predicate:
+ # need_split_stmt = False
+ # break
+ #
+ # if need_split_stmt:
+ # lst = list(sm_g.out_edges(nid, data=True, keys=True))
+ # for index, (_, vid, teid, edata) in enumerate(lst[1:]):
+ # n2 = n.clone(without_provenance=True)
+ # n2.id = n.id + f":{index}"
+ # sm_g.add_node(n2.id, data=n2)
+ # sm_g.add_edge(uid, n2.id, key=seid, data=source_e)
+ # sm_g.add_edge(n2.id, vid, key=teid, data=edata['data'])
+ # sm_g.remove_edge(n.id, vid, teid)
+
+ # remove redundant nodes:
+ # - a statement doesn't have any properties or incoming nodes
+ # - an entity node that is a leaf node, and is the only child of the parent statement.
+ while True:
+ remove_nodes = set()
+ for nid in sm_g:
+ n: SGNode = sm_g.nodes[nid]['data']
+ outdegree = sm_g.out_degree(nid)
+ indegree = sm_g.in_degree(nid)
+
+ if n.is_statement and (outdegree == 0 or indegree == 0):
+ # a statement doesn't have any properties or incoming nodes
+ remove_nodes.add(nid)
+ elif n.is_value:
+ if outdegree == 0:
+ # an entity that doesn't have any outgoing edges, that can be used as something for the statement
+ # so we only remove it when the parent class has only one class
+ total_outdegree_parent = sum(
+ sm_g.out_degree(uid) for uid, vid, edata in sm_g.in_edges(nid, data=True))
+ if total_outdegree_parent == 1:
+ remove_nodes.add(nid)
+
+ # this extra entity will only be useful if it is not the qualifiers of the statement
+ if all(sm_g.in_degree(uid) == 1 and eid !=
+ next(iter(sm_g.in_edges(uid, data=True, keys=True)))[2] for uid, vid, eid, edata in
+ sm_g.in_edges(nid, data=True, keys=True)):
+ remove_nodes.add(nid)
+ if indegree == 0:
+ # an entity that doesn't have any incoming edges, but the only on predicate
+ total_outdegree_children = sum(
+ sm_g.out_degree(uid) for uid, vid, eid in sm_g.out_edges(nid, data=True))
+ if outdegree == 1 and total_outdegree_children == 1:
+ remove_nodes.add(nid)
+
+ for nid in remove_nodes:
+ sm_g.remove_node(nid)
+
+ if len(remove_nodes) == 0:
+ break
+
+ # remove statement that has no qualifiers
+ # for nid, ndata in list(sm_g.nodes(data=True)):
+ # n: SGNode = ndata['data']
+ # if n.is_statement and sm_g.in_degree(nid) == 1 and sm_g.out_degree(nid) == 1:
+ # uid, _, seid, sedata = list(sm_g.in_edges(nid, data=True, keys=True))[0]
+ # _, vid, teid, tedata = list(sm_g.out_edges(nid, data=True, keys=True))[0]
+ #
+ # if sedata['data'].predicate == tedata['data'].predicate:
+ # # remove the statement that has no qualifiers
+ # sm_g.remove_node(nid)
+ # e: SGEdge = sedata['data'].clone(without_provenance=True)
+ # sm_g.add_edge(uid, vid, seid, data=e)
+ args.sg = sm_g
+
+ def st_networkx_st_solver(self, args: SemanticGraphConstructorArgs):
+ sm_g = nx.MultiDiGraph()
+ terminal_nodes = []
+ for nid, ndata in args.sg.nodes(data=True):
+ n: SGNode = ndata['data']
+ if n.is_column or n.is_context:
+ terminal_nodes.append(nid)
+
+ return nx.algorithms.approximation.steinertree.steiner_tree(args.sg, terminal_nodes, weight='weight')
+
+
+def viz_sg(sg, qnodes: Dict[str, QNode], wdclasses: Dict[str, WDClass], wdprops: Dict[str, WDProperty], outdir: str, graph_id: str):
+ get_label_fn = lambda x: get_label(x, qnodes, wdclasses, wdprops)
+ colors = {
+ "context": dict(fill="#C6E5FF", stroke="#5B8FF9"),
+ "statement": dict(fill="#d9d9d9", stroke="#434343"),
+ "kg": dict(fill="#b7eb8f", stroke="#135200"),
+ "column": dict(fill='#ffd666', stroke='#874d00')
+ }
+
+ def node_fn(uid, udata):
+ u: SGNode = udata['data']
+ html = ""
+ label = ""
+ if u.is_value:
+ nodetype = 'context' if u.is_in_context else 'kg'
+ label = u.label
+ if u.is_entity_value:
+ qnodeid = u.qnode_id
+ html = f"""{get_label_fn(qnodeid)}"""
+ elif u.is_statement:
+ nodetype = 'statement'
+ else:
+ assert u.is_column
+ label = f"{u.label} ({u.column})"
+ nodetype = 'column'
+
+ return {
+ "label": label,
+ "style": colors[nodetype],
+ "labelCfg": {
+ "style": {
+ "fill": "black",
+ "background": {
+ "padding": [4, 4, 4, 4],
+ "radius": 3,
+ **colors[nodetype]
+ }
+ }
+ },
+ "html": html
+ }
+
+ def edge_fn(eid, edata):
+ edge: SGEdge = edata['data']
+ label = get_label_fn(eid)
+ if 'prob' in edge.features:
+ label += f" p={edge.features['prob']:.6f}"
+
+ html = f'{get_label_fn(eid)}'
+ if len(edge.features) > 0:
+ html_feat = []
+ for feat, val in edge.features.items():
+ html_feat.append(f"{feat}: {val:.2f}")
+ html += f""
+
+ return {
+ "label": label,
+ "html": html
+ }
+
+ return viz_graph(sg, node_fn, edge_fn, outdir, graph_id)
+
+
+if __name__ == '__main__':
+ from sm_unk.config import HOME_DIR
+ from sm_unk.dev.wikitable2wikidata.sxx_evaluation import get_input_data
+ from sm_unk.dev.wikitable2wikidata.kg_index import KGObjectIndex
+
+ table_index = 6
+
+ max_n_hop = 2
+ dataset_dir = HOME_DIR / "wikitable2wikidata/250tables"
+ gold_models = get_input_data(dataset_dir, dataset_dir.name, only_curated=True)
+
+ REDIS_CACHE_URL = 'redis://localhost:6379/8'
+ qnodes = get_qnodes(dataset_dir, n_hop=max_n_hop + 1, test=True)
+ wdclasses = WDClass.from_file(dataset_dir / "ontology", load_parent_closure=True)
+ wdprops = WDProperty.from_file(load_parent_closure=True)
+
+ kg_index_file = dataset_dir / "kg_index" / "object_index.2hop_transitive.pkl.gz"
+ if kg_index_file.exists():
+ kg_object_index = KGObjectIndex.deserialize(kg_index_file, verbose=True)
+ else:
+ index_qnode_ids = list(get_qnodes(dataset_dir, n_hop=1).keys())
+ kg_object_index = KGObjectIndex.from_qnodes(index_qnode_ids, qnodes, wdprops,
+ n_hop=2,
+ verbose=True)
+ kg_object_index.serialize(kg_index_file)
+
+ table = gold_models[table_index][1]
+ dg = build_data_graph(table, qnodes, wdprops, kg_object_index, max_n_hop=2)
+ constructor = SemanticGraphConstructor([
+ SemanticGraphConstructor.init_sg,
+ # SemanticGraphConstructor.calculate_link_frequency,
+ # SemanticGraphConstructor.prune_sg_redundant_entity,
+ ], qnodes, wdclasses, wdprops)
+ resp = constructor.run(table, dg, debug=False)
\ No newline at end of file
diff --git a/grams/algorithm/semtab2020.py b/grams/algorithm/semtab2020.py
new file mode 100644
index 0000000..fdb955c
--- /dev/null
+++ b/grams/algorithm/semtab2020.py
@@ -0,0 +1,66 @@
+from collections import defaultdict
+from operator import itemgetter
+from grams.algorithm.semantic_graph import SGEdge, SemanticGraphConstructor
+from typing import *
+import sm.misc as M
+import networkx as nx
+
+
+class SemTab2020PostProcessing:
+ instance = None
+
+ def __init__(self):
+ from sm_unk.config import HOME_DIR
+ dataset_dir = HOME_DIR / "wikitable2wikidata/semtab2020"
+ self.CPA_targets = defaultdict(list)
+ self.CTA_targets = defaultdict(list)
+ for r in M.deserialize_csv(dataset_dir / "CPA_Round4_targets.csv"):
+ self.CPA_targets[r[0]].append((int(r[1]), int(r[2])))
+ for r in M.deserialize_csv(dataset_dir / "CTA_Round4_targets.csv"):
+ self.CTA_targets[r[0]].append(int(r[1]))
+
+ @staticmethod
+ def get_instance():
+ if SemTab2020PostProcessing.instance is None:
+ SemTab2020PostProcessing.instance = SemTab2020PostProcessing()
+ return SemTab2020PostProcessing.instance
+
+ def solve_post_process(self, table, sg, dg, pred_with_probs: Dict[Tuple[str, str, str], float], threshold):
+ # get the steiner tree first
+ ci2id = {
+ unode.column: uid
+ for uid, unode in sg.nodes(data='data')
+ if unode.is_column
+ }
+ selected_edges = set()
+ for sci, tci in self.CPA_targets[table.id]:
+ uid = ci2id[sci]
+ vid = ci2id[tci]
+
+ if not sg.has_node(uid) or not sg.has_node(vid):
+ continue
+ # among those paths, select the one with best prob
+ paths = list(nx.all_simple_edge_paths(sg, uid, vid, cutoff=2))
+ if len(paths) == 0:
+ continue
+ path_probs = []
+ for path in paths:
+ path_prob = 0
+ for uid, vid, eid in path:
+ edge: SGEdge = sg.edges[uid, vid, eid]['data']
+ path_prob += pred_with_probs[uid, vid, eid]
+ path_probs.append(path_prob)
+
+ select_path, select_path_prob = max(zip(paths, path_probs), key=itemgetter(1))
+ selected_edges = selected_edges.union(select_path)
+
+ new_sg = SemanticGraphConstructor.get_sg_subgraph(sg, selected_edges)
+ return new_sg
+
+ def remove_dangling_statement(self, sg: nx.MultiDiGraph):
+ ids = set()
+ for sid, s in list(sg.nodes(data='data')):
+ if s.is_statement and (sg.in_degree(sid) == 0 or sg.out_degree(sid) == 0):
+ ids.add(sid)
+ for id in ids:
+ sg.remove_node(id)
\ No newline at end of file
diff --git a/grams/algorithm/sm_wikidata.py b/grams/algorithm/sm_wikidata.py
new file mode 100644
index 0000000..bdc3efd
--- /dev/null
+++ b/grams/algorithm/sm_wikidata.py
@@ -0,0 +1,551 @@
+import itertools
+
+import numpy as np
+from enum import IntEnum
+from typing import Dict, Set, List
+from uuid import uuid4
+
+from rdflib import RDFS
+
+from grams.inputs.linked_table import LinkedTable
+from grams.evaluation import sm_metrics
+import sm.outputs as O
+from kgdata.wikidata.models import QNode, WDProperty, WDClass
+from sm.misc.graph import viz_graph
+from sm.misc import identity_func
+
+
+class SMNodeType(IntEnum):
+ Column = 0
+ Class = 1
+ Statement = 2
+ Entity = 3
+ Literal = 4
+
+
+class OutOfNamespace(Exception):
+ pass
+
+
+class WDOnt:
+ STATEMENT_URI = "http://wikiba.se/ontology#Statement"
+ STATEMENT_REL_URI = "wikibase:Statement"
+
+ def __init__(self, qnodes: Dict[str, QNode], wdclasses: Dict[str, WDClass], wdprops: Dict[str, WDProperty]):
+ self.qnodes = qnodes
+ self.wdclasses = wdclasses
+ self.wdprops = wdprops
+ self.get_qid_fn = {
+ "Q": identity_func,
+ "q": identity_func,
+ # http
+ "h": self.get_qnode_id
+ }
+ self.get_pid_fn = {
+ "P": identity_func,
+ "p": identity_func,
+ "h": self.get_prop_id
+ }
+
+ @classmethod
+ def is_uri_statement(cls, uri: str):
+ return uri == 'http://wikiba.se/ontology#Statement'
+
+ @classmethod
+ def is_uri_dummy_class(cls, uri: str):
+ return uri == 'http://wikiba.se/ontology#DummyClassForInversion'
+
+ @classmethod
+ def is_uri_property(cls, uri: str):
+ return uri.startswith(f"http://www.wikidata.org/prop/")
+
+ @classmethod
+ def is_uri_qnode(cls, uri: str):
+ return uri.startswith("http://www.wikidata.org/entity/")
+
+ @classmethod
+ def get_qnode_id(cls, uri: str):
+ if not cls.is_uri_qnode(uri):
+ raise OutOfNamespace(f"{uri} is not in wikidata qnode namespace")
+ return uri.replace("http://www.wikidata.org/entity/", "")
+
+ @classmethod
+ def get_qnode_uri(cls, qnode_id: str):
+ return f"http://www.wikidata.org/entity/{qnode_id}"
+
+ @classmethod
+ def get_prop_id(cls, uri: str):
+ if not cls.is_uri_property(uri):
+ raise OutOfNamespace(f"{uri} is not in wikidata property namespace")
+ return uri.replace(f"http://www.wikidata.org/prop/", "")
+
+ @classmethod
+ def get_prop_uri(cls, pid: str):
+ return f"http://www.wikidata.org/prop/{pid}"
+
+ def get_qnode_label(self, uri_or_id: str):
+ qid = self.get_qid_fn[uri_or_id[0]](uri_or_id)
+ if qid in self.wdclasses:
+ return f"{self.wdclasses[qid].label} ({qid})"
+ return f"{self.qnodes[qid].label} ({qid})"
+
+ def get_pnode_label(self, uri_or_id: str):
+ pid = self.get_pid_fn[uri_or_id[0]](uri_or_id)
+ # TODO: fix me! should not do this
+ if pid not in self.wdprops:
+ return pid
+ return f"{self.wdprops[pid].label} ({pid})"
+
+
+class WikidataSemanticModelHelper(WDOnt):
+ ID_PROPS = {str(RDFS.label)}
+
+ def norm_sm(self, sm: O.SemanticModel):
+ """ "Normalize the semantic model with the following modifications:
+ 1. Add readable label to edge and class
+ 2. Convert direct link (without statement) to have statement except the id props.
+ """
+ new_sm = sm.clone()
+
+ # update readable label
+ for n in new_sm.iter_nodes():
+ if n.is_class_node:
+ if self.is_uri_qnode(n.abs_uri):
+ n.readable_label = self.get_qnode_label(n.abs_uri)
+ elif n.is_literal_node:
+ if self.is_uri_qnode(n.value):
+ n.readable_label = self.get_qnode_label(n.value)
+ for e in new_sm.iter_edges():
+ if e.abs_uri not in self.ID_PROPS:
+ e.readable_label = self.get_pnode_label(e.abs_uri)
+
+ # convert direct link
+ for edge in list(new_sm.iter_edges()):
+ if edge.abs_uri in self.ID_PROPS:
+ continue
+ source = new_sm.get_node(edge.source)
+ target = new_sm.get_node(edge.target)
+
+ if (not source.is_class_node or (source.is_class_node and source.abs_uri != WDOnt.STATEMENT_URI)) \
+ and (not target.is_class_node or (target.is_class_node and target.abs_uri != WDOnt.STATEMENT_URI)):
+ # this is direct link, we replace its edge
+ assert len(new_sm.get_edges_between_nodes(source.id, target.id)) == 1
+ new_sm.remove_edges_between_nodes(source.id, target.id)
+ stmt = O.ClassNode(str(uuid4()), WDOnt.STATEMENT_URI, WDOnt.STATEMENT_REL_URI, False,
+ "Statement")
+ new_sm.add_node(stmt)
+
+ new_sm.add_edge(O.Edge(source=edge.source,
+ target=stmt.id,
+ abs_uri=edge.abs_uri, rel_uri=edge.rel_uri,
+ approximation=edge.approximation, readable_label=edge.readable_label))
+ new_sm.add_edge(O.Edge(source=stmt.id,
+ target=edge.target,
+ abs_uri=edge.abs_uri, rel_uri=edge.rel_uri,
+ approximation=edge.approximation, readable_label=edge.readable_label))
+ return new_sm
+
+ @staticmethod
+ def minify_sm(sm: O.SemanticModel):
+ """This is a reverse function of `norm_sm`:
+ 1. Remove an intermediate statement if it doesn't have any qualifiers
+ """
+ new_sm = sm.clone()
+ for n in sm.iter_nodes():
+ if n.is_class_node and n.abs_uri == WDOnt.STATEMENT_URI:
+ inedges = sm.incoming_edges(n.id)
+ outedges = sm.outgoing_edges(n.id)
+ if len(outedges) == 1 and outedges[0].abs_uri == inedges[0].abs_uri:
+ # no qualifiers
+ new_sm.remove_node(n.id)
+ for inedge in inedges:
+ assert inedge.abs_uri == outedges[0].abs_uri
+ new_sm.add_edge(O.Edge(inedge.source, outedges[0].target, inedge.abs_uri, inedge.rel_uri,
+ # just in case user misannotate to not include approximation in both links
+ inedge.approximation or outedges[0].approximation,
+ inedge.readable_label))
+ return new_sm
+
+ def gen_equivalent_sm(self, sm: O.SemanticModel, strict: bool = True, force_inversion: bool = False):
+ """Given a semantic model (not being modified), generate equivalent models by inferring inverse properties.
+
+ Parameters
+ ----------
+ sm: the input semantic model (original)
+ strict: whether to throw exception when target of an inverse property is not a class.
+ force_inversion: only work when strict mode is set to false. Without force_inverse, we skip inverse properties,
+ otherwise, we generate an inverse model with a special class: wikibase:DummyClassForInversion
+
+ Returns
+ -------
+
+ """
+ """Given an semantic model (not being modified), generate equivalent models
+ by inferring inverse properties. Running on strict mode mean it will check if the invertible property is apply
+ to a non-class node (column that .
+
+ Currently, we only inverse the properties, not qualifiers.
+ """
+ sm = self.norm_sm(sm)
+ invertible_stmts = []
+ is_class_fn = lambda n1: n1.is_class_node or (n1.is_literal_node and self.is_uri_qnode(n1.value))
+
+ for n in sm.iter_nodes():
+ if n.is_class_node and WDOnt.is_uri_statement(n.abs_uri):
+ inedges = sm.incoming_edges(n.id)
+ outedges = sm.outgoing_edges(n.id)
+ # only has one prop
+ prop, = list({inedge.abs_uri for inedge in inedges})
+ pid = self.get_prop_id(prop)
+ stmt_has_value = False
+ for outedge in outedges:
+ if outedge.abs_uri != prop:
+ # assert len(self.wdprops[self.get_prop_id(outedge.abs_uri)].inverse_properties) == 0, "Just to make sure" \
+ # "that qualifiers is not invertable. Otherwise, this algorithm will missing one generated SMs"
+ # character role has an inverse property: performer. They can be used as qualifier so nothing to do here just pass
+ pass
+ else:
+ stmt_has_value = True
+ if len(self.wdprops[pid].inverse_properties) > 0 and stmt_has_value:
+ # invertible property
+ # people seem to misunderstand what inverse_property means in RDF;
+ # inverse doesn't apply to data property but only object property.
+ # so we catch the error here to detect what we should fix.
+ outedge, = [outedge for outedge in outedges if outedge.abs_uri == prop]
+ targets_are_class = is_class_fn(sm.get_node(outedge.target))
+ if targets_are_class:
+ invertible_stmts.append(n)
+ elif strict:
+ raise Exception(f"{pid} is not invertible")
+ elif force_inversion:
+ assert sm.get_node(outedge.target).is_data_node, "Clearly the model is wrong, you have an inverse property to a literal node"
+ invertible_stmts.append(n)
+
+ # we have N statement, so we are going to have N! - 1 ways. It's literally a cartesian product
+ all_choices = []
+ for stmt in invertible_stmts:
+ # assume that each statement only has one incoming link! fix the for loop if this assumption doesn't hold
+ inedge, = sm.incoming_edges(stmt.id)
+ choice = [(stmt, None, None)]
+ for invprop in self.wdprops[self.get_prop_id(inedge.abs_uri)].inverse_properties:
+ choice.append((stmt, self.get_prop_uri(invprop), f"p:{invprop}"))
+ all_choices.append(choice)
+
+ n_choices = np.prod([len(c) for c in all_choices]) - 1
+ if n_choices > 256:
+ raise sm_metrics.PermutationExploding("Too many possible semantic models")
+
+ all_choices = list(itertools.product(*all_choices))
+ assert all(invprop is None for _, invprop, _ in all_choices[0]), "First choice is always the current semantic model"
+ new_sms = [sm]
+ for choice in all_choices[1:]:
+ new_sm = sm.clone()
+ # we now change the statement from original prop to use the inverse prop (change direction)
+ # if the invprop is not None
+ for stmt, invprop_abs_uri, invprop_rel_uri in choice:
+ if invprop_abs_uri is None:
+ continue
+ readable_label = self.get_pnode_label(invprop_abs_uri)
+ # assume that each statement only has one incoming link! fix the for loop if this assumption doesn't hold
+ inedge, = sm.incoming_edges(stmt.id)
+ # statement must have only one property
+ outedge, = [outedge for outedge in sm.outgoing_edges(stmt.id) if outedge.abs_uri == inedge.abs_uri]
+ assert len(new_sm.get_edges_between_nodes(inedge.source, stmt.id)) == 1 and \
+ len(new_sm.get_edges_between_nodes(stmt.id, outedge.target)) == 1
+ new_sm.remove_edges_between_nodes(inedge.source, stmt.id)
+ new_sm.remove_edges_between_nodes(stmt.id, outedge.target)
+
+ target = sm.get_node(outedge.target)
+ if not is_class_fn(target):
+ assert target.is_data_node
+ dummy_class_node = O.ClassNode(str(uuid4()), abs_uri='http://wikiba.se/ontology#DummyClassForInversion',
+ rel_uri='wikibase:DummyClassForInversion')
+ new_sm.add_node(dummy_class_node)
+ new_sm.add_edge(O.Edge(source=dummy_class_node.id, target=target.id, abs_uri=str(RDFS.label),
+ rel_uri="rdfs:label"))
+ outedge_target = dummy_class_node.id
+ else:
+ outedge_target = outedge.target
+ new_sm.add_edge(O.Edge(source=outedge_target, target=stmt.id,
+ abs_uri=invprop_abs_uri, rel_uri=invprop_rel_uri,
+ approximation=outedge.approximation, readable_label=readable_label))
+ new_sm.add_edge(O.Edge(source=stmt.id, target=inedge.source,
+ abs_uri=invprop_abs_uri, rel_uri=invprop_rel_uri,
+ approximation=inedge.approximation, readable_label=readable_label))
+ new_sms.append(new_sm)
+ return new_sms
+
+ def viz_sm(self, sm: O.SemanticModel, outdir, graph_id) -> 'WikidataSemanticModelHelper':
+ colors = {
+ SMNodeType.Column: dict(fill='#ffd666', stroke='#874d00'),
+ SMNodeType.Class: dict(fill="#b7eb8f", stroke="#135200"),
+ SMNodeType.Statement: dict(fill="#d9d9d9", stroke="#434343"),
+ SMNodeType.Entity: dict(fill="#C6E5FF", stroke="#5B8FF9"),
+ SMNodeType.Literal: dict(fill="#C6E5FF", stroke="#5B8FF9"),
+ }
+
+ def node_fn(uid, udata):
+ u: O.Node = udata['data']
+ nodetype = self.get_node_type(u)
+ if isinstance(u, O.DataNode):
+ label = u.label
+ elif isinstance(u, O.ClassNode):
+ if u.abs_uri == WDOnt.STATEMENT_URI:
+ label = ""
+ elif u.readable_label is None:
+ if self.is_uri_qnode(u.abs_uri):
+ label = self.get_qnode_label(u.abs_uri)
+ else:
+ label = u.label
+ else:
+ label = u.readable_label
+ else:
+ label = u.readable_label
+
+ return {
+ "label": label,
+ "style": colors[nodetype],
+ "labelCfg": {
+ "style": {
+ "fill": "black",
+ "background": {
+ "padding": [4, 4, 4, 4],
+ "radius": 3,
+ **colors[nodetype]
+ }
+ }
+ },
+ }
+
+ def edge_fn(eid, edata):
+ edge: O.Edge = edata['data']
+ label = edge.readable_label
+ if label is None:
+ if edge.abs_uri not in self.ID_PROPS:
+ label = self.get_pnode_label(edge.abs_uri)
+ else:
+ label = edge.label
+
+ return {
+ "label": label
+ }
+
+ viz_graph(sm.g, node_fn, edge_fn, outdir, graph_id)
+ return self
+
+ def get_node_type(self, n: O.Node):
+ if isinstance(n, O.LiteralNode):
+ if self.is_uri_qnode(n.value):
+ return SMNodeType.Entity
+ return SMNodeType.Literal
+ if isinstance(n, O.DataNode) or (isinstance(n, O.ClassNode) and self.is_uri_column(n.abs_uri)):
+ return SMNodeType.Column
+ if isinstance(n, O.ClassNode):
+ if n.abs_uri == WDOnt.STATEMENT_URI:
+ return SMNodeType.Statement
+ return SMNodeType.Class
+ raise Exception("Unreachable!")
+
+ def get_entity_columns(self, sm: O.SemanticModel) -> List[int]:
+ ent_columns = []
+ for dnode in sm.iter_nodes():
+ if dnode.is_data_node:
+ inedges = sm.incoming_edges(dnode.id)
+ if len(inedges) == 0:
+ continue
+ assert len({edge.abs_uri for edge in inedges}) == 1, inedges
+ edge_abs_uri = inedges[0].abs_uri
+ if edge_abs_uri in self.ID_PROPS:
+ assert len(inedges) == 1, inedges
+ source = sm.get_node(inedges[0].source)
+ assert not self.is_uri_statement(source.abs_uri)
+ ent_columns.append(dnode.col_index)
+ return ent_columns
+
+ @classmethod
+ def is_uri_column(cls, uri: str):
+ """Test if an uri is for specifying the column"""
+ return uri.startswith("http://example.com/table/")
+
+ @staticmethod
+ def get_column_uri(column_index: int):
+ return f"http://example.com/table/{column_index}"
+
+ @staticmethod
+ def get_column_index(uri: str):
+ assert WikidataSemanticModelHelper.is_uri_column(uri)
+ return int(uri.replace("http://example.com/table/", ""))
+
+ def extract_claims(self, tbl: LinkedTable, sm: O.SemanticModel, allow_multiple_ent: bool = True):
+ """Extract claims from the table given a semantic model.
+
+ If an entity doesn't have link, its id will be null
+ """
+ # norm the semantic model first
+ sm = self.norm_sm(sm)
+ schemas = {}
+ for u in sm.iter_nodes():
+ if not u.is_class_node or WDOnt.is_uri_statement(u.abs_uri):
+ continue
+
+ schema = {"props": {}, "subject": None, "sm_node_id": u.id}
+ for us_edge in sm.outgoing_edges(u.id):
+ if us_edge.abs_uri in self.ID_PROPS:
+ v = sm.get_node(us_edge.target)
+ assert v.is_data_node
+ assert schema['subject'] is None
+ schema['subject'] = v.col_index
+ continue
+
+ s = sm.get_node(us_edge.target)
+ assert s.is_class_node and WDOnt.is_uri_statement(s.abs_uri)
+ assert WDOnt.is_uri_property(us_edge.abs_uri)
+
+ pnode = WDOnt.get_prop_id(us_edge.abs_uri)
+ if pnode not in schema['props']:
+ schema['props'][pnode] = []
+
+ stmt = {
+ "index": len(schema['props'][pnode]),
+ 'value': None,
+ 'qualifiers': []
+ }
+ schema['props'][pnode].append(stmt)
+ for sv_edge in sm.outgoing_edges(s.id):
+ v = sm.get_node(sv_edge.target)
+
+ assert WDOnt.is_uri_property(sv_edge.abs_uri)
+ if sv_edge.abs_uri == us_edge.abs_uri:
+ assert stmt['value'] is None, "only one property"
+ # this is property
+ if v.is_class_node:
+ stmt['value'] = {'type': 'classnode', 'value': v.id}
+ elif v.is_data_node:
+ stmt['value'] = {'type': 'datanode', 'value': v.col_index}
+ else:
+ assert v.is_literal_node
+ stmt['value'] = {'type': 'literalnode', 'value': v.value}
+ else:
+ # this is qualifier
+ if v.is_class_node:
+ stmt['qualifiers'].append({'type': 'classnode', 'pnode': WDOnt.get_prop_id(sv_edge.abs_uri), 'value': v.id})
+ elif v.is_data_node:
+ stmt['qualifiers'].append({'type': 'datanode', 'pnode': WDOnt.get_prop_id(sv_edge.abs_uri), 'value': v.col_index})
+ else:
+ assert v.is_literal_node
+ stmt['qualifiers'].append({'type': 'literalnode', 'pnode': WDOnt.get_prop_id(sv_edge.abs_uri), 'value': v.value})
+ schemas[u.id] = schema
+
+ assert all(c.index == ci for ci, c in enumerate(tbl.table.columns)), "Cannot handle table with missing columns yet"
+
+ records = [{} for ri in range(len(tbl.table.columns[0].values))]
+ node2ents = {}
+
+ # extract data props first
+ for cid, schema in schemas.items():
+ ci = schema['subject']
+ col = tbl.table.columns[ci]
+ for ri, val in enumerate(col.values):
+ # get entities
+ qnode_ids = sorted({e.qnode_id for e in tbl.links[ri][ci] if e.qnode_id is not None and e.start < e.end})
+ if len(qnode_ids) == 0:
+ # create new entity
+ ents = [{
+ "id": f"{ri}-{ci}",
+ "column": ci,
+ "row": ri,
+ "uri": None,
+ "label": val,
+ "props": {}
+ }]
+ else:
+ ents = [
+ {
+ "id": qnode_id, "uri": WDOnt.get_qnode_uri(qnode_id),
+ "label": self.qnodes[qnode_id].label, "props": {}
+ }
+ for qnode_id in qnode_ids
+ ]
+
+ if len(ents) > 1:
+ if not allow_multiple_ent:
+ raise Exception("Encounter multiple entities")
+
+ for prop, stmts in schema['props'].items():
+ for ent in ents:
+ assert prop not in ent['props']
+ ent['props'][prop] = [{'value': None, 'qualifiers': {}} for stmt in stmts]
+ for stmt in stmts:
+ # set statement property
+ if stmt['value']['type'] == 'classnode':
+ # do it in later phase
+ pass
+ elif stmt['value']['type'] == 'datanode':
+ tci = stmt['value']['value']
+ ent['props'][prop][stmt['index']]['value'] = tbl.table.columns[tci].values[ri]
+ else:
+ assert stmt['value']['type'] == 'literalnode'
+ ent['props'][prop][stmt['index']]['value'] = stmt['value']['value']
+
+ # set statement qualifiers
+ for qual in stmt['qualifiers']:
+ if qual['pnode'] not in ent['props'][prop][stmt['index']]['qualifiers']:
+ ent['props'][prop][stmt['index']]['qualifiers'][qual['pnode']] = []
+ if qual['type'] == 'classnode':
+ # do it in later phase
+ pass
+ elif qual['type'] == 'datanode':
+ tci = qual['value']
+ ent['props'][prop][stmt['index']]['qualifiers'][qual['pnode']].append(tbl.table.columns[tci].values[ri])
+ elif qual['type'] == 'literalnode':
+ ent['props'][prop][stmt['index']]['qualifiers'][qual['pnode']].append(qual['value'])
+
+ for ent in ents:
+ assert (ent['id'], ci) not in records[ri]
+ records[ri][ent['id'], ci] = ent
+ node2ents[schema['sm_node_id'], ri] = [ent for ent in ents]
+
+ for cid, schema in schemas.items():
+ ci = schema['subject']
+ col = tbl.table.columns[ci]
+ for ri in range(len(col.values)):
+ ulst = node2ents[schema['sm_node_id'], ri]
+ for prop, stmts in schema['props'].items():
+ for stmt in stmts:
+ if stmt['value']['type'] == 'classnode':
+ vlst = node2ents[stmt['value']['value'], ri]
+ for u in ulst:
+ assert len(vlst) > 0
+ u['props'][prop][stmt['index']]['value'] = vlst[0]['id']
+ if len(vlst) > 1:
+ # this statement must not have other qualifiers, so that v can be a list
+ # and we can create extra statement
+ assert len(stmt['qualifiers']) == 0
+ for v in vlst[1:]:
+ u['props'][prop].append({'value': v['id'], 'qualifiers': {}})
+ for qual in stmt['qualifiers']:
+ if qual['type'] == 'classnode':
+ for u in ulst:
+ vlst = node2ents[qual['value'], ri]
+ u['props'][prop][stmt['index']]['qualifiers'][qual['pnode']] = [v['id'] for v in vlst]
+
+ new_records = []
+ for ri, record in enumerate(records):
+ new_record = {}
+ for (ent_id, ci), ent in record.items():
+ if ent_id in new_record:
+ # merge the entity
+ for pid, _stmts in ent['props'].items():
+ if pid not in new_record[ent_id]['props']:
+ new_record[ent_id]['props'][pid] = _stmts
+ else:
+ for _stmt in _stmts:
+ if not any(x == _stmt for x in new_record[ent_id]['props'][pid]):
+ new_record[ent_id]['props'][pid].append(_stmt)
+ else:
+ new_record[ent_id] = ent
+ new_records.append(new_record)
+ return records
+
+
+
diff --git a/grams/algorithm/type_feature.py b/grams/algorithm/type_feature.py
new file mode 100644
index 0000000..08ecbae
--- /dev/null
+++ b/grams/algorithm/type_feature.py
@@ -0,0 +1,105 @@
+from collections import defaultdict
+from typing import Dict, List
+
+import networkx as nx
+import numpy as np
+
+from grams.algorithm.data_graph import CellNode
+from grams.algorithm.literal_match import TextParser
+from grams.algorithm.semantic_graph import SGColumnNode
+from grams.inputs.linked_table import LinkedTable
+from kgdata.wikidata.models import QNode, WDProperty, WDQuantityPropertyStats
+
+
+class TypeFeatureExtraction:
+ Freq = "FrequencyOfType"
+ FreqOverRow = "FreqOfTypeOverRow"
+
+ def __init__(self,
+ table: LinkedTable, sg: nx.MultiDiGraph, dg: nx.MultiDiGraph,
+ qnodes: Dict[str, QNode], wdprops: Dict[str, WDProperty],
+ wd_num_prop_stats: Dict[str, WDQuantityPropertyStats]):
+ self.table = table
+ self.sg = sg
+ self.dg = dg
+ self.qnodes = qnodes
+ self.wdprops = wdprops
+ self.wd_num_prop_stats = wd_num_prop_stats
+ self.text_parser = TextParser()
+
+ # self.transitive_props = [p.id for p in self.wdprops.values() if p.is_transitive()]
+ self.hierarchy_props = {"P131", "P276"}
+
+ def extract_features(self):
+ freq_type = {}
+ freq_over_row = {}
+ cell2qnodes = {}
+ column2types = {}
+
+ for uid, udata in self.sg.nodes(data=True):
+ u: SGColumnNode = udata['data']
+ if not u.is_column:
+ continue
+
+ # cells in this column
+ cells: List[CellNode] = [self.dg.nodes[cid]['data'] for cid in u.nodes]
+ covered_fractions = [
+ sum(span.length for spans in cell.qnodes_span.values() for span in spans) / max(len(cell.value), 1)
+ for cell in cells
+ if len(cell.qnode_ids) > 0
+ ]
+ if len(covered_fractions) == 0:
+ continue
+ avg_covered_fractions = np.mean(covered_fractions)
+ if avg_covered_fractions < 0.8:
+ continue
+
+ for cell in cells:
+ self.add_merge_qnodes(cell, cell2qnodes)
+
+ type2freq = defaultdict(int)
+ for cell in cells:
+ classes = set()
+ for qnode in cell2qnodes[cell.id]:
+ for stmt in qnode.props.get("P31", []):
+ classes.add(stmt.value.as_qnode_id())
+ for c in classes:
+ type2freq[c] += 1
+
+ for c, freq in type2freq.items():
+ freq_type[uid, c] = freq
+ freq_over_row[uid, c] = freq / self.table.size()
+ column2types[uid] = list(type2freq.keys())
+
+ return {
+ self.Freq: freq_type,
+ self.FreqOverRow: freq_over_row,
+ "_column_to_types": column2types
+ }
+
+ def add_merge_qnodes(self, cell: CellNode, cell2qnodes: Dict[str, List[QNode]]):
+ # merge qnodes that are sub of each other
+ # attempt to merge qnodes (spatial) if they are contained in each other
+ # we should go even higher order
+ if len(cell.qnode_ids) > 1:
+ # attempt to merge qnodes (spatial) if they are contained in each other
+ # we should go even higher order
+ ignore_qnodes = set()
+ for q0_id in cell.qnode_ids:
+ q0 = self.qnodes[q0_id]
+ vals = {
+ stmt.value.as_qnode_id()
+ for p in self.hierarchy_props
+ for stmt in q0.props.get(p, [])
+ }
+ for q1_id in cell.qnode_ids:
+ if q0_id == q1_id:
+ continue
+ if q1_id in vals:
+ # q0 is inside q1, ignore q1
+ ignore_qnodes.add(q1_id)
+ cell2qnodes[cell.id] = [self.qnodes[q_id] for q_id in cell.qnode_ids if q_id not in ignore_qnodes]
+ elif len(cell.qnode_ids) > 0:
+ cell2qnodes[cell.id] = [self.qnodes[cell.qnode_ids[0]]]
+ else:
+ cell2qnodes[cell.id] = []
\ No newline at end of file
diff --git a/grams/config.py b/grams/config.py
new file mode 100644
index 0000000..bed350b
--- /dev/null
+++ b/grams/config.py
@@ -0,0 +1,13 @@
+import os
+from pathlib import Path
+
+from omegaconf import OmegaConf
+
+ROOT_DIR = Path(os.path.abspath(__file__)).parent.parent
+if 'DATA_DIR' not in os.environ:
+ DATA_DIR = ROOT_DIR / "data"
+else:
+ DATA_DIR = Path(os.environ['DATA_DIR'])
+
+assert DATA_DIR.exists(), f"{DATA_DIR} does not exist"
+DEFAULT_CONFIG = OmegaConf.load(ROOT_DIR / "grams.yaml")
diff --git a/grams/db.py b/grams/db.py
new file mode 100644
index 0000000..03289db
--- /dev/null
+++ b/grams/db.py
@@ -0,0 +1,19 @@
+from pathlib import Path
+
+from sm.misc import RocksDBStore
+from typing import Optional, Union
+
+
+class Wikipedia2WikidataDB(RocksDBStore[str, str]):
+ instance = None
+
+ @staticmethod
+ def get_instance(dbfile: Optional[Union[Path, str]] = None, read_only: bool = False):
+ if Wikipedia2WikidataDB.instance is None:
+ Wikipedia2WikidataDB.instance = Wikipedia2WikidataDB(dbfile, read_only=read_only)
+ return Wikipedia2WikidataDB.instance
+
+ def deserialize(self, value):
+ return value.decode()
+
+
diff --git a/grams/entity_linker_interface.py b/grams/entity_linker_interface.py
new file mode 100644
index 0000000..5db239f
--- /dev/null
+++ b/grams/entity_linker_interface.py
@@ -0,0 +1,38 @@
+import glob
+from collections import defaultdict
+from pathlib import Path
+from typing import List, Dict, Tuple, Callable, Any, Optional
+import grams.misc as M
+from grams.config import ROOT_DIR
+
+
+def clean_table_linker(infile, outfile):
+ rows = M.deserialize_csv(infile)
+ cname2idx = {v: k for k, v in enumerate(rows[0])}
+
+ cells = defaultdict(dict)
+ for row in rows[1:]:
+ ci = int(row[cname2idx['column']])
+ ri = int(row[cname2idx['row']])
+ gt_ent = row[cname2idx['GT_kg_id']]
+ pred_ent = row[cname2idx['kg_id']]
+ score = row[cname2idx['siamese_pred']]
+
+ if (ri, ci) in cells:
+ assert cells[ri, ci]['gt'] == gt_ent
+ cells[ri, ci]['gt'] = gt_ent
+ if 'pred_ent' not in cells[ri, ci]:
+ cells[ri, ci]['pred_ent'] = []
+ cells[ri, ci]['pred_ent'].append(f"{pred_ent}:{score}")
+
+ links = []
+ for (ri, ci), o in cells.items():
+ links.append([ri, ci, o['gt']] + o['pred_ent'])
+ M.serialize_csv(links, outfile, delimiter="\t")
+
+
+if __name__ == '__main__':
+ for infile in glob.glob(str(ROOT_DIR / "examples/t2dv2/tables/*.table_linker.csv")):
+ infile = Path(infile)
+ outfile = infile.parent / f"{infile.name.split('.')[0]}.candidates.tsv"
+ clean_table_linker(infile, outfile)
\ No newline at end of file
diff --git a/grams/evaluation/__init__.py b/grams/evaluation/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/grams/evaluation/prelude.py b/grams/evaluation/prelude.py
new file mode 100644
index 0000000..59db79a
--- /dev/null
+++ b/grams/evaluation/prelude.py
@@ -0,0 +1 @@
+from . import sm_metrics
diff --git a/grams/evaluation/sm_metrics.py b/grams/evaluation/sm_metrics.py
new file mode 100644
index 0000000..e392b6b
--- /dev/null
+++ b/grams/evaluation/sm_metrics.py
@@ -0,0 +1,586 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+import math
+import os
+from abc import ABC
+from dataclasses import dataclass
+from enum import IntEnum
+from itertools import permutations, chain
+from typing import Dict, Tuple, List, Set, Optional, Callable, Generator, TYPE_CHECKING
+
+from pyrsistent import pvector, PVector
+from typing_extensions import TypedDict
+
+if TYPE_CHECKING:
+ from grams.outputs import SemanticModel
+
+"""
+Compute precision, recall and f1 score of the semantic model according to Mohsen paper.
+
+Convention: x' and x are nodes in predicted model and gold model, respectively.
+"""
+
+
+class PermutationExploding(Exception):
+ pass
+
+
+class Node(object):
+ def __init__(self, id: str, label: str) -> None:
+ self.id: str = id
+ self.label: str = label
+ self.incoming_links: List[Link] = []
+ self.outgoing_links: List[Link] = []
+
+ @staticmethod
+ def add_incoming_link(self: 'Node', link: 'Link'):
+ self.incoming_links.append(link)
+ link.target = self
+
+ @staticmethod
+ def add_outgoing_link(self: 'Node', link: 'Link'):
+ self.outgoing_links.append(link)
+ link.source = self
+
+ def __str__(self):
+ return "Node(id=%s, label=%s)" % (self.id, self.label)
+
+
+@dataclass(frozen=True, eq=True)
+class NodeTriple:
+ __slots__ = ('source_id', 'link_label', 'target_id')
+ source_id: str
+ link_label: str
+ target_id: str
+
+ def __setstate__(self, state):
+ assert state[0] is None and isinstance(state[1], dict)
+ for slot, value in state[1].items():
+ object.__setattr__(self, slot, value)
+
+
+class Link(object):
+ def __init__(self, id: int, label: str, source_id: str, target_id: str) -> None:
+ self.id: int = id
+ self.label: str = label
+ self.target_id: str = target_id
+ self.source_id: str = source_id
+ # noinspection PyTypeChecker
+ self.source: Node = None
+ # noinspection PyTypeChecker
+ self.target: Node = None
+
+
+class LabelGroup(object):
+ """Represent a group of nodes that have same label"""
+
+ def __init__(self, nodes: List[Node]) -> None:
+ self.nodes: List[Node] = nodes
+ self.node_triples: Set[NodeTriple] = {
+ NodeTriple(link.source_id, link.label, link.target_id)
+ for node in self.nodes for link in chain(node.incoming_links, node.outgoing_links)
+ }
+ self.size: int = len(nodes)
+
+ def __repr__(self):
+ return "(#nodes=%d)" % (len(self.nodes))
+
+ # noinspection PyUnusedLocal
+ @staticmethod
+ def group_by_structures(group: 'LabelGroup', pred_group: 'LabelGroup'):
+ """
+ A structure of a node is defined by its links, or we can treat it as a set of triple.
+ Unbounded nodes should be assumed to be different, therefore a node have unbounded nodes will have
+ it own structure group.
+ We need not consider triple that are impossible to map to node in pred_group. This trick will improve
+ the performance.
+ """
+ # TODO: implement it
+ return [StructureGroup([n]) for n in group.nodes]
+
+
+class StructureGroup(object):
+ """Represent a group of nodes that have same structure"""
+
+ def __init__(self, nodes: List[Node]) -> None:
+ self.nodes: List[Node] = nodes
+ self.size: int = len(nodes)
+
+
+class PairLabelGroup(object):
+ def __init__(self, label: str, X: LabelGroup, X_prime: LabelGroup) -> None:
+ self.label = label
+ self.X: LabelGroup = X
+ self.X_prime: LabelGroup = X_prime
+
+ def __repr__(self):
+ return "(label=%s, X=%s, X_prime=%s)" % (self.label, self.X, self.X_prime)
+
+
+class DependentGroups(object):
+ """Represent a list of groups of nodes that are dependent on each other"""
+
+ def __init__(self, pair_groups: List[PairLabelGroup]):
+ self.pair_groups: List[PairLabelGroup] = pair_groups
+ self.X_triples: Set[NodeTriple] = pair_groups[0].X.node_triples
+ self.X_prime_triples: Set[NodeTriple] = pair_groups[0].X_prime.node_triples
+
+ for pair in pair_groups[1:]:
+ self.X_triples = self.X_triples.union(pair.X.node_triples)
+ self.X_prime_triples = self.X_prime_triples.union(pair.X_prime.node_triples)
+
+ # a mapping from (source id, target id) to list of predicates
+ # use this for computing if we want to take into account the ancestor/descendant in the predicates
+ self.x_pairs = {}
+ for triple in self.X_triples:
+ if (triple.source_id, triple.target_id) not in self.x_pairs:
+ self.x_pairs[triple.source_id, triple.target_id] = []
+ self.x_pairs[triple.source_id, triple.target_id].append(triple.link_label)
+
+ def get_n_permutations(self):
+ n_permutation = 1
+ for pair_group in self.pair_groups:
+ n = max(pair_group.X.size, pair_group.X_prime.size)
+ m = min(pair_group.X.size, pair_group.X_prime.size)
+ n_permutation *= math.factorial(n) / math.factorial(n - m)
+
+ return n_permutation
+
+
+class Bijection(object):
+ """A bijection defined a one-one mapping from x' => x"""
+
+ def __init__(self) -> None:
+ # a map from x' => x (pred_sm to gold_sm)
+ self.prime2x: Dict[str, str] = {}
+ # map from x => x'
+ self.x2prime: Dict[str, str] = {}
+
+ @staticmethod
+ def construct_from_mapping(mapping: List[Tuple[Optional[int], Optional[int]]]) -> 'Bijection':
+ """
+ :param mapping: a list of map from x' => x
+ """
+ self = Bijection()
+ self.prime2x = {x_prime: x for x_prime, x in mapping}
+ self.x2prime = {x: x_prime for x_prime, x in mapping}
+ return self
+
+ def extends(self, bijection: 'Bijection') -> 'Bijection':
+ another = Bijection()
+ another.prime2x = dict(self.prime2x)
+ another.prime2x.update(bijection.prime2x)
+ another.x2prime = dict(self.x2prime)
+ another.x2prime.update(bijection.x2prime)
+ return another
+
+ def extends_(self, bijection: 'Bijection') -> None:
+ self.prime2x.update(bijection.prime2x)
+ self.x2prime.update(bijection.x2prime)
+
+ def is_gold_node_bounded(self, node_id: str) -> bool:
+ return node_id in self.x2prime
+
+ def is_pred_node_bounded(self, node_id: str) -> bool:
+ return node_id in self.prime2x
+
+
+class IterGroupMapsUsingGroupingArgs(object):
+ def __init__(self, node_index: int, bijection: PVector, G_sizes) -> None:
+ self.node_index: int = node_index
+ self.bijection: PVector = bijection
+ self.G_sizes = G_sizes
+
+
+class IterGroupMapsGeneralApproachArgs(object):
+ def __init__(self, node_index: int, bijection: PVector) -> None:
+ self.node_index: int = node_index
+ self.bijection: PVector = bijection
+
+
+class FindBestMapArgs(object):
+ def __init__(self, group_index: int, bijection: Bijection) -> None:
+ self.group_index: int = group_index
+ self.bijection = bijection
+
+
+class ScoringFn:
+ # noinspection PyMethodMayBeStatic
+ def get_match_score(self, pred_predicate: str, target_predicate: str) -> float:
+ return int(pred_predicate == target_predicate)
+
+
+def find_best_map(dependent_group: DependentGroups, bijection: Bijection, scoring_fn: ScoringFn) -> Bijection:
+ terminate_index: int = len(dependent_group.pair_groups)
+ # This code find the size of this
+ # array: sum([min(gold_group.size, pred_group.size) for gold_group, pred_group in dependent_group.groups])
+ call_stack = [FindBestMapArgs(group_index=0, bijection=bijection)]
+ n_called = 0
+
+ best_map = None
+ best_score = -1
+
+ while True:
+ call_args = call_stack.pop()
+ n_called += 1
+ if call_args.group_index == terminate_index:
+ # it is terminated, calculate score
+ score = eval_score(dependent_group, call_args.bijection, scoring_fn)
+ if score > best_score:
+ best_score = score
+ best_map = call_args.bijection
+ else:
+ pair_group = dependent_group.pair_groups[call_args.group_index]
+ X, X_prime = pair_group.X, pair_group.X_prime
+ for group_map in iter_group_maps(X, X_prime, call_args.bijection):
+ bijection = Bijection.construct_from_mapping(group_map)
+
+ call_stack.append(FindBestMapArgs(
+ group_index=call_args.group_index + 1,
+ bijection=call_args.bijection.extends(bijection)))
+
+ if len(call_stack) == 0:
+ break
+
+ return best_map
+
+
+def get_unbounded_nodes(X: LabelGroup, is_bounded_func: Callable[[str], bool]) -> List[Node]:
+ """Get nodes of a label group which have not been bounded by a bijection"""
+ unbounded_nodes = []
+
+ for x in X.nodes:
+ for link in x.incoming_links:
+ if not is_bounded_func(link.source_id):
+ unbounded_nodes.append(link.source)
+
+ for link in x.outgoing_links:
+ if not is_bounded_func(link.target_id):
+ unbounded_nodes.append(link.target)
+
+ return unbounded_nodes
+
+
+def get_common_unbounded_nodes(X: LabelGroup, X_prime: LabelGroup, bijection: Bijection) -> Set[str]:
+ """Finding unbounded nodes in X and X_prime that have same labels"""
+ unbounded_X = get_unbounded_nodes(X, bijection.is_gold_node_bounded)
+ unbounded_X_prime = get_unbounded_nodes(X_prime, bijection.is_pred_node_bounded)
+
+ labeled_unbounded_X = {}
+ labeled_unbounded_X_prime = {}
+ for x in unbounded_X:
+ if x.label not in labeled_unbounded_X:
+ labeled_unbounded_X[x.label] = []
+ labeled_unbounded_X[x.label].append(x)
+
+ for x in unbounded_X_prime:
+ if x.label not in labeled_unbounded_X_prime:
+ labeled_unbounded_X_prime[x.label] = []
+ labeled_unbounded_X_prime[x.label].append(x)
+
+ common_unbounded_nodes = set(labeled_unbounded_X.keys()).intersection(labeled_unbounded_X_prime.keys())
+ return common_unbounded_nodes
+
+
+def group_dependent_elements(dependency_map: List[List[int]]) -> List[int]:
+ # algorithm to merge the dependencies
+ # input:
+ # - dependency_map: [, ...] list of dependencies where element at ith position is list of index of elements
+ # that element at ith position depends upon.
+ # output:
+ # - dependency_groups: [, ...] list of group id, where element at ith position is group id that element belongs to
+ dependency_groups: List[int] = [-1 for _ in range(len(dependency_map))]
+ invert_dependency_groups = {}
+
+ for i, g in enumerate(dependency_map):
+ dependent_elements = g + [i]
+ groups = {dependency_groups[j] for j in dependent_elements}
+ valid_groups = groups.difference([-1])
+ if len(valid_groups) == 0:
+ group_id = len(invert_dependency_groups)
+ invert_dependency_groups[group_id] = set()
+ else:
+ group_id = next(iter(valid_groups))
+
+ if -1 in groups:
+ # map unbounded elements to group has group_id
+ for j in dependent_elements:
+ if dependency_groups[j] == -1:
+ dependency_groups[j] = group_id
+ invert_dependency_groups[group_id].add(j)
+
+ for another_group_id in valid_groups.difference([group_id]):
+ for j in invert_dependency_groups[another_group_id]:
+ dependency_groups[j] = group_id
+ invert_dependency_groups[group_id].add(j)
+
+ return dependency_groups
+
+
+def split_by_dependency(map_groups: List[PairLabelGroup], bijection: Bijection) -> List[DependentGroups]:
+ """This method takes a list of groups (X, X') and group them based on their dependencies.
+ D = {D1, D2, …} s.t for all Di, Dj, (Xi, Xi') in Di, (Xj, Xj’) in Dj, they are independent
+
+ Two groups of nodes are dependent when at least one unbounded nodes in a group is a label of other group.
+ For example, "actor_appellation" has link to "type", so group "actor_appellation" depends on group "type"
+ """
+ group_label2idx = {map_group.label: i for i, map_group in enumerate(map_groups)}
+
+ # build group dependencies
+ dependency_map = [[] for _ in range(len(map_groups))]
+ for i, map_group in enumerate(map_groups):
+ X, X_prime = map_group.X, map_group.X_prime
+ common_labels = get_common_unbounded_nodes(X, X_prime, bijection)
+
+ for common_label in common_labels:
+ group_id = group_label2idx[common_label]
+ dependency_map[i].append(group_id)
+
+ dependency_groups = group_dependent_elements(dependency_map)
+ dependency_pair_groups: Dict[int, List[PairLabelGroup]] = {}
+ dependency_map_groups: List[DependentGroups] = []
+
+ for i, map_group in enumerate(map_groups):
+ if dependency_groups[i] not in dependency_pair_groups:
+ dependency_pair_groups[dependency_groups[i]] = []
+ dependency_pair_groups[dependency_groups[i]].append(map_group)
+
+ for pair_groups in dependency_pair_groups.values():
+ dependency_map_groups.append(DependentGroups(pair_groups))
+
+ return dependency_map_groups
+
+
+# noinspection PyUnusedLocal
+def iter_group_maps(X: LabelGroup, X_prime: LabelGroup,
+ bijection: Bijection) -> Generator[List[Tuple[int, int]], None, None]:
+ if X.size < X_prime.size:
+ return iter_group_maps_general_approach(X, X_prime)
+ else:
+ G = LabelGroup.group_by_structures(X, X_prime)
+ return iter_group_maps_using_grouping(X_prime, G)
+
+
+def iter_group_maps_general_approach(X: LabelGroup, X_prime: LabelGroup) -> Generator[List[Tuple[int, int]], None, None]:
+ """
+ Generate all mapping from X to X_prime
+ NOTE: |X| < |X_prime|
+
+ Return mapping from (x_prime to x)
+ """
+ mapping_mold: List[Optional[str]] = [None for _ in range(X_prime.size)]
+
+ for perm in permutations(range(X_prime.size), X.size):
+ mapping: List[Tuple[str, str]] = []
+ for i, j in enumerate(perm):
+ mapping_mold[j] = X.nodes[i].id
+
+ for i in range(X_prime.size):
+ mapping.append((X_prime.nodes[i].id, mapping_mold[i]))
+ mapping_mold[i] = None
+ yield mapping
+
+
+def iter_group_maps_using_grouping(X_prime: LabelGroup, G: List[StructureGroup]) -> Generator[List[Tuple[int, int]], None, None]:
+ """
+ Generate all mapping from X_prime to G (nodes in X grouped by their structures)
+ NOTE: |X_prime| <= |X|
+
+ Return mapping from (x_prime to x)
+ """
+ G_sizes = pvector((g.size for g in G))
+ bijection: PVector = pvector([-1 for _ in range(X_prime.size)])
+ terminate_index: int = X_prime.size
+
+ call_stack: List[IterGroupMapsUsingGroupingArgs] = [
+ IterGroupMapsUsingGroupingArgs(node_index=0, bijection=bijection, G_sizes=G_sizes)
+ ]
+
+ while True:
+ call_args = call_stack.pop()
+
+ if call_args.node_index == terminate_index:
+ # convert bijection into a final mapping
+ G_numerator = [0 for _ in range(len(G))]
+ bijection = call_args.bijection
+ mapping = []
+ for i in range(len(bijection)):
+ x_prime = X_prime.nodes[i].id
+ x = G[bijection[i]].nodes[G_numerator[bijection[i]]].id
+
+ G_numerator[bijection[i]] += 1
+ mapping.append((x_prime, x))
+
+ yield mapping
+ else:
+ for i, G_i in enumerate(G):
+ if call_args.G_sizes[i] == 0:
+ continue
+
+ bijection = call_args.bijection.set(call_args.node_index, i)
+ G_sizes = call_args.G_sizes.set(i, call_args.G_sizes[i] - 1)
+
+ call_stack.append(
+ IterGroupMapsUsingGroupingArgs(
+ node_index=call_args.node_index + 1, bijection=bijection, G_sizes=G_sizes))
+
+ if len(call_stack) == 0:
+ break
+
+
+def prepare_args(gold_sm: 'SemanticModel', pred_sm: 'SemanticModel') -> List[PairLabelGroup]:
+ """Prepare data for evaluation
+
+ + data_node_mode = 0, mean we don't touch anything (note that the label of data_node must be unique)
+ + data_node_mode = 1, mean we ignore label of data node (convert it to DATA_NODE, DATA_NODE2 if there are duplication columns)
+ + data_node_mode = 2, mean we ignore data node
+ """
+
+ def convert_graph(graph: 'SemanticModel'):
+ node_index: Dict[str, Node] = {}
+
+ for v in graph.iter_nodes():
+ if v.is_class_node:
+ label = v.abs_uri
+ elif v.is_data_node:
+ label = f"C{v.col_index:02d}:{v.label}"
+ else:
+ assert v.is_literal_node
+ label = v.value
+ node_index[v.id] = Node(v.id, label)
+
+ for i, e in enumerate(graph.iter_edges()):
+ link = Link(i, e.abs_uri, e.source, e.target)
+ Node.add_outgoing_link(node_index[e.source], link)
+ Node.add_incoming_link(node_index[e.target], link)
+
+ return node_index
+
+ label2nodes = {}
+ gold_id2node = convert_graph(gold_sm)
+ pred_id2node = convert_graph(pred_sm)
+
+ for node in gold_id2node.values():
+ if node.label not in label2nodes:
+ label2nodes[node.label] = ([], [])
+ label2nodes[node.label][0].append(node)
+ for node in pred_id2node.values():
+ if node.label not in label2nodes:
+ label2nodes[node.label] = ([], [])
+ label2nodes[node.label][1].append(node)
+
+ return [PairLabelGroup(label, LabelGroup(g[0]), LabelGroup(g[1])) for label, g in label2nodes.items()]
+
+
+def eval_score(dependent_groups: DependentGroups, bijection: Bijection, scoring_fn: ScoringFn) -> float:
+ x_pairs = dependent_groups.x_pairs
+ mapped_xprime_triples = {}
+ for triple in dependent_groups.X_prime_triples:
+ if triple.source_id not in bijection.prime2x or triple.target_id not in bijection.prime2x:
+ continue
+
+ s = bijection.prime2x[triple.source_id]
+ o = bijection.prime2x[triple.target_id]
+ if (s, o) in x_pairs:
+ if (s, o) not in mapped_xprime_triples:
+ mapped_xprime_triples[(s, o)] = []
+ mapped_xprime_triples[(s, o)].append(triple.link_label)
+
+ score = 0.0
+ for so, edges in mapped_xprime_triples.items():
+ if len(x_pairs[so]) == 1:
+ gold_edge = x_pairs[so][0]
+ # if save some computation time
+ if len(edges) > 1:
+ edge = max(edges, key=lambda e: scoring_fn.get_match_score(e, gold_edge))
+ else:
+ edge = edges[0]
+ score += scoring_fn.get_match_score(edge, gold_edge)
+ else:
+ free_prime_edge_index = set(range(len(edges)))
+ for gold_edge in x_pairs[so]:
+ edge_idx = max(free_prime_edge_index, key=lambda idx: scoring_fn.get_match_score(edges[idx], gold_edge))
+ free_prime_edge_index.remove(edge_idx)
+ score += scoring_fn.get_match_score(edges[edge_idx], gold_edge)
+
+ return score
+
+
+def precision_recall_f1(gold_sm: 'SemanticModel', pred_sm: 'SemanticModel',
+ scoring_fn: Optional[ScoringFn] = None, debug_dir: str = None):
+ if scoring_fn is None:
+ scoring_fn = ScoringFn()
+ pair_groups: List[PairLabelGroup] = prepare_args(gold_sm, pred_sm)
+
+ mapping = []
+ map_groups: List[PairLabelGroup] = []
+ for pair in pair_groups:
+ X, X_prime = pair.X, pair.X_prime
+
+ if max(X.size, X_prime.size) == 1:
+ x_prime = None if X_prime.size == 0 else X_prime.nodes[0].id
+ x = None if X.size == 0 else X.nodes[0].id
+ mapping.append((x_prime, x))
+ else:
+ map_groups.append(pair)
+
+ bijection = Bijection.construct_from_mapping(mapping)
+ list_of_dependent_groups: List[DependentGroups] = split_by_dependency(map_groups, bijection)
+
+ best_bijections = []
+ n_permutations = sum([dependent_groups.get_n_permutations() for dependent_groups in list_of_dependent_groups])
+
+ # TODO: remove debugging code or change to logging
+ if n_permutations > 50000:
+ print("Number of permutation is: %d" % n_permutations)
+
+ if n_permutations > 1000000:
+ if debug_dir is not None:
+ gold_sm.draw(os.path.join(debug_dir, "/gold.png"))
+ pred_sm.draw(os.path.join(debug_dir, "/pred.png"))
+ for dependent_groups in list_of_dependent_groups:
+ print(dependent_groups.pair_groups)
+ raise PermutationExploding("Cannot run evaluation because number of permutation is too high.")
+
+ for dependent_groups in list_of_dependent_groups:
+ best_bijections.append(find_best_map(dependent_groups, bijection, scoring_fn))
+
+ for best_bijection in best_bijections:
+ bijection = bijection.extends(best_bijection)
+
+ all_groups = DependentGroups(pair_groups)
+
+ TP = eval_score(all_groups, bijection, scoring_fn)
+
+ if len(all_groups.X_triples) == 0:
+ # gold is empty, recall must be 1
+ recall = 1
+ else:
+ recall = TP / len(all_groups.X_triples)
+
+ if len(all_groups.X_prime_triples) == 0:
+ # predict nothing, the precision must be 1
+ precision = 1
+ else:
+ precision = TP / len(all_groups.X_prime_triples)
+
+ if precision == 0 or recall == 0:
+ f1 = 0.0
+ else:
+ f1 = 2 * precision * recall / (precision + recall)
+
+ # remove a useless key which causes confusion
+ if None in bijection.prime2x:
+ bijection.prime2x.pop(None)
+
+ return {
+ 'f1': f1,
+ 'precision': precision,
+ 'recall': recall,
+ '_bijection': bijection,
+ "_n_corrects": TP,
+ "_n_examples": len(all_groups.X_triples),
+ "_n_predictions": len(all_groups.X_prime_triples),
+ "_gold_triples": all_groups.X_triples,
+ "_pred_triples": all_groups.X_prime_triples
+ }
diff --git a/grams/inputs/__init__.py b/grams/inputs/__init__.py
new file mode 100644
index 0000000..df7c73c
--- /dev/null
+++ b/grams/inputs/__init__.py
@@ -0,0 +1 @@
+from grams.inputs.linked_table import *
diff --git a/grams/inputs/linked_table.py b/grams/inputs/linked_table.py
new file mode 100644
index 0000000..d7c92dd
--- /dev/null
+++ b/grams/inputs/linked_table.py
@@ -0,0 +1,144 @@
+from dataclasses import dataclass, asdict
+from hashlib import md5
+from pathlib import Path
+from typing import List, Optional, Union
+from urllib.parse import urlparse
+
+import orjson
+from slugify import slugify
+
+import sm.misc as M
+from sm.inputs.table import ColumnBasedTable, Column
+
+
+@dataclass
+class LinkedTable:
+ # the table that we are working on
+ table: ColumnBasedTable
+
+ context: 'Context'
+
+ # a mapping from (row id, column id) to the list of links attach to that cell
+ links: List[List[List['Link']]]
+
+ @property
+ def id(self):
+ return self.table.table_id
+
+ def size(self):
+ if len(self.table.columns) == 0:
+ return 0
+ return len(self.table.columns[0].values)
+
+ def to_json(self):
+ """This function convert the current table into a dictionary to store in json
+ Note that this function doesn't store the features since it's not all kinds of features are json serializable
+ """
+ return {
+ "table": self.table.to_json(),
+ "context": asdict(self.context),
+ "links": [
+ [
+ [asdict(link) for link in links]
+ for links in rlinks
+ ]
+ for rlinks in self.links
+ ],
+ }
+
+ def get_friendly_fs_id(self):
+ id = self.id
+ if id.startswith("http://") or id.startswith("https://"):
+ if id.find("dbpedia.org") != -1:
+ id = slugify(urlparse(id).path.replace("/resource/", "").replace("/", "_")).replace("-", "_")
+ id += "_" + md5(self.id.encode()).hexdigest()
+ elif id.find("wikipedia.org") != -1:
+ id = slugify(urlparse(id).path.replace("/wiki/", "").replace("/", "_")).replace("-", "_")
+ id += "_" + md5(self.id.encode()).hexdigest()
+ else:
+ raise NotImplementedError()
+
+ return id
+
+ @staticmethod
+ def from_json(odict: dict):
+ tbl = ColumnBasedTable.from_json(odict['table'])
+ context = Context(**odict['context'])
+ links = [
+ [
+ [Link(**link) for link in links]
+ for links in rlinks
+ ]
+ for rlinks in odict['links']
+ ]
+ return LinkedTable(tbl, context, links)
+
+ @staticmethod
+ def from_column_based_table(tbl: ColumnBasedTable):
+ links = []
+ if len(tbl.columns) > 0:
+ links = [
+ [
+ []
+ for ci in range(len(tbl.columns))
+ ]
+ for ri in range(len(tbl.columns[0].values))
+ ]
+ return LinkedTable(tbl, Context(None, None, None), links)
+
+ @staticmethod
+ def from_csv_file(infile: Union[Path, str], link_file: Optional[str]=None, first_row_header: bool = True, table_id: Optional[str] = None):
+ infile = Path(infile)
+ if link_file is None:
+ link_file = infile.parent / f"{infile.stem}.links.tsv"
+ else:
+ link_file = Path(link_file)
+
+ if table_id is None:
+ table_id = infile.stem
+ rows = M.deserialize_csv(infile)
+
+ assert len(rows) > 0, "Empty table"
+ columns = []
+ if first_row_header:
+ headers = rows[0]
+ rows = rows[1:]
+ else:
+ headers = [f'column-{i:03d}' for i in range(len(rows[0]))]
+
+ for ci, cname in enumerate(headers):
+ columns.append(Column(ci, cname, [r[ci] for r in rows]))
+ table = ColumnBasedTable(table_id, columns)
+ links = []
+ for ri in range(len(rows)):
+ links.append([[] for ci in range(len(headers))])
+
+ if link_file.exists():
+ for row in M.deserialize_csv(link_file, delimiter="\t"):
+ ri, ci, ents = int(row[0]), int(row[1]), row[2:]
+ for ent in ents:
+ if ent.startswith("{"):
+ # it's json, encoding the hyperlinks
+ link = Link(**orjson.loads(ent))
+ elif ent.startswith("http"):
+ assert ent.startswith("http://www.wikidata.org/entity/")
+ link = Link(0, len(table.columns[ci][ri]), ent, ent.replace("http://www.wikidata.org/entity/", ""))
+ else:
+ link = Link(0, len(table.columns[ci][ri]), f"http://www.wikidata.org/entity/{ent}", ent)
+ links[ri][ci].append(link)
+ return LinkedTable(table, Context(), links)
+
+
+@dataclass
+class Context:
+ page_title: Optional[str] = None
+ page_url: Optional[str] = None
+ page_qnode: Optional[str] = None
+
+
+@dataclass
+class Link:
+ start: int
+ end: int
+ url: str
+ qnode_id: Optional[str]
diff --git a/grams/main.py b/grams/main.py
new file mode 100644
index 0000000..7e4551b
--- /dev/null
+++ b/grams/main.py
@@ -0,0 +1,288 @@
+from dataclasses import dataclass
+import os
+from operator import itemgetter
+from pathlib import Path
+from typing import List, Dict, Tuple, Callable, Any, Optional, Set, Iterable, Union
+
+import networkx as nx
+import requests
+from omegaconf import OmegaConf
+from rdflib import RDFS
+
+import sm.misc as M
+import sm.outputs as O
+import grams.inputs as I
+
+from grams.algorithm.data_graph import build_data_graph, BuildDGOption
+from grams.algorithm.kg_index import TraversalOption, KGObjectIndex
+from grams.algorithm.semantic_graph import SemanticGraphConstructor, SemanticGraphConstructorArgs
+from grams.algorithm.sm_wikidata import WikidataSemanticModelHelper
+from grams.config import DEFAULT_CONFIG, ROOT_DIR
+
+from grams.algorithm.psl_solver import PSLSteinerTreeSolver
+
+from kgdata.wikidata.models import QNode, WDProperty, WDClass, WDQuantityPropertyStats
+from kgdata.wikidata.db import get_qnode_db, get_wdprop_db, get_wdclass_db, query_wikidata_entities
+
+
+@dataclass
+class Annotation:
+ sm: O.SemanticModel
+ dg: nx.MultiDiGraph
+ sg: nx.MultiDiGraph
+ pred_sg: nx.MultiDiGraph
+ pred_cta: Dict[int, Dict[str, float]]
+
+
+class GRAMS:
+ def __init__(self, data_dir: str, cfg=None, proxy: bool=True):
+ self.timer = M.Timer()
+ self.cfg = cfg if cfg is None else DEFAULT_CONFIG
+
+ with self.timer.watch('init grams db'):
+ self.qnodes = get_qnode_db(os.path.join(data_dir, "qnodes.db"), proxy=proxy, is_singleton=True)
+ self.wdclasses = get_wdclass_db(os.path.join(data_dir, "wdclasses.db"), proxy=proxy, is_singleton=True)
+ self.wdprops = get_wdprop_db(os.path.join(data_dir, "wdprops.db"), proxy=proxy, is_singleton=True)
+ self.wd_numprop_stats = WDQuantityPropertyStats.from_dir(os.path.join(data_dir, "quantity_prop_stats"))
+
+ self.build_dg_option = getattr(BuildDGOption, cfg.data_graph.options[0])
+ for op in cfg.data_graph.options[1:]:
+ self.build_dg_option = self.build_dg_option | getattr(BuildDGOption, op)
+
+ def annotate(self, table: I.LinkedTable, verbose: bool=False):
+ qnode_ids = {link.qnode_id
+ for rlinks in table.links for links in rlinks
+ for link in links if link.qnode_id is not None}
+ with self.timer.watch('retrieving qnodes'):
+ qnodes = self.get_entities(qnode_ids, n_hop=2, verbose=verbose)
+ wdclasses = self.wdclasses.cache_dict()
+
+ with self.timer.watch('build kg object index'):
+ kg_object_index = KGObjectIndex.from_qnodes(
+ qnode_ids, qnodes, self.wdprops,
+ n_hop=self.cfg.data_graph.max_n_hop, traversal_option=TraversalOption.TransitiveOnly)
+
+ with self.timer.watch("build dg & sg"):
+ dg = build_data_graph(table, qnodes, self.wdprops,
+ kg_object_index, max_n_hop=self.cfg.data_graph.max_n_hop,
+ options=self.build_dg_option)
+ constructor = SemanticGraphConstructor([
+ SemanticGraphConstructor.init_sg,
+ ], qnodes, wdclasses, self.wdprops)
+ sg = constructor.run(table, dg, debug=False).sg
+
+ with self.timer.watch('run inference'):
+ psl_solver = PSLSteinerTreeSolver(
+ qnodes, wdclasses, self.wdprops, self.wd_numprop_stats,
+ disable_rules=set(self.cfg.psl.disable_rules), sim_fn=None,
+ # cache_dir=outdir / f"{override_psl_cachedir}cache/psl",
+ postprocessing_method=self.cfg.psl.postprocessing, enable_logging=self.cfg.psl.enable_logging)
+ pred_sg, pred_cta = psl_solver.run(dict(table=table, semanticgraph=sg, datagraph=dg))
+ pred_cta = {int(ci.replace("column-", "")): classes for ci, classes in pred_cta.items()}
+ cta = {ci: max(classes.items(), key=itemgetter(1))[0] for ci, classes in pred_cta.items()}
+
+ sm = self.create_sm_from_cta_cpa(table, pred_sg, cta, qnodes, wdclasses)
+ return Annotation(sm=sm, dg=dg, sg=sg, pred_sg=pred_sg, pred_cta=cta)
+
+ def create_sm_from_cta_cpa(self, table: I.LinkedTable, sg: nx.MultiDiGraph, cta: Dict[int, str], qnodes: Dict[str, QNode], wdclasses: Dict[str, WDClass]):
+ sm = O.SemanticModel()
+ sm_helper = WikidataSemanticModelHelper(qnodes, wdclasses, self.wdprops)
+ # create class nodes first
+ classcount = {}
+ classmap = {}
+ for cid, qnode_id in cta.items():
+ dnode = O.DataNode(id=f'col-{cid}', col_index=cid, label=table.table.columns[cid].name)
+
+ # somehow, they may end-up predict multiple classes, we need to select one
+ if qnode_id.find(" ") != -1:
+ qnode_id = qnode_id.split(" ")[0]
+ curl = sm_helper.get_qnode_uri(qnode_id)
+ cnode_id = f"{curl}:{classcount.get(qnode_id, 0)}"
+ classcount[qnode_id] = classcount.get(qnode_id, 0) + 1
+
+ try:
+ cnode_label = sm_helper.get_qnode_label(curl)
+ except KeyError:
+ cnode_label = f"wd:{qnode_id}"
+ cnode = O.ClassNode(id=cnode_id, abs_uri=curl, rel_uri=f"wd:{qnode_id}", readable_label=cnode_label)
+ classmap[dnode.id] = cnode.id
+
+ sm.add_node(dnode)
+ sm.add_node(cnode)
+ sm.add_edge(O.Edge(source=cnode.id, target=dnode.id,
+ abs_uri=str(RDFS.label), rel_uri="rdfs:label"))
+
+ for uid, vid, edge in sg.edges(data='data'):
+ unode = sg.nodes[uid]['data']
+ vnode = sg.nodes[vid]['data']
+
+ if unode.is_column:
+ suid = f"col-{unode.column}"
+ else:
+ suid = unode.id
+ if vnode.is_column:
+ svid = f"col-{vnode.column}"
+ else:
+ svid = vnode.id
+
+ if sm.has_node(suid):
+ if suid in classmap:
+ suid = classmap[suid]
+ source = sm.get_node(suid)
+ elif unode.is_column:
+ # create a data node
+ source = O.DataNode(id=suid, col_index=unode.column, label=table.table.get_column_by_index(unode.column).name)
+ sm.add_node(source)
+ else:
+ # create a statement node
+ source = O.ClassNode(id=suid, abs_uri='http://wikiba.se/ontology#Statement', rel_uri='wikibase:Statement')
+ sm.add_node(source)
+ if sm.has_node(svid):
+ if svid in classmap:
+ svid = classmap[svid]
+ target = sm.get_node(svid)
+ elif vnode.is_column:
+ target = O.DataNode(id=svid, col_index=vnode.column, label=table.table.get_column_by_index(vnode.column).name)
+ sm.add_node(target)
+ else:
+ target = O.ClassNode(id=svid, abs_uri='http://wikiba.se/ontology#Statement',
+ rel_uri='wikibase:Statement')
+ sm.add_node(target)
+
+ prop_uri = sm_helper.get_prop_uri(edge.predicate)
+ sm.add_edge(O.Edge(source=source.id, target=target.id, abs_uri=prop_uri, rel_uri=f"p:{edge.predicate}",
+ readable_label=sm_helper.get_pnode_label(prop_uri)))
+
+ return sm
+
+ def get_entities(self, qnode_ids: Set[str], n_hop: int = 1, verbose: bool = False) -> Dict[str, QNode]:
+ assert n_hop <= 2
+ batch_size = 30
+ qnodes: Dict[str, QNode] = {}
+ for qnode_id in qnode_ids:
+ qnode = self.qnodes.get(qnode_id, None)
+ if qnode is not None:
+ qnodes[qnode_id] = qnode
+ qnode_ids = [qnode_id for qnode_id in qnode_ids if qnode_id not in qnodes]
+ if len(qnode_ids) > 0:
+ resp = M.parallel_map(
+ query_wikidata_entities,
+ [qnode_ids[i:i+batch_size] for i in range(0, len(qnode_ids), batch_size)],
+ show_progress=verbose,
+ progress_desc=f'query wikidata for get entities in hop: {n_hop}',
+ is_parallel=True)
+ for odict in resp:
+ for k, v in odict.items():
+ qnodes[k] = v
+ self.qnodes[k] = v
+
+ if n_hop > 1:
+ next_qnode_ids = set()
+ for qnode in qnodes.values():
+ for p, stmts in qnode.props.items():
+ for stmt in stmts:
+ if stmt.value.is_qnode():
+ next_qnode_ids.add(stmt.value.as_qnode_id())
+ for qvals in stmt.qualifiers.values():
+ next_qnode_ids = next_qnode_ids.union(qval.as_qnode_id() for qval in qvals if qval.is_qnode())
+ next_qnode_ids = list(next_qnode_ids.difference(qnodes.keys()))
+ for qnode_id in next_qnode_ids:
+ qnode = self.qnodes.get(qnode_id, None)
+ if qnode is not None:
+ qnodes[qnode_id] = qnode
+ next_qnode_ids = [qnode_id for qnode_id in next_qnode_ids if qnode_id not in qnodes]
+
+ if len(next_qnode_ids) > 0:
+ resp = M.parallel_map(
+ query_wikidata_entities,
+ [next_qnode_ids[i:i+batch_size] for i in range(0, len(next_qnode_ids), batch_size)],
+ show_progress=verbose,
+ progress_desc=f'query wikidata for get entities in hop: {n_hop}',
+ is_parallel=True)
+ for odict in resp:
+ for k, v in odict.items():
+ qnodes[k] = v
+ self.qnodes[k] = v
+ return qnodes
+
+
+if __name__ == '__main__':
+ cfg = OmegaConf.load(ROOT_DIR / "grams.yaml")
+
+ tbl = I.LinkedTable.from_csv_file(ROOT_DIR / "examples/novartis/tables/table_03.csv")
+ data = M.deserialize_json(ROOT_DIR / "examples/novartis/ground-truth/table_03/version.01.json")
+ sm = O.SemanticModel.from_json(data['semantic_models'][0])
+ sm.draw()
+ exit(0)
+ # tbl = I.W2WTable.from_json(M.deserialize_json(ROOT_DIR / "examples/misc/tables/list_of_largest_selling_pharmaceutical_products_bebff3652629c07e82fa5897b54f612f.json"))
+ # tbl = I.W2WTable.from_csv_file(ROOT_DIR / "examples/t2dv2/tables/29414811_2_4773219892816395776.csv")
+ # for ri in range(tbl.size()):
+ # for ci in range(len(tbl.table.columns)):
+ # tbl.links[ri][ci] = []
+ # cea = M.deserialize_csv(ROOT_DIR / "examples/t2dv2/tables/29414811_2_4773219892816395776.candidates.tsv", delimiter="\t")
+ # gold_cea = {}
+ # for r in cea:
+ # ri, ci = int(r[0]), int(r[1])
+ # gold_cea[ri, ci] = r[2]
+ # tbl.links[ri][ci] = [
+ # I.Link(0, len(tbl.table.columns[ci].values[ri]), "", x.split(":")[0])
+ # for x in r[3:]
+ # ]
+ grams = GRAMS(ROOT_DIR / "data", cfg)
+ annotation = grams.annotate(tbl)
+ annotation.sm.draw()
+ print(annotation.sm.to_json())
+ grams.timer.report()
+
+ # # %%
+ # # ent column
+ # from grams.algorithm.sm_wikidata import WDOnt
+ # wdont = WDOnt(grams.qnodes, grams.wdclasses, grams.wdprops)
+ # from collections import defaultdict
+ # from grams.algorithm.data_graph import *
+ # # %%
+ # ci, classid = list(annotation.pred_cta.items())[0]
+ # pred_ents = []
+ # props = {'P136', 'P178', 'P400', 'P577'}
+ # final_prediction = []
+ # original_prediction = []
+ # for ri in range(tbl.size()):
+ # can_ents = []
+ # for link in tbl.links[ri][ci]:
+ # if link.qnode_id is not None:
+ # qnode = grams.qnodes[link.qnode_id]
+ # if any(stmt.value.as_qnode_id() == classid for stmt in qnode.props.get("P31", [])):
+ # can_ents.append(qnode.id)
+ #
+ # qnode2score = defaultdict(set)
+ # for stmt, edges in annotation.dg[f"{ri}-{ci}"].items():
+ # assert len(edges) == 1
+ # edgeid = list(edges.keys())[0]
+ # if edgeid not in props:
+ # continue
+ # stmt = annotation.dg.nodes[stmt]['data']
+ # for target_flow in stmt.forward_flow[EdgeFlowSource(source_id=f"{ri}-{ci}", edge_id=edgeid)]:
+ # target = annotation.dg.nodes[target_flow.target_id]['data']
+ # if target.is_cell and target.column != ci:
+ # qnode2score[stmt.qnode_id].add(target.column)
+ #
+ # best_can, best_can_score = None, -1
+ # for can_ent in can_ents:
+ # can_ent_score = len(qnode2score.get(can_ent, []))
+ # if can_ent_score > best_can_score:
+ # best_can = can_ent
+ # best_can_score = can_ent_score
+ #
+ # # print(ri, gold_cea[ri, ci], best_can == gold_cea[ri, ci], can_ents[0] == gold_cea[ri, ci], can_ents)
+ # print(ri, wdont.get_qnode_label(gold_cea[ri, ci]), best_can == gold_cea[ri, ci], can_ents[0] == gold_cea[ri, ci], [wdont.get_qnode_label(x) for x in can_ents])
+ # final_prediction.append((gold_cea[ri, ci], best_can))
+ # original_prediction.append((gold_cea[ri, ci], tbl.links[ri][ci][0].qnode_id))
+ # #%%
+ # print("TOP1", sum(x[0] == x[1] for x in final_prediction) / len(final_prediction))
+ # print("TOP1 origin", sum(x[0] == x[1] for x in original_prediction) / len(final_prediction))
+ #
+ # count = 0
+ # for ri in range(tbl.size()):
+ # if any(link.qnode_id == gold_cea[ri, ci] for link in tbl.links[ri][ci][:5]):
+ # count += 1
+ # print("TOP5", count / len(final_prediction))
\ No newline at end of file
diff --git a/grams/prelude.py b/grams/prelude.py
new file mode 100644
index 0000000..3e7cd91
--- /dev/null
+++ b/grams/prelude.py
@@ -0,0 +1,5 @@
+import grams.inputs as I
+from grams.main import GRAMS
+from grams.remote_table_fetcher import fetch_tables
+from grams.config import ROOT_DIR, DATA_DIR
+from grams.algorithm.sm_wikidata import WikidataSemanticModelHelper, WDOnt
\ No newline at end of file
diff --git a/grams/remote_table_fetcher.py b/grams/remote_table_fetcher.py
new file mode 100644
index 0000000..83af204
--- /dev/null
+++ b/grams/remote_table_fetcher.py
@@ -0,0 +1,632 @@
+import copy
+from dataclasses import dataclass
+from operator import itemgetter
+from urllib.parse import urlparse, unquote_plus
+
+import requests, re, pandas as pd
+from bs4 import BeautifulSoup, NavigableString, Tag
+from typing import List, Dict, Optional
+
+from sm.prelude import I, M
+import grams.inputs as GI
+from grams.config import DATA_DIR
+from grams.db import Wikipedia2WikidataDB
+
+
+@dataclass
+class RemoteExtractedTableCellHTMLElement:
+ # html tag (lower case)
+ tag: str
+ start: int
+ # end (exclusive)
+ end: int
+ # html attributes
+ attrs: Dict[str, str]
+ children: List['RemoteExtractedTableCellHTMLElement']
+
+ def post_order(self):
+ for c in self.children:
+ for el in c.post_order():
+ yield el
+ yield self
+
+ def clone(self):
+ return RemoteExtractedTableCellHTMLElement(self.tag, self.start, self.end,
+ copy.copy(self.attrs), [c.clone() for c in self.children])
+
+
+@dataclass
+class RemoteExtractedTableCell:
+ # text value of the cell
+ value: str
+ rowspan: int
+ colspan: int
+ # html of the cell
+ html: str
+ # list of html elements that created this cell
+ # except that:
+ # - BR and HR are replaced by `\n` character and not in this list
+ # - div, span are container and won't in the list
+ # for more details, look at the _extract_cell_recur function
+ elements: List[RemoteExtractedTableCellHTMLElement]
+
+ # original row & col span
+ original_rowspan: Optional[int] = None
+ original_colspan: Optional[int] = None
+
+ def travel_elements_post_order(self):
+ for el in self.elements:
+ for pointer in el.post_order():
+ yield pointer
+
+ def clone(self):
+ return RemoteExtractedTableCell(
+ value=self.value,
+ rowspan=self.rowspan,
+ colspan=self.colspan,
+ html=self.html,
+ elements=[el.clone() for el in self.elements],
+ original_rowspan=self.original_rowspan,
+ original_colspan=self.original_colspan
+ )
+
+
+@dataclass
+class RemoteExtractedTableRow:
+ cells: List[RemoteExtractedTableCell]
+ # html attributes of the tr elements
+ attrs: Dict[str, str]
+
+
+@dataclass
+class ContextLevel:
+ level: int
+ header: str
+ content: str
+
+ def clone(self):
+ return ContextLevel(self.level, self.header, self.content)
+
+
+class OverlapSpanException(Exception):
+ """Indicating the table has cell rowspan and cell colspan overlaps"""
+ pass
+
+
+class InvalidColumnSpanException(Exception):
+ """Indicating that the column span is not used in a standard way. In particular, the total of columns' span is beyond the maximum number of columns is considered
+ to be non standard with one exception that only the last column spans more than the maximum number of columns
+ """
+ pass
+
+
+@dataclass
+class RemoteExtractedTable:
+ page_url: str
+ # value of html caption
+ caption: str
+ # html attributes of the table html element
+ attrs: Dict[str, str]
+ # context
+ context: List[ContextLevel]
+ # list of rows in the table
+ rows: List[RemoteExtractedTableRow]
+
+ def span(self) -> "RemoteExtractedTable":
+ """Span the table by copying values to merged field
+ """
+ pi = 0
+ data = []
+ pending_ops = {}
+
+ # >>> begin find the max #cols
+ # calculate the number of columns as some people may actually set unrealistic colspan as they are lazy..
+ # I try to make its behaviour as much closer to the browser as possible.
+ # one thing I notice that to find the correct value of colspan, they takes into account the #cells of rows below the current row
+ # so we may have to iterate several times
+ cols = [0 for _ in range(len(self.rows))]
+ for i, row in enumerate(self.rows):
+ cols[i] += len(row.cells)
+ for cell in row.cells:
+ if cell.rowspan > 1:
+ for j in range(1, cell.rowspan):
+ if i + j < len(cols):
+ cols[i + j] += 1
+
+ _row_index, max_ncols = max(enumerate(cols), key=itemgetter(1))
+ # sometimes they do show an extra cell for over-colspan row, but it's not consistent or at least not easy for me to find the rule
+ # so I decide to not handle that. Hope that we don't have many tables like that.
+ # >>> finish find the max #cols
+
+ for row in self.rows:
+ new_row = []
+ pj = 0
+ for cell_index, cell in enumerate(row.cells):
+ cell = cell.clone()
+ cell.original_colspan = cell.colspan
+ cell.original_rowspan = cell.rowspan
+ cell.colspan = 1
+ cell.rowspan = 1
+
+ # adding cell from the top
+ while (pi, pj) in pending_ops:
+ new_row.append(pending_ops[pi, pj].clone())
+ pending_ops.pop((pi, pj))
+ pj += 1
+
+ # now add cell and expand the column
+ for _ in range(cell.original_colspan):
+ if (pi, pj) in pending_ops:
+ # exception, overlapping between colspan and rowspan
+ raise OverlapSpanException()
+ new_row.append(cell.clone())
+ for ioffset in range(1, cell.original_rowspan):
+ # no need for this if below
+ # if (pi+ioffset, pj) in pending_ops:
+ # raise OverlapSpanException()
+ pending_ops[pi + ioffset, pj] = cell
+ pj += 1
+
+ if pj >= max_ncols:
+ # our algorithm cannot handle the case where people are bullying the colspan system, and only can handle the case
+ # where the span that goes beyond the maximum number of columns is in the last column.
+ if cell_index != len(row.cells) - 1:
+ raise InvalidColumnSpanException()
+ else:
+ break
+
+ # add more cells from the top since we reach the end
+ while (pi, pj) in pending_ops and pj < max_ncols:
+ new_row.append(pending_ops[pi, pj].clone())
+ pending_ops.pop((pi, pj))
+ pj += 1
+
+ data.append(RemoteExtractedTableRow(cells=new_row, attrs=copy.copy(row.attrs)))
+ pi += 1
+
+ # len(pending_ops) may > 0, but fortunately, it doesn't matter as the browser also does not render that extra empty lines
+ return RemoteExtractedTable(
+ page_url=self.page_url,
+ caption=self.caption,
+ attrs=copy.copy(self.attrs),
+ context=[c.clone() for c in self.context],
+ rows=data)
+
+ def pad(self) -> "RemoteExtractedTable":
+ """Pad the irregular table (missing cells) to make it become regular table.
+
+ This function only return new table when it's padded
+ """
+ if len(self.rows) == 0:
+ return self
+
+ ncols = len(self.rows[0].cells)
+ is_regular_table = all(len(r.cells) == ncols for r in self.rows)
+ if is_regular_table:
+ return self
+
+ max_ncols = max(len(r.cells) for r in self.rows)
+ default_cell = RemoteExtractedTableCell(
+ value="", rowspan=1, colspan=1, html="", elements=[], original_rowspan=1, original_colspan=1
+ )
+
+ rows = []
+ for r in self.rows:
+ row = RemoteExtractedTableRow(cells=[c.clone() for c in r.cells], attrs=copy.copy(r.attrs))
+ while len(row.cells) < max_ncols:
+ row.cells.append(default_cell.clone())
+ rows.append(row)
+
+ return RemoteExtractedTable(
+ page_url=self.page_url,
+ caption=self.caption,
+ attrs=copy.copy(self.attrs),
+ context=[c.clone() for c in self.context],
+ rows=rows)
+
+ def as_df(self):
+ return pd.DataFrame([
+ [c.value for c in r.cells]
+ for r in self.rows
+ ])
+
+ def as_relational_linked_table(self, table_id=None):
+ assert len(self.rows) > 0
+ header = [c.value for c in self.rows[0].cells]
+ table = I.ColumnBasedTable(table_id or self.page_url, [
+ I.Column(ci, cname, [self.rows[ri].cells[ci].value for ri in range(1, len(self.rows))])
+ for ci, cname in enumerate(header)
+ ])
+
+ wikidb = Wikipedia2WikidataDB.get_instance(DATA_DIR / "enwiki_links.db", read_only=True)
+ links = [
+ [
+ [
+ GI.Link(el.start, el.end, el.attrs['href'],
+ wikidb.get(self.get_title_from_url(el.attrs['href']), None))
+ for el in row.cells[ci].travel_elements_post_order()
+ if el.tag == 'a'
+ ]
+ for ci in range(len(header))
+ ]
+ for ri, row in enumerate(self.rows[1:])
+ ]
+ return GI.LinkedTable(table, GI.Context(), links)
+
+ def get_title_from_url(self, url: str) -> str:
+ """This function converts a wikipedia page/article's URL to its title. The function is tested manually in `20200425-wikipedia-links` notebook in section 2.2.
+
+ Parameters
+ ----------
+ url : str
+ a wikipedia page/article's URL
+
+ Returns
+ -------
+ str
+ a wikipedia page/article's title
+ """
+ path = urlparse(url).path
+ if not path.startswith("/wiki/"):
+ return ""
+
+ assert path.startswith("/wiki/"), path
+ path = path[6:]
+ title = unquote_plus(path).replace("_", " ")
+ return title.strip()
+
+
+class InvalidCellSpanException(Exception):
+ """Indicating that the html colspan or rowspan is wrong
+ """
+ pass
+
+
+class HTMLTableExtractor:
+ NUM_REGEX = re.compile("(\d+)")
+ IGNORE_TAGS = set(["div", "span"])
+
+ @dataclass
+ class Header:
+ level: int
+ value: str
+
+ @dataclass
+ class Text:
+ value: str
+
+ """Extract tables from an html table, which may contains multiple nested tables. I assume that the outer tables are used for
+ formatting and the inner tables are the interested ones. So this extractor will discard the outer tables and only keep
+ tables at the bottom level.
+
+ The main function of this extractor are extract
+ """
+
+ def extract(self, page_url: str, html: str) -> List[RemoteExtractedTable]:
+ """Extract just one html table, which may contains multiple nested tables. I assume that the outer tables are used for
+ formatting and the inner tables are the interested ones. So this function will discard the outer tables and only keep
+ tables at the bottom level.
+
+ This function right now ignore tables with invalid rowspan or cellspan
+ """
+ el = BeautifulSoup(html, "html5lib")
+ table_els = el.find_all("table")
+ results = []
+ for table_el in table_els:
+ temp_results = []
+ self._extract_table(table_el, temp_results)
+ if len(temp_results) > 0:
+ context = self._locate_context(table_el)
+ for r in temp_results:
+ r['context'] = context
+ results += temp_results
+
+ tables = [
+ RemoteExtractedTable(page_url, **r)
+ for r in results
+ ]
+
+ # convert relative links to absolute links
+ parsed_resp = urlparse(page_url)
+ domain = f"{parsed_resp.scheme}://{parsed_resp.netloc}"
+ for table in tables:
+ for row in table.rows:
+ for cell in row.cells:
+ for el in cell.travel_elements_post_order():
+ if el.tag == "a":
+ if 'href' in el.attrs:
+ href = el.attrs['href']
+ if href[0] == '/':
+ el.attrs['href'] = domain + href
+
+ if parsed_resp.netloc.endswith("wikipedia.org"):
+ for table in tables:
+ self._postprocess_wikipedia(table)
+
+ return tables
+
+ def _extract_table(self, el: Tag, results: List[dict]):
+ """Extract tables from the table tag. Ignore tables that contain other tables
+
+ Parameters
+ ----------
+ el : Tag
+ html table tag
+ results : List[dict]
+ list of results
+ """
+ assert el.name == "table"
+ caption = None
+ rows = []
+ contain_nested_table = any(
+ c.find("table") is not None is not None for c in el.contents
+ )
+
+ for c in el.contents:
+ if isinstance(c, NavigableString):
+ continue
+ if c.name == "caption":
+ caption = c.get_text().strip()
+ continue
+ if c.name == "style":
+ continue
+ assert (
+ c.name == "thead" or c.name == "tbody"
+ ), f"not implemented {c.name} tag"
+ for row_el in c.contents:
+ if isinstance(row_el, NavigableString):
+ continue
+ if row_el.name == "style":
+ continue
+
+ assert row_el.name == "tr", f"Invalid tag: {row_el.name}"
+ cells = []
+ for cell_el in row_el.contents:
+ if isinstance(cell_el, NavigableString):
+ continue
+ if cell_el.name == "style":
+ continue
+
+ assert (
+ cell_el.name == "th" or cell_el.name == "td"
+ ), f"Invalid tag: {row_el.name}"
+
+ if contain_nested_table:
+ nested_tables = []
+ self._extract_tbl_tags(cell_el, nested_tables)
+ for tag in nested_tables:
+ self._extract_table(tag, results)
+ else:
+ try:
+ cell = self._extract_cell(cell_el)
+ cells.append(cell)
+ except InvalidCellSpanException:
+ # not extracting this table, this table is not table at the intermediate level
+ return
+
+ if not contain_nested_table:
+ rows.append(RemoteExtractedTableRow(cells=cells, attrs=row_el.attrs))
+
+ if not contain_nested_table:
+ results.append(dict(rows=rows, caption=caption, attrs=el.attrs))
+
+ def _extract_tbl_tags(self, el: Tag, tags: List[Tag]):
+ """Recursive find the first table node
+
+ Parameters
+ ----------
+ el : Tag
+ root node that we start finding
+ tags : List[Tag]
+ list of results
+ """
+ for c in el.children:
+ if c.name == "table":
+ tags.append(c)
+ elif isinstance(c, NavigableString):
+ continue
+ elif len(c.contents) > 0:
+ self._extract_tbl_tags(c, tags)
+
+ def _extract_cell(self, el: Tag) -> RemoteExtractedTableCell:
+ """Extract cell from td/th tag. This function does not expect a nested table in the cell
+
+ Parameters
+ ----------
+ tbl : RawHTMLTable
+ need the table to get the page URI in case we have cell reference
+ el : Tag
+ cell tag (th or td)
+ """
+ assert el.name == "th" or el.name == "td"
+
+ # extract other attributes
+ is_header = el.name == "th"
+ attrs = el.attrs
+
+ colspan = attrs.get("colspan", "1").strip()
+ rowspan = attrs.get("rowspan", "1").strip()
+
+ if colspan == "":
+ colspan = 1
+ else:
+ m = self.NUM_REGEX.search(colspan)
+ if m is None:
+ raise InvalidCellSpanException()
+ colspan = int(m.group(0))
+ if rowspan == "":
+ rowspan = 1
+ else:
+ m = self.NUM_REGEX.search(rowspan)
+ if m is None:
+ # this is not correct, but
+ raise InvalidCellSpanException()
+ rowspan = int(m.group(0))
+
+ # extract value
+ result = {"text": "", "elements": []}
+ self._extract_cell_recur(el, result)
+ value, elements = result["text"], result["elements"]
+ if len(value) > 0 and value[-1] == " ":
+ value = value[:-1]
+
+ return RemoteExtractedTableCell(
+ value=value,
+ html=str(el),
+ elements=elements,
+ colspan=colspan,
+ rowspan=rowspan,
+ )
+
+ def _extract_cell_recur(self, el: Tag, result: dict):
+ """Real implementation of the _extract_cell function"""
+ for c in el.children:
+ c_name = c.name
+ if c_name is None:
+ result["text"] += c.strip()
+ result["text"] += " "
+ elif c_name == "br" or c_name == "hr":
+ result["text"] += "\n"
+ elif c_name in self.IGNORE_TAGS:
+ self._extract_cell_recur(c, result)
+ else:
+ start = len(result["text"])
+ children_result = {"text": result['text'], "elements": []}
+ self._extract_cell_recur(c, children_result)
+ # -1 for the trailing space
+ if len(children_result['text']) > 0 and children_result['text'][-1] == " ":
+ if start == len(children_result['text']):
+ # no new text
+ start -= 1
+ end = len(children_result['text']) - 1
+ else:
+ end = len(children_result['text'])
+ assert end >= start
+ result['text'] = children_result['text']
+ result["elements"].append(RemoteExtractedTableCellHTMLElement(
+ tag=c_name, start=start, end=end, attrs=c.attrs, children=children_result['elements']
+ ))
+
+ def _locate_context_flatten_hierarchy(self, el):
+ if el.name is None:
+ # navigable string
+ return [self.Text(str(el))]
+
+ # flatten the tree to list of text? -> (header -> text)*
+ if el.name in {'h1', 'h2', 'h3', 'h4', 'h5', 'h6'}:
+ return [self.Header(level=int(el.name[1:]), value=el.get_text())]
+ else:
+ assert el.name == "hr" or not el.name.startswith("h")
+ lst = []
+ for c in el.contents:
+ lst += self._locate_context_flatten_hierarchy(c)
+ return lst
+
+ def _locate_context_optimize_flatten_hierarchy(self, lst):
+ if len(lst) == 0:
+ return lst
+ new_lst = []
+ for item in lst:
+ if isinstance(item, self.Header):
+ new_lst.append(item)
+ else:
+ if len(new_lst) > 0 and isinstance(new_lst[-1], self.Text):
+ new_lst[-1].value += "\n" + item.value
+ else:
+ new_lst.append(item)
+ return new_lst
+
+ def _locate_context_recur(self, el):
+ if el.name in {'body', 'html'}:
+ # hit the top
+ return []
+
+ parent = el.parent
+ prev_sibs = []
+ for i, e in enumerate(parent.contents):
+ if e == el:
+ # this is the index
+ break
+ prev_sibs.append(e)
+
+ lst = []
+ for sib in prev_sibs:
+ lst += self._locate_context_flatten_hierarchy(sib)
+ lst = self._locate_context_optimize_flatten_hierarchy(lst)
+ return self._locate_context_optimize_flatten_hierarchy(self._locate_context_recur(el.parent) + lst)
+
+ def _locate_context(self, el):
+ """Locate the context of the elements"""
+ context = self._locate_context_recur(el)
+ # optimize the context
+ optimized_context = []
+ last_header = None
+ if isinstance(context[-1], self.Header):
+ last_header = ContextLevel(context[-1].level, header=context[-1].value, content="")
+ context.pop()
+ if isinstance(context[0], self.Text):
+ assert isinstance(context[1], self.Header) and context[1].level == 1
+ context.pop(0)
+ else:
+ assert isinstance(context[0], self.Header) and context[0].level == 1
+
+ for i in range(0, len(context), 2):
+ header, text = context[i], context[i + 1]
+ optimized_context.append(ContextLevel(level=header.level, header=header.value, content=text.value))
+ if last_header is not None:
+ optimized_context.append(last_header)
+
+ lst = [optimized_context[-1]]
+ for i in range(len(optimized_context) - 2, -1, -1):
+ if optimized_context[i].level < lst[-1].level:
+ lst.append(optimized_context[i])
+ return list(reversed(lst))
+
+ def _postprocess_wikipedia(self, table: RemoteExtractedTable):
+ for row in table.rows:
+ for cell in row.cells:
+ for el in cell.travel_elements_post_order():
+ if el.tag == "a":
+ if "href" not in el.attrs:
+ if 'selflink' in el.attrs['class']:
+ el.attrs['href'] = table.page_url
+ else:
+ raise Exception(f"An anchor in a Wikipedia table does not have href: {el}")
+
+
+@M.cache_func()
+def requests_get(url):
+ return requests.get(url)
+
+
+def fetch_tables(url: str, auto_span: bool = True, auto_pad: bool = True, cache: bool = False):
+ if cache:
+ resp = requests_get(url)
+ else:
+ resp = requests.get(url)
+
+ assert resp.status_code == 200
+ html = resp.text
+ tables = HTMLTableExtractor().extract(url, html)
+ if auto_span:
+ new_tables = []
+ for tbl in tables:
+ try:
+ tbl = tbl.span()
+ new_tables.append(tbl)
+ except OverlapSpanException:
+ pass
+ except InvalidColumnSpanException:
+ pass
+ tables = new_tables
+ if auto_pad:
+ tables = [tbl.pad() for tbl in tables]
+ return tables
+
+
+if __name__ == '__main__':
+ # tables = fetch_tables("https://en.wikipedia.org/wiki/President_of_the_National_Council_(Austria)")
+ tables = fetch_tables("https://en.wikipedia.org/wiki/List_of_largest_selling_pharmaceutical_products")
+ print(tables[1].as_df())
+ # M.serialize_json(tables[6].as_relational_linked_table().to_json(),
+ # "/workspace/sm-dev/grams/examples/misc/table_01.json")
diff --git a/grams/update_ontology.py b/grams/update_ontology.py
new file mode 100644
index 0000000..b410efb
--- /dev/null
+++ b/grams/update_ontology.py
@@ -0,0 +1,25 @@
+from typing import List, Dict, Tuple, Callable, Any, Optional
+
+import grams.misc as M
+from grams.config import DATA_DIR
+from grams.kg_data.wikidatamodels import WDProperty, QNode
+from grams.main import GRAMS
+
+
+def update_props(props: List[str]):
+ props: Dict[str, QNode] = GRAMS._query_wikidata_entities(props)
+ ser = []
+ for p in props.values():
+ np = WDProperty(p.id, str(p.label), str(p.description), str(p.datatype), [str(s) for s in p.aliases],
+ sorted({stmt.value.as_qnode_id() for stmt in p.props.get("P1647", [])}),
+ sorted({stmt.value.as_qnode_id() for stmt in p.props.get("P1659", [])}),
+ sorted({stmt.value.as_string() for stmt in p.props.get("P1628", [])}),
+ sorted({stmt.value.as_qnode_id() for stmt in p.props.get("P1629", [])}),
+ sorted({stmt.value.as_qnode_id() for stmt in p.props.get("P1696", [])}),
+ sorted({stmt.value.as_qnode_id() for stmt in p.props.get("P31", [])}))
+ ser.append(np.serialize())
+ M.serialize_byte_lines(ser, DATA_DIR / "new_properties.jl")
+
+
+if __name__ == '__main__':
+ update_props(["P8901"])
diff --git a/poetry.lock b/poetry.lock
new file mode 100644
index 0000000..c523206
--- /dev/null
+++ b/poetry.lock
@@ -0,0 +1,2881 @@
+[[package]]
+name = "antlr4-python3-runtime"
+version = "4.8"
+description = "ANTLR 4.8 runtime for Python 3.7"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "appnope"
+version = "0.1.2"
+description = "Disable App Nap on macOS >= 10.9"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "argon2-cffi"
+version = "20.1.0"
+description = "The secure Argon2 password hashing algorithm."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+cffi = ">=1.0.0"
+six = "*"
+
+[package.extras]
+dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pytest", "sphinx", "wheel", "pre-commit"]
+docs = ["sphinx"]
+tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pytest"]
+
+[[package]]
+name = "async-generator"
+version = "1.10"
+description = "Async generators and context managers for Python 3.5+"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
+name = "attrs"
+version = "21.2.0"
+description = "Classes Without Boilerplate"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[package.extras]
+dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"]
+docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
+tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"]
+tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"]
+
+[[package]]
+name = "backcall"
+version = "0.2.0"
+description = "Specifications for callback functions passed in to an API"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "beautifulsoup4"
+version = "4.9.3"
+description = "Screen-scraping library"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+soupsieve = {version = ">1.2", markers = "python_version >= \"3.0\""}
+
+[package.extras]
+html5lib = ["html5lib"]
+lxml = ["lxml"]
+
+[[package]]
+name = "bleach"
+version = "3.3.0"
+description = "An easy safelist-based HTML-sanitizing tool."
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[package.dependencies]
+packaging = "*"
+six = ">=1.9.0"
+webencodings = "*"
+
+[[package]]
+name = "bravado"
+version = "11.0.3"
+description = "Library for accessing Swagger-enabled API's"
+category = "dev"
+optional = false
+python-versions = "!=3.0,!=3.1,!=3.2,!=3.3,!=3.4,!=3.5.0"
+
+[package.dependencies]
+bravado-core = ">=5.16.1"
+monotonic = "*"
+msgpack = "*"
+python-dateutil = "*"
+pyyaml = "*"
+requests = ">=2.17"
+simplejson = "*"
+six = "*"
+typing-extensions = "*"
+
+[package.extras]
+fido = ["fido (>=4.2.1)"]
+integration-tests = ["bottle", "ephemeral-port-reserve", "pytest"]
+
+[[package]]
+name = "bravado-core"
+version = "5.17.0"
+description = "Library for adding Swagger support to clients and servers"
+category = "dev"
+optional = false
+python-versions = "!=3.0,!=3.1,!=3.2,!=3.3,!=3.4,!=3.5.0"
+
+[package.dependencies]
+jsonref = "*"
+jsonschema = {version = ">=2.5.1", extras = ["format"]}
+msgpack = ">=0.5.2"
+python-dateutil = "*"
+pytz = "*"
+pyyaml = "*"
+simplejson = "*"
+six = "*"
+swagger-spec-validator = ">=2.0.1"
+
+[[package]]
+name = "certifi"
+version = "2021.5.30"
+description = "Python package for providing Mozilla's CA Bundle."
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "cffi"
+version = "1.14.5"
+description = "Foreign Function Interface for Python calling C code."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+pycparser = "*"
+
+[[package]]
+name = "chardet"
+version = "4.0.0"
+description = "Universal encoding detector for Python 2 and 3"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[[package]]
+name = "cityhash"
+version = "0.2.3.post9"
+description = "Python-bindings for CityHash, a fast non-cryptographic hash algorithm"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "click"
+version = "8.0.1"
+description = "Composable command line interface toolkit"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+colorama = {version = "*", markers = "platform_system == \"Windows\""}
+
+[[package]]
+name = "cloudpickle"
+version = "1.6.0"
+description = "Extended pickling support for Python objects"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
+name = "colorama"
+version = "0.4.4"
+description = "Cross-platform colored terminal text."
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[[package]]
+name = "cycler"
+version = "0.10.0"
+description = "Composable style cycles"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+six = "*"
+
+[[package]]
+name = "cython"
+version = "0.29.23"
+description = "The Cython compiler for writing C extensions for the Python language."
+category = "main"
+optional = false
+python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
+
+[[package]]
+name = "dask"
+version = "2021.6.0"
+description = "Parallel PyData with Task Scheduling"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+cloudpickle = ">=1.1.1"
+fsspec = ">=0.6.0"
+partd = ">=0.3.10"
+pyyaml = "*"
+toolz = ">=0.8.2"
+
+[package.extras]
+array = ["numpy (>=1.16)"]
+complete = ["bokeh (>=1.0.0,!=2.0.0)", "distributed (==2021.06.0)", "numpy (>=1.16)", "pandas (>=0.25.0)"]
+dataframe = ["numpy (>=1.16)", "pandas (>=0.25.0)"]
+diagnostics = ["bokeh (>=1.0.0,!=2.0.0)"]
+distributed = ["distributed (==2021.06.0)"]
+test = ["pytest", "pytest-rerunfailures", "pytest-xdist"]
+
+[[package]]
+name = "decorator"
+version = "4.4.2"
+description = "Decorators for Humans"
+category = "main"
+optional = false
+python-versions = ">=2.6, !=3.0.*, !=3.1.*"
+
+[[package]]
+name = "defusedxml"
+version = "0.7.1"
+description = "XML bomb protection for Python stdlib modules"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[[package]]
+name = "dill"
+version = "0.3.3"
+description = "serialize all of python"
+category = "main"
+optional = false
+python-versions = ">=2.6, !=3.0.*"
+
+[package.extras]
+graph = ["objgraph (>=1.7.2)"]
+
+[[package]]
+name = "distributed"
+version = "2021.6.0"
+description = "Distributed scheduler for Dask"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+click = ">=6.6"
+cloudpickle = ">=1.5.0"
+dask = "2021.06.0"
+msgpack = ">=0.6.0"
+psutil = ">=5.0"
+pyyaml = "*"
+sortedcontainers = "<2.0.0 || >2.0.0,<2.0.1 || >2.0.1"
+tblib = ">=1.6.0"
+toolz = ">=0.8.2"
+tornado = {version = ">=6.0.3", markers = "python_version >= \"3.8\""}
+zict = ">=0.1.3"
+
+[[package]]
+name = "elasticsearch"
+version = "7.13.1"
+description = "Python client for Elasticsearch"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4"
+
+[package.dependencies]
+certifi = "*"
+urllib3 = ">=1.21.1,<2"
+
+[package.extras]
+async = ["aiohttp (>=3,<4)"]
+develop = ["requests (>=2.0.0,<3.0.0)", "coverage", "mock", "pyyaml", "pytest", "pytest-cov", "sphinx (<1.7)", "sphinx-rtd-theme", "black", "jinja2"]
+docs = ["sphinx (<1.7)", "sphinx-rtd-theme"]
+requests = ["requests (>=2.4.0,<3.0.0)"]
+
+[[package]]
+name = "entrypoints"
+version = "0.3"
+description = "Discover and load entry points from installed packages."
+category = "main"
+optional = false
+python-versions = ">=2.7"
+
+[[package]]
+name = "fastnumbers"
+version = "3.1.0"
+description = "Super-fast and clean conversions to numbers."
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
+name = "fsspec"
+version = "2021.6.0"
+description = "File-system specification"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.extras]
+abfs = ["adlfs"]
+adl = ["adlfs"]
+dask = ["dask", "distributed"]
+dropbox = ["dropboxdrivefs", "requests", "dropbox"]
+entrypoints = ["importlib-metadata"]
+gcs = ["gcsfs"]
+git = ["pygit2"]
+github = ["requests"]
+gs = ["gcsfs"]
+hdfs = ["pyarrow (>=1)"]
+http = ["requests", "aiohttp"]
+s3 = ["s3fs"]
+sftp = ["paramiko"]
+smb = ["smbprotocol"]
+ssh = ["paramiko"]
+
+[[package]]
+name = "ftfy"
+version = "6.0.3"
+description = "Fixes some problems with Unicode text after the fact"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+wcwidth = "*"
+
+[package.extras]
+docs = ["furo", "sphinx"]
+
+[[package]]
+name = "future"
+version = "0.18.2"
+description = "Clean single-source support for Python 3 and 2"
+category = "dev"
+optional = false
+python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
+
+[[package]]
+name = "fuzzywuzzy"
+version = "0.18.0"
+description = "Fuzzy string matching in python"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.extras]
+speedup = ["python-levenshtein (>=0.12)"]
+
+[[package]]
+name = "gitdb"
+version = "4.0.7"
+description = "Git Object Database"
+category = "dev"
+optional = false
+python-versions = ">=3.4"
+
+[package.dependencies]
+smmap = ">=3.0.1,<5"
+
+[[package]]
+name = "gitpython"
+version = "3.1.17"
+description = "Python Git Library"
+category = "dev"
+optional = false
+python-versions = ">=3.5"
+
+[package.dependencies]
+gitdb = ">=4.0.1,<5"
+
+[[package]]
+name = "heapdict"
+version = "1.0.1"
+description = "a heap with decrease-key and increase-key operations"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "html5lib"
+version = "1.1"
+description = "HTML parser based on the WHATWG HTML specification"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[package.dependencies]
+six = ">=1.9"
+webencodings = "*"
+
+[package.extras]
+all = ["genshi", "chardet (>=2.2)", "lxml"]
+chardet = ["chardet (>=2.2)"]
+genshi = ["genshi"]
+lxml = ["lxml"]
+
+[[package]]
+name = "idna"
+version = "2.10"
+description = "Internationalized Domain Names in Applications (IDNA)"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[[package]]
+name = "ipycallback"
+version = "0.2.5"
+description = "Use this widget to allow client side (javascript) to trigger event on the server side (python)"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+ipywidgets = ">=7.0.0"
+
+[package.extras]
+docs = ["sphinx (>=1.5)", "recommonmark", "sphinx-rtd-theme", "nbsphinx (>=0.2.13,<0.4.0)", "jupyter-sphinx", "nbsphinx-link", "pytest-check-links", "pypandoc"]
+test = ["pytest (>=3.6)", "pytest-cov", "nbval"]
+
+[[package]]
+name = "ipykernel"
+version = "5.5.5"
+description = "IPython Kernel for Jupyter"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[package.dependencies]
+appnope = {version = "*", markers = "platform_system == \"Darwin\""}
+ipython = ">=5.0.0"
+jupyter-client = "*"
+tornado = ">=4.2"
+traitlets = ">=4.1.0"
+
+[package.extras]
+test = ["pytest (!=5.3.4)", "pytest-cov", "flaky", "nose", "jedi (<=0.17.2)"]
+
+[[package]]
+name = "ipython"
+version = "7.24.1"
+description = "IPython: Productive Interactive Computing"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+appnope = {version = "*", markers = "sys_platform == \"darwin\""}
+backcall = "*"
+colorama = {version = "*", markers = "sys_platform == \"win32\""}
+decorator = "*"
+jedi = ">=0.16"
+matplotlib-inline = "*"
+pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""}
+pickleshare = "*"
+prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0"
+pygments = "*"
+traitlets = ">=4.2"
+
+[package.extras]
+all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.17)", "pygments", "qtconsole", "requests", "testpath"]
+doc = ["Sphinx (>=1.3)"]
+kernel = ["ipykernel"]
+nbconvert = ["nbconvert"]
+nbformat = ["nbformat"]
+notebook = ["notebook", "ipywidgets"]
+parallel = ["ipyparallel"]
+qtconsole = ["qtconsole"]
+test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.17)"]
+
+[[package]]
+name = "ipython-genutils"
+version = "0.2.0"
+description = "Vestigial utilities from IPython"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "ipywidgets"
+version = "7.6.3"
+description = "IPython HTML widgets for Jupyter"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+ipykernel = ">=4.5.1"
+ipython = {version = ">=4.0.0", markers = "python_version >= \"3.3\""}
+jupyterlab-widgets = {version = ">=1.0.0", markers = "python_version >= \"3.6\""}
+nbformat = ">=4.2.0"
+traitlets = ">=4.3.1"
+widgetsnbextension = ">=3.5.0,<3.6.0"
+
+[package.extras]
+test = ["pytest (>=3.6.0)", "pytest-cov", "mock"]
+
+[[package]]
+name = "isodate"
+version = "0.6.0"
+description = "An ISO 8601 date/time/duration parser and formatter"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+six = "*"
+
+[[package]]
+name = "jedi"
+version = "0.18.0"
+description = "An autocompletion tool for Python that can be used for text editors."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+parso = ">=0.8.0,<0.9.0"
+
+[package.extras]
+qa = ["flake8 (==3.8.3)", "mypy (==0.782)"]
+testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<6.0.0)"]
+
+[[package]]
+name = "jinja2"
+version = "3.0.1"
+description = "A very fast and expressive template engine."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+MarkupSafe = ">=2.0"
+
+[package.extras]
+i18n = ["Babel (>=2.7)"]
+
+[[package]]
+name = "jsonpointer"
+version = "2.1"
+description = "Identify specific nodes in a JSON document (RFC 6901)"
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[[package]]
+name = "jsonref"
+version = "0.2"
+description = "An implementation of JSON Reference for Python"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "jsonschema"
+version = "3.2.0"
+description = "An implementation of JSON Schema validation for Python"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+attrs = ">=17.4.0"
+idna = {version = "*", optional = true, markers = "extra == \"format\""}
+jsonpointer = {version = ">1.13", optional = true, markers = "extra == \"format\""}
+pyrsistent = ">=0.14.0"
+rfc3987 = {version = "*", optional = true, markers = "extra == \"format\""}
+six = ">=1.11.0"
+strict-rfc3339 = {version = "*", optional = true, markers = "extra == \"format\""}
+webcolors = {version = "*", optional = true, markers = "extra == \"format\""}
+
+[package.extras]
+format = ["idna", "jsonpointer (>1.13)", "rfc3987", "strict-rfc3339", "webcolors"]
+format_nongpl = ["idna", "jsonpointer (>1.13)", "webcolors", "rfc3986-validator (>0.1.0)", "rfc3339-validator"]
+
+[[package]]
+name = "jupyter-client"
+version = "6.2.0"
+description = "Jupyter protocol implementation and client libraries"
+category = "main"
+optional = false
+python-versions = ">=3.6.1"
+
+[package.dependencies]
+jupyter-core = ">=4.6.0"
+nest-asyncio = ">=1.5"
+python-dateutil = ">=2.1"
+pyzmq = ">=13"
+tornado = ">=4.1"
+traitlets = "*"
+
+[package.extras]
+doc = ["sphinx (>=1.3.6)", "sphinx-rtd-theme", "sphinxcontrib-github-alt"]
+test = ["async-generator", "ipykernel", "ipython", "mock", "pytest-asyncio", "pytest-timeout", "pytest", "mypy", "pre-commit", "jedi (<0.18)"]
+
+[[package]]
+name = "jupyter-core"
+version = "4.7.1"
+description = "Jupyter core package. A base package on which Jupyter projects rely."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+pywin32 = {version = ">=1.0", markers = "sys_platform == \"win32\""}
+traitlets = "*"
+
+[[package]]
+name = "jupyterlab-pygments"
+version = "0.1.2"
+description = "Pygments theme using JupyterLab CSS variables"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+pygments = ">=2.4.1,<3"
+
+[[package]]
+name = "jupyterlab-widgets"
+version = "1.0.0"
+description = "A JupyterLab extension."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "kgdata"
+version = "1.1.7"
+description = "Library to process dumps of knowledge graphs (Wikipedia, DBpedia, Wikidata)"
+category = "main"
+optional = false
+python-versions = ">=3.8,<4.0"
+
+[package.dependencies]
+beautifulsoup4 = ">=4.9.3,<5.0.0"
+chardet = ">=4.0.0,<5.0.0"
+cityhash = ">=0.2.3,<0.3.0"
+fastnumbers = ">=3.1.0,<4.0.0"
+loguru = ">=0.5.3,<0.6.0"
+networkx = ">=2.5.1,<3.0.0"
+numpy = ">=1.20.3,<2.0.0"
+orjson = ">=3.5.2,<4.0.0"
+pyspark = "3.0.1"
+rdflib = ">=5.0.0,<6.0.0"
+redis = ">=3.5.3,<4.0.0"
+requests = ">=2.25.1,<3.0.0"
+rocksdb = ">=0.7.0,<0.8.0"
+"ruamel.yaml" = ">=0.17.4,<0.18.0"
+six = ">=1.16.0,<2.0.0"
+tqdm = ">=4.60.0,<5.0.0"
+ujson = ">=4.0.2,<5.0.0"
+
+[[package]]
+name = "kiwisolver"
+version = "1.3.1"
+description = "A fast implementation of the Cassowary constraint solver"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "locket"
+version = "0.2.1"
+description = "File-based locks for Python for Linux and Windows"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[[package]]
+name = "loguru"
+version = "0.5.3"
+description = "Python logging made (stupidly) simple"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[package.dependencies]
+colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""}
+win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
+
+[package.extras]
+dev = ["codecov (>=2.0.15)", "colorama (>=0.3.4)", "flake8 (>=3.7.7)", "tox (>=3.9.0)", "tox-travis (>=0.12)", "pytest (>=4.6.2)", "pytest-cov (>=2.7.1)", "Sphinx (>=2.2.1)", "sphinx-autobuild (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "black (>=19.10b0)", "isort (>=5.1.1)"]
+
+[[package]]
+name = "markupsafe"
+version = "2.0.1"
+description = "Safely add untrusted strings to HTML/XML markup."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "matplotlib"
+version = "3.4.2"
+description = "Python plotting package"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+cycler = ">=0.10"
+kiwisolver = ">=1.0.1"
+numpy = ">=1.16"
+pillow = ">=6.2.0"
+pyparsing = ">=2.2.1"
+python-dateutil = ">=2.7"
+
+[[package]]
+name = "matplotlib-inline"
+version = "0.1.2"
+description = "Inline Matplotlib backend for Jupyter"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[package.dependencies]
+traitlets = "*"
+
+[[package]]
+name = "mistune"
+version = "0.8.4"
+description = "The fastest markdown parser in pure Python"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "monotonic"
+version = "1.6"
+description = "An implementation of time.monotonic() for Python 2 & < 3.3"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "msgpack"
+version = "1.0.2"
+description = "MessagePack (de)serializer."
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "multiprocess"
+version = "0.70.11.1"
+description = "better multiprocessing and multithreading in python"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+dill = ">=0.3.3"
+
+[[package]]
+name = "nbclient"
+version = "0.5.3"
+description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor."
+category = "main"
+optional = false
+python-versions = ">=3.6.1"
+
+[package.dependencies]
+async-generator = "*"
+jupyter-client = ">=6.1.5"
+nbformat = ">=5.0"
+nest-asyncio = "*"
+traitlets = ">=4.2"
+
+[package.extras]
+dev = ["codecov", "coverage", "ipython", "ipykernel", "ipywidgets", "pytest (>=4.1)", "pytest-cov (>=2.6.1)", "check-manifest", "flake8", "mypy", "tox", "bumpversion", "xmltodict", "pip (>=18.1)", "wheel (>=0.31.0)", "setuptools (>=38.6.0)", "twine (>=1.11.0)", "black"]
+sphinx = ["Sphinx (>=1.7)", "sphinx-book-theme", "mock", "moto", "myst-parser"]
+test = ["codecov", "coverage", "ipython", "ipykernel", "ipywidgets", "pytest (>=4.1)", "pytest-cov (>=2.6.1)", "check-manifest", "flake8", "mypy", "tox", "bumpversion", "xmltodict", "pip (>=18.1)", "wheel (>=0.31.0)", "setuptools (>=38.6.0)", "twine (>=1.11.0)", "black"]
+
+[[package]]
+name = "nbconvert"
+version = "6.0.7"
+description = "Converting Jupyter Notebooks"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+bleach = "*"
+defusedxml = "*"
+entrypoints = ">=0.2.2"
+jinja2 = ">=2.4"
+jupyter-core = "*"
+jupyterlab-pygments = "*"
+mistune = ">=0.8.1,<2"
+nbclient = ">=0.5.0,<0.6.0"
+nbformat = ">=4.4"
+pandocfilters = ">=1.4.1"
+pygments = ">=2.4.1"
+testpath = "*"
+traitlets = ">=4.2"
+
+[package.extras]
+all = ["pytest", "pytest-cov", "pytest-dependency", "ipykernel", "ipywidgets (>=7)", "pyppeteer (==0.2.2)", "tornado (>=4.0)", "sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "ipython"]
+docs = ["sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "ipython"]
+serve = ["tornado (>=4.0)"]
+test = ["pytest", "pytest-cov", "pytest-dependency", "ipykernel", "ipywidgets (>=7)", "pyppeteer (==0.2.2)"]
+webpdf = ["pyppeteer (==0.2.2)"]
+
+[[package]]
+name = "nbformat"
+version = "5.1.3"
+description = "The Jupyter Notebook format"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[package.dependencies]
+ipython-genutils = "*"
+jsonschema = ">=2.4,<2.5.0 || >2.5.0"
+jupyter-core = "*"
+traitlets = ">=4.1"
+
+[package.extras]
+fast = ["fastjsonschema"]
+test = ["check-manifest", "fastjsonschema", "testpath", "pytest", "pytest-cov"]
+
+[[package]]
+name = "neptune-client"
+version = "0.9.16"
+description = "Neptune Client"
+category = "dev"
+optional = false
+python-versions = ">=3.6.0"
+
+[package.dependencies]
+bravado = "*"
+click = ">=7.0"
+future = ">=0.17.1"
+GitPython = ">=2.0.8"
+oauthlib = ">=2.1.0"
+packaging = "*"
+pandas = "*"
+Pillow = ">=1.1.6"
+PyJWT = "*"
+requests = ">=2.20.0"
+requests-oauthlib = ">=1.0.0"
+six = ">=1.12.0"
+urllib3 = "*"
+websocket-client = ">=0.35.0,<1.0.0 || >1.0.0"
+
+[package.extras]
+lightgbm = ["neptune-lightgbm"]
+optuna = ["neptune-optuna"]
+pytorch-lightning = ["neptune-pytorch-lightning"]
+sacred = ["neptune-sacred"]
+sklearn = ["neptune-sklearn"]
+tensorflow-keras = ["neptune-tensorflow-keras"]
+xgboost = ["neptune-xgboost"]
+
+[[package]]
+name = "nest-asyncio"
+version = "1.5.1"
+description = "Patch asyncio to allow nested event loops"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
+name = "networkx"
+version = "2.5.1"
+description = "Python package for creating and manipulating graphs and networks"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+decorator = ">=4.3,<5"
+
+[package.extras]
+all = ["numpy", "scipy", "pandas", "matplotlib", "pygraphviz", "pydot", "pyyaml", "lxml", "pytest"]
+gdal = ["gdal"]
+lxml = ["lxml"]
+matplotlib = ["matplotlib"]
+numpy = ["numpy"]
+pandas = ["pandas"]
+pydot = ["pydot"]
+pygraphviz = ["pygraphviz"]
+pytest = ["pytest"]
+pyyaml = ["pyyaml"]
+scipy = ["scipy"]
+
+[[package]]
+name = "notebook"
+version = "6.4.0"
+description = "A web-based notebook environment for interactive computing"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+argon2-cffi = "*"
+ipykernel = "*"
+ipython-genutils = "*"
+jinja2 = "*"
+jupyter-client = ">=5.3.4"
+jupyter-core = ">=4.6.1"
+nbconvert = "*"
+nbformat = "*"
+prometheus-client = "*"
+pyzmq = ">=17"
+Send2Trash = ">=1.5.0"
+terminado = ">=0.8.3"
+tornado = ">=6.1"
+traitlets = ">=4.2.1"
+
+[package.extras]
+docs = ["sphinx", "nbsphinx", "sphinxcontrib-github-alt", "sphinx-rtd-theme", "myst-parser"]
+json-logging = ["json-logging"]
+test = ["pytest", "coverage", "requests", "nbval", "selenium", "pytest-cov", "requests-unixsocket"]
+
+[[package]]
+name = "numpy"
+version = "1.20.3"
+description = "NumPy is the fundamental package for array computing with Python."
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[[package]]
+name = "oauthlib"
+version = "3.1.1"
+description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[package.extras]
+rsa = ["cryptography (>=3.0.0,<4)"]
+signals = ["blinker (>=1.4.0)"]
+signedtoken = ["cryptography (>=3.0.0,<4)", "pyjwt (>=2.0.0,<3)"]
+
+[[package]]
+name = "omegaconf"
+version = "2.1.0"
+description = "A flexible configuration library"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+antlr4-python3-runtime = "4.8"
+PyYAML = ">=5.1"
+
+[[package]]
+name = "orjson"
+version = "3.5.3"
+description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "packaging"
+version = "20.9"
+description = "Core utilities for Python packages"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[package.dependencies]
+pyparsing = ">=2.0.2"
+
+[[package]]
+name = "pandas"
+version = "1.2.4"
+description = "Powerful data structures for data analysis, time series, and statistics"
+category = "main"
+optional = false
+python-versions = ">=3.7.1"
+
+[package.dependencies]
+numpy = ">=1.16.5"
+python-dateutil = ">=2.7.3"
+pytz = ">=2017.3"
+
+[package.extras]
+test = ["pytest (>=5.0.1)", "pytest-xdist", "hypothesis (>=3.58)"]
+
+[[package]]
+name = "pandocfilters"
+version = "1.4.3"
+description = "Utilities for writing pandoc filters in python"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[[package]]
+name = "parso"
+version = "0.8.2"
+description = "A Python Parser"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.extras]
+qa = ["flake8 (==3.8.3)", "mypy (==0.782)"]
+testing = ["docopt", "pytest (<6.0.0)"]
+
+[[package]]
+name = "partd"
+version = "1.2.0"
+description = "Appendable key-value storage"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[package.dependencies]
+locket = "*"
+toolz = "*"
+
+[package.extras]
+complete = ["numpy (>=1.9.0)", "pandas (>=0.19.0)", "pyzmq", "blosc"]
+
+[[package]]
+name = "pexpect"
+version = "4.8.0"
+description = "Pexpect allows easy control of interactive console applications."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+ptyprocess = ">=0.5"
+
+[[package]]
+name = "pickleshare"
+version = "0.7.5"
+description = "Tiny 'shelve'-like database with concurrency support"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "pillow"
+version = "8.2.0"
+description = "Python Imaging Library (Fork)"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "prometheus-client"
+version = "0.11.0"
+description = "Python client for the Prometheus monitoring system."
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[package.extras]
+twisted = ["twisted"]
+
+[[package]]
+name = "prompt-toolkit"
+version = "3.0.18"
+description = "Library for building powerful interactive command lines in Python"
+category = "main"
+optional = false
+python-versions = ">=3.6.1"
+
+[package.dependencies]
+wcwidth = "*"
+
+[[package]]
+name = "pslpython"
+version = "2.2.2"
+description = "A python inferface to the PSL SRL/ML software."
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[package.dependencies]
+pandas = "*"
+pyyaml = "*"
+
+[[package]]
+name = "psutil"
+version = "5.8.0"
+description = "Cross-platform lib for process and system monitoring in Python."
+category = "main"
+optional = false
+python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[package.extras]
+test = ["ipaddress", "mock", "unittest2", "enum34", "pywin32", "wmi"]
+
+[[package]]
+name = "ptyprocess"
+version = "0.7.0"
+description = "Run a subprocess in a pseudo terminal"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "py"
+version = "1.10.0"
+description = "library with cross-python path, ini-parsing, io, code, log facilities"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[[package]]
+name = "py4j"
+version = "0.10.9"
+description = "Enables Python programs to dynamically access arbitrary Java objects"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "pycparser"
+version = "2.20"
+description = "C parser in Python"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[[package]]
+name = "pydot"
+version = "1.4.2"
+description = "Python interface to Graphviz's Dot"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[package.dependencies]
+pyparsing = ">=2.1.4"
+
+[[package]]
+name = "pygments"
+version = "2.9.0"
+description = "Pygments is a syntax highlighting package written in Python."
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
+name = "pyjwt"
+version = "2.1.0"
+description = "JSON Web Token implementation in Python"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[package.extras]
+crypto = ["cryptography (>=3.3.1,<4.0.0)"]
+dev = ["sphinx", "sphinx-rtd-theme", "zope.interface", "cryptography (>=3.3.1,<4.0.0)", "pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)", "mypy", "pre-commit"]
+docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"]
+tests = ["pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)"]
+
+[[package]]
+name = "pyparsing"
+version = "2.4.7"
+description = "Python parsing module"
+category = "main"
+optional = false
+python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
+
+[[package]]
+name = "pyrallel.lib"
+version = "0.0.10"
+description = "Yet another easy-to-use python3 parallel library for humans."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+dill = ">=0.3"
+multiprocess = ">=0.70"
+typing = ">=3.6"
+
+[[package]]
+name = "pyrsistent"
+version = "0.17.3"
+description = "Persistent/Functional/Immutable data structures"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
+name = "pyspark"
+version = "3.0.1"
+description = "Apache Spark Python API"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+py4j = "0.10.9"
+
+[package.extras]
+ml = ["numpy (>=1.7)"]
+mllib = ["numpy (>=1.7)"]
+sql = ["pandas (>=0.23.2)", "pyarrow (>=0.15.1)"]
+
+[[package]]
+name = "python-dateutil"
+version = "2.8.1"
+description = "Extensions to the standard Python datetime module"
+category = "main"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
+
+[package.dependencies]
+six = ">=1.5"
+
+[[package]]
+name = "python-dotenv"
+version = "0.17.1"
+description = "Read key-value pairs from a .env file and set them as environment variables"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.extras]
+cli = ["click (>=5.0)"]
+
+[[package]]
+name = "python-levenshtein"
+version = "0.12.2"
+description = "Python extension for computing string edit distances and similarities."
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "python-slugify"
+version = "5.0.2"
+description = "A Python Slugify application that handles Unicode"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+text-unidecode = ">=1.3"
+
+[package.extras]
+unidecode = ["Unidecode (>=1.1.1)"]
+
+[[package]]
+name = "pytz"
+version = "2021.1"
+description = "World timezone definitions, modern and historical"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "pywin32"
+version = "301"
+description = "Python for Window Extensions"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "pywinpty"
+version = "1.1.1"
+description = "Pseudo terminal support for Windows from Python."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "pyyaml"
+version = "5.4.1"
+description = "YAML parser and emitter for Python"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
+
+[[package]]
+name = "pyzmq"
+version = "22.1.0"
+description = "Python bindings for 0MQ"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+cffi = {version = "*", markers = "implementation_name == \"pypy\""}
+py = {version = "*", markers = "implementation_name == \"pypy\""}
+
+[[package]]
+name = "rdflib"
+version = "5.0.0"
+description = "RDFLib is a Python library for working with RDF, a simple yet powerful language for representing information."
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+isodate = "*"
+pyparsing = "*"
+six = "*"
+
+[package.extras]
+docs = ["sphinx (<3)", "sphinxcontrib-apidoc"]
+html = ["html5lib"]
+sparql = ["requests"]
+tests = ["html5lib", "networkx", "nose", "doctest-ignore-unicode"]
+
+[[package]]
+name = "redis"
+version = "3.5.3"
+description = "Python client for Redis key-value store"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[package.extras]
+hiredis = ["hiredis (>=0.1.3)"]
+
+[[package]]
+name = "requests"
+version = "2.25.1"
+description = "Python HTTP for Humans."
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[package.dependencies]
+certifi = ">=2017.4.17"
+chardet = ">=3.0.2,<5"
+idna = ">=2.5,<3"
+urllib3 = ">=1.21.1,<1.27"
+
+[package.extras]
+security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"]
+socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
+
+[[package]]
+name = "requests-oauthlib"
+version = "1.3.0"
+description = "OAuthlib authentication support for Requests."
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[package.dependencies]
+oauthlib = ">=3.0.0"
+requests = ">=2.0.0"
+
+[package.extras]
+rsa = ["oauthlib[signedtoken] (>=3.0.0)"]
+
+[[package]]
+name = "rfc3987"
+version = "1.3.8"
+description = "Parsing and validation of URIs (RFC 3986) and IRIs (RFC 3987)"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "rltk"
+version = "2.0.0a15"
+description = "Record Linkage ToolKit"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+Cython = ">=0.28.0"
+dask = ">=0.19.2"
+distributed = ">=1.23"
+matplotlib = ">=2.0.0"
+numpy = ">=1.12.0"
+pandas = ">=0.20.0"
+"pyrallel.lib" = "*"
+scipy = ">=1.1.0"
+
+[[package]]
+name = "rocksdb"
+version = "0.7.0"
+description = "Python bindings for RocksDB"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.extras]
+doc = ["sphinx-rtd-theme", "sphinx"]
+test = ["pytest"]
+
+[[package]]
+name = "ruamel.yaml"
+version = "0.17.7"
+description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order"
+category = "main"
+optional = false
+python-versions = ">=3"
+
+[package.dependencies]
+"ruamel.yaml.clib" = {version = ">=0.1.2", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.10\""}
+
+[package.extras]
+docs = ["ryd"]
+jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"]
+
+[[package]]
+name = "ruamel.yaml.clib"
+version = "0.2.2"
+description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "scipy"
+version = "1.6.1"
+description = "SciPy: Scientific Library for Python"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+numpy = ">=1.16.5"
+
+[[package]]
+name = "sem-desc"
+version = "0.1.3"
+description = "Package providing basic functionalities for the semantic modeling problem"
+category = "main"
+optional = false
+python-versions = ">=3.8,<4.0"
+
+[package.dependencies]
+chardet = ">=4.0.0,<5.0.0"
+ipython = ">=7.23.1,<8.0.0"
+loguru = ">=0.5.3,<0.6.0"
+matplotlib = ">=3.4.2,<4.0.0"
+networkx = ">=2.5.1,<3.0.0"
+orjson = ">=3.5.2,<4.0.0"
+pandas = ">=1.2.4,<2.0.0"
+pydot = ">=1.4.2,<2.0.0"
+pyrsistent = ">=0.17.3,<0.18.0"
+python-slugify = ">=5.0.2,<6.0.0"
+redis = ">=3.5.3,<4.0.0"
+rocksdb = ">=0.7.0,<0.8.0"
+"ruamel.yaml" = ">=0.17.4,<0.18.0"
+tqdm = ">=4.60.0,<5.0.0"
+ujson = ">=4.0.2,<5.0.0"
+
+[[package]]
+name = "send2trash"
+version = "1.5.0"
+description = "Send file to trash natively under Mac OS X, Windows and Linux."
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "simplejson"
+version = "3.17.2"
+description = "Simple, fast, extensible JSON encoder/decoder for Python"
+category = "dev"
+optional = false
+python-versions = ">=2.5, !=3.0.*, !=3.1.*, !=3.2.*"
+
+[[package]]
+name = "six"
+version = "1.16.0"
+description = "Python 2 and 3 compatibility utilities"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
+
+[[package]]
+name = "sm-widgets"
+version = "0.1.10"
+description = ""
+category = "main"
+optional = false
+python-versions = ">=3.8,<4.0"
+
+[package.dependencies]
+elasticsearch = ">=7.12.1,<8.0.0"
+fuzzywuzzy = ">=0.18.0,<0.19.0"
+ipycallback = ">=0.2.5,<0.3.0"
+kgdata = ">=1.1.0,<2.0.0"
+loguru = ">=0.5.3,<0.6.0"
+orjson = ">=3.5.2,<4.0.0"
+python-slugify = ">=5.0.2,<6.0.0"
+requests = ">=2.25.1,<3.0.0"
+sem-desc = ">=0.1.2,<0.2.0"
+tqdm = ">=4.60.0,<5.0.0"
+
+[[package]]
+name = "smmap"
+version = "4.0.0"
+description = "A pure Python implementation of a sliding window memory map manager"
+category = "dev"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
+name = "sortedcontainers"
+version = "2.4.0"
+description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "soupsieve"
+version = "2.2.1"
+description = "A modern CSS selector implementation for Beautiful Soup."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "strict-rfc3339"
+version = "0.7"
+description = "Strict, simple, lightweight RFC3339 functions"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "swagger-spec-validator"
+version = "2.7.3"
+description = "Validation of Swagger specifications"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+jsonschema = "*"
+pyyaml = "*"
+six = "*"
+
+[[package]]
+name = "tblib"
+version = "1.7.0"
+description = "Traceback serialization library."
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[[package]]
+name = "terminado"
+version = "0.10.0"
+description = "Tornado websocket backend for the Xterm.js Javascript terminal emulator library."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+ptyprocess = {version = "*", markers = "os_name != \"nt\""}
+pywinpty = {version = ">=1.1.0", markers = "os_name == \"nt\""}
+tornado = ">=4"
+
+[package.extras]
+test = ["pytest"]
+
+[[package]]
+name = "testpath"
+version = "0.5.0"
+description = "Test utilities for code working with files and commands"
+category = "main"
+optional = false
+python-versions = ">= 3.5"
+
+[package.extras]
+test = ["pytest", "pathlib2"]
+
+[[package]]
+name = "text-unidecode"
+version = "1.3"
+description = "The most basic Text::Unidecode port"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "toolz"
+version = "0.11.1"
+description = "List processing tools and functional utilities"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
+name = "tornado"
+version = "6.1"
+description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed."
+category = "main"
+optional = false
+python-versions = ">= 3.5"
+
+[[package]]
+name = "tqdm"
+version = "4.61.0"
+description = "Fast, Extensible Progress Meter"
+category = "main"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
+
+[package.extras]
+dev = ["py-make (>=0.1.0)", "twine", "wheel"]
+notebook = ["ipywidgets (>=6)"]
+telegram = ["requests"]
+
+[[package]]
+name = "traitlets"
+version = "5.0.5"
+description = "Traitlets Python configuration system"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+ipython-genutils = "*"
+
+[package.extras]
+test = ["pytest"]
+
+[[package]]
+name = "typing"
+version = "3.7.4.3"
+description = "Type Hints for Python"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+
+[[package]]
+name = "typing-extensions"
+version = "3.10.0.0"
+description = "Backported and Experimental Type Hints for Python 3.5+"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "ujson"
+version = "4.0.2"
+description = "Ultra fast JSON encoder and decoder for Python"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "urllib3"
+version = "1.26.5"
+description = "HTTP library with thread-safe connection pooling, file post, and more."
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
+
+[package.extras]
+brotli = ["brotlipy (>=0.6.0)"]
+secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
+socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
+
+[[package]]
+name = "wcwidth"
+version = "0.2.5"
+description = "Measures the displayed width of unicode strings in a terminal"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "webcolors"
+version = "1.11.1"
+description = "A library for working with color names and color values formats defined by HTML and CSS."
+category = "dev"
+optional = false
+python-versions = ">=3.5,"
+
+[[package]]
+name = "webencodings"
+version = "0.5.1"
+description = "Character encoding aliases for legacy web content"
+category = "main"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "websocket-client"
+version = "1.0.1"
+description = "WebSocket client for Python with low level API options"
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "widgetsnbextension"
+version = "3.5.1"
+description = "IPython HTML widgets for Jupyter"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+notebook = ">=4.4.1"
+
+[[package]]
+name = "win32-setctime"
+version = "1.0.3"
+description = "A small Python utility to set file creation time on Windows"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[package.extras]
+dev = ["pytest (>=4.6.2)", "black (>=19.3b0)"]
+
+[[package]]
+name = "zict"
+version = "2.0.0"
+description = "Mutable mapping tools"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+heapdict = "*"
+
+[metadata]
+lock-version = "1.1"
+python-versions = "^3.8"
+content-hash = "ea1a3f9dff115335b7a25ebe1d582e135d0570223f9d639a71ef4c6ea5d04774"
+
+[metadata.files]
+antlr4-python3-runtime = [
+ {file = "antlr4-python3-runtime-4.8.tar.gz", hash = "sha256:15793f5d0512a372b4e7d2284058ad32ce7dd27126b105fb0b2245130445db33"},
+]
+appnope = [
+ {file = "appnope-0.1.2-py2.py3-none-any.whl", hash = "sha256:93aa393e9d6c54c5cd570ccadd8edad61ea0c4b9ea7a01409020c9aa019eb442"},
+ {file = "appnope-0.1.2.tar.gz", hash = "sha256:dd83cd4b5b460958838f6eb3000c660b1f9caf2a5b1de4264e941512f603258a"},
+]
+argon2-cffi = [
+ {file = "argon2-cffi-20.1.0.tar.gz", hash = "sha256:d8029b2d3e4b4cea770e9e5a0104dd8fa185c1724a0f01528ae4826a6d25f97d"},
+ {file = "argon2_cffi-20.1.0-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:6ea92c980586931a816d61e4faf6c192b4abce89aa767ff6581e6ddc985ed003"},
+ {file = "argon2_cffi-20.1.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:05a8ac07c7026542377e38389638a8a1e9b78f1cd8439cd7493b39f08dd75fbf"},
+ {file = "argon2_cffi-20.1.0-cp27-cp27m-win32.whl", hash = "sha256:0bf066bc049332489bb2d75f69216416329d9dc65deee127152caeb16e5ce7d5"},
+ {file = "argon2_cffi-20.1.0-cp27-cp27m-win_amd64.whl", hash = "sha256:57358570592c46c420300ec94f2ff3b32cbccd10d38bdc12dc6979c4a8484fbc"},
+ {file = "argon2_cffi-20.1.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:7d455c802727710e9dfa69b74ccaab04568386ca17b0ad36350b622cd34606fe"},
+ {file = "argon2_cffi-20.1.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:b160416adc0f012fb1f12588a5e6954889510f82f698e23ed4f4fa57f12a0647"},
+ {file = "argon2_cffi-20.1.0-cp35-cp35m-win32.whl", hash = "sha256:9bee3212ba4f560af397b6d7146848c32a800652301843df06b9e8f68f0f7361"},
+ {file = "argon2_cffi-20.1.0-cp35-cp35m-win_amd64.whl", hash = "sha256:392c3c2ef91d12da510cfb6f9bae52512a4552573a9e27600bdb800e05905d2b"},
+ {file = "argon2_cffi-20.1.0-cp36-cp36m-win32.whl", hash = "sha256:ba7209b608945b889457f949cc04c8e762bed4fe3fec88ae9a6b7765ae82e496"},
+ {file = "argon2_cffi-20.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:da7f0445b71db6d3a72462e04f36544b0de871289b0bc8a7cc87c0f5ec7079fa"},
+ {file = "argon2_cffi-20.1.0-cp37-abi3-macosx_10_6_intel.whl", hash = "sha256:cc0e028b209a5483b6846053d5fd7165f460a1f14774d79e632e75e7ae64b82b"},
+ {file = "argon2_cffi-20.1.0-cp37-cp37m-win32.whl", hash = "sha256:18dee20e25e4be86680b178b35ccfc5d495ebd5792cd00781548d50880fee5c5"},
+ {file = "argon2_cffi-20.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:6678bb047373f52bcff02db8afab0d2a77d83bde61cfecea7c5c62e2335cb203"},
+ {file = "argon2_cffi-20.1.0-cp38-cp38-win32.whl", hash = "sha256:77e909cc756ef81d6abb60524d259d959bab384832f0c651ed7dcb6e5ccdbb78"},
+ {file = "argon2_cffi-20.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:9dfd5197852530294ecb5795c97a823839258dfd5eb9420233c7cfedec2058f2"},
+ {file = "argon2_cffi-20.1.0-cp39-cp39-win32.whl", hash = "sha256:e2db6e85c057c16d0bd3b4d2b04f270a7467c147381e8fd73cbbe5bc719832be"},
+ {file = "argon2_cffi-20.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:8a84934bd818e14a17943de8099d41160da4a336bcc699bb4c394bbb9b94bd32"},
+]
+async-generator = [
+ {file = "async_generator-1.10-py3-none-any.whl", hash = "sha256:01c7bf666359b4967d2cda0000cc2e4af16a0ae098cbffcb8472fb9e8ad6585b"},
+ {file = "async_generator-1.10.tar.gz", hash = "sha256:6ebb3d106c12920aaae42ccb6f787ef5eefdcdd166ea3d628fa8476abe712144"},
+]
+attrs = [
+ {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"},
+ {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"},
+]
+backcall = [
+ {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"},
+ {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"},
+]
+beautifulsoup4 = [
+ {file = "beautifulsoup4-4.9.3-py2-none-any.whl", hash = "sha256:4c98143716ef1cb40bf7f39a8e3eec8f8b009509e74904ba3a7b315431577e35"},
+ {file = "beautifulsoup4-4.9.3-py3-none-any.whl", hash = "sha256:fff47e031e34ec82bf17e00da8f592fe7de69aeea38be00523c04623c04fb666"},
+ {file = "beautifulsoup4-4.9.3.tar.gz", hash = "sha256:84729e322ad1d5b4d25f805bfa05b902dd96450f43842c4e99067d5e1369eb25"},
+]
+bleach = [
+ {file = "bleach-3.3.0-py2.py3-none-any.whl", hash = "sha256:6123ddc1052673e52bab52cdc955bcb57a015264a1c57d37bea2f6b817af0125"},
+ {file = "bleach-3.3.0.tar.gz", hash = "sha256:98b3170739e5e83dd9dc19633f074727ad848cbedb6026708c8ac2d3b697a433"},
+]
+bravado = [
+ {file = "bravado-11.0.3-py2.py3-none-any.whl", hash = "sha256:8ac8bbb645e49607917a5c07808116c708521f51e80d9c29bc4a168ff4dd22c6"},
+ {file = "bravado-11.0.3.tar.gz", hash = "sha256:1bb6ef75d84140c851fffe6420baaee5037d840070cfe11d60913be6ab8e0530"},
+]
+bravado-core = [
+ {file = "bravado-core-5.17.0.tar.gz", hash = "sha256:b3b06ae86d3c80de5694340e55df7c9097857ff965b76642979e2a961f332abf"},
+ {file = "bravado_core-5.17.0-py2.py3-none-any.whl", hash = "sha256:fa53e796ea574f905635a43871439a44713c2ef128c62a8fcc1d0ca8765cf855"},
+]
+certifi = [
+ {file = "certifi-2021.5.30-py2.py3-none-any.whl", hash = "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"},
+ {file = "certifi-2021.5.30.tar.gz", hash = "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee"},
+]
+cffi = [
+ {file = "cffi-1.14.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:bb89f306e5da99f4d922728ddcd6f7fcebb3241fc40edebcb7284d7514741991"},
+ {file = "cffi-1.14.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:34eff4b97f3d982fb93e2831e6750127d1355a923ebaeeb565407b3d2f8d41a1"},
+ {file = "cffi-1.14.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:99cd03ae7988a93dd00bcd9d0b75e1f6c426063d6f03d2f90b89e29b25b82dfa"},
+ {file = "cffi-1.14.5-cp27-cp27m-win32.whl", hash = "sha256:65fa59693c62cf06e45ddbb822165394a288edce9e276647f0046e1ec26920f3"},
+ {file = "cffi-1.14.5-cp27-cp27m-win_amd64.whl", hash = "sha256:51182f8927c5af975fece87b1b369f722c570fe169f9880764b1ee3bca8347b5"},
+ {file = "cffi-1.14.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:43e0b9d9e2c9e5d152946b9c5fe062c151614b262fda2e7b201204de0b99e482"},
+ {file = "cffi-1.14.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:cbde590d4faaa07c72bf979734738f328d239913ba3e043b1e98fe9a39f8b2b6"},
+ {file = "cffi-1.14.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:5de7970188bb46b7bf9858eb6890aad302577a5f6f75091fd7cdd3ef13ef3045"},
+ {file = "cffi-1.14.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:a465da611f6fa124963b91bf432d960a555563efe4ed1cc403ba5077b15370aa"},
+ {file = "cffi-1.14.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:d42b11d692e11b6634f7613ad8df5d6d5f8875f5d48939520d351007b3c13406"},
+ {file = "cffi-1.14.5-cp35-cp35m-win32.whl", hash = "sha256:72d8d3ef52c208ee1c7b2e341f7d71c6fd3157138abf1a95166e6165dd5d4369"},
+ {file = "cffi-1.14.5-cp35-cp35m-win_amd64.whl", hash = "sha256:29314480e958fd8aab22e4a58b355b629c59bf5f2ac2492b61e3dc06d8c7a315"},
+ {file = "cffi-1.14.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:3d3dd4c9e559eb172ecf00a2a7517e97d1e96de2a5e610bd9b68cea3925b4892"},
+ {file = "cffi-1.14.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:48e1c69bbacfc3d932221851b39d49e81567a4d4aac3b21258d9c24578280058"},
+ {file = "cffi-1.14.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:69e395c24fc60aad6bb4fa7e583698ea6cc684648e1ffb7fe85e3c1ca131a7d5"},
+ {file = "cffi-1.14.5-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:9e93e79c2551ff263400e1e4be085a1210e12073a31c2011dbbda14bda0c6132"},
+ {file = "cffi-1.14.5-cp36-cp36m-win32.whl", hash = "sha256:58e3f59d583d413809d60779492342801d6e82fefb89c86a38e040c16883be53"},
+ {file = "cffi-1.14.5-cp36-cp36m-win_amd64.whl", hash = "sha256:005a36f41773e148deac64b08f233873a4d0c18b053d37da83f6af4d9087b813"},
+ {file = "cffi-1.14.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2894f2df484ff56d717bead0a5c2abb6b9d2bf26d6960c4604d5c48bbc30ee73"},
+ {file = "cffi-1.14.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:0857f0ae312d855239a55c81ef453ee8fd24136eaba8e87a2eceba644c0d4c06"},
+ {file = "cffi-1.14.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:cd2868886d547469123fadc46eac7ea5253ea7fcb139f12e1dfc2bbd406427d1"},
+ {file = "cffi-1.14.5-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:35f27e6eb43380fa080dccf676dece30bef72e4a67617ffda586641cd4508d49"},
+ {file = "cffi-1.14.5-cp37-cp37m-win32.whl", hash = "sha256:9ff227395193126d82e60319a673a037d5de84633f11279e336f9c0f189ecc62"},
+ {file = "cffi-1.14.5-cp37-cp37m-win_amd64.whl", hash = "sha256:9cf8022fb8d07a97c178b02327b284521c7708d7c71a9c9c355c178ac4bbd3d4"},
+ {file = "cffi-1.14.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8b198cec6c72df5289c05b05b8b0969819783f9418e0409865dac47288d2a053"},
+ {file = "cffi-1.14.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:ad17025d226ee5beec591b52800c11680fca3df50b8b29fe51d882576e039ee0"},
+ {file = "cffi-1.14.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6c97d7350133666fbb5cf4abdc1178c812cb205dc6f41d174a7b0f18fb93337e"},
+ {file = "cffi-1.14.5-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8ae6299f6c68de06f136f1f9e69458eae58f1dacf10af5c17353eae03aa0d827"},
+ {file = "cffi-1.14.5-cp38-cp38-win32.whl", hash = "sha256:b85eb46a81787c50650f2392b9b4ef23e1f126313b9e0e9013b35c15e4288e2e"},
+ {file = "cffi-1.14.5-cp38-cp38-win_amd64.whl", hash = "sha256:1f436816fc868b098b0d63b8920de7d208c90a67212546d02f84fe78a9c26396"},
+ {file = "cffi-1.14.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1071534bbbf8cbb31b498d5d9db0f274f2f7a865adca4ae429e147ba40f73dea"},
+ {file = "cffi-1.14.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:9de2e279153a443c656f2defd67769e6d1e4163952b3c622dcea5b08a6405322"},
+ {file = "cffi-1.14.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:6e4714cc64f474e4d6e37cfff31a814b509a35cb17de4fb1999907575684479c"},
+ {file = "cffi-1.14.5-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:158d0d15119b4b7ff6b926536763dc0714313aa59e320ddf787502c70c4d4bee"},
+ {file = "cffi-1.14.5-cp39-cp39-win32.whl", hash = "sha256:afb29c1ba2e5a3736f1c301d9d0abe3ec8b86957d04ddfa9d7a6a42b9367e396"},
+ {file = "cffi-1.14.5-cp39-cp39-win_amd64.whl", hash = "sha256:f2d45f97ab6bb54753eab54fffe75aaf3de4ff2341c9daee1987ee1837636f1d"},
+ {file = "cffi-1.14.5.tar.gz", hash = "sha256:fd78e5fee591709f32ef6edb9a015b4aa1a5022598e36227500c8f4e02328d9c"},
+]
+chardet = [
+ {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"},
+ {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"},
+]
+cityhash = [
+ {file = "cityhash-0.2.3.post9.tar.gz", hash = "sha256:aa8919c00e8d8699df42dd788eda4f24ad13086ea9d83407fd7da4a0206d9d18"},
+]
+click = [
+ {file = "click-8.0.1-py3-none-any.whl", hash = "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"},
+ {file = "click-8.0.1.tar.gz", hash = "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a"},
+]
+cloudpickle = [
+ {file = "cloudpickle-1.6.0-py3-none-any.whl", hash = "sha256:3a32d0eb0bc6f4d0c57fbc4f3e3780f7a81e6fee0fa935072884d58ae8e1cc7c"},
+ {file = "cloudpickle-1.6.0.tar.gz", hash = "sha256:9bc994f9e9447593bd0a45371f0e7ac7333710fcf64a4eb9834bf149f4ef2f32"},
+]
+colorama = [
+ {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
+ {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
+]
+cycler = [
+ {file = "cycler-0.10.0-py2.py3-none-any.whl", hash = "sha256:1d8a5ae1ff6c5cf9b93e8811e581232ad8920aeec647c37316ceac982b08cb2d"},
+ {file = "cycler-0.10.0.tar.gz", hash = "sha256:cd7b2d1018258d7247a71425e9f26463dfb444d411c39569972f4ce586b0c9d8"},
+]
+cython = [
+ {file = "Cython-0.29.23-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ff885f18d169759b57f116d3956e45cd2b9cba989fde348bba091544c668dc11"},
+ {file = "Cython-0.29.23-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:3b29224eb62309a10819d923dc6262f769e4f3facfee3cd06372c355e5b38b33"},
+ {file = "Cython-0.29.23-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:355a6e768d91e21fbf477b61881bab64b7a2da386a166898997bccefd532cf5d"},
+ {file = "Cython-0.29.23-cp27-cp27m-win32.whl", hash = "sha256:2af52d312e96b38ded38b34d06e22685c226b1b0e58278bd27209f5d2385d115"},
+ {file = "Cython-0.29.23-cp27-cp27m-win_amd64.whl", hash = "sha256:519fccf526d26b377e1db22f22aa44889b28bc5833ec106588cb13557e8ba2da"},
+ {file = "Cython-0.29.23-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:625a16103770fd92b487b701fb0c07e5790b080f40fa11ce572a2d56d9e9fcca"},
+ {file = "Cython-0.29.23-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:7b7a766726d207d7cd57aff0fcb4b35ce042d3cc88a421fcdb45eeb61a5b9d12"},
+ {file = "Cython-0.29.23-cp34-cp34m-win32.whl", hash = "sha256:474c1a29ab43e29d990df279e2cf6aa96baa9208f5cd4bc76ac87ffcdf1e2945"},
+ {file = "Cython-0.29.23-cp34-cp34m-win_amd64.whl", hash = "sha256:f4aca6bffb1c1c3c4ada3347d0b162a699c18a66e097ee08b63b3a35118fdfcc"},
+ {file = "Cython-0.29.23-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:58dc06871bfdb0592542d779714fe9f918e11ba20ac07757dd63b198bdc704fe"},
+ {file = "Cython-0.29.23-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:2a3bbce689a2fddb85aa66712d93875c99bf7f64ac82b1d149ecce522a7a4e0c"},
+ {file = "Cython-0.29.23-cp35-cp35m-win32.whl", hash = "sha256:3ef530f975e3a760e7282fce2a25f900fa63f96d17321b4aa5f5542eb9859cdf"},
+ {file = "Cython-0.29.23-cp35-cp35m-win_amd64.whl", hash = "sha256:ef21c51350462160456eb71df31b0869e5141e940f22c61c358bdb6e3ebc3388"},
+ {file = "Cython-0.29.23-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:20402ef316393168909926ab21848aa6e08e39bed5003b657139774e66166cd0"},
+ {file = "Cython-0.29.23-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:4858043ac5f96a8f0277cf63760bb39b9521c1f897678cf1d22423f3e758f4ed"},
+ {file = "Cython-0.29.23-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4d7c3b0882d8757c601eaf288fc0d321d5c7ac6c3afb8c42eddf9325a3419cf5"},
+ {file = "Cython-0.29.23-cp36-cp36m-win32.whl", hash = "sha256:37ff66039e3d138ec968ee1d1e12441fa5fb4e6a9c5458bc3c3a232f01be4a7d"},
+ {file = "Cython-0.29.23-cp36-cp36m-win_amd64.whl", hash = "sha256:5be3ae3189cf7d0e9bbeafb854496dc7030c6f6a5602d809435fab8223543a41"},
+ {file = "Cython-0.29.23-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b0699f0dc90181f2458fdb8170455e7798a309e18f41379eda7a2dc8c7aadee0"},
+ {file = "Cython-0.29.23-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:41cd0dd2ff5d78466e73409db509887a84449b400074d4f217980cedbb18e4be"},
+ {file = "Cython-0.29.23-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:aa3bb0928fb2aa3a8828801eb8b29af2261c199f805ae835467489e2bdd00372"},
+ {file = "Cython-0.29.23-cp37-cp37m-win32.whl", hash = "sha256:20cb50d9fede8029bdb50875458f07a27f909289aeed4cdb9c19544dd9a9bc45"},
+ {file = "Cython-0.29.23-cp37-cp37m-win_amd64.whl", hash = "sha256:c4b82461edbbcf90f19b319006345b77474a2d7514e1476d49a14bbd55d6b797"},
+ {file = "Cython-0.29.23-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:794e3df0b57e16bce7583ac909126f4cb381fe566adadb20484d89095855eedb"},
+ {file = "Cython-0.29.23-cp38-cp38-manylinux1_i686.whl", hash = "sha256:0c4b9f7e3aa004cf3f364e3e772f55fec5740485bafea99d1f13bdc9bbd8a545"},
+ {file = "Cython-0.29.23-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:ceccc03b633113ede1f14ad914a6db5c278ce108c8ddb308a5c01c1567d8a02a"},
+ {file = "Cython-0.29.23-cp38-cp38-win32.whl", hash = "sha256:4b0bcf2e06a9063fc78c3243ed4003228375d532ef13b9e5d7183be8f0a52cf5"},
+ {file = "Cython-0.29.23-cp38-cp38-win_amd64.whl", hash = "sha256:5a6792153b728a0240e55bbb5b643f4f7e45c76319e03abf15bf367471ea1d1a"},
+ {file = "Cython-0.29.23-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a8eed9c82e8fe07b8a8ffbd36018871a17458903fc25c9d015f37b54513a3efd"},
+ {file = "Cython-0.29.23-cp39-cp39-manylinux1_i686.whl", hash = "sha256:266459c7e48fe3c6c492b297e4033e42d4c6863cc1a1ff7cc4034949fc574fa6"},
+ {file = "Cython-0.29.23-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:4b6824b58d4373224fc76ee8bee6b35c2d17c91a1ed0fa67b88440f63daebe50"},
+ {file = "Cython-0.29.23-cp39-cp39-win32.whl", hash = "sha256:7d6a33c8a11f05f698e215bfdb837f32c27f63c20f3af863557ed91c748dc2be"},
+ {file = "Cython-0.29.23-cp39-cp39-win_amd64.whl", hash = "sha256:2365f3b5e6451b6bc6dcd262230656f4ade1d862ec2f6c22154deebef37c08b6"},
+ {file = "Cython-0.29.23-py2.py3-none-any.whl", hash = "sha256:282263628c5d601b313d5920f7b6d7e08c7fedbddacd080c4858aa04d86b6b4b"},
+ {file = "Cython-0.29.23.tar.gz", hash = "sha256:6a0d31452f0245daacb14c979c77e093eb1a546c760816b5eed0047686baad8e"},
+]
+dask = [
+ {file = "dask-2021.6.0-py3-none-any.whl", hash = "sha256:ac4ec1e622bc220a057ad424ec5303f8ad96a70d672ccdb4ad60bf56d00fa1b7"},
+ {file = "dask-2021.6.0.tar.gz", hash = "sha256:234f62f8e9e5fd60cd669de16ad29b2c7bdad76c3a2ff0ac1eb854e80106f5af"},
+]
+decorator = [
+ {file = "decorator-4.4.2-py2.py3-none-any.whl", hash = "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760"},
+ {file = "decorator-4.4.2.tar.gz", hash = "sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7"},
+]
+defusedxml = [
+ {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"},
+ {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"},
+]
+dill = [
+ {file = "dill-0.3.3-py2.py3-none-any.whl", hash = "sha256:78370261be6ea49037ace8c17e0b7dd06d0393af6513cc23f9b222d9367ce389"},
+ {file = "dill-0.3.3.zip", hash = "sha256:efb7f6cb65dba7087c1e111bb5390291ba3616741f96840bfc75792a1a9b5ded"},
+]
+distributed = [
+ {file = "distributed-2021.6.0-py3-none-any.whl", hash = "sha256:3f4b1ab97a4027c57314ab9e546dae6e4ce4dc61c7ea47e80abe32cfd475c05f"},
+ {file = "distributed-2021.6.0.tar.gz", hash = "sha256:0ad3db5d2618fc29291b02e8ebc7a1ff6cfce5013455143c3e1bce438b2b5a48"},
+]
+elasticsearch = [
+ {file = "elasticsearch-7.13.1-py2.py3-none-any.whl", hash = "sha256:a09ae1de8869efa6ef2d9a0a9b9f6d9260b0c2506e83dd32bc1119a23fff49a5"},
+ {file = "elasticsearch-7.13.1.tar.gz", hash = "sha256:d6bcca0b2e5665d08e6fe6fadc2d4d321affd76ce483603078fc9d3ccd2bc0f9"},
+]
+entrypoints = [
+ {file = "entrypoints-0.3-py2.py3-none-any.whl", hash = "sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19"},
+ {file = "entrypoints-0.3.tar.gz", hash = "sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"},
+]
+fastnumbers = [
+ {file = "fastnumbers-3.1.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:19e16a6066bc06d21710cf99d4881af0a4384e391d939bf9eca4f12bdb3d2383"},
+ {file = "fastnumbers-3.1.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7bdc1516492b9c291bd599b82d601dc93f19b4a92ef8ac8a7243a43c21682f28"},
+ {file = "fastnumbers-3.1.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:c164a0a37983b278ea2d86f14d64e981963efb0dc39ae26ea00d91cb684f4f90"},
+ {file = "fastnumbers-3.1.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:c0918f006de187590e8717cae8bfc0f7133c3d42c639e95ede2052a6c8be2348"},
+ {file = "fastnumbers-3.1.0-cp35-cp35m-win32.whl", hash = "sha256:cd14ac3c9917ad50dfcb393739830a7da2315fcc740039b75c28d02630d343df"},
+ {file = "fastnumbers-3.1.0-cp35-cp35m-win_amd64.whl", hash = "sha256:2ae191a5bfbac7170d2b9038667d739aa6c4fc84a3ad2a32f0714544995d33d9"},
+ {file = "fastnumbers-3.1.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a833da85291c480e94b3ab6b80cebf6960136ebe7f35e3875d1dee94165e348b"},
+ {file = "fastnumbers-3.1.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:962b91dc05f09b86b31c709852d51cf0fde220722b5f561f5e08e1410faaccb2"},
+ {file = "fastnumbers-3.1.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:b12416e487cb27408745204baad48858d0c46bea317e403654c1e3cba76e9e4e"},
+ {file = "fastnumbers-3.1.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:ff346146c546f421084358b26e70112aa6ee41946d67d8b19237beeb58b76f36"},
+ {file = "fastnumbers-3.1.0-cp36-cp36m-win32.whl", hash = "sha256:44ae9cff6df880e56219c3169718d3a92f18c839e688809c0136d49780aa00d1"},
+ {file = "fastnumbers-3.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f282072f7ee47f2211afbf437f13b56d1cdb36bef34c4c3b5537071534e8134a"},
+ {file = "fastnumbers-3.1.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:209792d02be613f59e58b68af733f9ddd80bf69945f1e28969d476f84dcf6752"},
+ {file = "fastnumbers-3.1.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:9843edacf97749b1bcd8b65d5a3fc99ae61da619f384882d783f6bec19f7f0e6"},
+ {file = "fastnumbers-3.1.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:864dd02f84e00228861f28c8a2ffcbe1ac82c98e21315381bf8e1620dca894b9"},
+ {file = "fastnumbers-3.1.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:0e39daa871868b032939df5b021c473b8a3bdf30ff06a39a89d88230b833c4e5"},
+ {file = "fastnumbers-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:e97f89f342c9814ed1091eea4eec9ccdbe91d30fd607e27c6db9d534b4e4ecb2"},
+ {file = "fastnumbers-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:6b3a8a9d64088f2f6d2461c2e490ccff2b2fe1243f567d5fd8ee09a8b6e16579"},
+ {file = "fastnumbers-3.1.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:6bbec8e6f747c1b43a638f9537809d0f01fc55f70087574e7207ca7526067d0a"},
+ {file = "fastnumbers-3.1.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:a338f4850c8bdf3b6a6e268dda215dc20e248a8e6d06da337c89dcd1747552d2"},
+ {file = "fastnumbers-3.1.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:26601cdcc65a583571b7b4b7827aedbf3114f1d20f674764c1c768bc63b2d06b"},
+ {file = "fastnumbers-3.1.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:61a1845c5aa7b3108451930e808167811e674d5cd50b2021e7d6d1c94b7c1534"},
+ {file = "fastnumbers-3.1.0-cp38-cp38-win32.whl", hash = "sha256:01b0e4f99cbf50ba46aeb5ae464832353a50bb04e56a37b15287855e2d45a848"},
+ {file = "fastnumbers-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:e16c2870d8034b0a2dd3190659f19eb905d3c0d634a8a1bce6c331e9bef181d2"},
+ {file = "fastnumbers-3.1.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:d7e02b949f08b46c63c340d96aba28d9727d92ad938324696fa5a2726d376a4c"},
+ {file = "fastnumbers-3.1.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:826032270aa20b1119892e2399563d0361053be11d6628fe138bde8de6ab3794"},
+ {file = "fastnumbers-3.1.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:299dec0125e361f6c13863d9bc9bd8daea72831e38c7e9d52d9f3ee124979a09"},
+ {file = "fastnumbers-3.1.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:c0d015edcf75a9c5192d745747f68f3a3616d5212d4a6571012fc3c09d7d54c7"},
+ {file = "fastnumbers-3.1.0-cp39-cp39-win32.whl", hash = "sha256:2cc01ecd5fa093a2ce842dce05b7eb2eb53c061b680460aaa9b52be8d2fe9e8f"},
+ {file = "fastnumbers-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:66f402fc2126c9241e44b6b65779f42938d93c2f5f3459486013833d92bc5168"},
+ {file = "fastnumbers-3.1.0.tar.gz", hash = "sha256:7cc4f96981ccd3cf212b6ea7b84ef621620b62da8b454f420aa2e81c8529e68f"},
+]
+fsspec = [
+ {file = "fsspec-2021.6.0-py3-none-any.whl", hash = "sha256:e926b66c074ff3fc732d7678187a294e4a0bf112ccb51594fa9dff5a9fbdc4a1"},
+ {file = "fsspec-2021.6.0.tar.gz", hash = "sha256:931961514c67acab86519ca16985491e639ebf992dbc3a1aae24d44202b52426"},
+]
+ftfy = [
+ {file = "ftfy-6.0.3.tar.gz", hash = "sha256:ba71121a9c8d7790d3e833c6c1021143f3e5c4118293ec3afb5d43ed9ca8e72b"},
+]
+future = [
+ {file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"},
+]
+fuzzywuzzy = [
+ {file = "fuzzywuzzy-0.18.0-py2.py3-none-any.whl", hash = "sha256:928244b28db720d1e0ee7587acf660ea49d7e4c632569cad4f1cd7e68a5f0993"},
+ {file = "fuzzywuzzy-0.18.0.tar.gz", hash = "sha256:45016e92264780e58972dca1b3d939ac864b78437422beecebb3095f8efd00e8"},
+]
+gitdb = [
+ {file = "gitdb-4.0.7-py3-none-any.whl", hash = "sha256:6c4cc71933456991da20917998acbe6cf4fb41eeaab7d6d67fbc05ecd4c865b0"},
+ {file = "gitdb-4.0.7.tar.gz", hash = "sha256:96bf5c08b157a666fec41129e6d327235284cca4c81e92109260f353ba138005"},
+]
+gitpython = [
+ {file = "GitPython-3.1.17-py3-none-any.whl", hash = "sha256:29fe82050709760081f588dd50ce83504feddbebdc4da6956d02351552b1c135"},
+ {file = "GitPython-3.1.17.tar.gz", hash = "sha256:ee24bdc93dce357630764db659edaf6b8d664d4ff5447ccfeedd2dc5c253f41e"},
+]
+heapdict = [
+ {file = "HeapDict-1.0.1-py3-none-any.whl", hash = "sha256:6065f90933ab1bb7e50db403b90cab653c853690c5992e69294c2de2b253fc92"},
+ {file = "HeapDict-1.0.1.tar.gz", hash = "sha256:8495f57b3e03d8e46d5f1b2cc62ca881aca392fd5cc048dc0aa2e1a6d23ecdb6"},
+]
+html5lib = [
+ {file = "html5lib-1.1-py2.py3-none-any.whl", hash = "sha256:0d78f8fde1c230e99fe37986a60526d7049ed4bf8a9fadbad5f00e22e58e041d"},
+ {file = "html5lib-1.1.tar.gz", hash = "sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f"},
+]
+idna = [
+ {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"},
+ {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"},
+]
+ipycallback = [
+ {file = "ipycallback-0.2.5-py2.py3-none-any.whl", hash = "sha256:91762bca49a248c7953df7f7ea1cf339d1aef320961011fb6948670b9bf29f00"},
+ {file = "ipycallback-0.2.5.tar.gz", hash = "sha256:bec0ce9232a905d9d626e56651b127c9e90497d53aea305cdd9e92c1ac10d52f"},
+]
+ipykernel = [
+ {file = "ipykernel-5.5.5-py3-none-any.whl", hash = "sha256:29eee66548ee7c2edb7941de60c0ccf0a7a8dd957341db0a49c5e8e6a0fcb712"},
+ {file = "ipykernel-5.5.5.tar.gz", hash = "sha256:e976751336b51082a89fc2099fb7f96ef20f535837c398df6eab1283c2070884"},
+]
+ipython = [
+ {file = "ipython-7.24.1-py3-none-any.whl", hash = "sha256:d513e93327cf8657d6467c81f1f894adc125334ffe0e4ddd1abbb1c78d828703"},
+ {file = "ipython-7.24.1.tar.gz", hash = "sha256:9bc24a99f5d19721fb8a2d1408908e9c0520a17fff2233ffe82620847f17f1b6"},
+]
+ipython-genutils = [
+ {file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"},
+ {file = "ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"},
+]
+ipywidgets = [
+ {file = "ipywidgets-7.6.3-py2.py3-none-any.whl", hash = "sha256:e6513cfdaf5878de30f32d57f6dc2474da395a2a2991b94d487406c0ab7f55ca"},
+ {file = "ipywidgets-7.6.3.tar.gz", hash = "sha256:9f1a43e620530f9e570e4a493677d25f08310118d315b00e25a18f12913c41f0"},
+]
+isodate = [
+ {file = "isodate-0.6.0-py2.py3-none-any.whl", hash = "sha256:aa4d33c06640f5352aca96e4b81afd8ab3b47337cc12089822d6f322ac772c81"},
+ {file = "isodate-0.6.0.tar.gz", hash = "sha256:2e364a3d5759479cdb2d37cce6b9376ea504db2ff90252a2e5b7cc89cc9ff2d8"},
+]
+jedi = [
+ {file = "jedi-0.18.0-py2.py3-none-any.whl", hash = "sha256:18456d83f65f400ab0c2d3319e48520420ef43b23a086fdc05dff34132f0fb93"},
+ {file = "jedi-0.18.0.tar.gz", hash = "sha256:92550a404bad8afed881a137ec9a461fed49eca661414be45059329614ed0707"},
+]
+jinja2 = [
+ {file = "Jinja2-3.0.1-py3-none-any.whl", hash = "sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4"},
+ {file = "Jinja2-3.0.1.tar.gz", hash = "sha256:703f484b47a6af502e743c9122595cc812b0271f661722403114f71a79d0f5a4"},
+]
+jsonpointer = [
+ {file = "jsonpointer-2.1-py2.py3-none-any.whl", hash = "sha256:150f80c5badd02c757da6644852f612f88e8b4bc2f9852dcbf557c8738919686"},
+ {file = "jsonpointer-2.1.tar.gz", hash = "sha256:5a34b698db1eb79ceac454159d3f7c12a451a91f6334a4f638454327b7a89962"},
+]
+jsonref = [
+ {file = "jsonref-0.2-py3-none-any.whl", hash = "sha256:b1e82fa0b62e2c2796a13e5401fe51790b248f6d9bf9d7212a3e31a3501b291f"},
+ {file = "jsonref-0.2.tar.gz", hash = "sha256:f3c45b121cf6257eafabdc3a8008763aed1cd7da06dbabc59a9e4d2a5e4e6697"},
+]
+jsonschema = [
+ {file = "jsonschema-3.2.0-py2.py3-none-any.whl", hash = "sha256:4e5b3cf8216f577bee9ce139cbe72eca3ea4f292ec60928ff24758ce626cd163"},
+ {file = "jsonschema-3.2.0.tar.gz", hash = "sha256:c8a85b28d377cc7737e46e2d9f2b4f44ee3c0e1deac6bf46ddefc7187d30797a"},
+]
+jupyter-client = [
+ {file = "jupyter_client-6.2.0-py3-none-any.whl", hash = "sha256:9715152067e3f7ea3b56f341c9a0f9715c8c7cc316ee0eb13c3c84f5ca0065f5"},
+ {file = "jupyter_client-6.2.0.tar.gz", hash = "sha256:e2ab61d79fbf8b56734a4c2499f19830fbd7f6fefb3e87868ef0545cb3c17eb9"},
+]
+jupyter-core = [
+ {file = "jupyter_core-4.7.1-py3-none-any.whl", hash = "sha256:8c6c0cac5c1b563622ad49321d5ec47017bd18b94facb381c6973a0486395f8e"},
+ {file = "jupyter_core-4.7.1.tar.gz", hash = "sha256:79025cb3225efcd36847d0840f3fc672c0abd7afd0de83ba8a1d3837619122b4"},
+]
+jupyterlab-pygments = [
+ {file = "jupyterlab_pygments-0.1.2-py2.py3-none-any.whl", hash = "sha256:abfb880fd1561987efaefcb2d2ac75145d2a5d0139b1876d5be806e32f630008"},
+ {file = "jupyterlab_pygments-0.1.2.tar.gz", hash = "sha256:cfcda0873626150932f438eccf0f8bf22bfa92345b814890ab360d666b254146"},
+]
+jupyterlab-widgets = [
+ {file = "jupyterlab_widgets-1.0.0-py3-none-any.whl", hash = "sha256:caeaf3e6103180e654e7d8d2b81b7d645e59e432487c1d35a41d6d3ee56b3fef"},
+ {file = "jupyterlab_widgets-1.0.0.tar.gz", hash = "sha256:5c1a29a84d3069208cb506b10609175b249b6486d6b1cbae8fcde2a11584fb78"},
+]
+kgdata = [
+ {file = "kgdata-1.1.7-py3-none-any.whl", hash = "sha256:8266a4a96a2cf95d8529fd1591e6e5881ed89c743e94b4a21715af3d9e9f633a"},
+ {file = "kgdata-1.1.7.tar.gz", hash = "sha256:cbce18eb70f3bf32d086caa9dd2924a781674c4ac4e1b113f7ae25da1ce81c82"},
+]
+kiwisolver = [
+ {file = "kiwisolver-1.3.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:fd34fbbfbc40628200730bc1febe30631347103fc8d3d4fa012c21ab9c11eca9"},
+ {file = "kiwisolver-1.3.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:d3155d828dec1d43283bd24d3d3e0d9c7c350cdfcc0bd06c0ad1209c1bbc36d0"},
+ {file = "kiwisolver-1.3.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5a7a7dbff17e66fac9142ae2ecafb719393aaee6a3768c9de2fd425c63b53e21"},
+ {file = "kiwisolver-1.3.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:f8d6f8db88049a699817fd9178782867bf22283e3813064302ac59f61d95be05"},
+ {file = "kiwisolver-1.3.1-cp36-cp36m-manylinux2014_ppc64le.whl", hash = "sha256:5f6ccd3dd0b9739edcf407514016108e2280769c73a85b9e59aa390046dbf08b"},
+ {file = "kiwisolver-1.3.1-cp36-cp36m-win32.whl", hash = "sha256:225e2e18f271e0ed8157d7f4518ffbf99b9450fca398d561eb5c4a87d0986dd9"},
+ {file = "kiwisolver-1.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:cf8b574c7b9aa060c62116d4181f3a1a4e821b2ec5cbfe3775809474113748d4"},
+ {file = "kiwisolver-1.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:232c9e11fd7ac3a470d65cd67e4359eee155ec57e822e5220322d7b2ac84fbf0"},
+ {file = "kiwisolver-1.3.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:b38694dcdac990a743aa654037ff1188c7a9801ac3ccc548d3341014bc5ca278"},
+ {file = "kiwisolver-1.3.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ca3820eb7f7faf7f0aa88de0e54681bddcb46e485beb844fcecbcd1c8bd01689"},
+ {file = "kiwisolver-1.3.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:c8fd0f1ae9d92b42854b2979024d7597685ce4ada367172ed7c09edf2cef9cb8"},
+ {file = "kiwisolver-1.3.1-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:1e1bc12fb773a7b2ffdeb8380609f4f8064777877b2225dec3da711b421fda31"},
+ {file = "kiwisolver-1.3.1-cp37-cp37m-win32.whl", hash = "sha256:72c99e39d005b793fb7d3d4e660aed6b6281b502e8c1eaf8ee8346023c8e03bc"},
+ {file = "kiwisolver-1.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:8be8d84b7d4f2ba4ffff3665bcd0211318aa632395a1a41553250484a871d454"},
+ {file = "kiwisolver-1.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:31dfd2ac56edc0ff9ac295193eeaea1c0c923c0355bf948fbd99ed6018010b72"},
+ {file = "kiwisolver-1.3.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:563c649cfdef27d081c84e72a03b48ea9408c16657500c312575ae9d9f7bc1c3"},
+ {file = "kiwisolver-1.3.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:78751b33595f7f9511952e7e60ce858c6d64db2e062afb325985ddbd34b5c131"},
+ {file = "kiwisolver-1.3.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:a357fd4f15ee49b4a98b44ec23a34a95f1e00292a139d6015c11f55774ef10de"},
+ {file = "kiwisolver-1.3.1-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:5989db3b3b34b76c09253deeaf7fbc2707616f130e166996606c284395da3f18"},
+ {file = "kiwisolver-1.3.1-cp38-cp38-win32.whl", hash = "sha256:c08e95114951dc2090c4a630c2385bef681cacf12636fb0241accdc6b303fd81"},
+ {file = "kiwisolver-1.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:44a62e24d9b01ba94ae7a4a6c3fb215dc4af1dde817e7498d901e229aaf50e4e"},
+ {file = "kiwisolver-1.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:50af681a36b2a1dee1d3c169ade9fdc59207d3c31e522519181e12f1b3ba7000"},
+ {file = "kiwisolver-1.3.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:a53d27d0c2a0ebd07e395e56a1fbdf75ffedc4a05943daf472af163413ce9598"},
+ {file = "kiwisolver-1.3.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:834ee27348c4aefc20b479335fd422a2c69db55f7d9ab61721ac8cd83eb78882"},
+ {file = "kiwisolver-1.3.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:5c3e6455341008a054cccee8c5d24481bcfe1acdbc9add30aa95798e95c65621"},
+ {file = "kiwisolver-1.3.1-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:acef3d59d47dd85ecf909c359d0fd2c81ed33bdff70216d3956b463e12c38a54"},
+ {file = "kiwisolver-1.3.1-cp39-cp39-win32.whl", hash = "sha256:c5518d51a0735b1e6cee1fdce66359f8d2b59c3ca85dc2b0813a8aa86818a030"},
+ {file = "kiwisolver-1.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:b9edd0110a77fc321ab090aaa1cfcaba1d8499850a12848b81be2222eab648f6"},
+ {file = "kiwisolver-1.3.1-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0cd53f403202159b44528498de18f9285b04482bab2a6fc3f5dd8dbb9352e30d"},
+ {file = "kiwisolver-1.3.1-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:33449715e0101e4d34f64990352bce4095c8bf13bed1b390773fc0a7295967b3"},
+ {file = "kiwisolver-1.3.1-pp36-pypy36_pp73-win32.whl", hash = "sha256:401a2e9afa8588589775fe34fc22d918ae839aaaf0c0e96441c0fdbce6d8ebe6"},
+ {file = "kiwisolver-1.3.1.tar.gz", hash = "sha256:950a199911a8d94683a6b10321f9345d5a3a8433ec58b217ace979e18f16e248"},
+]
+locket = [
+ {file = "locket-0.2.1-py2.py3-none-any.whl", hash = "sha256:12b6ada59d1f50710bca9704dbadd3f447dbf8dac6664575c1281cadab8e6449"},
+ {file = "locket-0.2.1.tar.gz", hash = "sha256:3e1faba403619fe201552f083f1ecbf23f550941bc51985ac6ed4d02d25056dd"},
+]
+loguru = [
+ {file = "loguru-0.5.3-py3-none-any.whl", hash = "sha256:f8087ac396b5ee5f67c963b495d615ebbceac2796379599820e324419d53667c"},
+ {file = "loguru-0.5.3.tar.gz", hash = "sha256:b28e72ac7a98be3d28ad28570299a393dfcd32e5e3f6a353dec94675767b6319"},
+]
+markupsafe = [
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"},
+ {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"},
+ {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"},
+ {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"},
+ {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"},
+ {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"},
+]
+matplotlib = [
+ {file = "matplotlib-3.4.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c541ee5a3287efe066bbe358320853cf4916bc14c00c38f8f3d8d75275a405a9"},
+ {file = "matplotlib-3.4.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:3a5c18dbd2c7c366da26a4ad1462fe3e03a577b39e3b503bbcf482b9cdac093c"},
+ {file = "matplotlib-3.4.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:a9d8cb5329df13e0cdaa14b3b43f47b5e593ec637f13f14db75bb16e46178b05"},
+ {file = "matplotlib-3.4.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:7ad19f3fb6145b9eb41c08e7cbb9f8e10b91291396bee21e9ce761bb78df63ec"},
+ {file = "matplotlib-3.4.2-cp37-cp37m-win32.whl", hash = "sha256:7a58f3d8fe8fac3be522c79d921c9b86e090a59637cb88e3bc51298d7a2c862a"},
+ {file = "matplotlib-3.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6382bc6e2d7e481bcd977eb131c31dee96e0fb4f9177d15ec6fb976d3b9ace1a"},
+ {file = "matplotlib-3.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6a6a44f27aabe720ec4fd485061e8a35784c2b9ffa6363ad546316dfc9cea04e"},
+ {file = "matplotlib-3.4.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1c1779f7ab7d8bdb7d4c605e6ffaa0614b3e80f1e3c8ccf7b9269a22dbc5986b"},
+ {file = "matplotlib-3.4.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5826f56055b9b1c80fef82e326097e34dc4af8c7249226b7dd63095a686177d1"},
+ {file = "matplotlib-3.4.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:0bea5ec5c28d49020e5d7923c2725b837e60bc8be99d3164af410eb4b4c827da"},
+ {file = "matplotlib-3.4.2-cp38-cp38-win32.whl", hash = "sha256:6475d0209024a77f869163ec3657c47fed35d9b6ed8bccba8aa0f0099fbbdaa8"},
+ {file = "matplotlib-3.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:21b31057bbc5e75b08e70a43cefc4c0b2c2f1b1a850f4a0f7af044eb4163086c"},
+ {file = "matplotlib-3.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b26535b9de85326e6958cdef720ecd10bcf74a3f4371bf9a7e5b2e659c17e153"},
+ {file = "matplotlib-3.4.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:32fa638cc10886885d1ca3d409d4473d6a22f7ceecd11322150961a70fab66dd"},
+ {file = "matplotlib-3.4.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:956c8849b134b4a343598305a3ca1bdd3094f01f5efc8afccdebeffe6b315247"},
+ {file = "matplotlib-3.4.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:85f191bb03cb1a7b04b5c2cca4792bef94df06ef473bc49e2818105671766fee"},
+ {file = "matplotlib-3.4.2-cp39-cp39-win32.whl", hash = "sha256:b1d5a2cedf5de05567c441b3a8c2651fbde56df08b82640e7f06c8cd91e201f6"},
+ {file = "matplotlib-3.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:df815378a754a7edd4559f8c51fc7064f779a74013644a7f5ac7a0c31f875866"},
+ {file = "matplotlib-3.4.2.tar.gz", hash = "sha256:d8d994cefdff9aaba45166eb3de4f5211adb4accac85cbf97137e98f26ea0219"},
+]
+matplotlib-inline = [
+ {file = "matplotlib-inline-0.1.2.tar.gz", hash = "sha256:f41d5ff73c9f5385775d5c0bc13b424535c8402fe70ea8210f93e11f3683993e"},
+ {file = "matplotlib_inline-0.1.2-py3-none-any.whl", hash = "sha256:5cf1176f554abb4fa98cb362aa2b55c500147e4bdbb07e3fda359143e1da0811"},
+]
+mistune = [
+ {file = "mistune-0.8.4-py2.py3-none-any.whl", hash = "sha256:88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"},
+ {file = "mistune-0.8.4.tar.gz", hash = "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e"},
+]
+monotonic = [
+ {file = "monotonic-1.6-py2.py3-none-any.whl", hash = "sha256:68687e19a14f11f26d140dd5c86f3dba4bf5df58003000ed467e0e2a69bca96c"},
+]
+msgpack = [
+ {file = "msgpack-1.0.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:b6d9e2dae081aa35c44af9c4298de4ee72991305503442a5c74656d82b581fe9"},
+ {file = "msgpack-1.0.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:a99b144475230982aee16b3d249170f1cccebf27fb0a08e9f603b69637a62192"},
+ {file = "msgpack-1.0.2-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:1026dcc10537d27dd2d26c327e552f05ce148977e9d7b9f1718748281b38c841"},
+ {file = "msgpack-1.0.2-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:fe07bc6735d08e492a327f496b7850e98cb4d112c56df69b0c844dbebcbb47f6"},
+ {file = "msgpack-1.0.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:9ea52fff0473f9f3000987f313310208c879493491ef3ccf66268eff8d5a0326"},
+ {file = "msgpack-1.0.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:26a1759f1a88df5f1d0b393eb582ec022326994e311ba9c5818adc5374736439"},
+ {file = "msgpack-1.0.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:497d2c12426adcd27ab83144057a705efb6acc7e85957a51d43cdcf7f258900f"},
+ {file = "msgpack-1.0.2-cp36-cp36m-win32.whl", hash = "sha256:e89ec55871ed5473a041c0495b7b4e6099f6263438e0bd04ccd8418f92d5d7f2"},
+ {file = "msgpack-1.0.2-cp36-cp36m-win_amd64.whl", hash = "sha256:a4355d2193106c7aa77c98fc955252a737d8550320ecdb2e9ac701e15e2943bc"},
+ {file = "msgpack-1.0.2-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:d6c64601af8f3893d17ec233237030e3110f11b8a962cb66720bf70c0141aa54"},
+ {file = "msgpack-1.0.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:f484cd2dca68502de3704f056fa9b318c94b1539ed17a4c784266df5d6978c87"},
+ {file = "msgpack-1.0.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:f3e6aaf217ac1c7ce1563cf52a2f4f5d5b1f64e8729d794165db71da57257f0c"},
+ {file = "msgpack-1.0.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:8521e5be9e3b93d4d5e07cb80b7e32353264d143c1f072309e1863174c6aadb1"},
+ {file = "msgpack-1.0.2-cp37-cp37m-win32.whl", hash = "sha256:31c17bbf2ae5e29e48d794c693b7ca7a0c73bd4280976d408c53df421e838d2a"},
+ {file = "msgpack-1.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:8ffb24a3b7518e843cd83538cf859e026d24ec41ac5721c18ed0c55101f9775b"},
+ {file = "msgpack-1.0.2-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:b28c0876cce1466d7c2195d7658cf50e4730667196e2f1355c4209444717ee06"},
+ {file = "msgpack-1.0.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:87869ba567fe371c4555d2e11e4948778ab6b59d6cc9d8460d543e4cfbbddd1c"},
+ {file = "msgpack-1.0.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:b55f7db883530b74c857e50e149126b91bb75d35c08b28db12dcb0346f15e46e"},
+ {file = "msgpack-1.0.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:ac25f3e0513f6673e8b405c3a80500eb7be1cf8f57584be524c4fa78fe8e0c83"},
+ {file = "msgpack-1.0.2-cp38-cp38-win32.whl", hash = "sha256:0cb94ee48675a45d3b86e61d13c1e6f1696f0183f0715544976356ff86f741d9"},
+ {file = "msgpack-1.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:e36a812ef4705a291cdb4a2fd352f013134f26c6ff63477f20235138d1d21009"},
+ {file = "msgpack-1.0.2-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:2a5866bdc88d77f6e1370f82f2371c9bc6fc92fe898fa2dec0c5d4f5435a2694"},
+ {file = "msgpack-1.0.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:92be4b12de4806d3c36810b0fe2aeedd8d493db39e2eb90742b9c09299eb5759"},
+ {file = "msgpack-1.0.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:de6bd7990a2c2dabe926b7e62a92886ccbf809425c347ae7de277067f97c2887"},
+ {file = "msgpack-1.0.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:5a9ee2540c78659a1dd0b110f73773533ee3108d4e1219b5a15a8d635b7aca0e"},
+ {file = "msgpack-1.0.2-cp39-cp39-win32.whl", hash = "sha256:c747c0cc08bd6d72a586310bda6ea72eeb28e7505990f342552315b229a19b33"},
+ {file = "msgpack-1.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:d8167b84af26654c1124857d71650404336f4eb5cc06900667a493fc619ddd9f"},
+ {file = "msgpack-1.0.2.tar.gz", hash = "sha256:fae04496f5bc150eefad4e9571d1a76c55d021325dcd484ce45065ebbdd00984"},
+]
+multiprocess = [
+ {file = "multiprocess-0.70.11.1-cp27-cp27m-macosx_10_8_x86_64.whl", hash = "sha256:8f0d0640642acc654fe2fb5cb529ebbe116468a1dd1544d484db6e79033767c8"},
+ {file = "multiprocess-0.70.11.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:4b33a0111e341fad5e3c6bb6dd7f592596f2974cc5ecddee06b9a999bac4cbb0"},
+ {file = "multiprocess-0.70.11.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:0eab6e0e87acba9586e5d6869d21271cc865d72d74b7f6b30b6290dffca5caae"},
+ {file = "multiprocess-0.70.11.1-cp27-cp27m-win32.whl", hash = "sha256:4d97020a50a18862fbb1f84d81914a2a28f2d78bc315de9a6699459682df2a67"},
+ {file = "multiprocess-0.70.11.1-cp27-cp27m-win_amd64.whl", hash = "sha256:217e96638fbfd951a203b8dc17410839e4aea8aa3fb9cc393c37e491dcac2c65"},
+ {file = "multiprocess-0.70.11.1-py35-none-any.whl", hash = "sha256:ebb92b67a61b901bfc277c4525e86afba24a60638d192b62f8c332933da995f4"},
+ {file = "multiprocess-0.70.11.1-py36-none-any.whl", hash = "sha256:d8e87b086373fbd19c28659391e5b8888aadeaeb88f0e448e55502578bde4920"},
+ {file = "multiprocess-0.70.11.1-py37-none-any.whl", hash = "sha256:164c77448e357ebee0dc6abc7ee8c823e40e295e629a5fc6d31725109a3a7ee9"},
+ {file = "multiprocess-0.70.11.1-py38-none-any.whl", hash = "sha256:7761fed45cae123aa4b7bb918e77a5cfef6fd436c65bc87453e76bf2bdc3e29e"},
+ {file = "multiprocess-0.70.11.1-py39-none-any.whl", hash = "sha256:ae026110257fc551fc949d96d69160768810d9019786c8c84c0c28d1f88fab67"},
+ {file = "multiprocess-0.70.11.1.zip", hash = "sha256:9d5e417f3ebce4d027a3c900995840f167f316d9f73c0a7a1fbb4ac0116298d0"},
+]
+nbclient = [
+ {file = "nbclient-0.5.3-py3-none-any.whl", hash = "sha256:e79437364a2376892b3f46bedbf9b444e5396cfb1bc366a472c37b48e9551500"},
+ {file = "nbclient-0.5.3.tar.gz", hash = "sha256:db17271330c68c8c88d46d72349e24c147bb6f34ec82d8481a8f025c4d26589c"},
+]
+nbconvert = [
+ {file = "nbconvert-6.0.7-py3-none-any.whl", hash = "sha256:39e9f977920b203baea0be67eea59f7b37a761caa542abe80f5897ce3cf6311d"},
+ {file = "nbconvert-6.0.7.tar.gz", hash = "sha256:cbbc13a86dfbd4d1b5dee106539de0795b4db156c894c2c5dc382062bbc29002"},
+]
+nbformat = [
+ {file = "nbformat-5.1.3-py3-none-any.whl", hash = "sha256:eb8447edd7127d043361bc17f2f5a807626bc8e878c7709a1c647abda28a9171"},
+ {file = "nbformat-5.1.3.tar.gz", hash = "sha256:b516788ad70771c6250977c1374fcca6edebe6126fd2adb5a69aa5c2356fd1c8"},
+]
+neptune-client = [
+ {file = "neptune-client-0.9.16.tar.gz", hash = "sha256:f0a09d048db988bfc0e96265689bc05775b159f8a0f6f3c000bd0341f9003c98"},
+]
+nest-asyncio = [
+ {file = "nest_asyncio-1.5.1-py3-none-any.whl", hash = "sha256:76d6e972265063fe92a90b9cc4fb82616e07d586b346ed9d2c89a4187acea39c"},
+ {file = "nest_asyncio-1.5.1.tar.gz", hash = "sha256:afc5a1c515210a23c461932765691ad39e8eba6551c055ac8d5546e69250d0aa"},
+]
+networkx = [
+ {file = "networkx-2.5.1-py3-none-any.whl", hash = "sha256:0635858ed7e989f4c574c2328380b452df892ae85084144c73d8cd819f0c4e06"},
+ {file = "networkx-2.5.1.tar.gz", hash = "sha256:109cd585cac41297f71103c3c42ac6ef7379f29788eb54cb751be5a663bb235a"},
+]
+notebook = [
+ {file = "notebook-6.4.0-py3-none-any.whl", hash = "sha256:f7f0a71a999c7967d9418272ae4c3378a220bd28330fbfb49860e46cf8a5838a"},
+ {file = "notebook-6.4.0.tar.gz", hash = "sha256:9c4625e2a2aa49d6eae4ce20cbc3d8976db19267e32d2a304880e0c10bf8aef9"},
+]
+numpy = [
+ {file = "numpy-1.20.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:70eb5808127284c4e5c9e836208e09d685a7978b6a216db85960b1a112eeace8"},
+ {file = "numpy-1.20.3-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6ca2b85a5997dabc38301a22ee43c82adcb53ff660b89ee88dded6b33687e1d8"},
+ {file = "numpy-1.20.3-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c5bf0e132acf7557fc9bb8ded8b53bbbbea8892f3c9a1738205878ca9434206a"},
+ {file = "numpy-1.20.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db250fd3e90117e0312b611574cd1b3f78bec046783195075cbd7ba9c3d73f16"},
+ {file = "numpy-1.20.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:637d827248f447e63585ca3f4a7d2dfaa882e094df6cfa177cc9cf9cd6cdf6d2"},
+ {file = "numpy-1.20.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:8b7bb4b9280da3b2856cb1fc425932f46fba609819ee1c62256f61799e6a51d2"},
+ {file = "numpy-1.20.3-cp37-cp37m-win32.whl", hash = "sha256:67d44acb72c31a97a3d5d33d103ab06d8ac20770e1c5ad81bdb3f0c086a56cf6"},
+ {file = "numpy-1.20.3-cp37-cp37m-win_amd64.whl", hash = "sha256:43909c8bb289c382170e0282158a38cf306a8ad2ff6dfadc447e90f9961bef43"},
+ {file = "numpy-1.20.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f1452578d0516283c87608a5a5548b0cdde15b99650efdfd85182102ef7a7c17"},
+ {file = "numpy-1.20.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6e51534e78d14b4a009a062641f465cfaba4fdcb046c3ac0b1f61dd97c861b1b"},
+ {file = "numpy-1.20.3-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e515c9a93aebe27166ec9593411c58494fa98e5fcc219e47260d9ab8a1cc7f9f"},
+ {file = "numpy-1.20.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1c09247ccea742525bdb5f4b5ceeacb34f95731647fe55774aa36557dbb5fa4"},
+ {file = "numpy-1.20.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:66fbc6fed94a13b9801fb70b96ff30605ab0a123e775a5e7a26938b717c5d71a"},
+ {file = "numpy-1.20.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ea9cff01e75a956dbee133fa8e5b68f2f92175233de2f88de3a682dd94deda65"},
+ {file = "numpy-1.20.3-cp38-cp38-win32.whl", hash = "sha256:f39a995e47cb8649673cfa0579fbdd1cdd33ea497d1728a6cb194d6252268e48"},
+ {file = "numpy-1.20.3-cp38-cp38-win_amd64.whl", hash = "sha256:1676b0a292dd3c99e49305a16d7a9f42a4ab60ec522eac0d3dd20cdf362ac010"},
+ {file = "numpy-1.20.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:830b044f4e64a76ba71448fce6e604c0fc47a0e54d8f6467be23749ac2cbd2fb"},
+ {file = "numpy-1.20.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:55b745fca0a5ab738647d0e4db099bd0a23279c32b31a783ad2ccea729e632df"},
+ {file = "numpy-1.20.3-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5d050e1e4bc9ddb8656d7b4f414557720ddcca23a5b88dd7cff65e847864c400"},
+ {file = "numpy-1.20.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9c65473ebc342715cb2d7926ff1e202c26376c0dcaaee85a1fd4b8d8c1d3b2f"},
+ {file = "numpy-1.20.3-cp39-cp39-win32.whl", hash = "sha256:16f221035e8bd19b9dc9a57159e38d2dd060b48e93e1d843c49cb370b0f415fd"},
+ {file = "numpy-1.20.3-cp39-cp39-win_amd64.whl", hash = "sha256:6690080810f77485667bfbff4f69d717c3be25e5b11bb2073e76bb3f578d99b4"},
+ {file = "numpy-1.20.3-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e465afc3b96dbc80cf4a5273e5e2b1e3451286361b4af70ce1adb2984d392f9"},
+ {file = "numpy-1.20.3.zip", hash = "sha256:e55185e51b18d788e49fe8305fd73ef4470596b33fc2c1ceb304566b99c71a69"},
+]
+oauthlib = [
+ {file = "oauthlib-3.1.1-py2.py3-none-any.whl", hash = "sha256:42bf6354c2ed8c6acb54d971fce6f88193d97297e18602a3a886603f9d7730cc"},
+ {file = "oauthlib-3.1.1.tar.gz", hash = "sha256:8f0215fcc533dd8dd1bee6f4c412d4f0cd7297307d43ac61666389e3bc3198a3"},
+]
+omegaconf = [
+ {file = "omegaconf-2.1.0-py3-none-any.whl", hash = "sha256:a9c576639f66793c874e32d17138ecce7a57e132a4537d2b6dff899d6958f956"},
+ {file = "omegaconf-2.1.0.tar.gz", hash = "sha256:a08aec03a63c66449b550b85d70238f4dee9c6c4a0541d6a98845dcfeb12439d"},
+]
+orjson = [
+ {file = "orjson-3.5.3-cp36-cp36m-macosx_10_7_x86_64.whl", hash = "sha256:f22e2b3a1686a0f90aca920a522033b326cb2f945c8ed8fd8effa9f302672627"},
+ {file = "orjson-3.5.3-cp36-cp36m-macosx_10_9_universal2.whl", hash = "sha256:eb0cfe56687ac915e83dcfa1aa100e68883b42fe8eecae7275dc05da8cf96faa"},
+ {file = "orjson-3.5.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f697b8e3dceb787c173184cd4ec8331c27e0af7cc75d43759abcb5d2464d1ade"},
+ {file = "orjson-3.5.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2add8eeb14746f961330330ab5ce3dd09c858fb634eeeb26ceac14443e82830"},
+ {file = "orjson-3.5.3-cp36-none-win_amd64.whl", hash = "sha256:7e65fc393a77b5db391f28c7ccfcdc844f9dd0624e42dcf17d36fc20ddd3f3a0"},
+ {file = "orjson-3.5.3-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:4c80de99cb9617fe023201b543b8ed4b02dd8b52fbf7dd9b399d3b9d5f352398"},
+ {file = "orjson-3.5.3-cp37-cp37m-macosx_10_9_universal2.whl", hash = "sha256:b3b7ffdca6408b268aed9492e8558ac80f2e3bb362b992c2e7ecbbeb49b2a51e"},
+ {file = "orjson-3.5.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed823902b9e8c5130e0c67d317eab9ec200e45d26b96510efb7ae39f732ef24c"},
+ {file = "orjson-3.5.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b427ad034625ed522b683c1333ab2de83c25c1787fee47968a27f72fa2b55dca"},
+ {file = "orjson-3.5.3-cp37-none-win_amd64.whl", hash = "sha256:0c70bee40f215ede3949b34f1ae6b5260e108c00c914a7c62741ce6f8de2e27c"},
+ {file = "orjson-3.5.3-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:e0e74f47a3aafc6751d6dc238e34b38ae9a77a2373b98a722c428d832c919617"},
+ {file = "orjson-3.5.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:91c31999cbd4650459ef5160f5cf248cb4a7f1e24407f90cd9c58d113d335561"},
+ {file = "orjson-3.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbe2b73de6febbcfd8b8ee9629e11d33f88f54bf675cacced7bfee84684fec93"},
+ {file = "orjson-3.5.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27fa08fe5d2b9913b3ac8728960971544f255778e120849add596d67a7720f1f"},
+ {file = "orjson-3.5.3-cp38-none-win_amd64.whl", hash = "sha256:dcf711f6e4f5ee33206d51436eb9a2322a4338fd9081729c662e37d062f51c9d"},
+ {file = "orjson-3.5.3-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:6186755180e53436ebac3e0ce1590b27f218727f888c6e3f4c8fdabcb3ef840e"},
+ {file = "orjson-3.5.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d61edb73c5a7287e776dc000c056d59e1cc8d548cc672977b74e74c0164be3ef"},
+ {file = "orjson-3.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0eeb1dd42a4613d7032146e4693f44b334c150eae193a91a14789ac89c1d7455"},
+ {file = "orjson-3.5.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45b249d9d7ef6f241bca0a09cde57c99d019a0ca73df9bffb25c768b0f806b6d"},
+ {file = "orjson-3.5.3-cp39-none-win_amd64.whl", hash = "sha256:111ebdbca5fe51d4b22d155861ec8d35ce48f62d92717ed5828566b13a284c1a"},
+ {file = "orjson-3.5.3.tar.gz", hash = "sha256:8818f651ef7ed55f7c0ee34fa51f3de0988dd35386e8cefd0c2e1f32ff9f1966"},
+]
+packaging = [
+ {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"},
+ {file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"},
+]
+pandas = [
+ {file = "pandas-1.2.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c601c6fdebc729df4438ec1f62275d6136a0dd14d332fc0e8ce3f7d2aadb4dd6"},
+ {file = "pandas-1.2.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:8d4c74177c26aadcfb4fd1de6c1c43c2bf822b3e0fc7a9b409eeaf84b3e92aaa"},
+ {file = "pandas-1.2.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:b730add5267f873b3383c18cac4df2527ac4f0f0eed1c6cf37fcb437e25cf558"},
+ {file = "pandas-1.2.4-cp37-cp37m-win32.whl", hash = "sha256:2cb7e8f4f152f27dc93f30b5c7a98f6c748601ea65da359af734dd0cf3fa733f"},
+ {file = "pandas-1.2.4-cp37-cp37m-win_amd64.whl", hash = "sha256:2111c25e69fa9365ba80bbf4f959400054b2771ac5d041ed19415a8b488dc70a"},
+ {file = "pandas-1.2.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:167693a80abc8eb28051fbd184c1b7afd13ce2c727a5af47b048f1ea3afefff4"},
+ {file = "pandas-1.2.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:612add929bf3ba9d27b436cc8853f5acc337242d6b584203f207e364bb46cb12"},
+ {file = "pandas-1.2.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:971e2a414fce20cc5331fe791153513d076814d30a60cd7348466943e6e909e4"},
+ {file = "pandas-1.2.4-cp38-cp38-win32.whl", hash = "sha256:68d7baa80c74aaacbed597265ca2308f017859123231542ff8a5266d489e1858"},
+ {file = "pandas-1.2.4-cp38-cp38-win_amd64.whl", hash = "sha256:bd659c11a4578af740782288cac141a322057a2e36920016e0fc7b25c5a4b686"},
+ {file = "pandas-1.2.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9db70ffa8b280bb4de83f9739d514cd0735825e79eef3a61d312420b9f16b758"},
+ {file = "pandas-1.2.4-cp39-cp39-manylinux1_i686.whl", hash = "sha256:298f0553fd3ba8e002c4070a723a59cdb28eda579f3e243bc2ee397773f5398b"},
+ {file = "pandas-1.2.4-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:52d2472acbb8a56819a87aafdb8b5b6d2b3386e15c95bde56b281882529a7ded"},
+ {file = "pandas-1.2.4-cp39-cp39-win32.whl", hash = "sha256:d0877407359811f7b853b548a614aacd7dea83b0c0c84620a9a643f180060950"},
+ {file = "pandas-1.2.4-cp39-cp39-win_amd64.whl", hash = "sha256:2b063d41803b6a19703b845609c0b700913593de067b552a8b24dd8eeb8c9895"},
+ {file = "pandas-1.2.4.tar.gz", hash = "sha256:649ecab692fade3cbfcf967ff936496b0cfba0af00a55dfaacd82bdda5cb2279"},
+]
+pandocfilters = [
+ {file = "pandocfilters-1.4.3.tar.gz", hash = "sha256:bc63fbb50534b4b1f8ebe1860889289e8af94a23bff7445259592df25a3906eb"},
+]
+parso = [
+ {file = "parso-0.8.2-py2.py3-none-any.whl", hash = "sha256:a8c4922db71e4fdb90e0d0bc6e50f9b273d3397925e5e60a717e719201778d22"},
+ {file = "parso-0.8.2.tar.gz", hash = "sha256:12b83492c6239ce32ff5eed6d3639d6a536170723c6f3f1506869f1ace413398"},
+]
+partd = [
+ {file = "partd-1.2.0-py3-none-any.whl", hash = "sha256:5c3a5d70da89485c27916328dc1e26232d0e270771bd4caef4a5124b6a457288"},
+ {file = "partd-1.2.0.tar.gz", hash = "sha256:aa67897b84d522dcbc86a98b942afab8c6aa2f7f677d904a616b74ef5ddbc3eb"},
+]
+pexpect = [
+ {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"},
+ {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"},
+]
+pickleshare = [
+ {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"},
+ {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"},
+]
+pillow = [
+ {file = "Pillow-8.2.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:dc38f57d8f20f06dd7c3161c59ca2c86893632623f33a42d592f097b00f720a9"},
+ {file = "Pillow-8.2.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a013cbe25d20c2e0c4e85a9daf438f85121a4d0344ddc76e33fd7e3965d9af4b"},
+ {file = "Pillow-8.2.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8bb1e155a74e1bfbacd84555ea62fa21c58e0b4e7e6b20e4447b8d07990ac78b"},
+ {file = "Pillow-8.2.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c5236606e8570542ed424849f7852a0ff0bce2c4c8d0ba05cc202a5a9c97dee9"},
+ {file = "Pillow-8.2.0-cp36-cp36m-win32.whl", hash = "sha256:12e5e7471f9b637762453da74e390e56cc43e486a88289995c1f4c1dc0bfe727"},
+ {file = "Pillow-8.2.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5afe6b237a0b81bd54b53f835a153770802f164c5570bab5e005aad693dab87f"},
+ {file = "Pillow-8.2.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:cb7a09e173903541fa888ba010c345893cd9fc1b5891aaf060f6ca77b6a3722d"},
+ {file = "Pillow-8.2.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:0d19d70ee7c2ba97631bae1e7d4725cdb2ecf238178096e8c82ee481e189168a"},
+ {file = "Pillow-8.2.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:083781abd261bdabf090ad07bb69f8f5599943ddb539d64497ed021b2a67e5a9"},
+ {file = "Pillow-8.2.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:c6b39294464b03457f9064e98c124e09008b35a62e3189d3513e5148611c9388"},
+ {file = "Pillow-8.2.0-cp37-cp37m-win32.whl", hash = "sha256:01425106e4e8cee195a411f729cff2a7d61813b0b11737c12bd5991f5f14bcd5"},
+ {file = "Pillow-8.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3b570f84a6161cf8865c4e08adf629441f56e32f180f7aa4ccbd2e0a5a02cba2"},
+ {file = "Pillow-8.2.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:031a6c88c77d08aab84fecc05c3cde8414cd6f8406f4d2b16fed1e97634cc8a4"},
+ {file = "Pillow-8.2.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:66cc56579fd91f517290ab02c51e3a80f581aba45fd924fcdee01fa06e635812"},
+ {file = "Pillow-8.2.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6c32cc3145928c4305d142ebec682419a6c0a8ce9e33db900027ddca1ec39178"},
+ {file = "Pillow-8.2.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:624b977355cde8b065f6d51b98497d6cd5fbdd4f36405f7a8790e3376125e2bb"},
+ {file = "Pillow-8.2.0-cp38-cp38-win32.whl", hash = "sha256:5cbf3e3b1014dddc45496e8cf38b9f099c95a326275885199f427825c6522232"},
+ {file = "Pillow-8.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:463822e2f0d81459e113372a168f2ff59723e78528f91f0bd25680ac185cf797"},
+ {file = "Pillow-8.2.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:95d5ef984eff897850f3a83883363da64aae1000e79cb3c321915468e8c6add5"},
+ {file = "Pillow-8.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b91c36492a4bbb1ee855b7d16fe51379e5f96b85692dc8210831fbb24c43e484"},
+ {file = "Pillow-8.2.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:d68cb92c408261f806b15923834203f024110a2e2872ecb0bd2a110f89d3c602"},
+ {file = "Pillow-8.2.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f217c3954ce5fd88303fc0c317af55d5e0204106d86dea17eb8205700d47dec2"},
+ {file = "Pillow-8.2.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:5b70110acb39f3aff6b74cf09bb4169b167e2660dabc304c1e25b6555fa781ef"},
+ {file = "Pillow-8.2.0-cp39-cp39-win32.whl", hash = "sha256:a7d5e9fad90eff8f6f6106d3b98b553a88b6f976e51fce287192a5d2d5363713"},
+ {file = "Pillow-8.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:238c197fc275b475e87c1453b05b467d2d02c2915fdfdd4af126145ff2e4610c"},
+ {file = "Pillow-8.2.0-pp36-pypy36_pp73-macosx_10_10_x86_64.whl", hash = "sha256:0e04d61f0064b545b989126197930807c86bcbd4534d39168f4aa5fda39bb8f9"},
+ {file = "Pillow-8.2.0-pp36-pypy36_pp73-manylinux2010_i686.whl", hash = "sha256:63728564c1410d99e6d1ae8e3b810fe012bc440952168af0a2877e8ff5ab96b9"},
+ {file = "Pillow-8.2.0-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:c03c07ed32c5324939b19e36ae5f75c660c81461e312a41aea30acdd46f93a7c"},
+ {file = "Pillow-8.2.0-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:4d98abdd6b1e3bf1a1cbb14c3895226816e666749ac040c4e2554231068c639b"},
+ {file = "Pillow-8.2.0-pp37-pypy37_pp73-manylinux2010_i686.whl", hash = "sha256:aac00e4bc94d1b7813fe882c28990c1bc2f9d0e1aa765a5f2b516e8a6a16a9e4"},
+ {file = "Pillow-8.2.0-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:22fd0f42ad15dfdde6c581347eaa4adb9a6fc4b865f90b23378aa7914895e120"},
+ {file = "Pillow-8.2.0-pp37-pypy37_pp73-win32.whl", hash = "sha256:e98eca29a05913e82177b3ba3d198b1728e164869c613d76d0de4bde6768a50e"},
+ {file = "Pillow-8.2.0.tar.gz", hash = "sha256:a787ab10d7bb5494e5f76536ac460741788f1fbce851068d73a87ca7c35fc3e1"},
+]
+prometheus-client = [
+ {file = "prometheus_client-0.11.0-py2.py3-none-any.whl", hash = "sha256:b014bc76815eb1399da8ce5fc84b7717a3e63652b0c0f8804092c9363acab1b2"},
+ {file = "prometheus_client-0.11.0.tar.gz", hash = "sha256:3a8baade6cb80bcfe43297e33e7623f3118d660d41387593758e2fb1ea173a86"},
+]
+prompt-toolkit = [
+ {file = "prompt_toolkit-3.0.18-py3-none-any.whl", hash = "sha256:bf00f22079f5fadc949f42ae8ff7f05702826a97059ffcc6281036ad40ac6f04"},
+ {file = "prompt_toolkit-3.0.18.tar.gz", hash = "sha256:e1b4f11b9336a28fa11810bc623c357420f69dfdb6d2dac41ca2c21a55c033bc"},
+]
+pslpython = [
+ {file = "pslpython-2.2.2-py3-none-any.whl", hash = "sha256:76172b9643c0fe5fb2958e83d46299fc1e8f98030e680f299ef260eae5ae52ff"},
+]
+psutil = [
+ {file = "psutil-5.8.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:0066a82f7b1b37d334e68697faba68e5ad5e858279fd6351c8ca6024e8d6ba64"},
+ {file = "psutil-5.8.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:0ae6f386d8d297177fd288be6e8d1afc05966878704dad9847719650e44fc49c"},
+ {file = "psutil-5.8.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:12d844996d6c2b1d3881cfa6fa201fd635971869a9da945cf6756105af73d2df"},
+ {file = "psutil-5.8.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:02b8292609b1f7fcb34173b25e48d0da8667bc85f81d7476584d889c6e0f2131"},
+ {file = "psutil-5.8.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:6ffe81843131ee0ffa02c317186ed1e759a145267d54fdef1bc4ea5f5931ab60"},
+ {file = "psutil-5.8.0-cp27-none-win32.whl", hash = "sha256:ea313bb02e5e25224e518e4352af4bf5e062755160f77e4b1767dd5ccb65f876"},
+ {file = "psutil-5.8.0-cp27-none-win_amd64.whl", hash = "sha256:5da29e394bdedd9144c7331192e20c1f79283fb03b06e6abd3a8ae45ffecee65"},
+ {file = "psutil-5.8.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:74fb2557d1430fff18ff0d72613c5ca30c45cdbfcddd6a5773e9fc1fe9364be8"},
+ {file = "psutil-5.8.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:74f2d0be88db96ada78756cb3a3e1b107ce8ab79f65aa885f76d7664e56928f6"},
+ {file = "psutil-5.8.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:99de3e8739258b3c3e8669cb9757c9a861b2a25ad0955f8e53ac662d66de61ac"},
+ {file = "psutil-5.8.0-cp36-cp36m-win32.whl", hash = "sha256:36b3b6c9e2a34b7d7fbae330a85bf72c30b1c827a4366a07443fc4b6270449e2"},
+ {file = "psutil-5.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:52de075468cd394ac98c66f9ca33b2f54ae1d9bff1ef6b67a212ee8f639ec06d"},
+ {file = "psutil-5.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c6a5fd10ce6b6344e616cf01cc5b849fa8103fbb5ba507b6b2dee4c11e84c935"},
+ {file = "psutil-5.8.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:61f05864b42fedc0771d6d8e49c35f07efd209ade09a5afe6a5059e7bb7bf83d"},
+ {file = "psutil-5.8.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:0dd4465a039d343925cdc29023bb6960ccf4e74a65ad53e768403746a9207023"},
+ {file = "psutil-5.8.0-cp37-cp37m-win32.whl", hash = "sha256:1bff0d07e76114ec24ee32e7f7f8d0c4b0514b3fae93e3d2aaafd65d22502394"},
+ {file = "psutil-5.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:fcc01e900c1d7bee2a37e5d6e4f9194760a93597c97fee89c4ae51701de03563"},
+ {file = "psutil-5.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6223d07a1ae93f86451d0198a0c361032c4c93ebd4bf6d25e2fb3edfad9571ef"},
+ {file = "psutil-5.8.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d225cd8319aa1d3c85bf195c4e07d17d3cd68636b8fc97e6cf198f782f99af28"},
+ {file = "psutil-5.8.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:28ff7c95293ae74bf1ca1a79e8805fcde005c18a122ca983abf676ea3466362b"},
+ {file = "psutil-5.8.0-cp38-cp38-win32.whl", hash = "sha256:ce8b867423291cb65cfc6d9c4955ee9bfc1e21fe03bb50e177f2b957f1c2469d"},
+ {file = "psutil-5.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:90f31c34d25b1b3ed6c40cdd34ff122b1887a825297c017e4cbd6796dd8b672d"},
+ {file = "psutil-5.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6323d5d845c2785efb20aded4726636546b26d3b577aded22492908f7c1bdda7"},
+ {file = "psutil-5.8.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:245b5509968ac0bd179287d91210cd3f37add77dad385ef238b275bad35fa1c4"},
+ {file = "psutil-5.8.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:90d4091c2d30ddd0a03e0b97e6a33a48628469b99585e2ad6bf21f17423b112b"},
+ {file = "psutil-5.8.0-cp39-cp39-win32.whl", hash = "sha256:ea372bcc129394485824ae3e3ddabe67dc0b118d262c568b4d2602a7070afdb0"},
+ {file = "psutil-5.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:f4634b033faf0d968bb9220dd1c793b897ab7f1189956e1aa9eae752527127d3"},
+ {file = "psutil-5.8.0.tar.gz", hash = "sha256:0c9ccb99ab76025f2f0bbecf341d4656e9c1351db8cc8a03ccd62e318ab4b5c6"},
+]
+ptyprocess = [
+ {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
+ {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"},
+]
+py = [
+ {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"},
+ {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"},
+]
+py4j = [
+ {file = "py4j-0.10.9-py2.py3-none-any.whl", hash = "sha256:859ba728a7bb43e9c2bf058832759fb97a598bb28cc12f34f5fc4abdec08ede6"},
+ {file = "py4j-0.10.9.tar.gz", hash = "sha256:36ec57f43ff8ced260a18aa9a4e46c3500a730cac8860e259cbaa546c2b9db2f"},
+]
+pycparser = [
+ {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"},
+ {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"},
+]
+pydot = [
+ {file = "pydot-1.4.2-py2.py3-none-any.whl", hash = "sha256:66c98190c65b8d2e2382a441b4c0edfdb4f4c025ef9cb9874de478fb0793a451"},
+ {file = "pydot-1.4.2.tar.gz", hash = "sha256:248081a39bcb56784deb018977e428605c1c758f10897a339fce1dd728ff007d"},
+]
+pygments = [
+ {file = "Pygments-2.9.0-py3-none-any.whl", hash = "sha256:d66e804411278594d764fc69ec36ec13d9ae9147193a1740cd34d272ca383b8e"},
+ {file = "Pygments-2.9.0.tar.gz", hash = "sha256:a18f47b506a429f6f4b9df81bb02beab9ca21d0a5fee38ed15aef65f0545519f"},
+]
+pyjwt = [
+ {file = "PyJWT-2.1.0-py3-none-any.whl", hash = "sha256:934d73fbba91b0483d3857d1aff50e96b2a892384ee2c17417ed3203f173fca1"},
+ {file = "PyJWT-2.1.0.tar.gz", hash = "sha256:fba44e7898bbca160a2b2b501f492824fc8382485d3a6f11ba5d0c1937ce6130"},
+]
+pyparsing = [
+ {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"},
+ {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"},
+]
+"pyrallel.lib" = [
+ {file = "pyrallel.lib-0.0.10-py3-none-any.whl", hash = "sha256:b83b55d1521202f162908f3d7d8a9e302025d5fe9a29a6989772f3ad8804fe26"},
+ {file = "pyrallel.lib-0.0.10.tar.gz", hash = "sha256:be03a2d212bd1bd3f6da9de3aabf76ba5781aa59fef0c2358316d8b1cd59b03e"},
+]
+pyrsistent = [
+ {file = "pyrsistent-0.17.3.tar.gz", hash = "sha256:2e636185d9eb976a18a8a8e96efce62f2905fea90041958d8cc2a189756ebf3e"},
+]
+pyspark = [
+ {file = "pyspark-3.0.1.tar.gz", hash = "sha256:38b485d3634a86c9a2923c39c8f08f003fdd0e0a3d7f07114b2fb4392ce60479"},
+]
+python-dateutil = [
+ {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"},
+ {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"},
+]
+python-dotenv = [
+ {file = "python-dotenv-0.17.1.tar.gz", hash = "sha256:b1ae5e9643d5ed987fc57cc2583021e38db531946518130777734f9589b3141f"},
+ {file = "python_dotenv-0.17.1-py2.py3-none-any.whl", hash = "sha256:00aa34e92d992e9f8383730816359647f358f4a3be1ba45e5a5cefd27ee91544"},
+]
+python-levenshtein = [
+ {file = "python-Levenshtein-0.12.2.tar.gz", hash = "sha256:dc2395fbd148a1ab31090dd113c366695934b9e85fe5a4b2a032745efd0346f6"},
+]
+python-slugify = [
+ {file = "python-slugify-5.0.2.tar.gz", hash = "sha256:f13383a0b9fcbe649a1892b9c8eb4f8eab1d6d84b84bb7a624317afa98159cab"},
+ {file = "python_slugify-5.0.2-py2.py3-none-any.whl", hash = "sha256:6d8c5df75cd4a7c3a2d21e257633de53f52ab0265cd2d1dc62a730e8194a7380"},
+]
+pytz = [
+ {file = "pytz-2021.1-py2.py3-none-any.whl", hash = "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"},
+ {file = "pytz-2021.1.tar.gz", hash = "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da"},
+]
+pywin32 = [
+ {file = "pywin32-301-cp35-cp35m-win32.whl", hash = "sha256:93367c96e3a76dfe5003d8291ae16454ca7d84bb24d721e0b74a07610b7be4a7"},
+ {file = "pywin32-301-cp35-cp35m-win_amd64.whl", hash = "sha256:9635df6998a70282bd36e7ac2a5cef9ead1627b0a63b17c731312c7a0daebb72"},
+ {file = "pywin32-301-cp36-cp36m-win32.whl", hash = "sha256:c866f04a182a8cb9b7855de065113bbd2e40524f570db73ef1ee99ff0a5cc2f0"},
+ {file = "pywin32-301-cp36-cp36m-win_amd64.whl", hash = "sha256:dafa18e95bf2a92f298fe9c582b0e205aca45c55f989937c52c454ce65b93c78"},
+ {file = "pywin32-301-cp37-cp37m-win32.whl", hash = "sha256:98f62a3f60aa64894a290fb7494bfa0bfa0a199e9e052e1ac293b2ad3cd2818b"},
+ {file = "pywin32-301-cp37-cp37m-win_amd64.whl", hash = "sha256:fb3b4933e0382ba49305cc6cd3fb18525df7fd96aa434de19ce0878133bf8e4a"},
+ {file = "pywin32-301-cp38-cp38-win32.whl", hash = "sha256:88981dd3cfb07432625b180f49bf4e179fb8cbb5704cd512e38dd63636af7a17"},
+ {file = "pywin32-301-cp38-cp38-win_amd64.whl", hash = "sha256:8c9d33968aa7fcddf44e47750e18f3d034c3e443a707688a008a2e52bbef7e96"},
+ {file = "pywin32-301-cp39-cp39-win32.whl", hash = "sha256:595d397df65f1b2e0beaca63a883ae6d8b6df1cdea85c16ae85f6d2e648133fe"},
+ {file = "pywin32-301-cp39-cp39-win_amd64.whl", hash = "sha256:87604a4087434cd814ad8973bd47d6524bd1fa9e971ce428e76b62a5e0860fdf"},
+]
+pywinpty = [
+ {file = "pywinpty-1.1.1-cp36-none-win_amd64.whl", hash = "sha256:fa2a0af28eaaacc59227c6edbc0f1525704d68b2dfa3e5b47ae21c5aa25d6d78"},
+ {file = "pywinpty-1.1.1-cp37-none-win_amd64.whl", hash = "sha256:0fe3f538860c6b06e6fbe63da0ee5dab5194746b0df1be7ed65b4fce5da21d21"},
+ {file = "pywinpty-1.1.1-cp38-none-win_amd64.whl", hash = "sha256:12c89765b3102d2eea3d39d191d1b0baea68fb5e3bd094c67b2575b3c9ebfa12"},
+ {file = "pywinpty-1.1.1-cp39-none-win_amd64.whl", hash = "sha256:50bce6f7d9857ffe9694847af7e8bf989b198d0ebc2bf30e26d54c4622cb5c50"},
+ {file = "pywinpty-1.1.1.tar.gz", hash = "sha256:4a3ffa2444daf15c5f65a76b5b2864447cc915564e41e2876816b9e4fe849070"},
+]
+pyyaml = [
+ {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"},
+ {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"},
+ {file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"},
+ {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"},
+ {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"},
+ {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"},
+ {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347"},
+ {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541"},
+ {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"},
+ {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"},
+ {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"},
+ {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"},
+ {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa"},
+ {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"},
+ {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"},
+ {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"},
+ {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"},
+ {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"},
+ {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247"},
+ {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc"},
+ {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"},
+ {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"},
+ {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"},
+ {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"},
+ {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122"},
+ {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6"},
+ {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"},
+ {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"},
+ {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"},
+]
+pyzmq = [
+ {file = "pyzmq-22.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:4e9b9a2f6944acdaf57316436c1acdcb30b8df76726bcf570ad9342bc5001654"},
+ {file = "pyzmq-22.1.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:24fb5bb641f0b2aa25fc3832f4b6fc62430f14a7d328229fe994b2bcdc07c93a"},
+ {file = "pyzmq-22.1.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:c4674004ed64685a38bee222cd75afa769424ec603f9329f0dd4777138337f48"},
+ {file = "pyzmq-22.1.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:461ed80d741692d9457ab820b1cc057ba9c37c394e67b647b639f623c8b321f6"},
+ {file = "pyzmq-22.1.0-cp36-cp36m-win32.whl", hash = "sha256:de5806be66c9108e4dcdaced084e8ceae14100aa559e2d57b4f0cceb98c462de"},
+ {file = "pyzmq-22.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:a1c77796f395804d6002ff56a6a8168c1f98579896897ad7e35665a9b4a9eec5"},
+ {file = "pyzmq-22.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c6a81c9e6754465d09a87e3acd74d9bb1f0039b2d785c6899622f0afdb41d760"},
+ {file = "pyzmq-22.1.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:0f0f27eaab9ba7b92d73d71c51d1a04464a1da6097a252d007922103253d2313"},
+ {file = "pyzmq-22.1.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:4b8fb1b3174b56fd020e4b10232b1764e52cf7f3babcfb460c5253bdc48adad0"},
+ {file = "pyzmq-22.1.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:c8fff75af4c7af92dce9f81fa2a83ed009c3e1f33ee8b5222db2ef80b94e242e"},
+ {file = "pyzmq-22.1.0-cp37-cp37m-win32.whl", hash = "sha256:cb9f9fe1305ef69b65794655fd89b2209b11bff3e837de981820a8aa051ef914"},
+ {file = "pyzmq-22.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:bf80b2cec42d96117248b99d3c86e263a00469c840a778e6cb52d916f4fdf82c"},
+ {file = "pyzmq-22.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0ea7f4237991b0f745a4432c63e888450840bf8cb6c48b93fb7d62864f455529"},
+ {file = "pyzmq-22.1.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:12ffcf33db6ba7c0e5aaf901e65517f5e2b719367b80bcbfad692f546a297c7a"},
+ {file = "pyzmq-22.1.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d3ecfee2ee8d91ab2e08d2d8e89302c729b244e302bbc39c5b5dde42306ff003"},
+ {file = "pyzmq-22.1.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:68e2c4505992ab5b89f976f89a9135742b18d60068f761bef994a6805f1cae0c"},
+ {file = "pyzmq-22.1.0-cp38-cp38-win32.whl", hash = "sha256:285514956c08c7830da9d94e01f5414661a987831bd9f95e4d89cc8aaae8da10"},
+ {file = "pyzmq-22.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:d5e5be93e1714a59a535bbbc086b9e4fd2448c7547c5288548f6fd86353cad9e"},
+ {file = "pyzmq-22.1.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:b2f707b52e09098a7770503e39294ca6e22ae5138ffa1dd36248b6436d23d78e"},
+ {file = "pyzmq-22.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:18dd2ca4540c476558099891c129e6f94109971d110b549db2a9775c817cedbd"},
+ {file = "pyzmq-22.1.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:c6d0c32532a0519997e1ded767e184ebb8543bdb351f8eff8570bd461e874efc"},
+ {file = "pyzmq-22.1.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:9ee48413a2d3cd867fd836737b4c89c24cea1150a37f4856d82d20293fa7519f"},
+ {file = "pyzmq-22.1.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:4c4fe69c7dc0d13d4ae180ad650bb900854367f3349d3c16f0569f6c6447f698"},
+ {file = "pyzmq-22.1.0-cp39-cp39-win32.whl", hash = "sha256:fc712a90401bcbf3fa25747f189d6dcfccbecc32712701cad25c6355589dac57"},
+ {file = "pyzmq-22.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:68be16107f41563b9f67d93dff1c9f5587e0f76aa8fd91dc04c83d813bcdab1f"},
+ {file = "pyzmq-22.1.0-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:734ea6565c71fc2d03d5b8c7d0d7519c96bb5567e0396da1b563c24a4ac66f0c"},
+ {file = "pyzmq-22.1.0-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:1389b615917d4196962a9b469e947ba862a8ec6f5094a47da5e7a8d404bc07a4"},
+ {file = "pyzmq-22.1.0-pp36-pypy36_pp73-win32.whl", hash = "sha256:41049cff5265e9cd75606aa2c90a76b9c80b98d8fe70ee08cf4af3cedb113358"},
+ {file = "pyzmq-22.1.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f49755684a963731479ff3035d45a8185545b4c9f662d368bd349c419839886d"},
+ {file = "pyzmq-22.1.0-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:6355f81947e1fe6e7bb9e123aeb3067264391d3ebe8402709f824ef8673fa6f3"},
+ {file = "pyzmq-22.1.0-pp37-pypy37_pp73-win32.whl", hash = "sha256:089b974ec04d663b8685ac90e86bfe0e4da9d911ff3cf52cb765ff22408b102d"},
+ {file = "pyzmq-22.1.0.tar.gz", hash = "sha256:7040d6dd85ea65703904d023d7f57fab793d7ffee9ba9e14f3b897f34ff2415d"},
+]
+rdflib = [
+ {file = "rdflib-5.0.0-py3-none-any.whl", hash = "sha256:88208ea971a87886d60ae2b1a4b2cdc263527af0454c422118d43fe64b357877"},
+ {file = "rdflib-5.0.0.tar.gz", hash = "sha256:78149dd49d385efec3b3adfbd61c87afaf1281c30d3fcaf1b323b34f603fb155"},
+]
+redis = [
+ {file = "redis-3.5.3-py2.py3-none-any.whl", hash = "sha256:432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24"},
+ {file = "redis-3.5.3.tar.gz", hash = "sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2"},
+]
+requests = [
+ {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"},
+ {file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"},
+]
+requests-oauthlib = [
+ {file = "requests-oauthlib-1.3.0.tar.gz", hash = "sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a"},
+ {file = "requests_oauthlib-1.3.0-py2.py3-none-any.whl", hash = "sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d"},
+ {file = "requests_oauthlib-1.3.0-py3.7.egg", hash = "sha256:fa6c47b933f01060936d87ae9327fead68768b69c6c9ea2109c48be30f2d4dbc"},
+]
+rfc3987 = [
+ {file = "rfc3987-1.3.8-py2.py3-none-any.whl", hash = "sha256:10702b1e51e5658843460b189b185c0366d2cf4cff716f13111b0ea9fd2dce53"},
+ {file = "rfc3987-1.3.8.tar.gz", hash = "sha256:d3c4d257a560d544e9826b38bc81db676890c79ab9d7ac92b39c7a253d5ca733"},
+]
+rltk = [
+ {file = "rltk-2.0.0a15.tar.gz", hash = "sha256:97d0c5a2df9e22de800c1052e3b0492e9566c9ba485d577c40ac0597c73fcd61"},
+]
+rocksdb = [
+ {file = "rocksdb-0.7.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:3f78c5ba0659489e4653b8318eceaa803533c29fc7b4d9585514c7a071bc91ee"},
+ {file = "rocksdb-0.7.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:6f89999367e79e40a9e45e69676b5672e491652d35c0062b26ee7f61303e9378"},
+ {file = "rocksdb-0.7.0.tar.gz", hash = "sha256:27c366d7e6e9058eb47aeae607671544cc63a4413abd2035aad82a674dbf9f84"},
+]
+"ruamel.yaml" = [
+ {file = "ruamel.yaml-0.17.7-py3-none-any.whl", hash = "sha256:25c3eaf4f0c52bd15c50c39b100a32168891240f4d2177a4690d5d9b85944bbe"},
+ {file = "ruamel.yaml-0.17.7.tar.gz", hash = "sha256:5c3fa739bbedd2f23769656784e671c6335d17a5bf163c3c3901d8663c0af287"},
+]
+"ruamel.yaml.clib" = [
+ {file = "ruamel.yaml.clib-0.2.2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:28116f204103cb3a108dfd37668f20abe6e3cafd0d3fd40dba126c732457b3cc"},
+ {file = "ruamel.yaml.clib-0.2.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:daf21aa33ee9b351f66deed30a3d450ab55c14242cfdfcd377798e2c0d25c9f1"},
+ {file = "ruamel.yaml.clib-0.2.2-cp27-cp27m-win32.whl", hash = "sha256:30dca9bbcbb1cc858717438218d11eafb78666759e5094dd767468c0d577a7e7"},
+ {file = "ruamel.yaml.clib-0.2.2-cp27-cp27m-win_amd64.whl", hash = "sha256:f6061a31880c1ed6b6ce341215336e2f3d0c1deccd84957b6fa8ca474b41e89f"},
+ {file = "ruamel.yaml.clib-0.2.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:73b3d43e04cc4b228fa6fa5d796409ece6fcb53a6c270eb2048109cbcbc3b9c2"},
+ {file = "ruamel.yaml.clib-0.2.2-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:53b9dd1abd70e257a6e32f934ebc482dac5edb8c93e23deb663eac724c30b026"},
+ {file = "ruamel.yaml.clib-0.2.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:839dd72545ef7ba78fd2aa1a5dd07b33696adf3e68fae7f31327161c1093001b"},
+ {file = "ruamel.yaml.clib-0.2.2-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:1236df55e0f73cd138c0eca074ee086136c3f16a97c2ac719032c050f7e0622f"},
+ {file = "ruamel.yaml.clib-0.2.2-cp35-cp35m-win32.whl", hash = "sha256:b1e981fe1aff1fd11627f531524826a4dcc1f26c726235a52fcb62ded27d150f"},
+ {file = "ruamel.yaml.clib-0.2.2-cp35-cp35m-win_amd64.whl", hash = "sha256:4e52c96ca66de04be42ea2278012a2342d89f5e82b4512fb6fb7134e377e2e62"},
+ {file = "ruamel.yaml.clib-0.2.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a873e4d4954f865dcb60bdc4914af7eaae48fb56b60ed6daa1d6251c72f5337c"},
+ {file = "ruamel.yaml.clib-0.2.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ab845f1f51f7eb750a78937be9f79baea4a42c7960f5a94dde34e69f3cce1988"},
+ {file = "ruamel.yaml.clib-0.2.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:2fd336a5c6415c82e2deb40d08c222087febe0aebe520f4d21910629018ab0f3"},
+ {file = "ruamel.yaml.clib-0.2.2-cp36-cp36m-win32.whl", hash = "sha256:e9f7d1d8c26a6a12c23421061f9022bb62704e38211fe375c645485f38df34a2"},
+ {file = "ruamel.yaml.clib-0.2.2-cp36-cp36m-win_amd64.whl", hash = "sha256:2602e91bd5c1b874d6f93d3086f9830f3e907c543c7672cf293a97c3fabdcd91"},
+ {file = "ruamel.yaml.clib-0.2.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:44c7b0498c39f27795224438f1a6be6c5352f82cb887bc33d962c3a3acc00df6"},
+ {file = "ruamel.yaml.clib-0.2.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:8e8fd0a22c9d92af3a34f91e8a2594eeb35cba90ab643c5e0e643567dc8be43e"},
+ {file = "ruamel.yaml.clib-0.2.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:75f0ee6839532e52a3a53f80ce64925ed4aed697dd3fa890c4c918f3304bd4f4"},
+ {file = "ruamel.yaml.clib-0.2.2-cp37-cp37m-win32.whl", hash = "sha256:464e66a04e740d754170be5e740657a3b3b6d2bcc567f0c3437879a6e6087ff6"},
+ {file = "ruamel.yaml.clib-0.2.2-cp37-cp37m-win_amd64.whl", hash = "sha256:52ae5739e4b5d6317b52f5b040b1b6639e8af68a5b8fd606a8b08658fbd0cab5"},
+ {file = "ruamel.yaml.clib-0.2.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4df5019e7783d14b79217ad9c56edf1ba7485d614ad5a385d1b3c768635c81c0"},
+ {file = "ruamel.yaml.clib-0.2.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5254af7d8bdf4d5484c089f929cb7f5bafa59b4f01d4f48adda4be41e6d29f99"},
+ {file = "ruamel.yaml.clib-0.2.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8be05be57dc5c7b4a0b24edcaa2f7275866d9c907725226cdde46da09367d923"},
+ {file = "ruamel.yaml.clib-0.2.2-cp38-cp38-win32.whl", hash = "sha256:74161d827407f4db9072011adcfb825b5258a5ccb3d2cd518dd6c9edea9e30f1"},
+ {file = "ruamel.yaml.clib-0.2.2-cp38-cp38-win_amd64.whl", hash = "sha256:058a1cc3df2a8aecc12f983a48bda99315cebf55a3b3a5463e37bb599b05727b"},
+ {file = "ruamel.yaml.clib-0.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c6ac7e45367b1317e56f1461719c853fd6825226f45b835df7436bb04031fd8a"},
+ {file = "ruamel.yaml.clib-0.2.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:b4b0d31f2052b3f9f9b5327024dc629a253a83d8649d4734ca7f35b60ec3e9e5"},
+ {file = "ruamel.yaml.clib-0.2.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:1f8c0a4577c0e6c99d208de5c4d3fd8aceed9574bb154d7a2b21c16bb924154c"},
+ {file = "ruamel.yaml.clib-0.2.2-cp39-cp39-win32.whl", hash = "sha256:46d6d20815064e8bb023ea8628cfb7402c0f0e83de2c2227a88097e239a7dffd"},
+ {file = "ruamel.yaml.clib-0.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:6c0a5dc52fc74eb87c67374a4e554d4761fd42a4d01390b7e868b30d21f4b8bb"},
+ {file = "ruamel.yaml.clib-0.2.2.tar.gz", hash = "sha256:2d24bd98af676f4990c4d715bcdc2a60b19c56a3fb3a763164d2d8ca0e806ba7"},
+]
+scipy = [
+ {file = "scipy-1.6.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a15a1f3fc0abff33e792d6049161b7795909b40b97c6cc2934ed54384017ab76"},
+ {file = "scipy-1.6.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:e79570979ccdc3d165456dd62041d9556fb9733b86b4b6d818af7a0afc15f092"},
+ {file = "scipy-1.6.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:a423533c55fec61456dedee7b6ee7dce0bb6bfa395424ea374d25afa262be261"},
+ {file = "scipy-1.6.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:33d6b7df40d197bdd3049d64e8e680227151673465e5d85723b3b8f6b15a6ced"},
+ {file = "scipy-1.6.1-cp37-cp37m-win32.whl", hash = "sha256:6725e3fbb47da428794f243864f2297462e9ee448297c93ed1dcbc44335feb78"},
+ {file = "scipy-1.6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:5fa9c6530b1661f1370bcd332a1e62ca7881785cc0f80c0d559b636567fab63c"},
+ {file = "scipy-1.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bd50daf727f7c195e26f27467c85ce653d41df4358a25b32434a50d8870fc519"},
+ {file = "scipy-1.6.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:f46dd15335e8a320b0fb4685f58b7471702234cba8bb3442b69a3e1dc329c345"},
+ {file = "scipy-1.6.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:0e5b0ccf63155d90da576edd2768b66fb276446c371b73841e3503be1d63fb5d"},
+ {file = "scipy-1.6.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:2481efbb3740977e3c831edfd0bd9867be26387cacf24eb5e366a6a374d3d00d"},
+ {file = "scipy-1.6.1-cp38-cp38-win32.whl", hash = "sha256:68cb4c424112cd4be886b4d979c5497fba190714085f46b8ae67a5e4416c32b4"},
+ {file = "scipy-1.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:5f331eeed0297232d2e6eea51b54e8278ed8bb10b099f69c44e2558c090d06bf"},
+ {file = "scipy-1.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0c8a51d33556bf70367452d4d601d1742c0e806cd0194785914daf19775f0e67"},
+ {file = "scipy-1.6.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:83bf7c16245c15bc58ee76c5418e46ea1811edcc2e2b03041b804e46084ab627"},
+ {file = "scipy-1.6.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:794e768cc5f779736593046c9714e0f3a5940bc6dcc1dba885ad64cbfb28e9f0"},
+ {file = "scipy-1.6.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:5da5471aed911fe7e52b86bf9ea32fb55ae93e2f0fac66c32e58897cfb02fa07"},
+ {file = "scipy-1.6.1-cp39-cp39-win32.whl", hash = "sha256:8e403a337749ed40af60e537cc4d4c03febddcc56cd26e774c9b1b600a70d3e4"},
+ {file = "scipy-1.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:a5193a098ae9f29af283dcf0041f762601faf2e595c0db1da929875b7570353f"},
+ {file = "scipy-1.6.1.tar.gz", hash = "sha256:c4fceb864890b6168e79b0e714c585dbe2fd4222768ee90bc1aa0f8218691b11"},
+]
+sem-desc = [
+ {file = "sem-desc-0.1.3.tar.gz", hash = "sha256:9d7739397eb189cdd06e8ae55107f7f401d680cd5ba5751f5d7b1c3cc385b540"},
+ {file = "sem_desc-0.1.3-py3-none-any.whl", hash = "sha256:5994df0f08a9d27d18f78ad00fe663f0a7363283a69e0efc0fe24888ecf588cb"},
+]
+send2trash = [
+ {file = "Send2Trash-1.5.0-py3-none-any.whl", hash = "sha256:f1691922577b6fa12821234aeb57599d887c4900b9ca537948d2dac34aea888b"},
+ {file = "Send2Trash-1.5.0.tar.gz", hash = "sha256:60001cc07d707fe247c94f74ca6ac0d3255aabcb930529690897ca2a39db28b2"},
+]
+simplejson = [
+ {file = "simplejson-3.17.2-cp27-cp27m-macosx_10_13_x86_64.whl", hash = "sha256:2d3eab2c3fe52007d703a26f71cf649a8c771fcdd949a3ae73041ba6797cfcf8"},
+ {file = "simplejson-3.17.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:813846738277729d7db71b82176204abc7fdae2f566e2d9fcf874f9b6472e3e6"},
+ {file = "simplejson-3.17.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:292c2e3f53be314cc59853bd20a35bf1f965f3bc121e007ab6fd526ed412a85d"},
+ {file = "simplejson-3.17.2-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:0dd9d9c738cb008bfc0862c9b8fa6743495c03a0ed543884bf92fb7d30f8d043"},
+ {file = "simplejson-3.17.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:42b8b8dd0799f78e067e2aaae97e60d58a8f63582939af60abce4c48631a0aa4"},
+ {file = "simplejson-3.17.2-cp27-cp27m-win32.whl", hash = "sha256:8042040af86a494a23c189b5aa0ea9433769cc029707833f261a79c98e3375f9"},
+ {file = "simplejson-3.17.2-cp27-cp27m-win_amd64.whl", hash = "sha256:034550078a11664d77bc1a8364c90bb7eef0e44c2dbb1fd0a4d92e3997088667"},
+ {file = "simplejson-3.17.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:fed0f22bf1313ff79c7fc318f7199d6c2f96d4de3234b2f12a1eab350e597c06"},
+ {file = "simplejson-3.17.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:2e7b57c2c146f8e4dadf84977a83f7ee50da17c8861fd7faf694d55e3274784f"},
+ {file = "simplejson-3.17.2-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:da3c55cdc66cfc3fffb607db49a42448785ea2732f055ac1549b69dcb392663b"},
+ {file = "simplejson-3.17.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:c1cb29b1fced01f97e6d5631c3edc2dadb424d1f4421dad079cb13fc97acb42f"},
+ {file = "simplejson-3.17.2-cp33-cp33m-win32.whl", hash = "sha256:8f713ea65958ef40049b6c45c40c206ab363db9591ff5a49d89b448933fa5746"},
+ {file = "simplejson-3.17.2-cp33-cp33m-win_amd64.whl", hash = "sha256:344e2d920a7f27b4023c087ab539877a1e39ce8e3e90b867e0bfa97829824748"},
+ {file = "simplejson-3.17.2-cp34-cp34m-win32.whl", hash = "sha256:05b43d568300c1cd43f95ff4bfcff984bc658aa001be91efb3bb21df9d6288d3"},
+ {file = "simplejson-3.17.2-cp34-cp34m-win_amd64.whl", hash = "sha256:cff6453e25204d3369c47b97dd34783ca820611bd334779d22192da23784194b"},
+ {file = "simplejson-3.17.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:8acf76443cfb5c949b6e781c154278c059b09ac717d2757a830c869ba000cf8d"},
+ {file = "simplejson-3.17.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:869a183c8e44bc03be1b2bbcc9ec4338e37fa8557fc506bf6115887c1d3bb956"},
+ {file = "simplejson-3.17.2-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:5c659a0efc80aaaba57fcd878855c8534ecb655a28ac8508885c50648e6e659d"},
+ {file = "simplejson-3.17.2-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:72d8a3ffca19a901002d6b068cf746be85747571c6a7ba12cbcf427bfb4ed971"},
+ {file = "simplejson-3.17.2-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:4b3442249d5e3893b90cb9f72c7d6ce4d2ea144d2c0d9f75b9ae1e5460f3121a"},
+ {file = "simplejson-3.17.2-cp35-cp35m-win32.whl", hash = "sha256:e058c7656c44fb494a11443191e381355388443d543f6fc1a245d5d238544396"},
+ {file = "simplejson-3.17.2-cp35-cp35m-win_amd64.whl", hash = "sha256:934115642c8ba9659b402c8bdbdedb48651fb94b576e3b3efd1ccb079609b04a"},
+ {file = "simplejson-3.17.2-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:ffd4e4877a78c84d693e491b223385e0271278f5f4e1476a4962dca6824ecfeb"},
+ {file = "simplejson-3.17.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:10fc250c3edea4abc15d930d77274ddb8df4803453dde7ad50c2f5565a18a4bb"},
+ {file = "simplejson-3.17.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:76ac9605bf2f6d9b56abf6f9da9047a8782574ad3531c82eae774947ae99cc3f"},
+ {file = "simplejson-3.17.2-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:7f10f8ba9c1b1430addc7dd385fc322e221559d3ae49b812aebf57470ce8de45"},
+ {file = "simplejson-3.17.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:bc00d1210567a4cdd215ac6e17dc00cb9893ee521cee701adfd0fa43f7c73139"},
+ {file = "simplejson-3.17.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:af4868da7dd53296cd7630687161d53a7ebe2e63814234631445697bd7c29f46"},
+ {file = "simplejson-3.17.2-cp36-cp36m-win32.whl", hash = "sha256:7d276f69bfc8c7ba6c717ba8deaf28f9d3c8450ff0aa8713f5a3280e232be16b"},
+ {file = "simplejson-3.17.2-cp36-cp36m-win_amd64.whl", hash = "sha256:a55c76254d7cf8d4494bc508e7abb993a82a192d0db4552421e5139235604625"},
+ {file = "simplejson-3.17.2-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:9a2b7543559f8a1c9ed72724b549d8cc3515da7daf3e79813a15bdc4a769de25"},
+ {file = "simplejson-3.17.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:311f5dc2af07361725033b13cc3d0351de3da8bede3397d45650784c3f21fbcf"},
+ {file = "simplejson-3.17.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:2862beabfb9097a745a961426fe7daf66e1714151da8bb9a0c430dde3d59c7c0"},
+ {file = "simplejson-3.17.2-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:afebfc3dd3520d37056f641969ce320b071bc7a0800639c71877b90d053e087f"},
+ {file = "simplejson-3.17.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:d4813b30cb62d3b63ccc60dd12f2121780c7a3068db692daeb90f989877aaf04"},
+ {file = "simplejson-3.17.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3fabde09af43e0cbdee407555383063f8b45bfb52c361bc5da83fcffdb4fd278"},
+ {file = "simplejson-3.17.2-cp37-cp37m-win32.whl", hash = "sha256:ceaa28a5bce8a46a130cd223e895080e258a88d51bf6e8de2fc54a6ef7e38c34"},
+ {file = "simplejson-3.17.2-cp37-cp37m-win_amd64.whl", hash = "sha256:9551f23e09300a9a528f7af20e35c9f79686d46d646152a0c8fc41d2d074d9b0"},
+ {file = "simplejson-3.17.2-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:c94dc64b1a389a416fc4218cd4799aa3756f25940cae33530a4f7f2f54f166da"},
+ {file = "simplejson-3.17.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:b59aa298137ca74a744c1e6e22cfc0bf9dca3a2f41f51bc92eb05695155d905a"},
+ {file = "simplejson-3.17.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:ad8f41c2357b73bc9e8606d2fa226233bf4d55d85a8982ecdfd55823a6959995"},
+ {file = "simplejson-3.17.2-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:845a14f6deb124a3bcb98a62def067a67462a000e0508f256f9c18eff5847efc"},
+ {file = "simplejson-3.17.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d0b64409df09edb4c365d95004775c988259efe9be39697d7315c42b7a5e7e94"},
+ {file = "simplejson-3.17.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:55d65f9cc1b733d85ef95ab11f559cce55c7649a2160da2ac7a078534da676c8"},
+ {file = "simplejson-3.17.2.tar.gz", hash = "sha256:75ecc79f26d99222a084fbdd1ce5aad3ac3a8bd535cd9059528452da38b68841"},
+]
+six = [
+ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
+ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
+]
+sm-widgets = [
+ {file = "sm_widgets-0.1.10-py3-none-any.whl", hash = "sha256:a38cea1b60a55bf27f9ffbeee2ac1c7086a28bdf3ba76e1288245bd71f7d8f56"},
+ {file = "sm_widgets-0.1.10.tar.gz", hash = "sha256:8fdec20fe197213ddf5bb2479921dc9740a8a7bc56f6f2ebd0b7e3410a54e2f7"},
+]
+smmap = [
+ {file = "smmap-4.0.0-py2.py3-none-any.whl", hash = "sha256:a9a7479e4c572e2e775c404dcd3080c8dc49f39918c2cf74913d30c4c478e3c2"},
+ {file = "smmap-4.0.0.tar.gz", hash = "sha256:7e65386bd122d45405ddf795637b7f7d2b532e7e401d46bbe3fb49b9986d5182"},
+]
+sortedcontainers = [
+ {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"},
+ {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"},
+]
+soupsieve = [
+ {file = "soupsieve-2.2.1-py3-none-any.whl", hash = "sha256:c2c1c2d44f158cdbddab7824a9af8c4f83c76b1e23e049479aa432feb6c4c23b"},
+ {file = "soupsieve-2.2.1.tar.gz", hash = "sha256:052774848f448cf19c7e959adf5566904d525f33a3f8b6ba6f6f8f26ec7de0cc"},
+]
+strict-rfc3339 = [
+ {file = "strict-rfc3339-0.7.tar.gz", hash = "sha256:5cad17bedfc3af57b399db0fed32771f18fc54bbd917e85546088607ac5e1277"},
+]
+swagger-spec-validator = [
+ {file = "swagger-spec-validator-2.7.3.tar.gz", hash = "sha256:f4f23ee4dbd52bfcde90b1144dde22304add6260e9f29252e9fd7814c9b8fd16"},
+ {file = "swagger_spec_validator-2.7.3-py2.py3-none-any.whl", hash = "sha256:d1514ec7e3c058c701f27cc74f85ceb876d6418c9db57786b9c54085ed5e29eb"},
+]
+tblib = [
+ {file = "tblib-1.7.0-py2.py3-none-any.whl", hash = "sha256:289fa7359e580950e7d9743eab36b0691f0310fce64dee7d9c31065b8f723e23"},
+ {file = "tblib-1.7.0.tar.gz", hash = "sha256:059bd77306ea7b419d4f76016aef6d7027cc8a0785579b5aad198803435f882c"},
+]
+terminado = [
+ {file = "terminado-0.10.0-py3-none-any.whl", hash = "sha256:048ce7b271ad1f94c48130844af1de163e54913b919f8c268c89b36a6d468d7c"},
+ {file = "terminado-0.10.0.tar.gz", hash = "sha256:46fd07c9dc7db7321922270d544a1f18eaa7a02fd6cd4438314f27a687cabbea"},
+]
+testpath = [
+ {file = "testpath-0.5.0-py3-none-any.whl", hash = "sha256:8044f9a0bab6567fc644a3593164e872543bb44225b0e24846e2c89237937589"},
+ {file = "testpath-0.5.0.tar.gz", hash = "sha256:1acf7a0bcd3004ae8357409fc33751e16d37ccc650921da1094a86581ad1e417"},
+]
+text-unidecode = [
+ {file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"},
+ {file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"},
+]
+toolz = [
+ {file = "toolz-0.11.1-py3-none-any.whl", hash = "sha256:1bc473acbf1a1db4e72a1ce587be347450e8f08324908b8a266b486f408f04d5"},
+ {file = "toolz-0.11.1.tar.gz", hash = "sha256:c7a47921f07822fe534fb1c01c9931ab335a4390c782bd28c6bcc7c2f71f3fbf"},
+]
+tornado = [
+ {file = "tornado-6.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:d371e811d6b156d82aa5f9a4e08b58debf97c302a35714f6f45e35139c332e32"},
+ {file = "tornado-6.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0d321a39c36e5f2c4ff12b4ed58d41390460f798422c4504e09eb5678e09998c"},
+ {file = "tornado-6.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9de9e5188a782be6b1ce866e8a51bc76a0fbaa0e16613823fc38e4fc2556ad05"},
+ {file = "tornado-6.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:61b32d06ae8a036a6607805e6720ef00a3c98207038444ba7fd3d169cd998910"},
+ {file = "tornado-6.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:3e63498f680547ed24d2c71e6497f24bca791aca2fe116dbc2bd0ac7f191691b"},
+ {file = "tornado-6.1-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:6c77c9937962577a6a76917845d06af6ab9197702a42e1346d8ae2e76b5e3675"},
+ {file = "tornado-6.1-cp35-cp35m-win32.whl", hash = "sha256:6286efab1ed6e74b7028327365cf7346b1d777d63ab30e21a0f4d5b275fc17d5"},
+ {file = "tornado-6.1-cp35-cp35m-win_amd64.whl", hash = "sha256:fa2ba70284fa42c2a5ecb35e322e68823288a4251f9ba9cc77be04ae15eada68"},
+ {file = "tornado-6.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0a00ff4561e2929a2c37ce706cb8233b7907e0cdc22eab98888aca5dd3775feb"},
+ {file = "tornado-6.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:748290bf9112b581c525e6e6d3820621ff020ed95af6f17fedef416b27ed564c"},
+ {file = "tornado-6.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:e385b637ac3acaae8022e7e47dfa7b83d3620e432e3ecb9a3f7f58f150e50921"},
+ {file = "tornado-6.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:25ad220258349a12ae87ede08a7b04aca51237721f63b1808d39bdb4b2164558"},
+ {file = "tornado-6.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:65d98939f1a2e74b58839f8c4dab3b6b3c1ce84972ae712be02845e65391ac7c"},
+ {file = "tornado-6.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:e519d64089b0876c7b467274468709dadf11e41d65f63bba207e04217f47c085"},
+ {file = "tornado-6.1-cp36-cp36m-win32.whl", hash = "sha256:b87936fd2c317b6ee08a5741ea06b9d11a6074ef4cc42e031bc6403f82a32575"},
+ {file = "tornado-6.1-cp36-cp36m-win_amd64.whl", hash = "sha256:cc0ee35043162abbf717b7df924597ade8e5395e7b66d18270116f8745ceb795"},
+ {file = "tornado-6.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7250a3fa399f08ec9cb3f7b1b987955d17e044f1ade821b32e5f435130250d7f"},
+ {file = "tornado-6.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ed3ad863b1b40cd1d4bd21e7498329ccaece75db5a5bf58cd3c9f130843e7102"},
+ {file = "tornado-6.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:dcef026f608f678c118779cd6591c8af6e9b4155c44e0d1bc0c87c036fb8c8c4"},
+ {file = "tornado-6.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:70dec29e8ac485dbf57481baee40781c63e381bebea080991893cd297742b8fd"},
+ {file = "tornado-6.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:d3f7594930c423fd9f5d1a76bee85a2c36fd8b4b16921cae7e965f22575e9c01"},
+ {file = "tornado-6.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3447475585bae2e77ecb832fc0300c3695516a47d46cefa0528181a34c5b9d3d"},
+ {file = "tornado-6.1-cp37-cp37m-win32.whl", hash = "sha256:e7229e60ac41a1202444497ddde70a48d33909e484f96eb0da9baf8dc68541df"},
+ {file = "tornado-6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:cb5ec8eead331e3bb4ce8066cf06d2dfef1bfb1b2a73082dfe8a161301b76e37"},
+ {file = "tornado-6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:20241b3cb4f425e971cb0a8e4ffc9b0a861530ae3c52f2b0434e6c1b57e9fd95"},
+ {file = "tornado-6.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c77da1263aa361938476f04c4b6c8916001b90b2c2fdd92d8d535e1af48fba5a"},
+ {file = "tornado-6.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fba85b6cd9c39be262fcd23865652920832b61583de2a2ca907dbd8e8a8c81e5"},
+ {file = "tornado-6.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:1e8225a1070cd8eec59a996c43229fe8f95689cb16e552d130b9793cb570a288"},
+ {file = "tornado-6.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d14d30e7f46a0476efb0deb5b61343b1526f73ebb5ed84f23dc794bdb88f9d9f"},
+ {file = "tornado-6.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8f959b26f2634a091bb42241c3ed8d3cedb506e7c27b8dd5c7b9f745318ddbb6"},
+ {file = "tornado-6.1-cp38-cp38-win32.whl", hash = "sha256:34ca2dac9e4d7afb0bed4677512e36a52f09caa6fded70b4e3e1c89dbd92c326"},
+ {file = "tornado-6.1-cp38-cp38-win_amd64.whl", hash = "sha256:6196a5c39286cc37c024cd78834fb9345e464525d8991c21e908cc046d1cc02c"},
+ {file = "tornado-6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f0ba29bafd8e7e22920567ce0d232c26d4d47c8b5cf4ed7b562b5db39fa199c5"},
+ {file = "tornado-6.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:33892118b165401f291070100d6d09359ca74addda679b60390b09f8ef325ffe"},
+ {file = "tornado-6.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7da13da6f985aab7f6f28debab00c67ff9cbacd588e8477034c0652ac141feea"},
+ {file = "tornado-6.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:e0791ac58d91ac58f694d8d2957884df8e4e2f6687cdf367ef7eb7497f79eaa2"},
+ {file = "tornado-6.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:66324e4e1beede9ac79e60f88de548da58b1f8ab4b2f1354d8375774f997e6c0"},
+ {file = "tornado-6.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:a48900ecea1cbb71b8c71c620dee15b62f85f7c14189bdeee54966fbd9a0c5bd"},
+ {file = "tornado-6.1-cp39-cp39-win32.whl", hash = "sha256:d3d20ea5782ba63ed13bc2b8c291a053c8d807a8fa927d941bd718468f7b950c"},
+ {file = "tornado-6.1-cp39-cp39-win_amd64.whl", hash = "sha256:548430be2740e327b3fe0201abe471f314741efcb0067ec4f2d7dcfb4825f3e4"},
+ {file = "tornado-6.1.tar.gz", hash = "sha256:33c6e81d7bd55b468d2e793517c909b139960b6c790a60b7991b9b6b76fb9791"},
+]
+tqdm = [
+ {file = "tqdm-4.61.0-py2.py3-none-any.whl", hash = "sha256:736524215c690621b06fc89d0310a49822d75e599fcd0feb7cc742b98d692493"},
+ {file = "tqdm-4.61.0.tar.gz", hash = "sha256:cd5791b5d7c3f2f1819efc81d36eb719a38e0906a7380365c556779f585ea042"},
+]
+traitlets = [
+ {file = "traitlets-5.0.5-py3-none-any.whl", hash = "sha256:69ff3f9d5351f31a7ad80443c2674b7099df13cc41fc5fa6e2f6d3b0330b0426"},
+ {file = "traitlets-5.0.5.tar.gz", hash = "sha256:178f4ce988f69189f7e523337a3e11d91c786ded9360174a3d9ca83e79bc5396"},
+]
+typing = [
+ {file = "typing-3.7.4.3-py2-none-any.whl", hash = "sha256:283d868f5071ab9ad873e5e52268d611e851c870a2ba354193026f2dfb29d8b5"},
+ {file = "typing-3.7.4.3.tar.gz", hash = "sha256:1187fb9c82fd670d10aa07bbb6cfcfe4bdda42d6fab8d5134f04e8c4d0b71cc9"},
+]
+typing-extensions = [
+ {file = "typing_extensions-3.10.0.0-py2-none-any.whl", hash = "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497"},
+ {file = "typing_extensions-3.10.0.0-py3-none-any.whl", hash = "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"},
+ {file = "typing_extensions-3.10.0.0.tar.gz", hash = "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342"},
+]
+ujson = [
+ {file = "ujson-4.0.2-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:e390df0dcc7897ffb98e17eae1f4c442c39c91814c298ad84d935a3c5c7a32fa"},
+ {file = "ujson-4.0.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:84b1dca0d53b0a8d58835f72ea2894e4d6cf7a5dd8f520ab4cbd698c81e49737"},
+ {file = "ujson-4.0.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:91396a585ba51f84dc71c8da60cdc86de6b60ba0272c389b6482020a1fac9394"},
+ {file = "ujson-4.0.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:eb6b25a7670c7537a5998e695fa62ff13c7f9c33faf82927adf4daa460d5f62e"},
+ {file = "ujson-4.0.2-cp36-cp36m-win_amd64.whl", hash = "sha256:f8aded54c2bc554ce20b397f72101737dd61ee7b81c771684a7dd7805e6cca0c"},
+ {file = "ujson-4.0.2-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:30962467c36ff6de6161d784cd2a6aac1097f0128b522d6e9291678e34fb2b47"},
+ {file = "ujson-4.0.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:fc51e545d65689c398161f07fd405104956ec27f22453de85898fa088b2cd4bb"},
+ {file = "ujson-4.0.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e6e90330670c78e727d6637bb5a215d3e093d8e3570d439fd4922942f88da361"},
+ {file = "ujson-4.0.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:5e1636b94c7f1f59a8ead4c8a7bab1b12cc52d4c21ababa295ffec56b445fd2a"},
+ {file = "ujson-4.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:e2cadeb0ddc98e3963bea266cc5b884e5d77d73adf807f0bda9eca64d1c509d5"},
+ {file = "ujson-4.0.2-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:a214ba5a21dad71a43c0f5aef917cd56a2d70bc974d845be211c66b6742a471c"},
+ {file = "ujson-4.0.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:0190d26c0e990c17ad072ec8593647218fe1c675d11089cd3d1440175b568967"},
+ {file = "ujson-4.0.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:f273a875c0b42c2a019c337631bc1907f6fdfbc84210cc0d1fff0e2019bbfaec"},
+ {file = "ujson-4.0.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:d3a87888c40b5bfcf69b4030427cd666893e826e82cc8608d1ba8b4b5e04ea99"},
+ {file = "ujson-4.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:7333e8bc45ea28c74ae26157eacaed5e5629dbada32e0103c23eb368f93af108"},
+ {file = "ujson-4.0.2-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:b3a6dcc660220539aa718bcc9dbd6dedf2a01d19c875d1033f028f212e36d6bb"},
+ {file = "ujson-4.0.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:0ea07fe57f9157118ca689e7f6db72759395b99121c0ff038d2e38649c626fb1"},
+ {file = "ujson-4.0.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:4d6d061563470cac889c0a9fd367013a5dbd8efc36ad01ab3e67a57e56cad720"},
+ {file = "ujson-4.0.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b5c70704962cf93ec6ea3271a47d952b75ae1980d6c56b8496cec2a722075939"},
+ {file = "ujson-4.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:aad6d92f4d71e37ea70e966500f1951ecd065edca3a70d3861b37b176dd6702c"},
+ {file = "ujson-4.0.2.tar.gz", hash = "sha256:c615a9e9e378a7383b756b7e7a73c38b22aeb8967a8bfbffd4741f7ffd043c4d"},
+]
+urllib3 = [
+ {file = "urllib3-1.26.5-py2.py3-none-any.whl", hash = "sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c"},
+ {file = "urllib3-1.26.5.tar.gz", hash = "sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098"},
+]
+wcwidth = [
+ {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
+ {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"},
+]
+webcolors = [
+ {file = "webcolors-1.11.1-py3-none-any.whl", hash = "sha256:b8cd5d865a25c51ff1218f0c90d0c0781fc64312a49b746b320cf50de1648f6e"},
+ {file = "webcolors-1.11.1.tar.gz", hash = "sha256:76f360636957d1c976db7466bc71dcb713bb95ac8911944dffc55c01cb516de6"},
+]
+webencodings = [
+ {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"},
+ {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"},
+]
+websocket-client = [
+ {file = "websocket-client-1.0.1.tar.gz", hash = "sha256:3e2bf58191d4619b161389a95bdce84ce9e0b24eb8107e7e590db682c2d0ca81"},
+ {file = "websocket_client-1.0.1-py2.py3-none-any.whl", hash = "sha256:abf306dc6351dcef07f4d40453037e51cc5d9da2ef60d0fc5d0fe3bcda255372"},
+]
+widgetsnbextension = [
+ {file = "widgetsnbextension-3.5.1-py2.py3-none-any.whl", hash = "sha256:bd314f8ceb488571a5ffea6cc5b9fc6cba0adaf88a9d2386b93a489751938bcd"},
+ {file = "widgetsnbextension-3.5.1.tar.gz", hash = "sha256:079f87d87270bce047512400efd70238820751a11d2d8cb137a5a5bdbaf255c7"},
+]
+win32-setctime = [
+ {file = "win32_setctime-1.0.3-py3-none-any.whl", hash = "sha256:dc925662de0a6eb987f0b01f599c01a8236cb8c62831c22d9cada09ad958243e"},
+ {file = "win32_setctime-1.0.3.tar.gz", hash = "sha256:4e88556c32fdf47f64165a2180ba4552f8bb32c1103a2fafd05723a0bd42bd4b"},
+]
+zict = [
+ {file = "zict-2.0.0-py3-none-any.whl", hash = "sha256:26aa1adda8250a78dfc6a78d200bfb2ea43a34752cf58980bca75dde0ba0c6e9"},
+ {file = "zict-2.0.0.tar.gz", hash = "sha256:8e2969797627c8a663575c2fc6fcb53a05e37cdb83ee65f341fc6e0c3d0ced16"},
+]
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..d93860b
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,45 @@
+[tool.poetry]
+name = "grams"
+version = "0.1.0"
+description = ""
+authors = ["Binh Vu "]
+license = "MIT"
+
+[tool.poetry.dependencies]
+python = "^3.8"
+requests = "^2.25.1"
+beautifulsoup4 = "^4.9.3"
+orjson = "^3.5.2"
+python-slugify = "^5.0.2"
+tqdm = "^4.60.0"
+ujson = "^4.0.2"
+"ruamel.yaml" = "^0.17.4"
+loguru = "^0.5.3"
+redis = "^3.5.3"
+networkx = "^2.5.1"
+html5lib = "^1.1"
+omegaconf = "^2.0.6"
+rdflib = "^5.0.0"
+ipython = "^7.22.0"
+matplotlib = "^3.4.1"
+pydot = "^1.4.2"
+rltk = "==2.0.0-alpha.15"
+pslpython = "^2.2.2"
+fastnumbers = "^3.1.0"
+ftfy = "^6.0.1"
+elasticsearch = "^7.12.1"
+fuzzywuzzy = "^0.18.0"
+python-dotenv = "^0.17.0"
+python-Levenshtein = "^0.12.2"
+sem-desc = "^0.1.5"
+kgdata = "^1.1.7"
+sm-widgets = "^0.1.6"
+
+
+[tool.poetry.dev-dependencies]
+neptune-client = "^0.9.5"
+ipykernel = "^5.5.4"
+
+[build-system]
+requires = ["poetry-core>=1.0.0"]
+build-backend = "poetry.core.masonry.api"
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/smoke_test.py b/tests/smoke_test.py
new file mode 100644
index 0000000..737b697
--- /dev/null
+++ b/tests/smoke_test.py
@@ -0,0 +1,20 @@
+import glob
+
+from omegaconf import OmegaConf
+
+from grams.prelude import *
+
+cwd = ROOT_DIR / "examples/semtab2020_novartis"
+cfg = OmegaConf.load(ROOT_DIR / "grams.yaml")
+grams = GRAMS(DATA_DIR, cfg)
+
+gt = [
+ ([O.SemanticModel.from_json(sm) for sm in r['semantic_models']], I.LinkedTable.from_json(r['table']))
+ for r in [M.deserialize_json(infile) for infile in glob.glob(str(cwd / "tables/*.json"))]
+]
+
+def annotate(table):
+ global grams
+ return grams.annotate(table)
+
+annotations = M.parallel_map(annotate, [tbl for sms, tbl in gt], show_progress=True, is_parallel=True)