<commit_before>d3.scatterplot = d3.scatterplot || {};

/**
 * Scatter plot matrix for d3.js
 *
 * Usage:
 * var matrix = d3.scatterplot.matrix();
 * d3.select('#matrix')
 *   .datum(data)
 *   .call(matrix);
 */

d3.scatterplot.matrix = function(options) {

  function matrix(selection, yLabel) {
    selection.each(function(data) {
      // First convert all values from strings to numbers...
      // ... and keep track of which columns to skip
      var toSkip = [];
      var keys = d3.keys(data[0]);
      data.forEach(function(d) {
        keys.forEach(function(key) {
          d[key] = +d[key]; // TODO: What about empty cells? (+"" ==> 0)
          if (isNaN(d[key]) && toSkip.indexOf(key) < 0) {
            toSkip.push(key);
          }
        });
      });

      var domainByKey = {};
      var labels = [];
      keys.forEach(function(key) {
        if (toSkip.indexOf(key) < 0) {
          labels.push(key);
          // ... and then find the min and max of each column
          domainByKey[key] = d3.extent(data, function(d) { return d[key]; });
        }
      });
    
      // Try to make matrix square, if possible.
      var numCells = labels.length - 1;
      var numRows = d3.round(Math.sqrt(numCells));
      var numCols = Math.ceil(numCells / numRows);

      // For plots smaller than 'minSize', don't draw axis or labels.
      var minSize = 200;
      
      // Use the number of cells to determine the initial size and spacing
      var size = Math.floor(1500 / numCols);
      var padding = size >= minSize ? 19.5 : 5;
      var spaceBetweenCells = size >= minSize ? 50 : 10;

      var xScale = d3.scale.linear()
        .range([padding / 2, size - padding / 2]);

      var yScale = d3.scale.linear()
        .range([size - padding / 2, padding / 2]);

      var xAxis = d3.svg.axis()
        .scale(xScale)
        .orient("bottom")
        .ticks(5);
      xAxis.tickSize(size);

      var yAxis = d3.svg.axis()
        .scale(yScale)
        .orient("left")
        .ticks(5);
      yAxis.tickSize(-size);

      // ======================================================================
      // Add 'Filter' button
      // TODO: Disable button when nothing is selected
      var buttons = selection.append("div");
      buttons.append("button") // TODO: Add a margin
        .text("Filter")
        .attr("class", "filterButton")
        .on("click", function() { return filter(); });

      var svg = selection.append("svg");

      // Find the maximum y-tickmark label size - will be the same for all cells
      yScale.domain(domainByKey[yLabel]);
      var yLabelWidth = 0;
      svg.selectAll("text.temp")        // Just a placeholder
        .data(yScale.ticks(5))
        .enter().append("text")
        .text(function(d) { return yScale.tickFormat()(d); })
        .each(function(d) {
          yLabelWidth = Math.max(this.getBBox().width , yLabelWidth);
        })
        .remove();
        
      // Set the size of the SVG.
      // TODO: Could this ever be too small when plots are re-sized?
      var width = (size + yLabelWidth + spaceBetweenCells) * numCells + (yLabelWidth + spaceBetweenCells);
      var height = (size + yLabelWidth + spaceBetweenCells) * numCells;
      svg.attr("width", width)
        .attr("height", height);

      // Create the plots
      var cellData = getCellData(labels);
      var cells = svg.selectAll(".cell")
        .data(cellData)
        .enter().append("g")
        .attr("class", "cell")
        .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
        .each(plot);

      // ======================================================================

      function plot(p) {
        var cell = d3.select(this)
          .on('click', function() {
            p.selected = !p.selected;
            d3.event.stopPropagation();  // the svg click listener will not fire
            var rect = cell.select("rect")
              .style("stroke", function(d) { return p.selected ? "#FF9500" : "#aaa"; }) // #FF9500 --> "Firefox light orange"
              .style("fill", function(d) { return p.selected ? "#FF9500" : "white"; })
              .style("fill-opacity", function(d) { return p.selected ? 0.25 : 1.0; })
        });

        xScale.domain(domainByKey[p.xLabel]);
        yScale.domain(domainByKey[yLabel]);

        var rect = cell.append("rect")
          .attr("x", padding / 2)
          .attr("y", padding / 2)
          .attr("width", size - padding)
          .attr("height", size - padding)
          .style("fill", function(d) { return "white"; })
          .style("stroke", function(d) { return "#aaa"; }) // gray;
          
        cell.append("title")  // Tooltip
          .text(p.xLabel);

        var xGrid = cell.append("g")
          .attr("class", "x axis");
          
        var yGrid = cell.append("g")
          .attr("class", "y axis");

        if (size >= minSize) {
          xGrid.attr("x", padding / 2)
            .attr("y", padding / 2)
            .attr("width", size - padding)
            .attr("height", size - padding)
            .call(xAxis);

          yGrid.attr("x", padding / 2)
            .attr("y", padding / 2)
            .attr("width", size - padding)
            .attr("height", size - padding)
            .call(yAxis);
        }

        cell.selectAll("circle")
          .data(data)
          .enter().append("circle")
          .attr("cx", function(d) { return xScale(d[p.xLabel]); })
          .attr("cy", function(d) { return yScale(d[yLabel]); })
          .attr("r", size >= minSize ? 3 : 1)
          .style("fill", function(d) { return "#4682B4"; }) // steelblue
          .style("fill-opacity", function(d) { return 0.5; });
      
        // x- and y- axis labels
        var xLabelText = cell.append("text")
          .attr("class", "x label");

        var yLabelText = cell.append("text")
          .attr("class", "y label");  
        
        if (size >= minSize) {
          addXAxisLabel(xLabelText, p.xLabel);
          addYAxisLabel(yLabelText, yLabel);
        }
      }

      // ======================================================================
      function filter()
      {
        var selectedCells = [];
        cellData.forEach(function (d) {
          if (d.selected) {
            selectedCells.push(d.xLabel);
          }
        });

        numCells = selectedCells.length;
        numRows = d3.round(Math.sqrt(numCells));
        numCols = Math.ceil(numCells / numRows);

        // Use the number of cells to determine the size and spacing
        size = Math.floor(1500 /numCols)
        padding = size >= minSize ? 19.5 : 5,
        spaceBetweenCells = size >= minSize ? 50 : 10;
        
        xScale = d3.scale.linear()
          .range([padding / 2, size - padding / 2]);

        yScale = d3.scale.linear()
          .range([size - padding / 2, padding / 2]);

        xAxis = d3.svg.axis()
          .scale(xScale)
          .orient("bottom")
          .ticks(5);
        xAxis.tickSize(size);

        yAxis = d3.svg.axis()
          .scale(yScale)
          .orient("left")
          .ticks(5);
        yAxis.tickSize(-size);

        cellData = getCellData(selectedCells);

        var cells = svg.selectAll(".cell")
          .data(cellData)
          .attr("class", "cell")
          .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
        cells.exit()
          .transition()
          .duration(500)      // TODO: Better transition!?
          .remove();

          cells.each(updatePlot);
      }

      // ======================================================================
      function updatePlot(p) {
        var cell = d3.select(this)
          .on('click', function(){
            p.selected = !p.selected;
            d3.event.stopPropagation();  // the svg click listener will not fire
            var rect = cell.select("rect")
              .style("stroke", function(d) { return p.selected ? "#FF9500" : "#aaa"; }) // #FF9500 --> "Firefox light orange"
              .style("fill", function(d) { return p.selected ? "#FF9500" : "white"; })  
              .style("fill-opacity", function(d) { return p.selected ? 0.25 : 1.0; }) 
          });

        xScale.domain(domainByKey[p.xLabel]);
        yScale.domain(domainByKey[yLabel]);

        var rect = cell.selectAll("rect")
          .attr("x", padding / 2)
          .attr("y", padding / 2)
          .attr("width", size - padding)
          .attr("height", size - padding)
          .style("fill", function(d) { return "white"; })
          .style("stroke", function(d) { return "#aaa"; }) // gray;

        cell.selectAll("title")  // Tooltip
          .text(p.xLabel);

        cell.selectAll("circle")
          .data(data)
          .transition()
          .duration(500)
          .attr("cx", function(d) { return xScale(d[p.xLabel]); })
          .attr("cy", function(d) { return yScale(d[yLabel]); })
          .attr("r", size >= minSize ? 3 : 1)
          .style("fill", function(d) { return "#4682B4"; }) // steelblue
          .style("fill-opacity", function(d) { return 0.5; });

        if (size >= minSize) {
          cell.select(".x.axis")
            .transition()
            .duration(500)
            .attr("class", "x axis")
            .attr("x", padding / 2)
            .attr("y", padding / 2)
            .attr("width", size - padding)
            .attr("height", size - padding)
            .call(xAxis);

          cell.select(".y.axis")
            .attr("class", "y axis")
            .attr("x", padding / 2)
            .attr("y", padding / 2)
            .attr("width", size - padding)
            .attr("height", size - padding)
            .transition()
            .duration(500)
            .call(yAxis);

          // Update x- and y- axis labels
          addXAxisLabel(cell.select(".x.label"), p.xLabel);
          addYAxisLabel(cell.select(".y.label"), yLabel);
        }
      }

      // ======================================================================

      function addXAxisLabel(xLabel, xLabelText) {
        xLabel.attr("text-anchor", "middle")<commit_msg>Updates to ScatterPlot Matrix. 'Nan' entries are now ignored.  There was code for this previously, but it didn't work correctly. Hovering over points (after axes are displayed) will show point value.<commit_after>d3.scatterplot = d3.scatterplot || {};

/**
 * Scatter plot matrix for d3.js
 *
 * Usage:
 * var matrix = d3.scatterplot.matrix();
 * d3.select('#matrix')
 *   .datum(data)
 *   .call(matrix);
 */

d3.scatterplot.matrix = function(options) {

  function matrix(selection, yLabel) {
    selection.each(function(data) {
      // First convert all values from strings to numbers...
      var keys = d3.keys(data[0]);
      data.forEach(function(d) {
        keys.forEach(function(key) {
          if (d[key] == "") { d[key] = NaN; }
          else { d[key] = +d[key]; } // Note: Could also be NaN
        });
      });

      var domainByKey = {};
      var labels = [];
      keys.forEach(function(key) {
        labels.push(key);
        // ... and then find the min and max of each column
        domainByKey[key] = d3.extent(data, function(d) { return d[key]; });
      });
    
      // Try to make matrix square, if possible.
      var numCells = labels.length - 1;
      var numRows = d3.round(Math.sqrt(numCells));
      var numCols = Math.ceil(numCells / numRows);

      // For plots smaller than 'minSize', don't draw axis or labels.
      var minSize = 200;
      
      // Use the number of cells to determine the initial size and spacing
      var size = Math.floor(1500 / numCols);
      var padding = size >= minSize ? 19.5 : 5;
      var spaceBetweenCells = size >= minSize ? 50 : 10;

      var xScale = d3.scale.linear()
        .range([padding / 2, size - padding / 2]);

      var yScale = d3.scale.linear()
        .range([size - padding / 2, padding / 2]);

      var xAxis = d3.svg.axis()
        .scale(xScale)
        .orient("bottom")
        .ticks(5);
      xAxis.tickSize(size);

      var yAxis = d3.svg.axis()
        .scale(yScale)
        .orient("left")
        .ticks(5);
      yAxis.tickSize(-size);

      // ======================================================================
      // Add 'Filter' button
      // TODO: Disable button when nothing is selected
      var buttons = selection.append("div");
      buttons.append("button") // TODO: Add a margin
        .text("Filter")
        .attr("class", "filterButton")
        .on("click", function() { return filter(); });

      var svg = selection.append("svg");

      // Find the maximum y-tickmark label size - will be the same for all cells
      yScale.domain(domainByKey[yLabel]);
      var yLabelWidth = 0;
      svg.selectAll("text.temp")        // Just a placeholder
        .data(yScale.ticks(5))
        .enter().append("text")
        .text(function(d) { return yScale.tickFormat()(d); })
        .each(function(d) {
          yLabelWidth = Math.max(this.getBBox().width , yLabelWidth);
        })
        .remove();
        
      // Set the size of the SVG.
      // TODO: Could this ever be too small when plots are re-sized?
      var width = (size + yLabelWidth + spaceBetweenCells) * numCells + (yLabelWidth + spaceBetweenCells);
      var height = (size + yLabelWidth + spaceBetweenCells) * numCells;
      svg.attr("width", width)
        .attr("height", height);

      // Create the plots
      var cellData = getCellData(labels);
      var cells = svg.selectAll(".cell")
        .data(cellData)
        .enter().append("g")
        .attr("class", "cell")
        .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
        .each(plot);

      // ======================================================================

      function plot(p) {
        var cell = d3.select(this)
          .on('click', function() {
            p.selected = !p.selected;
            d3.event.stopPropagation();  // the svg click listener will not fire
            var rect = cell.select("rect")
              .style("stroke", function(d) { return p.selected ? "#FF9500" : "#aaa"; }) // #FF9500 --> "Firefox light orange"
              .style("fill", function(d) { return p.selected ? "#FF9500" : "white"; })
              .style("fill-opacity", function(d) { return p.selected ? 0.25 : 1.0; })
        });

        xScale.domain(domainByKey[p.xLabel]);
        yScale.domain(domainByKey[yLabel]);

        var rect = cell.append("rect")
          .attr("x", padding / 2)
          .attr("y", padding / 2)
          .attr("width", size - padding)
          .attr("height", size - padding)
          .style("fill", function(d) { return "white"; })
          .style("stroke", function(d) { return "#aaa"; }) // gray;

        var xGrid = cell.append("g")
          .attr("class", "x axis");
          
        var yGrid = cell.append("g")
          .attr("class", "y axis");

        if (size >= minSize) {
          xGrid.attr("x", padding / 2)
            .attr("y", padding / 2)
            .attr("width", size - padding)
            .attr("height", size - padding)
            .call(xAxis);

          yGrid.attr("x", padding / 2)
            .attr("y", padding / 2)
            .attr("width", size - padding)
            .attr("height", size - padding)
            .call(yAxis);
        }

        var circles = cell.selectAll("circle")
          .data(data.filter(function(d) {
            return !isNaN(d[yLabel]) && !isNaN(d[p.xLabel]);
          }))
          .enter().append("circle")
          .attr("cx", function(d) { return xScale(d[p.xLabel]); })
          .attr("cy", function(d) { return yScale(d[yLabel]); })
          .attr("r", size >= minSize ? 3 : 1)
          .style("fill", function(d) { return "#4682B4"; }) // steelblue
          .style("fill-opacity", function(d) { return 0.5; });

        // x- and y- axis labels
        var xLabelText = cell.append("text")
          .attr("class", "x label");

        var yLabelText = cell.append("text")
          .attr("class", "y label");  
        
        if (size >= minSize) {
          addXAxisLabel(xLabelText, p.xLabel);
          addYAxisLabel(yLabelText, yLabel);

          circles.append("title")  // Tooltip
            .text(function(d) {
              return "(" + d[p.xLabel] + "," + d[yLabel] + ")";
            });
        }
        else {
          cell.append("title")  // Tooltip
            .text(p.xLabel);
        }
      }

      // ======================================================================
      function filter()
      {
        var selectedCells = [];
        cellData.forEach(function (d) {
          if (d.selected) {
            selectedCells.push(d.xLabel);
          }
        });

        numCells = selectedCells.length;
        numRows = d3.round(Math.sqrt(numCells));
        numCols = Math.ceil(numCells / numRows);

        // Use the number of cells to determine the size and spacing
        size = Math.floor(1500 /numCols)
        padding = size >= minSize ? 19.5 : 5,
        spaceBetweenCells = size >= minSize ? 50 : 10;
        
        xScale = d3.scale.linear()
          .range([padding / 2, size - padding / 2]);

        yScale = d3.scale.linear()
          .range([size - padding / 2, padding / 2]);

        xAxis = d3.svg.axis()
          .scale(xScale)
          .orient("bottom")
          .ticks(5);
        xAxis.tickSize(size);

        yAxis = d3.svg.axis()
          .scale(yScale)
          .orient("left")
          .ticks(5);
        yAxis.tickSize(-size);

        cellData = getCellData(selectedCells);

        var cells = svg.selectAll(".cell")
          .data(cellData)
          .attr("class", "cell")
          .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
        cells.exit()
          .transition()
          .duration(500)      // TODO: Better transition!?
          .remove();

          cells.each(updatePlot);
      }

      // ======================================================================
      function updatePlot(p) {
        var cell = d3.select(this)
          .on('click', function(){
            p.selected = !p.selected;
            d3.event.stopPropagation();  // the svg click listener will not fire
            var rect = cell.select("rect")
              .style("stroke", function(d) { return p.selected ? "#FF9500" : "#aaa"; }) // #FF9500 --> "Firefox light orange"
              .style("fill", function(d) { return p.selected ? "#FF9500" : "white"; })  
              .style("fill-opacity", function(d) { return p.selected ? 0.25 : 1.0; }) 
          });

        xScale.domain(domainByKey[p.xLabel]);
        yScale.domain(domainByKey[yLabel]);

        var rect = cell.selectAll("rect")
          .attr("x", padding / 2)
          .attr("y", padding / 2)
          .attr("width", size - padding)
          .attr("height", size - padding)
          .style("fill", function(d) { return "white"; })
          .style("stroke", function(d) { return "#aaa"; }); // gray;

        var circles = cell.selectAll("circle")
          .data(data.filter(function(d) {
            return !isNaN(d[yLabel]) && !isNaN(d[p.xLabel]);
          }))
          .attr("cx", function(d) { return xScale(d[p.xLabel]); })
          .attr("cy", function(d) { return yScale(d[yLabel]); })
          .attr("r", size >= minSize ? 3 : 1)
          .style("fill", function(d) { return "#4682B4"; }) // steelblue
          .style("fill-opacity", function(d) { return 0.5; });

        circles.transition()
          .duration(500);

        if (size >= minSize) {
          cell.select(".x.axis")
            .transition()
            .duration(500)
            .attr("class", "x axis")
            .attr("x", padding / 2)
            .attr("y", padding / 2)
            .attr("width", size - padding)
            .attr("height", size - padding)
            .call(xAxis);

          cell.select(".y.axis")
            .attr("class", "y axis")
            .attr("x", padding / 2)
            .attr("y", padding / 2)
            .attr("width", size - padding)
            .attr("height", size - padding)
            .transition()
            .duration(500)
            .call(yAxis);

          // Update x- and y- axis labels
          addXAxisLabel(cell.select(".x.label"), p.xLabel);
          addYAxisLabel(cell.select(".y.label"), yLabel);

          // Tooltips
          cell.selectAll("title").remove();

          circles.append("title")
            .text(function(d) {
              return "(" + d[p.xLabel] + "," + d[yLabel] + ")";
            });
        }
        else {
          // Tooltip
          cell.selectAll("title")
            .text(p.xLabel);
        }
      }

      // ======================================================================

      function addXAxisLabel(xLabel, xLabelText) {
        xLabel.attr("text-anchor", "middle")
{'fraction_non_alphanumeric': 0.19156154434250763, 'fraction_numerical': 0.013618119266055046, 'mean_word_length': 1.844000543552113, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 26, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3078230', 'n_tokens_mistral': 6482, 'n_tokens_neox': 6108, 'n_words': 1410}