在 d3 js 中重现山脊线图时出现问题

问题描述 投票:0回答:1

我正在尝试在 javascript 中重现 d3 密度岭图,但使用不同的数据集。我正在修改的代码来自 d3 库的文档,可以在这里看到:d3 密度脊示例

我正在修改的代码的情节如下所示:

我看了一下这个例子中的数据。它是一个对象数组:

d3.csv("https://raw.githubusercontent.com/zonination/perceptions/master/probly.csv", 
    function(data) {console.log(data)}
)
[
   {
     "Almost Certainly": "95",
     "Highly Likely": "80",
     "Very Good Chance": "85",
     "Probable": "75",
     "Likely": "66",
     "Probably": "75",
     "We Believe": "66",
     "Better Than Even": "55",
     "About Even": "50",
     "We Doubt": "40",
     "Improbable": "20",
     "Unlikely": "30",
     "Probably Not": "15",
     "Little Chance": "20",
     "Almost No Chance": "5",
     "Highly Unlikely": "25",
     "Chances Are Slight": "25"
   }
...
]

所以我试图用我自己的数据集(一个著名的钻石数据集)替换原始数据集。为了重现我的问题,这里有一些代码,用于下载钻石数据集并将其转换为与上面数据集相同的形式。

let diamondsDataRaw;
d3.csv("https://raw.githubusercontent.com/tidyverse/ggplot2/main/data-raw/diamonds.csv", function(data){
    diamondsDataRaw_ = data
  }
)

// Get unique cuts
var cuts = [...new Set(diamondsDataRaw.map(d => d.cut))];

// map each distinct cut to cut & price
var distinctCutPrice = cuts.map(cut => {
  var cut_data = diamondsDataRaw.filter(d => d.cut === cut) 
  var distinctCutPrice = cut_data.map(i => {return({cut:i.cut, price:i.price})})
  return(distinctCutPrice)
})
// make all same length
var minLen = ss.min(distinctCutPrice.map(i => i.length))
distinctCutPrice_same_length = distinctCutPrice.map(i => i.slice(0, minLen))
// convert to dataframe
var seqMinLen = [...Array(minLen)].map((v, k)=>k)
var df = seqMinLen.map(i => {
  return({
    Ideal: distinctCutPrice_same_length[0][i].price,
    Premium: distinctCutPrice_same_length[1][i].price,
    Good: distinctCutPrice_same_length[2][i].price,
    "Very Good": distinctCutPrice_same_length[3][i].price,
    Fair: distinctCutPrice_same_length[4][i].price
  })
})

现在我尝试使用他们的代码,但交换我的数据集:

<div id = "plot33"></div>
function kernelDensityEstimator(kernel, X) {
  return function(V) {
    return X.map(function(x) {
      return [x, d3.mean(V, function(v) { return kernel(x - v); })];
    });
  };
}
function kernelEpanechnikov(k) {
  return function(v) {
    return Math.abs(v /= k) <= 1 ? 0.75 * (1 - v * v) / k : 0;
  };
}


var data = df


// Get the different categories and count them
var categories = cuts
var n = categories.length

// set the dimensions and margins of the graph
var margin = {top: 60, right: 30, bottom: 20, left:110},
    width = 460 - margin.left - margin.right,
    height = 400 - margin.top - margin.bottom;

// append the svg object to the body of the page
var svg = d3.select("#plot33")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
  "translate(" + margin.left + "," + margin.top + ")"
);


// Add X axis
var x = d3.scaleLinear()
  .domain([-10, 140])
  .range([ 0, width ]);
svg.append("g")
  .attr("transform", "translate(0," + height + ")")
  .call(d3.axisBottom(x));

// Create a Y scale for densities
var y = d3.scaleLinear()
  .domain([0, 0.4])
  .range([ height, 0]);

// Create the Y axis for names
var yName = d3.scaleBand()
  .domain(categories)
  .range([0, height])
  .paddingInner(1)
svg.append("g")
  .call(d3.axisLeft(yName));

// Compute kernel density estimation for each column:
var kde = kernelDensityEstimator(kernelEpanechnikov(7), x.ticks(40)) // increase this 40 for more accurate density.
var allDensity = []
for (i = 0; i < n; i++) {
  var key = categories[i];
  var density = kde(data.map(function(d) { return parseFloat(d[key]); }));
  allDensity.push({key: key, density: density});
}


// Add areas
svg.selectAll("areas")
.data(allDensity)
.enter()
.append("path")
.attr("transform", function(d){return("translate(0," + (yName(d.key)-height) +")" )})
.datum(function(d){return(d.density)})
.attr("fill", "#69b3a2")
.attr("stroke", "#000")
.attr("stroke-width", 1)
.attr("d",  d3.line()
  .curve(d3.curveBasis)
  .x(function(d) { return x(d[0]); })
  .y(function(d) { return y(d[1]); })
)

这就是结果,一个空的图:

如有任何帮助,我们将不胜感激。我的最终目标是尝试制作这样的东西:

javascript d3.js plot
1个回答
0
投票

代码中的一些问题:

1 - 您正在尝试在

diamondsDataRaw
中的 async 回调之外使用
d3.csv

2 - 我没有遵循您的数据操作,并且无法真正通过

ss.min
部分(它是未定义的)。所以我重写了它更简单(但我不确定这就是你想要的)。

3 - 您下面的示例在创建其比例时使用了一些硬编码的幻数。这些不适用于您的不同数据集。

将这些放在一起,这是一个可以工作的代码示例。它肯定需要进一步完善,因为作为数据可视化它没有什么意义。从数据来看,降价只是价格的驱动因素之一。

<!DOCTYPE html>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.js"></script>
<div id="my_dataviz"></div>

<script>
var margin = {top: 60, right: 30, bottom: 20, left:110},
    width = 460 - margin.left - margin.right,
    height = 400 - margin.top - margin.bottom;

var svg = d3.select("#my_dataviz")
  .append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform",
          "translate(" + margin.left + "," + margin.top + ")");

d3.csv("https://raw.githubusercontent.com/tidyverse/ggplot2/main/data-raw/diamonds.csv", function(data) {

    // Add X axis
  var x = d3.scaleLinear()
    .domain([0, d3.max(data.map(d => +d.price))])
    .range([ 0, width ]);

  svg.append("g")
    .attr("transform", "translate(0," + height + ")")
    .call(d3.axisBottom(x));

  var categories = [...new Set(data.map(d => d.cut))];

  var spacing = height / categories.length;

  var kde = kernelDensityEstimator(kernelEpanechnikov(7), x.ticks(40)) 

  var allDensity = [];
  var yMax = 0;
  categories.forEach( c => { 
    let density = kde( 
      data.filter(d => d.cut == c).map(d => +d.price)
    );
    let m = d3.max(density.map( d => d[1]) );
    if (m > yMax) yMax = m;
    allDensity.push({key: c, density: density});
  });

  var y = d3.scaleLinear()
      .domain([0, yMax])
      .range([spacing, 0]);

  var yName = d3.scaleBand()
    .domain(categories)
    .range([0, height])
    .paddingInner(1)
    
  svg.append("g")
    .call(d3.axisLeft(yName));

  svg.selectAll("areas")
    .data(allDensity)
    .enter()
    .append("path")
      .attr("transform", d => "translate(0," + (yName(d.key) - spacing) +")" )
      .attr("fill", "#69b3a2")
      .attr("stroke", "#000")
      .attr("stroke-width", 1)
      .attr("d",  d => {
        let p = d3.line()
            .curve(d3.curveBasis)
            .x(d => x(d[0]))
            .y(d => y(d[1]))(d.density);
        return p;
      });
});

// This is what I need to compute kernel density estimation
function kernelDensityEstimator(kernel, X) {
  return function(V) {
    return X.map(function(x) {
      return [x, d3.mean(V, function(v) { return kernel(x - v); })];
    });
  };
}
function kernelEpanechnikov(k) {
  return function(v) {
    return Math.abs(v /= k) <= 1 ? 0.75 * (1 - v * v) / k : 0;
  };
}
</script>

© www.soinside.com 2019 - 2024. All rights reserved.