diff --git a/notebooks/general_example.ipynb b/notebooks/general_example.ipynb index d10247a..82eb10a 100644 --- a/notebooks/general_example.ipynb +++ b/notebooks/general_example.ipynb @@ -2,16 +2,39 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: textualheatmap in /home/venktesh/anaconda3/lib/python3.7/site-packages (1.1.1)\n", + "Requirement already satisfied: ipython in /home/venktesh/anaconda3/lib/python3.7/site-packages (from textualheatmap) (7.4.0)\n", + "Requirement already satisfied: prompt-toolkit<2.1.0,>=2.0.0 in /home/venktesh/anaconda3/lib/python3.7/site-packages (from ipython->textualheatmap) (2.0.9)\n", + "Requirement already satisfied: traitlets>=4.2 in /home/venktesh/anaconda3/lib/python3.7/site-packages (from ipython->textualheatmap) (4.3.2)\n", + "Requirement already satisfied: backcall in /home/venktesh/anaconda3/lib/python3.7/site-packages (from ipython->textualheatmap) (0.1.0)\n", + "Requirement already satisfied: pygments in /home/venktesh/anaconda3/lib/python3.7/site-packages (from ipython->textualheatmap) (2.3.1)\n", + "Requirement already satisfied: pickleshare in /home/venktesh/anaconda3/lib/python3.7/site-packages (from ipython->textualheatmap) (0.7.5)\n", + "Requirement already satisfied: pexpect; sys_platform != \"win32\" in /home/venktesh/anaconda3/lib/python3.7/site-packages (from ipython->textualheatmap) (4.6.0)\n", + "Requirement already satisfied: decorator in /home/venktesh/anaconda3/lib/python3.7/site-packages (from ipython->textualheatmap) (4.4.0)\n", + "Requirement already satisfied: setuptools>=18.5 in /home/venktesh/anaconda3/lib/python3.7/site-packages (from ipython->textualheatmap) (46.1.3)\n", + "Requirement already satisfied: jedi>=0.10 in /home/venktesh/anaconda3/lib/python3.7/site-packages (from ipython->textualheatmap) (0.13.3)\n", + "Requirement already satisfied: six>=1.9.0 in /home/venktesh/anaconda3/lib/python3.7/site-packages (from prompt-toolkit<2.1.0,>=2.0.0->ipython->textualheatmap) (1.12.0)\n", + "Requirement already satisfied: wcwidth in /home/venktesh/anaconda3/lib/python3.7/site-packages (from prompt-toolkit<2.1.0,>=2.0.0->ipython->textualheatmap) (0.1.7)\n", + "Requirement already satisfied: ipython-genutils in /home/venktesh/anaconda3/lib/python3.7/site-packages (from traitlets>=4.2->ipython->textualheatmap) (0.2.0)\n", + "Requirement already satisfied: ptyprocess>=0.5 in /home/venktesh/anaconda3/lib/python3.7/site-packages (from pexpect; sys_platform != \"win32\"->ipython->textualheatmap) (0.6.0)\n", + "Requirement already satisfied: parso>=0.3.0 in /home/venktesh/anaconda3/lib/python3.7/site-packages (from jedi>=0.10->ipython->textualheatmap) (0.3.4)\n" + ] + } + ], "source": [ "!pip install textualheatmap" ] }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -27,7 +50,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -35,13 +58,13 @@ " with open('./heatmap.json') as fp:\n", " data = json.load(fp)\n", "except IOError:\n", - " with request.urlopen('https://raw.githubusercontent.com/AndreasMadsen/python-textualheatmap/master/notebooks/heatmap.json'):\n", + " with request.urlopen('https://raw.githubusercontent.com/AndreasMadsen/python-textualheatmap/master/notebooks/heatmap.json') as fp:\n", " data = json.load(fp) " ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 12, "metadata": {}, "outputs": [ { @@ -108,6 +131,7 @@ " height: 39px;\n", " background: #F3F3F3;\n", " border: 1px solid #E0E0E0;\n", + " color: black;\n", " border-bottom: none;\n", " text-overflow: ellipsis;\n", " overflow: hidden;\n", @@ -207,7 +231,8 @@ " constructor(settings, root, facetName) {\n", " this.settings = settings;\n", " this.highlightIndex = null;\n", - " this.data = [];\n", + " this.nonFormatData = [];\n", + " this.heatIndexToNodeElement = [];\n", " this.root = root;\n", " this.onmouseover = null;\n", "\n", @@ -238,7 +263,8 @@ " }\n", "\n", " setData(data) {\n", - " this.data = data;\n", + " this.nonFormatData = [];\n", + " this.heatIndexToNodeElement = []\n", "\n", " while (this.content.childNodes.length > 0) {\n", " this.content.removeChild(this.content.firstChild);\n", @@ -246,9 +272,12 @@ "\n", " for (let i = 0; i < data.length; i++) {\n", " const tokenNode = document.createElement('span');\n", + " const heatIndex = this.heatIndexToNodeElement.length;\n", " tokenNode.appendChild(document.createTextNode(data[i].token));\n", - " if (this.settings.interactive) {\n", - " tokenNode.addEventListener('mouseover', () => this.onmouseover(i), false);\n", + " if (this.settings.interactive && !data[i].format) {\n", + " tokenNode.addEventListener('mouseover', () => this.onmouseover(heatIndex), false);\n", + " this.heatIndexToNodeElement.push(tokenNode)\n", + " this.nonFormatData.push(data[i])\n", " }\n", " this.content.appendChild(tokenNode);\n", " }\n", @@ -261,9 +290,9 @@ " highlight(index) {\n", " this.highlightIndex = index;\n", "\n", - " for (let i = 0; i < this.data.length; i++) {\n", - " this.content.childNodes[i].style.backgroundColor = viridisSubset(this.data[index].heat[i]);\n", - " this.content.childNodes[i].classList.toggle('selected', i === index);\n", + " for (let i = 0; i < this.heatIndexToNodeElement.length; i++) {\n", + " this.heatIndexToNodeElement[i].style.backgroundColor = viridisSubset(this.nonFormatData[index].heat[i]);\n", + " this.heatIndexToNodeElement[i].classList.toggle('selected', i === index);\n", " }\n", "\n", " if (this.settings.showMeta) {\n", @@ -271,14 +300,14 @@ " this.meta.removeChild(this.meta.firstChild);\n", " }\n", "\n", - " for (let i = 0; i < this.data[index].meta.length; i++) {\n", + " for (let i = 0; i < this.nonFormatData[index].meta.length; i++) {\n", " const item = document.createElement('div');\n", " item.classList.add('meta-content-item');\n", - " item.appendChild(document.createTextNode(this.data[index].meta[i]));\n", + " item.appendChild(document.createTextNode(this.nonFormatData[index].meta[i]));\n", " this.meta.appendChild(item);\n", " }\n", "\n", - " if (this.data[index].meta.length === 0) {\n", + " if (this.nonFormatData[index].meta.length === 0) {\n", " const item = document.createElement('div');\n", " item.classList.add('meta-content-item');\n", " this.meta.appendChild(item);\n", @@ -298,7 +327,7 @@ " window.highlightTextualHeatmap = function (settings, index) {\n", " document.getElementById(settings.id).instance.highlight(index);\n", " };\n", - "})();
" + "})();
" ], "text/plain": [ "" @@ -310,7 +339,7 @@ { "data": { "text/html": [ - "" + "" ], "text/plain": [ "" @@ -322,7 +351,7 @@ { "data": { "text/html": [ - "" + "" ], "text/plain": [ "" @@ -340,7 +369,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -407,6 +436,7 @@ " height: 39px;\n", " background: #F3F3F3;\n", " border: 1px solid #E0E0E0;\n", + " color: black;\n", " border-bottom: none;\n", " text-overflow: ellipsis;\n", " overflow: hidden;\n", @@ -506,7 +536,8 @@ " constructor(settings, root, facetName) {\n", " this.settings = settings;\n", " this.highlightIndex = null;\n", - " this.data = [];\n", + " this.nonFormatData = [];\n", + " this.heatIndexToNodeElement = [];\n", " this.root = root;\n", " this.onmouseover = null;\n", "\n", @@ -537,7 +568,8 @@ " }\n", "\n", " setData(data) {\n", - " this.data = data;\n", + " this.nonFormatData = [];\n", + " this.heatIndexToNodeElement = []\n", "\n", " while (this.content.childNodes.length > 0) {\n", " this.content.removeChild(this.content.firstChild);\n", @@ -545,9 +577,12 @@ "\n", " for (let i = 0; i < data.length; i++) {\n", " const tokenNode = document.createElement('span');\n", + " const heatIndex = this.heatIndexToNodeElement.length;\n", " tokenNode.appendChild(document.createTextNode(data[i].token));\n", - " if (this.settings.interactive) {\n", - " tokenNode.addEventListener('mouseover', () => this.onmouseover(i), false);\n", + " if (this.settings.interactive && !data[i].format) {\n", + " tokenNode.addEventListener('mouseover', () => this.onmouseover(heatIndex), false);\n", + " this.heatIndexToNodeElement.push(tokenNode)\n", + " this.nonFormatData.push(data[i])\n", " }\n", " this.content.appendChild(tokenNode);\n", " }\n", @@ -560,9 +595,9 @@ " highlight(index) {\n", " this.highlightIndex = index;\n", "\n", - " for (let i = 0; i < this.data.length; i++) {\n", - " this.content.childNodes[i].style.backgroundColor = viridisSubset(this.data[index].heat[i]);\n", - " this.content.childNodes[i].classList.toggle('selected', i === index);\n", + " for (let i = 0; i < this.heatIndexToNodeElement.length; i++) {\n", + " this.heatIndexToNodeElement[i].style.backgroundColor = viridisSubset(this.nonFormatData[index].heat[i]);\n", + " this.heatIndexToNodeElement[i].classList.toggle('selected', i === index);\n", " }\n", "\n", " if (this.settings.showMeta) {\n", @@ -570,14 +605,14 @@ " this.meta.removeChild(this.meta.firstChild);\n", " }\n", "\n", - " for (let i = 0; i < this.data[index].meta.length; i++) {\n", + " for (let i = 0; i < this.nonFormatData[index].meta.length; i++) {\n", " const item = document.createElement('div');\n", " item.classList.add('meta-content-item');\n", - " item.appendChild(document.createTextNode(this.data[index].meta[i]));\n", + " item.appendChild(document.createTextNode(this.nonFormatData[index].meta[i]));\n", " this.meta.appendChild(item);\n", " }\n", "\n", - " if (this.data[index].meta.length === 0) {\n", + " if (this.nonFormatData[index].meta.length === 0) {\n", " const item = document.createElement('div');\n", " item.classList.add('meta-content-item');\n", " this.meta.appendChild(item);\n", @@ -597,7 +632,7 @@ " window.highlightTextualHeatmap = function (settings, index) {\n", " document.getElementById(settings.id).instance.highlight(index);\n", " };\n", - "})();
" + "})();
" ], "text/plain": [ "" @@ -609,7 +644,7 @@ { "data": { "text/html": [ - "" + "" ], "text/plain": [ "" @@ -621,7 +656,7 @@ { "data": { "text/html": [ - "" + "" ], "text/plain": [ "" @@ -664,7 +699,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.7.3" } }, "nbformat": 4, diff --git a/textualheatmap/web_assets/textual_heatmap.js b/textualheatmap/web_assets/textual_heatmap.js index dec7522..a22c853 100644 --- a/textualheatmap/web_assets/textual_heatmap.js +++ b/textualheatmap/web_assets/textual_heatmap.js @@ -1,6 +1,12 @@ ;(function () { 'use strict'; + /** + * + * + * @param {*} ratio: a number between 0 and 1 + * @return {*} color in hex format + */ function viridisSubset(ratio) { const colormap = [ '#365d8d', '#355e8d', '#355f8d', '#34608d', '#34618d', @@ -28,6 +34,46 @@ return colormap[Math.max(0, Math.min(n, Math.floor(ratio * n)))]; } + /** + * + * + * @param {*} ratio: a number between -1 and 0 + * @return {*} color in hex format + */ + function viridisSubsetNeg(ratio) { + const colormap = [ + "#440154", "#440256", "#450457", "#450559", "#46075a", + "#46085c", "#460a5d", "#460b5e", "#470d60", "#470e61", + "#471063", "#471164", "#471365", "#481467", "#481668", + "#481769", "#48186a", "#481a6c", "#481b6d", "#481c6e", + "#481d6f", "#481f70", "#482071", "#482173", "#482374", + "#482475", "#482576", "#482677", "#482878", "#482979", + "#472a7a", "#472c7a", "#472d7b", "#472e7c", "#472f7d", + "#46307e", "#46327e", "#46337f", "#463480", "#453581", + "#453781", "#453882", "#443983", "#443a83", "#443b84", + "#433d84", "#433e85", "#423f85", "#424086", "#424186", + "#414287", "#414487", "#404588", "#404688", "#3f4788", + "#3f4889", "#3e4989", "#3e4a89", "#3e4c8a", "#3d4d8a", + "#3d4e8a", "#3c4f8a", "#3c508b", "#3b518b", "#3b528b", + "#3a538b", "#3a548c", "#39558c", "#39568c", "#38588c", + "#38598c", "#375a8c", "#375b8d", "#365c8d", "#365d8d"]; + const n = colormap.length - 1; + return colormap[Math.max(0, Math.min(n, Math.floor((ratio + 1) * n)))]; + } + + /** + * + * @param {*} ratio: a number between -1 and 1 + * @return {*} color in hex format + */ + function color(ratio) { + if (ratio < 0) { + return viridisSubsetNeg(ratio); + } else { + return viridisSubset(ratio); + } + } + class TextualHeatmap { constructor(settings) { this.container = document.getElementById(settings.id); @@ -117,7 +163,7 @@ this.highlightIndex = index; for (let i = 0; i < this.heatIndexToNodeElement.length; i++) { - this.heatIndexToNodeElement[i].style.backgroundColor = viridisSubset(this.nonFormatData[index].heat[i]); + this.heatIndexToNodeElement[i].style.backgroundColor = color(this.nonFormatData[index].heat[i]); this.heatIndexToNodeElement[i].classList.toggle('selected', i === index); }