const colors = { RdBu: ["rgb(255, 13, 87)", "rgb(30, 136, 229)"], GnPR: ["rgb(24, 196, 93)", "rgb(124, 82, 255)"], CyPU: ["#0099C6", "#990099"], PkYg: ["#DD4477", "#66AA00"], DrDb: ["#B82E2E", "#316395"], LpLb: ["#994499", "#22AA99"], YlDp: ["#AAAA11", "#6633CC"], OrId: ["#E67300", "#3E0099"] } window.forceplot = (function() { var svgNS = 'http://www.w3.org/2000/svg'; var xhtmlNS = 'http://www.w3.org/1999/xhtml'; let view = {}; var layoutContainerID = "layoutContainer"; var minWidth = 100; var minHeight = 100; var ForcePlot = function () { this._representation = null; this._value = null; this._table = null; this._infoWrapperMinHeight = 80; this._svgs = null; this._colNamesToShapIndex = null; } ForcePlot.prototype.init = function(representation, value) { // If no data is available, set error message. if (!representation.table) { d3.select('body') .append('p') .text('Error: No data available'); return; } //console.log(ReactDOM); //console.log(_); this._representation = representation; this._value = value || {}; this._value.currentPage = value.currentPage || 1; this._infoWrapperMinHeight = 80; this._svgs = {}; this._table = new kt(); this._table.setDataTable(representation.table); var repColNames = this._representation.table.spec.colNames; this._colNames = repColNames; this._colNamesToShapIndex = {}; repColNames.forEach((hmColName) => { var indexOfShapCol = repColNames.indexOf("SHAP " + hmColName); if (indexOfShapCol > -1){ this._colNamesToShapIndex[repColNames.indexOf(hmColName)] = indexOfShapCol; } this._colNames[repColNames.indexOf(hmColName)] = hmColName; }); d3.select("html").style("width", "100%").style("height", "100%"); //d3.select("body").style("width", "100%").style("height", "100%"); /*var layoutContainer = body.append("div") .attr("id", layoutContainerID) .attr("class", "knime-layout-container") .style("min-width", minWidth + "px") .style("min-height", minHeight + "px");*/ //let l = function(React, ReactDOM, _) { var container = document.createElementNS(xhtmlNS, 'div'); container.classList.add('knime-layout-container'); document.body.appendChild(container); this.createForcePlot(); this.registerOneTimeEvents(); console.log("internal value "); console.log(this._value) console.log("KNIME value: "); console.log(value); //}; /*knimeService.loadConditionally(["react", "react-dom", "lodash"], (arg) => l(arg[0], arg[1], arg[2]), (err) => console.log("kinmeService failed to install " + err), requirejs_config);*/ }; /** * Create very basic pagination data from rows * @param {Array} data * @return {Object} pagination data */ ForcePlot.prototype.createPagination = function (data) { if (!data) { return { rows: data }; } var pageSize = 1; var pageCount = Math.ceil(data.length / pageSize); // jump to page 1 if total number of pages exceeds current page this._value.currentPage = this._value.currentPage <= pageCount ? this._value.currentPage : 1; var pageRowEndIndex = pageSize * this._value.currentPage; var pageRowStartIndex = pageSize * (this._value.currentPage - 1); var rows = data.slice(pageRowStartIndex, pageRowEndIndex); return { totalRowCount: data.length, rows: rows, pageCount: pageCount, pageRowEndIndex: pageRowEndIndex > data.length ? data.length : pageRowEndIndex, pageRowStartIndex: pageRowStartIndex, next: pageRowEndIndex < data.length ? this._value.currentPage + 1 : false, prev: pageRowStartIndex > 0 ? this._value.currentPage - 1 : false }; }; /** * Create intervals from the pagination data to limit the amount of links shown * @param {Object} pagination data * @return {Object} pagination intervals */ ForcePlot.prototype.createPaginationIntervals = function(pagination) { var delta = 2; // number of pages displayed left and right to "center" var left = this._value.currentPage - delta; var right = this._value.currentPage + delta; var range = []; var paginationRange = []; var curPage; for (var i = 1; i <= pagination.pageCount; i++) { if (i === 1 || i === pagination.pageCount || (left <= i && i <= right)) { range.push(i); } } range.forEach(function (page) { if (curPage) { if (page - curPage !== 1) { paginationRange.push('...'); } } paginationRange.push(page); curPage = page; }); return { prev: pagination.prev, next: pagination.next, pages: paginationRange }; }; ForcePlot.prototype.getPaginationHtml = function(pagination) { var paginationRange = this.createPaginationIntervals(pagination); var self = this; if (paginationRange.pages.length <= 1) { return ''; } var html = ''; return html; }; ForcePlot.prototype.drawMetaInfo = function(paginationData) { var displayedRows = ''; var paginationHtml = ''; // Flex containers need this so the pagination is centered. var emptyBlock = '
'; //if (this._representation.enablePaging) { paginationHtml += this.getPaginationHtml(paginationData); displayedRows += '

Showing entry ' + (paginationData.totalRowCount > 1 ? paginationData.pageRowStartIndex + 1 : paginationData.totalRowCount) + ' of ' + paginationData.totalRowCount + ' entries

'; //} var infoWrapper = document.body.querySelector('.info-wrapper'); infoWrapper.innerHTML = displayedRows + paginationHtml + emptyBlock; infoWrapper.style.minHeight = this._infoWrapperMinHeight + 'px'; }; ForcePlot.prototype.registerEvents = function () { var self = this; var pagination = document.body.querySelector('.pagination'); console.log("Add events to pagination" + pagination); if (pagination) { document.body.querySelector('.pagination').addEventListener('click', function (e) { if (e.target.tagName === 'A') { var pageNumber = parseInt(e.target.getAttribute('href').substr(1), 10); self._value.currentPage = pageNumber; self.createForcePlot(); } }); } // Events for the svg are native js event listeners not // d3 event listeners for better performance /*var domWrapper = document.querySelector('.knime-svg-container svg .transformer'); // Highlight mouseover cell and show tooltip domWrapper.addEventListener('mouseover', function (e) { domWrapper.addEventListener('mousemove', self.onMousemove.bind(self)); }); domWrapper.addEventListener('mouseout', function (e) { self.hideTooltip(); domWrapper.removeEventListener('mouseover', self.onMousemove.bind(self)); }); */ }; ForcePlot.prototype.getComponentValue = () => { /*console.log("GetComponentValue"); let i = 0; for(var svg in this._svgs){ console.log(svg); this._value.forcePlotsSVG[i] = svg; ++i; } console.log(this._value);*/ return this._value; }; ForcePlot.prototype.validate = function () { return true; }; ForcePlot.prototype.getSVG = function () { var svgElement = d3.select('.additiveForceVizSVG').node(); knimeService.inlineSvgStyles(svgElement); return (new XMLSerializer()).serializeToString(svgElement); }; ForcePlot.prototype.getSVG = function (nr) { console.log(d3.select('.additiveForceVizSVG' + nr).node()); var svgElement = d3.select('.additiveForceVizSVG' + nr).node(); knimeService.inlineSvgStyles(svgElement); return (new XMLSerializer()).serializeToString(svgElement); }; ForcePlot.prototype.drawSvgRow = function (row, rowIndex) { var self = this; const exampleData = { outNames: ["Probability of flower"], baseValue: self._table.getCell(row.rowKey, "Bias"), link: "identity", features: [], number: rowIndex }; row.data.forEach(function (value, currentIndex) { if (typeof self._colNamesToShapIndex[currentIndex] === 'undefined') { return; } var feature = { name: self._colNames[currentIndex], effect: self._table.getCell(row.rowKey, "SHAP " + self._colNames[currentIndex]), value: self._table.getCell(row.rowKey, self._colNames[currentIndex]) } exampleData.features.push(feature); }); console.log(exampleData); return exampleData; }; ForcePlot.prototype.registerOneTimeEvents = function () { var self = this; window.addEventListener('resize', function () { self.reset(); self.createForcePlot(); }); }; ForcePlot.prototype.createForcePlot = function() { var data = this._table.getRows(); let body = document.getElementsByTagName("body")[0]; const title = `

Force plot

`; const infoWrapper = `
`; var plots = ``; for(let j = 0; j < data.length; j++){ plots += `
`; } body.innerHTML = title + plots + infoWrapper; // Meta info var paginationData = this.createPagination(data); this.drawMetaInfo(paginationData); if (!this._representation.appendImagesToTable){ var svgRow = this.drawSvgRow(paginationData.rows[0], paginationData.pageRowStartIndex); } const select = d3.select; const scaleLinear = d3.scaleLinear; const format = d3.format; const axisBottom = d3.axisBottom; const line = d3.line; const hsl = d3.hsl; const sortBy = _.sortBy; const map = _.map; const each = _.each; const sum = _.sum; const filter = _.filter; const findIndex = _.findIndex; const debounce = _.debounce; class AdditiveForceVisualizer extends React.Component { constructor(props) { super(props); console.log(props); window.lastAdditiveForceVisualizer = this; this.effectFormat = format(".2"); this.redraw = debounce(() => this.draw(), 500); } componentDidMount() { // create our permanent elements this.mainGroup = this.svg.append("g"); this.axisElement = this.mainGroup .append("g") .attr("transform", "translate(0,35)") .attr("class", "force-bar-axis"); this.onTopGroup = this.svg.append("g"); this.baseValueTitle = this.svg.append("text"); this.joinPointLine = this.svg.append("line"); this.joinPointLabelOutline = this.svg.append("text"); this.joinPointLabel = this.svg.append("text"); this.joinPointTitleLeft = this.svg.append("text"); this.joinPointTitleLeftArrow = this.svg.append("text"); this.joinPointTitle = this.svg.append("text"); this.joinPointTitleRightArrow = this.svg.append("text"); this.joinPointTitleRight = this.svg.append("text"); // Define the tooltip objects this.hoverLabelBacking = this.svg.append("text") .attr("x", 10) .attr("y", 20) .attr("text-anchor", "middle") .attr("font-size", 12) .attr("stroke", "#fff") .attr("fill", "#fff") .attr("stroke-width", "4") .attr("stroke-linejoin", "round") .text("") .on("mouseover", () => { this.hoverLabel.attr("opacity", 1); this.hoverLabelBacking.attr("opacity", 1); }) .on("mouseout", () => { this.hoverLabel.attr("opacity", 0); this.hoverLabelBacking.attr("opacity", 0); }); this.hoverLabel = this.svg.append("text") .attr("x", 10) .attr("y", 20) .attr("text-anchor", "middle") .attr("font-size", 12) .attr("fill", "#0f0") .text("") .on("mouseover", () => { this.hoverLabel.attr("opacity", 1); this.hoverLabelBacking.attr("opacity", 1); }) .on("mouseout", () => { this.hoverLabel.attr("opacity", 0); this.hoverLabelBacking.attr("opacity", 0); }); // Create our colors and color gradients // Verify custom color map let plot_colors=colors.RdBu; if (typeof this.props.plot_cmap === "string") { if (!(this.props.plot_cmap in colors)) { console.log("Invalid color map name, reverting to default."); plot_colors=colors.RdBu; } else { plot_colors = colors[this.props.plot_cmap] } } else if (Array.isArray(this.props.plot_cmap)){ plot_colors = this.props.plot_cmap } this.colors = plot_colors.map(x => hsl(x)); this.brighterColors = [1.45, 1.6].map((v, i) => this.colors[i].brighter(v)); this.colors.map((c, i) => { let grad = this.svg .append("linearGradient") .attr("id", "linear-grad-" + i) .attr("x1", "0%") .attr("y1", "0%") .attr("x2", "0%") .attr("y2", "100%"); grad .append("stop") .attr("offset", "0%") .attr("stop-color", c) .attr("stop-opacity", 0.6); grad .append("stop") .attr("offset", "100%") .attr("stop-color", c) .attr("stop-opacity", 0); let grad2 = this.svg .append("linearGradient") .attr("id", "linear-backgrad-" + i) .attr("x1", "0%") .attr("y1", "0%") .attr("x2", "0%") .attr("y2", "100%"); grad2 .append("stop") .attr("offset", "0%") .attr("stop-color", c) .attr("stop-opacity", 0.5); grad2 .append("stop") .attr("offset", "100%") .attr("stop-color", c) .attr("stop-opacity", 0); }); // create our x axis this.tickFormat = format(",.4"); this.scaleCentered = scaleLinear(); this.axis = axisBottom() .scale(this.scaleCentered) .tickSizeInner(4) .tickSizeOuter(0) .tickFormat(d => this.tickFormat(this.invLinkFunction(d))) .tickPadding(-18); // draw and then listen for resize events this.draw(); window.addEventListener("resize", this.redraw); window.setTimeout(this.redraw, 50); // re-draw after interface has updated } componentDidUpdate() { this.draw(); } draw() { // copy the feature names onto the features each(this.props.featureNames, (n, i) => { if (this.props.features[i]) this.props.features[i].name = n; }); // create our link function if (this.props.link === "identity") { this.invLinkFunction = x => this.props.baseValue + x; } else if (this.props.link === "logit") { this.invLinkFunction = x => 1 / (1 + Math.exp(-(this.props.baseValue + x))); // logistic is inverse of logit } else { console.log("ERROR: Unrecognized link function: ", this.props.link); } // Set the dimensions of the plot let width = this.svg.node().parentNode.offsetWidth; if (width == 0) return setTimeout(() => this.draw(this.props), 500); this.svg.style("height", 150 + "px"); this.svg.style("width", width + "px"); let topOffset = 50; let data = sortBy(this.props.features, x => -1 / (x.effect + 1e-10)); let totalEffect = sum(map(data, x => Math.abs(x.effect))); let totalPosEffects = sum(map(filter(data, x => x.effect > 0), x => x.effect)) || 0; let totalNegEffects = sum(map(filter(data, x => x.effect < 0), x => -x.effect)) || 0; this.domainSize = Math.max(totalPosEffects, totalNegEffects) * 3; let scale = scaleLinear() .domain([0, this.domainSize]) .range([0, width]); let scaleOffset = width / 2 - scale(totalNegEffects); this.scaleCentered .domain([-this.domainSize / 2, this.domainSize / 2]) .range([0, width]) .clamp(true); this.axisElement .attr("transform", "translate(0," + topOffset + ")") .call(this.axis); // calculate the position of the join point between positive and negative effects // and also the positions of each feature effect block let pos = 0, i, joinPoint, joinPointIndex; for (i = 0; i < data.length; ++i) { data[i].x = pos; if (data[i].effect < 0 && joinPoint === undefined) { joinPoint = pos; joinPointIndex = i; } pos += Math.abs(data[i].effect); } if (joinPoint === undefined) { joinPoint = pos; joinPointIndex = i; } let lineFunction = line() .x(d => d[0]) .y(d => d[1]); let getLabel = d => { if (d.value !== undefined && d.value !== null && d.value !== "") { return ( d.name + " = " + (isNaN(d.value) ? d.value : this.tickFormat(d.value)) ); } else return d.name; }; data = this.props.hideBars ? [] : data; let blocks = this.mainGroup.selectAll(".force-bar-blocks").data(data); blocks .enter() .append("path") .attr("class", "force-bar-blocks") .merge(blocks) .attr("d", (d, i) => { let x = scale(d.x) + scaleOffset; let w = scale(Math.abs(d.effect)); let pointShiftStart = d.effect < 0 ? -4 : 4; let pointShiftEnd = pointShiftStart; if (i === joinPointIndex) pointShiftStart = 0; if (i === joinPointIndex - 1) pointShiftEnd = 0; return lineFunction([ [x, 6 + topOffset], [x + w, 6 + topOffset], [x + w + pointShiftEnd, 14.5 + topOffset], [x + w, 23 + topOffset], [x, 23 + topOffset], [x + pointShiftStart, 14.5 + topOffset] ]); }) .attr("fill", d => (d.effect > 0 ? this.colors[0] : this.colors[1])) .on("mouseover", d => { if (scale(Math.abs(d.effect)) < scale(totalEffect) / 50 || scale(Math.abs(d.effect)) < 10) { let x = scale(d.x) + scaleOffset; let w = scale(Math.abs(d.effect)); this.hoverLabel .attr("opacity", 1) .attr("x", x + w/2) .attr("y", topOffset + 0.5) .attr("fill", d.effect > 0 ? this.colors[0] : this.colors[1]) .text(getLabel(d)); this.hoverLabelBacking .attr("opacity", 1) .attr("x", x + w/2) .attr("y", topOffset + 0.5) .text(getLabel(d)); } }) .on("mouseout", () => { this.hoverLabel.attr("opacity", 0); this.hoverLabelBacking.attr("opacity", 0); }); blocks.exit().remove(); let filteredData = filter(data, d => { return ( scale(Math.abs(d.effect)) > scale(totalEffect) / 50 && scale(Math.abs(d.effect)) > 10 ); }); let labels = this.onTopGroup .selectAll(".force-bar-labels") .data(filteredData); labels.exit().remove(); labels = labels .enter() .append("text") .attr("class", "force-bar-labels") .attr("font-size", "12px") .attr("y", 48 + topOffset) .merge(labels) .text(d => { if (d.value !== undefined && d.value !== null && d.value !== "") { return ( d.name + " = " + (isNaN(d.value) ? d.value : this.tickFormat(d.value)) ); } else return d.name; }) .attr("fill", d => (d.effect > 0 ? this.colors[0] : this.colors[1])) .attr("stroke", function(d) { d.textWidth = Math.max( this.getComputedTextLength(), scale(Math.abs(d.effect)) - 10 ); d.innerTextWidth = this.getComputedTextLength(); return "none"; }); this.filteredData = filteredData; // compute where the text labels should go if (data.length > 0) { pos = joinPoint + scale.invert(5); for (let i = joinPointIndex; i < data.length; ++i) { data[i].textx = pos; pos += scale.invert(data[i].textWidth + 10); } pos = joinPoint - scale.invert(5); for (let i = joinPointIndex - 1; i >= 0; --i) { data[i].textx = pos; pos -= scale.invert(data[i].textWidth + 10); } } labels .attr( "x", d => scale(d.textx) + scaleOffset + (d.effect > 0 ? -d.textWidth / 2 : d.textWidth / 2) ) .attr("text-anchor", "middle"); //d => d.effect > 0 ? 'end' : 'start'); // Now that we know the text widths we further filter by what fits on the screen filteredData = filter(filteredData, d => { return ( scale(d.textx) + scaleOffset > this.props.labelMargin && scale(d.textx) + scaleOffset < width - this.props.labelMargin ); }); this.filteredData2 = filteredData; // Build an array with one extra feature added let filteredDataPlusOne = filteredData.slice(); let ind = findIndex(data, filteredData[0]) - 1; if (ind >= 0) filteredDataPlusOne.unshift(data[ind]); let labelBacking = this.mainGroup .selectAll(".force-bar-labelBacking") .data(filteredData); labelBacking .enter() .append("path") .attr("class", "force-bar-labelBacking") .attr("stroke", "none") .attr("opacity", 0.2) .merge(labelBacking) .attr("d", d => { return lineFunction([ [ scale(d.x) + scale(Math.abs(d.effect)) + scaleOffset, 23 + topOffset ], [ (d.effect > 0 ? scale(d.textx) : scale(d.textx) + d.textWidth) + scaleOffset + 5, 33 + topOffset ], [ (d.effect > 0 ? scale(d.textx) : scale(d.textx) + d.textWidth) + scaleOffset + 5, 54 + topOffset ], [ (d.effect > 0 ? scale(d.textx) - d.textWidth : scale(d.textx)) + scaleOffset - 5, 54 + topOffset ], [ (d.effect > 0 ? scale(d.textx) - d.textWidth : scale(d.textx)) + scaleOffset - 5, 33 + topOffset ], [scale(d.x) + scaleOffset, 23 + topOffset] ]); }) .attr("fill", d => `url(#linear-backgrad-${d.effect > 0 ? 0 : 1})`); labelBacking.exit().remove(); let labelDividers = this.mainGroup .selectAll(".force-bar-labelDividers") .data(filteredData.slice(0, -1)); labelDividers .enter() .append("rect") .attr("class", "force-bar-labelDividers") .attr("height", "21px") .attr("width", "1px") .attr("y", 33 + topOffset) .merge(labelDividers) .attr( "x", d => (d.effect > 0 ? scale(d.textx) : scale(d.textx) + d.textWidth) + scaleOffset + 4.5 ) .attr("fill", d => `url(#linear-grad-${d.effect > 0 ? 0 : 1})`); labelDividers.exit().remove(); let labelLinks = this.mainGroup .selectAll(".force-bar-labelLinks") .data(filteredData.slice(0, -1)); labelLinks .enter() .append("line") .attr("class", "force-bar-labelLinks") .attr("y1", 23 + topOffset) .attr("y2", 33 + topOffset) .attr("stroke-opacity", 0.5) .attr("stroke-width", 1) .merge(labelLinks) .attr("x1", d => scale(d.x) + scale(Math.abs(d.effect)) + scaleOffset) .attr( "x2", d => (d.effect > 0 ? scale(d.textx) : scale(d.textx) + d.textWidth) + scaleOffset + 5 ) .attr("stroke", d => (d.effect > 0 ? this.colors[0] : this.colors[1])); labelLinks.exit().remove(); let blockDividers = this.mainGroup .selectAll(".force-bar-blockDividers") .data(data.slice(0, -1)); blockDividers .enter() .append("path") .attr("class", "force-bar-blockDividers") .attr("stroke-width", 2) .attr("fill", "none") .merge(blockDividers) .attr("d", d => { let pos = scale(d.x) + scale(Math.abs(d.effect)) + scaleOffset; return lineFunction([ [pos, 6 + topOffset], [pos + (d.effect < 0 ? -4 : 4), 14.5 + topOffset], [pos, 23 + topOffset] ]); }) .attr("stroke", (d, i) => { if (joinPointIndex === i + 1 || Math.abs(d.effect) < 1e-8) return "#rgba(0,0,0,0)"; else if (d.effect > 0) return this.brighterColors[0]; else return this.brighterColors[1]; }); blockDividers.exit().remove(); this.joinPointLine .attr("x1", scale(joinPoint) + scaleOffset) .attr("x2", scale(joinPoint) + scaleOffset) .attr("y1", 0 + topOffset) .attr("y2", 6 + topOffset) .attr("stroke", "#F2F2F2") .attr("stroke-width", 1) .attr("opacity", 1); this.joinPointLabelOutline .attr("x", scale(joinPoint) + scaleOffset) .attr("y", -5 + topOffset) .attr("color", "#fff") .attr("text-anchor", "middle") .attr("font-weight", "bold") .attr("stroke", "#fff") .attr("stroke-width", 6) .text(format(",.2f")(this.invLinkFunction(joinPoint - totalNegEffects))) .attr("opacity", 1); console.log( "joinPoint", joinPoint, scaleOffset, topOffset, totalNegEffects ); this.joinPointLabel .attr("x", scale(joinPoint) + scaleOffset) .attr("y", -5 + topOffset) .attr("text-anchor", "middle") .attr("font-weight", "bold") .attr("fill", "#000") .text(format(",.2f")(this.invLinkFunction(joinPoint - totalNegEffects))) .attr("opacity", 1); this.joinPointTitle .attr("x", scale(joinPoint) + scaleOffset) .attr("y", -22 + topOffset) .attr("text-anchor", "middle") .attr("font-size", "12") .attr("fill", "#000") .text(this.props.outNames[0]) .attr("opacity", 0.5); if (!this.props.hideBars) { this.joinPointTitleLeft .attr("x", scale(joinPoint) + scaleOffset - 16) .attr("y", -38 + topOffset) .attr("text-anchor", "end") .attr("font-size", "13") .attr("fill", this.colors[0]) .text("higher") .attr("opacity", 1.0); this.joinPointTitleRight .attr("x", scale(joinPoint) + scaleOffset + 16) .attr("y", -38 + topOffset) .attr("text-anchor", "start") .attr("font-size", "13") .attr("fill", this.colors[1]) .text("lower") .attr("opacity", 1.0); this.joinPointTitleLeftArrow .attr("x", scale(joinPoint) + scaleOffset + 7) .attr("y", -42 + topOffset) .attr("text-anchor", "end") .attr("font-size", "13") .attr("fill", this.colors[0]) .text("→") .attr("opacity", 1.0); this.joinPointTitleRightArrow .attr("x", scale(joinPoint) + scaleOffset - 7) .attr("y", -36 + topOffset) .attr("text-anchor", "start") .attr("font-size", "13") .attr("fill", this.colors[1]) .text("←") .attr("opacity", 1.0); } if (!this.props.hideBaseValueLabel) { this.baseValueTitle .attr("x", this.scaleCentered(0)) .attr("y", -22 + topOffset) .attr("text-anchor", "middle") .attr("font-size", "12") .attr("fill", "#000") .text("base value") .attr("opacity", 0.5); } } componentWillUnmount() { window.removeEventListener("resize", this.redraw); } render() { const ref = x => this.svg = select(x); const style = { userSelect: "none", display: "block", fontFamily: "arial", sansSerif: true}; const style2 = { __html: ` .force-bar-axis path { fill: none; opacity: 0.4; } .force-bar-axis paths { display: none; } .tick line { stroke: #000; stroke-width: 1px; opacity: 0.4; } .tick text { fill: #000; opacity: 0.5; font-size: 12px; padding: 0px; }` }; const className = 'additiveForceVizSVG' + this.props.number; return (React.createElement('svg', {className: className, ref: ref, style: style}, React.createElement('style', {dangerouslySetInnerHTML: style2}))); } } const exampleData = { outNames: ["Probability of flower"], baseValue: 0.2, link: "identity", features: [ { name: "F1", effect: 0.6, value: 1 }, { name: "F2", effect: -0.6, value: 1 }, { name: "F3", effect: -0.2, value: 2 }, { name: "F4", effect: 0.2, value: 0 } ] // range(20).map(i => ({ // name: 'value_'+i, // effect: random()-0.5 // })) }; console.log(data) if (this._representation.appendImagesToTable && (this._value.forcePlotsSVG[0] == null || this._value.forcePlotsSVG[0] == "")){ var i = 0; for (var rowIndex in data){ let row = data[rowIndex]; console.log("Element " + i); console.log(row); console.time(); var el = React.createElement(AdditiveForceVisualizer, this.drawSvgRow(row, rowIndex), null); ReactDOM.render(el, document.getElementById('additiveForceViz'+i)); //this._svgs[rowIndex] = this.getSVG(i); this._value.forcePlotsSVG[rowIndex] = this.getSVG(i); console.timeEnd(); ++i; } } /*const exampleDataJSON = JSON.stringify(exampleData);*/ if(!this._representation.appendImagesToTable){ const element = React.createElement(AdditiveForceVisualizer, svgRow, null); ReactDOM.render(element, document.getElementById('additiveForceViz' + paginationData.pageRowStartIndex)); } this.registerEvents(); } return new ForcePlot(); })();