{"id":14977371,"url":"https://github.com/cfmtech/jupytab","last_synced_at":"2026-04-01T20:44:38.547Z","repository":{"id":46122545,"uuid":"200251314","full_name":"CFMTech/Jupytab","owner":"CFMTech","description":"Display in Tableau data from Jupyter notebooks","archived":false,"fork":false,"pushed_at":"2023-07-13T08:43:19.000Z","size":2036,"stargazers_count":102,"open_issues_count":9,"forks_count":32,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-04-10T01:08:33.097Z","etag":null,"topics":["jupyter-notebook","tableau-connector","tableau-workbooks"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/CFMTech.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-08-02T14:44:16.000Z","updated_at":"2025-03-04T06:15:47.000Z","dependencies_parsed_at":"2025-01-16T08:11:51.628Z","dependency_job_id":"a6f14c88-7d09-48de-acad-c7ec2e8943ac","html_url":"https://github.com/CFMTech/Jupytab","commit_stats":{"total_commits":46,"total_committers":5,"mean_commits":9.2,"dds":0.3913043478260869,"last_synced_commit":"bdc2abf6eae2bafe73b75de18aa7f784d1161096"},"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CFMTech%2FJupytab","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CFMTech%2FJupytab/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CFMTech%2FJupytab/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CFMTech%2FJupytab/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/CFMTech","download_url":"https://codeload.github.com/CFMTech/Jupytab/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248137887,"owners_count":21053775,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["jupyter-notebook","tableau-connector","tableau-workbooks"],"created_at":"2024-09-24T13:55:32.570Z","updated_at":"2026-04-01T20:44:38.488Z","avatar_url":"https://github.com/CFMTech.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Jupytab\n\n[![CircleCI](https://circleci.com/gh/CFMTech/Jupytab.svg?style=svg)](https://circleci.com/gh/CFMTech/Jupytab)\n\u0026nbsp;[![PyPI](https://badge.fury.io/py/jupytab.svg)](https://badge.fury.io/py/jupytab)\n\u0026nbsp; [![Anaconda-Server Badge](https://anaconda.org/conda-forge/jupytab/badges/version.svg)](https://anaconda.org/conda-forge/jupytab)\n\u0026nbsp;[![Anaconda-Server Badge](https://anaconda.org/conda-forge/jupytab/badges/platforms.svg)](https://anaconda.org/conda-forge/jupytab)\n\u0026nbsp;[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nJupytab allows you to **explore in [Tableau](https://www.tableau.com/) data which is generated dynamically by a Jupyter Notebook**. You can thus create Tableau data sources in a very flexible way using all the power of Python. This is achieved by having Tableau access data through a **web server created by Jupytab**.\n\n**New** : Jupytab 0.9.7 now implements the [TabPy](https://github.com/tableau/TabPy) protocol, you can create your datasource and compute data on the fly from your notebook functions !\n\nJupytab is built on **solid foundations**: Tableau's [Web Data Connector](https://tableau.github.io/webdataconnector/) and the [Jupyter Kernel Gateway](https://github.com/jupyter/kernel_gateway).\n\n![Jupytab Logo](jupytab-medium.png)\n\n## Overview\n\nFeatures:\n\n* **Expose multiple pandas dataframes** to Tableau from a Jupyter notebook\n* Access **several notebooks** from Tableau through a **single entry point** (web server)\n* Manage your notebooks using a **web interface**\n* **Secure access** to your data\n* **Compute data on the fly** using the [TabPy](https://github.com/tableau/TabPy) protocol\n\n## Articles\n\n* **[Interactive simulation with Tableau and Jupytab](https://btribonde.medium.com/interactive-simulation-with-tableau-and-jupytab-c26adb1be564)** published on [Toward Datascience](https://towardsdatascience.com)\n* **[Optimise an equity portfolio in Tableau](https://devpost.com/software/portfolio-optimisation)** by [Anya Prosvetova](https://devpost.com/anyalitica). Submitted to [DataDev Hackathon 2021](https://datadev-hackathon.devpost.com)\n\n## Examples\n\nYou can find the example Jupyter notebooks and Tableau workbooks below are available in the [samples](samples) folder of\nthe Jupytab project.\n\n### Preparation\n\nIf you want to run the example notebooks, it is necessary to define the Jupyter kernel that they run with:\n```\npython -m ipykernel install --user --name jupytab-demo\n```\nYou can then launch the Jupytab server as instructed below.\n\n### Air Flights\n\nThe first example illustrates how Jupytab allows you to **directly display realtime data** in Tableau (without going through the hassle of creating intermediate files or database tables). \nWe will display the position and altitude of all planes from the freely available [OpenSky](https://opensky-network.org/) service. (_This service does not show planes currently flying over the \nocean or uninhabited area!_)\n\nThe [AirFlights notebook](jupytab-server/samples/air-flights/AirFlights.ipynb) uses the [Requests](https://2.python-requests.org/en/master/) library to **access the OpenSky HTTP Rest API** and then exposes multiple metrics in a dataframe.\nThe provided [Tableau workbook](jupytab-server/samples/air-flights/AirFlights.twbx) gives the result below:\n\n![AirFlights](jupytab-server/docs/resources/AirFlights.png)\n\n### Real Estate Price, and Crime \n\nThe second example illustrates how simple it is to use Jupytab and **create a custom data source from multiple CSV files**. This is particularly convenient, because there is **no need to configure a new storage area** for these files in Tableau: the data is accessed through Jupytab's web service.\n\nThe [example notebook](jupytab-server/samples/real-estate_crime/RealEstateCrime.ipynb) exposes real estate and crime data for Sacramento, with a bit of [Pandas](http://pandas.pydata.org/) magic to combine several data sources. \n\nThanks to the combination of data in a single dataframe, the [Tableau workbook](jupytab-server/samples/air-flights/AirFlights.twbx) can automatically show **maps over the same area of the city**:\n\n![RealEstateCrime](jupytab-server/docs/resources/RealEstateCrime.png)\n\n### SkLearn Iris Predictor\n\nThe third example illustrate how you can use Jupytab to create your datasource and interact in real-time with your datas. This an ideal companion for your machine learning projects, as it allows you to keep all your python code in the notebook while offering the ability for Tableau users to freely interact with your datas and understand the impact of parameters change.\n\nThe [Iris Predictor notebook](jupytab-server/samples/sklearn-classifier/sklearn-classifier.ipynb) shows how you can combine data and code to create a all-in-one Tableau data source.\n\n![SKLearnClassifier](jupytab-server/docs/resources/SKLearnClassifier.png)\n\nThe python code is now only in your notebook ! The Tableau calculation is straightforward and do not rely on Python code.\n\n![SKLearnClassifier-Calculation](jupytab-server/docs/resources/SKLearnClassifier-Calculation.png)\n\n# Installation\n\n## Requirements\n\nPython 3.6+ is currently required to run the Jupytab server.\n\nThe notebook code itself requires Python 3.6+ too (but it shouldn't be difficult to adapt Jupytab for Python 2).\n\nJupytab server relies on the official [Jupyter Kernel Gateway](https://github.com/jupyter/kernel_gateway).\n\n## Automatic installation\n\nThe Jupytab server and its notebook library must both be installed. \n\nJupytab server and its dependencies can easily be installed through pip:\n\n```\npip install jupytab-server\n```\n\nFor notebook kernels, you must install the jupytab library that only have a dependency on Pandas.\n\n```\npip install jupytab\n```\n\n# Usage \n\n## Configuration file\n\nYou need to create a `config.ini` file in order to tell Jupytab which notebooks contain the tables that should be published for Tableau (this configuration file can be stored anywhere you choose). Here is an example of a working configuration file:\n\n```\n[main]\nlisten_port = 8765\nsecurity_token = myToken\nnotebooks = AirFlights|RealEstateCrime\nssl_enabled = True\nssl_key = /etc/pki/tls/certs/file.crt\nssl_cert = /etc/pki/tls/private/file.key\n\n[AirFlights]\nname = Air Flights\ndirectory = samples/air-flights\npath = ./AirFlights.ipynb\ndescription = Realtime Flights Visualisation (API)\n\n[RealEstateCrime]\nname = RealEstateCrime\ndirectory = samples/real-estate_crime\npath = ./RealEstateCrime.ipynb\ndescription = Real Estate Crime (static CSV)\n```\n\nThere is only one mandatory section, `main`, which contains:\n\n* `listen_port` (mandatory): Numeric port number (it must be available).\n* `notebooks` (mandatory): List of notebooks to be executed by Jupytab, provided as a section name in the config file\nand separated by the `|` (pipe) symbol. This must be a simple name compliant with [configparser](https://docs.python.org/3/library/configparser.html) sections.\n* `security_token` (optional): If provided, an encrypted security token will be required for all exchanges with\nJupytab.\n* `ssl_enabled` (optional): Enable or disable SSL\n* `ssl_key` (mandatory if ssl_enabled is true): The path name of the server private key file\n* `ssl_cert` (mandatory if ssl_enabled is true): The path name of the server public key certificate file\n \nAdditional sections contain information about each notebook to be run:\n\n* `name` (optional): If provided, replaces the section name by a more friendly notebook name in the Jupytab web interface.\n* `directory` (optional): If provided, the notebook will start with `directory` as its working directory instead of the one where the `jupytab` commands is launched (see below).\n* `path` (mandatory): Relative (compared to `directory`) or absolute path to your notebook.\n* `description` (optional): If provided, adds a description to your notebook in the Jupytab web interface.\n\nPlease make sure that the notebook name in the main section is exactly the same as in the section title!\n\n![ConfigSection](jupytab-server/docs/resources/ConfigSection.png)\n\n## Notebook preparation\n\nPublishing dataframes from a notebook is simple. Let's start by importing the necessary module:\n\n```python\nimport pandas as pd\n\nimport jupytab\n```\n\n### Tables definition\n\nThe publication of data sources for Tableau from a notebook is done through two classes:\n\n* Tables: Contains the publication-ready tables provided by the notebook. There is typically a single instance of this class in a given notebook.\n* DataFrameTable: Table for either static or dynamic publication in Tableau. Static tables never change on the Tableau side. Dynamic tables are regenerated for each Tableau Extract.\n\n```python\ndef dynamic_df():\n    return pd.DataFrame([[1, 2, 3], [4, 5, 6], [7, 8, 9]], columns=['a', 'b', 'c'])\n\ntables = jupytab.Tables()  # Publication-ready tables contained by this notebook\n\n# Example 1: Static data: it will never change on the Tableau side:\nstatic_df = dynamic_df()\ntables['static'] = jupytab.DataFrameTable('A static table', dataframe=static_df)\n\n# Example 2: Dynamic data: a new DataFrame is generated whenever Extract is requested on Tableau's side:\ntables['dynamic'] = jupytab.DataFrameTable('A dynamic table', refresh_method=dynamic_df)\n```\n\nThe tables listed in the Python variables `tables` now need to be explicitly marked for publication by Jupytab (both their schema and their contents). This is typically done at the very end of the notebook, with two special cells.\n\nPlease note that you can also include the index in the dataframe output using `include_index=True`. Index is not included by default.\n\n```python\n# Example 3: Static data with index included\nstatic_df = dynamic_df()\ntables['static'] = jupytab.DataFrameTable('A static table', dataframe=static_df, include_index=True)\n```\n\n### Functions definition\n\nFollowing the same principle, you can also expose your own python functions to Tableau through two classes:\n\n```python\ndef multiply(my_first_number, my_second_number):\n    return my_first_number * my_second_number\n    \nfunctions = jupytab.Functions() # Publication-ready functions contained by this notebook\n\nfunctions['multiplier'] = jupytab.Function('A multiplier function with two parameters', multiply)\n```\n\nThe function is now available in Tableau using the following calculation:\n\n`SCRIPT_REAL(\"MyNotebook.multiplier\", AVG([Value 1]), AVG([Value 2]))`\n\nYou must refer to the notebook code you used in the config section, not the notebook name which is used only for display.\n\n```\n[main]\nnotebooks = MyNotebook\n```\n\n### Expose tables schema\n\nWhen Tableau needs to retrieve the schema of all available tables, Jupytab executes the (mandatory) cell that starts with `# GET /schema`:\n\n```python\n# GET /schema\ntables.render_schema()\n```\n\n(`tables.render_schema()` will output a JSON string when executed in the notebook.)\n\n### Expose tables data\n\nWhen Tableau needs to retrieve the data from tables, Jupytab executes the (mandatory) cell that starts with `# GET /data`:\n\n```python\n# GET /data\ntables.render_data(REQUEST)\n```\n\n(Note that `tables.render_data(REQUEST)` will throw, as expected, `NameError: name 'REQUEST' is not defined` when executed in the notebook: `REQUEST` will only be defined when running with Jupytab, so the error is harmless.)\n\n### Expose functions data\n\nWhen Tableau needs to execute function, Jupytab executes the (mandatory) cell that starts with `# POST /evaluate`:\n\n```python\n# POST /evaluate\nfunctions.render_evaluate(REQUEST)\n```\n\n(Note that `functions.render_evaluate(REQUEST)` will throw, as expected, `NameError: name 'REQUEST' is not defined` when executed in the notebook: `REQUEST` will only be defined when running with Jupytab, so the error is harmless.)\n\n## Launching the Jupytab server\n\nOnce you have created your notebooks, it should be a matter of second before they become acessible from Tableau.\nTo start Jupytab, simply run the following command:\n```\njupytab --config=config.ini\n```\nYou should see the following ouput, which contains two important pieces of information:\n\n* The list of published notebooks.\n* The URL to be used in Tableau in order to access the data (including any security token declared in the configuration file).\n\n```\n(install-jupytab) user@localhost:~$ jupytab --config=tests/config.ini\nStart notebook ~/tests/resources/rt_flights.ipynb on 127.0.0.1:57149\nStart notebook ~/tests/resources/csv_reader.ipynb on 127.0.0.1:53351\nYour token is 02014868fe0eef123269397c5bc65a9608b3cedb73e3b84d8d02c220\n        Please open : http://localhost:8765/?security_token=02014868fe0eef123269397c5bc65a9608b3cedb73e3b84d8d02c220\nINFO:[KernelGatewayApp] Kernel started: 1befe373-aebd-4b31-9f98-2f90f235f255\nINFO:[KernelGatewayApp] Kernel started: 365bfdb6-887b-41b4-ad69-309a200f5137\nINFO:[KernelGatewayApp] Registering resource: /schema, methods: (['GET'])\nINFO:[KernelGatewayApp] Registering resource: /data, methods: (['GET'])\nINFO:[KernelGatewayApp] Registering resource: /_api/spec/swagger.json, methods: (GET)\nINFO:[KernelGatewayApp] Jupyter Kernel Gateway at http://127.0.0.1:53351\nINFO:[KernelGatewayApp] Registering resource: /schema, methods: (['GET'])\nINFO:[KernelGatewayApp] Registering resource: /data, methods: (['GET'])\nINFO:[KernelGatewayApp] Registering resource: /_api/spec/swagger.json, methods: (GET)\nINFO:[KernelGatewayApp] Jupyter Kernel Gateway at http://127.0.0.1:57149\n```\n\n## Connect Tableau to your notebooks\n\n### Web Data Connector for data sources\n\nConnecting Tableau to your notebooks is simply done by copying the URL provided by Jupytab upon startup to the Tableau Web Data Connector:\n\n![TableauStart](jupytab-server/docs/resources/TableauStart.png)\n\nYou can now use the Tableau Web Data Connector screen and access your data sources through the Jupytab interface.\n\n### TabPy Connector to execute functions\n\nConnecting Tableau to your notebooks to execute code on the fly using the [External Connection Service](https://help.tableau.com/current/pro/desktop/en-us/r_connection_manage.htm).\n\nThe address to use is the host where Jupytab is running. The port is the one you configured in the `config.ini` file.\n\nPlease take care to select the **TabPy / External API** and not RServe.\n\n## Troubleshooting\n\nIf you encounter a any problem when using Jupytab, you can find it useful to check the console where you launched \nJupytab for diagnostic messages. The console output can in particular be usefully included when you raise a GitHub issue.\n\n# Contact and contributing\n\nContributions are very welcome.  It can be\n\n- a new GitHub issue,\n- a feature request,\n- code (see the [Developement Guide](jupytab-server/docs/source/development-guide.md)),\n- or simply feedback on this project.\n\nThe main author of Jupytab is Brian Tribondeau, who can be reached at brian.tribondeau@cfm.fr.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcfmtech%2Fjupytab","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcfmtech%2Fjupytab","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcfmtech%2Fjupytab/lists"}