shahinrostami / plotapi

Engaging visualisations, made easy.
https://plotapi.com
MIT License
268 stars 17 forks source link

Chart not rendering in Jupyter Lab #17

Closed jcherry-risklens closed 4 years ago

jcherry-risklens commented 4 years ago

I'm trying to render a chord plot in Jupyter Lab, but am getting an empty space where the output should be:

jupyter

chrome_console

Currently running chord v0.3.0, and jupyterlab v2.1.1. Browser is Chrome.

shahinrostami commented 4 years ago

Hi @jcherry-risklens - can you please share the Python code that generates this error so I can look at it for you?

jcherry-risklens commented 4 years ago

Is that not what the images are showing? The error occurs after line 76.

shahinrostami commented 4 years ago

Hi @jcherry-risklens - the screenshots are showing the invocation of Chord followed by the error, I need to see what transition_matrix is to help you with why! Most of it is missing in out cell [75].

You could also call .to_html() and attach the .html file.

jcherry-risklens commented 4 years ago

The content of transition_matrix is showing on the first image, line 75.

Here's the .html file:

<!DOCTYPE html>
<html>
  <head>
    <!--Chord - Python wrapper around d3-chord

    This package enables the generation of Chord diagrams. They can be saved 
    directly to HTML files or displayed in a Jupyter Notebook output cell.

    Copyright 2020, Dr. Shahin Rostami
    http://shahinrostami.com
    https://github.com/shahinrostami/chord
    https://pypi.org/project/chord/
    -->
    <!--LICENSE
    Chord (https://github.com/shahinrostami/chord) generates interactive chord diagrams.
    Copyright (C) 2020  Dr. Shahin Rostami

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
    -->
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
    <title>Chord Diagram</title>
    <!-- Google Fonts -->
    <script src="https://d3js.org/d3.v5.min.js"></script>
    <link
      href="https://fonts.googleapis.com/css?family=Lato:400,900"
      rel="stylesheet"
      type="text/css"
    />

    <style>
      .tippy-content {
        font-family: "Lato", sans-serif;
      }

      #chart-d27af929, #featured-chart-d27af929 {

        font-size: 16px;
        font-family: "Lato", sans-serif;
        text-align: center;
        fill: #454545;
      }

      #chart-d27af929 svg, #featured-chart-d27af929 svg {
        max-width: 700px;
      }

      @media (min-width: 600px) {
                #chart-d27af929{
                    font-size: 20px;
                }
            }
    </style>
  </head>
  <body>
    <div id="chart-d27af929" class="chord">
    </div>
    <script src="https://unpkg.com/@popperjs/core@2"></script>
    <script src="https://unpkg.com/tippy.js@6"></script>
    <script>
      var script = document.createElement("script");
      script.type = "text/javascript";
      script.src = "https://d3js.org/d3.v5.min.js";

      script.onload = function () {

        var script2 = document.createElement("script");
        script2.type = "text/javascript";
        script2.src = "https://datacrayon.com/assets/chord/script.js";
        script2.onload = function () {
          margin = {
          left: 0,
          top: 0,
          right: 0,
          bottom: 0
        };
        width = Math.min(window.innerWidth, 700) - margin.left - margin.right;
        height = Math.min(window.innerWidth, 700) - margin.top - margin.bottom;
        innerRadius = Math.min(width, height) * 0.39;
        outerRadius = innerRadius * 1.1;

      tag_id = "chart-d27af929";
      padding = 0.01;
      Names = Index(['1/1', '13/1', '14/17', '14/18', '14/19', '14/20', '14/21', '14/23',
       '14/24', '14/25', '14/26', '14/27', '14/28', '14/29', '14/30', '14/31',
       '14/32', '14/34', '14/35', '14/36', '14/41', '15/1', '16/1', '17/12',
       '17/42', '17/45', '17/5', '18/12', '18/5', '19/12', '19/5', '2/1',
       '4/12', '4/14', '4/2', '4/3', '4/38', '4/42', '4/5', '4/7', '4/8',
       '4/9', '5/12', '5/13', '5/14', '5/2', '5/3', '5/37', '5/38', '5/4',
       '5/40', '5/42', '5/5', '5/6', '5/7', '5/8', '5/9', '6/12', '6/14',
       '6/3', '6/33', '6/38', '6/42', '6/43', '6/44', '6/5', '6/7', '6/8',
       '7/12', '7/14', '7/2', '7/3', '7/38', '7/42', '7/5', '7/7', '7/8',
       '7/9'],
      dtype='object', name='ClientActionObjectID');
      colors = d3.schemeSet1;
      opacityDefault = 0.8;
      matrix = [[1.45774427e-01 6.07393444e-03 2.09446015e-04 ... 0.00000000e+00
  1.04723008e-04 0.00000000e+00]
 [4.95121951e-01 3.25609756e-01 0.00000000e+00 ... 0.00000000e+00
  0.00000000e+00 0.00000000e+00]
 [0.00000000e+00 0.00000000e+00 1.25000000e-01 ... 0.00000000e+00
  0.00000000e+00 0.00000000e+00]
 ...
 [2.70270270e-02 0.00000000e+00 0.00000000e+00 ... 0.00000000e+00
  0.00000000e+00 0.00000000e+00]
 [1.75438596e-02 0.00000000e+00 0.00000000e+00 ... 0.00000000e+00
  3.50877193e-02 0.00000000e+00]
 [1.07526882e-02 0.00000000e+00 0.00000000e+00 ... 0.00000000e+00
  0.00000000e+00 0.00000000e+00]];
      wrap_labels = true;
      credit = false

      ////////////////////////////////////////////////////////////
      /////////// Create scale and layout functions //////////////
      ////////////////////////////////////////////////////////////

      var colors = d3
        .scaleOrdinal()
        .domain(d3.range(Names.length))
        .range(colors);

      //A "custom" d3 chord function that automatically sorts the order of the chords in such a manner to reduce overlap
      var chord = customChordLayout()
        .padding(padding)
        .sortChords(d3.descending) //which chord should be shown on top when chords cross. Now the biggest chord is at the bottom
        .matrix(matrix);

      var arc = d3
        .arc()
        .innerRadius(innerRadius * 1.01)
        .outerRadius(outerRadius);

      var path = d3.ribbon().radius(innerRadius);

      ////////////////////////////////////////////////////////////
      ////////////////////// Create SVG //////////////////////////
      ///////////////////////////////////////////////////////////

      var svg = d3
        .select("#" + tag_id)
        .append("svg")
        .attr(
          "viewBox",
          "0 0 " +
            (width + margin.left + margin.right) +
          " " +
          (height + margin.top + margin.bottom)
        )
        .attr("preserveAspectRatio", "xMinYMin meet")
        .append("g")
        .attr(
          "transform",
          "translate(" +
            (width / 2 + margin.left) +
            "," +
            (height / 2 + margin.top) +
            ")"
        );

        d3
          .select("#" + tag_id)
          .append("span")
          .style("display", "block")
          .style("font-size", "12px")
          .style("text-align", "right")
          .style("font-family", '"Arial", sans-serif')
          .html('get <a href="https://m8.fyi/chord">chord pro</a> [<a href="https://twitter.com/shahinrostami">@ShahinRostami</a>]');

        d3
          .select("#" + tag_id)
          .select("span")
          .append("span")
          .style("font-size", "12px")
          .style("font-family", '"Arial", sans-serif')
          .html('<br>see more at <a href="https://stamilabs.com">StamiLabs.com</a>');

      ////////////////////////////////////////////////////////////
      /////////////// Create the gradient fills //////////////////
      ////////////////////////////////////////////////////////////

      //Function to create the id for each chord gradient
      function getGradID(d) {
        return (
          "linkGrad-" + tag_id + "-" + d.source.index + "-" + d.target.index
        );
      }

      //Create the gradients definitions for each chord
      var grads = svg
        .append("defs")
        .selectAll("linearGradient")
        .data(chord.chords())
        .enter()
        .append("linearGradient")
        .attr("id", getGradID)
        .attr("gradientUnits", "userSpaceOnUse")
        .attr("x1", function (d, i) {
          return (
            innerRadius *
            Math.cos(
              (d.source.endAngle - d.source.startAngle) / 2 +
                d.source.startAngle -
                Math.PI / 2
            )
          );
        })
        .attr("y1", function (d, i) {
          return (
            innerRadius *
            Math.sin(
              (d.source.endAngle - d.source.startAngle) / 2 +
                d.source.startAngle -
                Math.PI / 2
            )
          );
        })
        .attr("x2", function (d, i) {
          return (
            innerRadius *
            Math.cos(
              (d.target.endAngle - d.target.startAngle) / 2 +
                d.target.startAngle -
                Math.PI / 2
            )
          );
        })
        .attr("y2", function (d, i) {
          return (
            innerRadius *
            Math.sin(
              (d.target.endAngle - d.target.startAngle) / 2 +
                d.target.startAngle -
                Math.PI / 2
            )
          );
        });

      //Set the starting color (at 0%)
      grads
        .append("stop")
        .attr("offset", "0%")
        .attr("stop-color", function (d) {
          return colors(d.source.index);
        });

      //Set the ending color (at 100%)
      grads
        .append("stop")
        .attr("offset", "100%")
        .attr("stop-color", function (d) {
          return colors(d.target.index);
        });

      ////////////////////////////////////////////////////////////
      ////////////////// Draw outer Arcs /////////////////////////
      ////////////////////////////////////////////////////////////

      var outerArcs = svg
        .selectAll("g.group")
        .data(chord.groups)
        .enter()
        .append("g")
        .attr("class", "group")
        .on("mouseover", fade(0.1, 1))
        .on("mouseout", fade(opacityDefault, opacityDefault));

      outerArcs
        .append("path")
        .style("fill", function (d) {
          return colors(d.index);
        })
        .attr("d", arc)
        .each(function (d, i) {
          //Search pattern for everything between the start and the first capital L
          var firstArcSection = /(^.+?)L/;

          //Grab everything up to the first Line statement
          var newArc = firstArcSection.exec(d3.select(this).attr("d"))[1];
          //Replace all the comma's so that IE can handle it
          newArc = newArc.replace(/,/g, " ");

          //If the end angle lies beyond a quarter of a circle (90 degrees or pi/2)
          //flip the end and start position
          if (
            (d.endAngle > (90 * Math.PI) / 180) &
            (d.startAngle < (270 * Math.PI) / 180)
          ) {
            var startLoc = /M(.*?)A/, //Everything between the first capital M and first capital A
              middleLoc = /A(.*?)0 0 1/, //Everything between the first capital A and 0 0 1
              endLoc = /0 0 1 (.*?)$/; //Everything between the first 0 0 1 and the end of the string (denoted by $)
            //Flip the direction of the arc by switching the start en end point (and sweep flag)
            //of those elements that are below the horizontal line
            var newStart = endLoc.exec(newArc)[1];
            var newEnd = startLoc.exec(newArc)[1];
            var middleSec = middleLoc.exec(newArc)[1];

            //Build up the new arc notation, set the sweep-flag to 0
            newArc = "M" + newStart + "A" + middleSec + "0 0 0 " + newEnd;
          } //if

          //Create a new invisible arc that the text can flow along
          svg
            .append("path")
            .attr("class", "hiddenArcs")
            .attr("id", "arc-" + tag_id + "-" + i)
            .attr("d", newArc)
            .style("fill", "none");
     });
          ////////////////////////////////////////////////////////////
          ////////////////// Append Names ////////////////////////////
          ////////////////////////////////////////////////////////////

          //Append the label names on the outside

          if (wrap_labels) {
            outerArcs
              .append("text")
              .attr("class", "titles")
              .attr("dy", function (d, i) {
                return (d.endAngle > (90 * Math.PI) / 180) &
                  (d.startAngle < (270 * Math.PI) / 180)
                  ? 25
                  : -16;
              })
              .append("textPath")
              .attr("startOffset", "50%")
              .style("text-anchor", "middle")
              .attr("xlink:href", function (d, i) {
                return "#arc-" + tag_id + "-" + i;
              })
              .text(function (d, i) {
                return Names[i];
              });
          } else {
            //Append the label names on the outside
            outerArcs
              .append("text")
              .each(function (d) {
                d.angle = (d.startAngle + d.endAngle) / 2;
              })
              .attr("dy", ".35em")
              .attr("class", "titles")
              .attr("text-anchor", function (d) {
                return d.angle > Math.PI ? "end" : null;
              })
              .attr("transform", function (d) {
                return (
                  "rotate(" +
                  ((d.angle * 180) / Math.PI - 90) +
                  ")" +
                  "translate(" +
                  (outerRadius + 10) +
                  ")" +
                  (d.angle > Math.PI ? "rotate(180)" : "")
                );
              })
              .text(function (d, i) {
                return Names[i];
              });
          }

          ////////////////////////////////////////////////////////////
          ////////////////// Draw inner chords ///////////////////////
          ////////////////////////////////////////////////////////////

          svg
            .selectAll("path.chord")
            .data(chord.chords)
            .enter()
            .append("path")
            .attr("class", "chord")
            .style("fill", function (d) {
              return "url(#" + getGradID(d) + ")";
            })
            .style("opacity", opacityDefault)
            .attr("d", path)
            .on("mouseover", mouseoverChord())
            .on("mouseout", mouseoutChord(opacityDefault, opacityDefault));

      ////////////////////////////////////////////////////////////
      ////////////////// Extra Functions /////////////////////////
      ////////////////////////////////////////////////////////////

      //Returns an event handler for fading a given chord group.
      function fade(opacityIn, opacityOut) {
        return function (d, i) {
          d3.select(this.ownerSVGElement)
            .selectAll("path.chord")
            .filter(function (d) {
              return d.source.index !== i && d.target.index !== i;
            })
            .transition()
            .style("opacity", opacityIn);

          d3.select(this.ownerSVGElement)
            .selectAll("path.chord")
            .filter(function (d) {
              return d.source.index == i || d.target.index == i;
            })
            .transition()
            .style("opacity", opacityOut);

        };
      } //fade

      //Highlight hovered over chord
      function mouseoverChord() {
        return function (d, i) {

        d3.select(this.ownerSVGElement)
          .selectAll("path.chord")
          .transition()
          .style("opacity", 0.1);
        //Show hovered over chord with full opacity
        d3.select(this).transition().style("opacity", 1);

        tippy_content = "<span style='font-weight:900'>" +
            Names[d.source.index] +
            "</span> and <span style='font-weight:900'>" +
            Names[d.target.index] +
            "</span><br>occur together in <span style='font-weight:900'>" +
            d.source.value +
            "</span> instances";

        if(this._tippy == null)
        {
          tippy(this, {
            allowHTML: true,
            followCursor: true,
            content: tippy_content,
            size: "large",
            arrow: true,
          });
        }

        };

      } //fade

      //Bring all chords back to default opacity
      function mouseoutChord(opacityIn, opacityOut) {
        return function (d, i) {
        d3.select(this.ownerSVGElement)
          .selectAll("path.chord")
          .transition()
          .style("opacity", opacityOut);
        };
        //Set opacity back to default for all
      } //function mouseoutChord

        };
        document.body.appendChild(script2);
      };

      document.body.appendChild(script);
    </script>
    <script></script>
  </body>
</html>

The matrix variable is concerning... it's truncating it for some reason...

jcherry-risklens commented 4 years ago

Well I'll be darned... this works perfectly: Chord(transition_matrix.values.tolist(), transition_matrix.index.values.tolist()).show().

shahinrostami commented 4 years ago

and there we have the culprit!

Try passing in .values.tolist()...

shahinrostami commented 4 years ago

Well I'll be darned... this works perfectly: Chord(transition_matrix.values.tolist(), transition_matrix.index.values.tolist()).show().

Seems you updated your reply as I was posting mine - I'm glad it's working for you now 👍! If you're interested, Chord Pro has many more features https://datacrayon.com/shop/product/chord-pro/ 💯

jcherry-risklens commented 4 years ago

Thanks, seems like such a simple little thing. I've been spoiled by using pandas and numpy!