javascript - 如何在 d3.js 中创建 'facetplot'?

标签 javascript d3.js data-visualization

假设我有一个不同运动员在不同日子进行相同测试的数据集。每天,他们将进行几次试验/运行。我想使用 d3.js 可视化这些天来每位运动员的发展,但我很难理解如何完成这项任务。

在 Python 中使用 seaborn 或在 R 中使用 ggplot2,我会使用 facetplot,其中每一天都是一个方面。在这些方面,我将在 x 轴上进行试验,在 I 轴上进行性能测试。但是如何在 d3.js 中做到这一点?

d3.group and group请允许我按运动员对数据集进行分组,并且我了解如何迭代每个运动员的值。但我不明白如何从这里开始在 d3.js 中实际创建 facetplot。

我曾尝试搜索有关 observable 的相关教程,但运气不佳。一种有趣且相关的可视化是[Trumps Golfs bt Bostock]。另一个is this scatterplot .

有人可以帮助我朝着正确的方向前进吗?我已经创建了一个简单的数据集和散点图,可以作为起始点

const data = d3.range(10).map(i => ({
        bib: Math.floor(i / 5) + 1,
        ratio: -1 + Math.random() * 5,
        run: [1, 2, 3, 4, 5][i % 5],
        run: [1, 2, 3, 4, 5][i % 5],
        name: ['GIRL1', 'GIRL2', 'GIRL3', 'GIRL4'][Math.floor(i / 5)]
        }));

        const width = 250;
        const height = 150;
       
        const svg = d3.select('svg')
            .attr('width', width)
            .attr('height', height)

        const margin = {
            top: 20,
            right: 20,
            bottom: 20,
            left: 50
        }

        const innerWidth = width - margin.left - margin.right;
        const innerHeight = height - margin.top - margin.bottom;

        const xScale = d3.scaleLinear()
            .domain(d3.extent(data, d => d.run))
            .range([0, innerWidth])
     
        const yScale = d3.scaleLinear()
            .domain(d3.extent(data, d => d.ratio))
            .range([0, innerHeight])

        const g = svg.append('g')
            .attr('transform', `translate(${margin.left},${margin.top})`)

        g.selectAll('circle')
            .data(data)
            .join('circle')
            .attr('r', 3)
            .attr('cx', d => xScale(d.run))
            .attr('cy', d => yScale(d.ratio))

            g.append("g")
                .call(d3.axisBottom(xScale))
                .attr('transform', `translate(0,${innerHeight})`);;
            
            g.append("g")
                .call(d3.axisLeft(yScale))
<script src="https://unpkg.com/d3@6.2.0/dist/d3.min.js"></script>
<svg></svg>

从 R 中绘制示例:

enter image description here

最佳答案

设置 SVG

首先,让我们制定一个关于如何分配和构建页面的约定。只有一行图表,这更容易一些。

enter image description here

我们需要在上图中定义变量。然后我们可以推导出每个图的宽度和高度:

const height = 500;
const width = 500;
const margin = {
   top: 50,
   left: 50,
   right: 50,
   bottom: 50
}
const padding = 10; // labelled `pad` in image due to space constraints

const plotWidth = (width-padding)/numberOfPlots - padding;
const plotHeight = height-padding*2;

当然你可以随心所欲地安排,我包含这个部分只是为了让其余的答案更容易理解。

下面我使用 g 来保持由边距界定的区域,以便以后定位更容易。

现在我们也有两种不同的比例,你已经在你的代码中做了这个,但是我们现在可以使用上面的 plotWidthplotHeight 定义范围间距约定。

绘制数据

现在我们准备好创建绘图了。为此,我们将创建一个嵌套数据集:我们希望按图对数据进行分组,正如您所指出的,我们可以使用 d3.group:

 const grouped = d3.group(data,d=>d.bib);

对于每个分组,我们将创建一个 g 并使用基于上述结构的翻译来定位:

 const plots = g.selectAll(".plot")
  .data(grouped)
  .enter()
  .append("g")
  .attr("transform", function(d,i) {
     return "translate("+[i*(padding+plotWidth)+padding,padding]+")";
   })
  

我们已经准备好使用嵌套数据进行绘图了:

   plots.selectAll(null)
     .data(d=>d[1])
     .enter()
     .append("circle")
     ... // and so forth.

下面的代码片段使用 d=>d[1],因为这是属于该组的项目数组所在的位置。 d[0] 是组的标识符。如果我的数据数组是一个数组数组,我会简单地使用 d=>d;

现在我们可以在每个图上附加 x 轴:

 plots.append("g")
   .attr("transform","translate("+[0,plotHeight]+")")
   .call(d3.axisBottom(x));

还有一个y轴:

 g.append("g")
   .attr("transform","translate("+[0,padding]+")");
   .call(d3.axisLeft(y));

我没有添加父 x 轴,根据您的使用情况,您可能想要标记它,或者不添加它。我还注意到我按 bib 而非 run 对数据进行分组,但原则保持不变:对数据进行分组,输入并定位父图,然后将数据点添加到父图。

示例

// Data and manipluation:
const data = d3.range(15).map(i => ({
        bib: Math.floor(i / 5) + 1,
        ratio: -1 + Math.random() * 5,
        run: [1, 2, 3, 4, 5][i % 5],
        run: [1, 2, 3, 4, 5][i % 5],
        name: ['GIRL1', 'GIRL2', 'GIRL3', 'GIRL4'][Math.floor(i / 5)]
        }));
        
const grouped = d3.group(data,d=>d.bib);


// Dimensions:
const height = 500;
const width = 500;
const margin = {
       top: 10,
       left: 50,
       right: 50,
       bottom: 50
    }
const padding = 30; // labelled `pad` in image due to space constraints

const plotWidth = (width-padding)/grouped.size - padding;
const plotHeight = height-padding*2;

const svg = d3.select("body")
  .append("svg")
  .attr("width", margin.left+width+margin.right)
  .attr("height", margin.top+height+margin.bottom);
  
const g = svg.append("g")
  .attr("transform","translate("+[margin.left,margin.top]+")");

//Scales:
 const xScale = d3.scaleLinear()
   .domain(d3.extent(data, d => d.run))
   .range([0, plotWidth]);
     
const yScale = d3.scaleLinear()
   .domain(d3.extent(data, d => d.ratio))
   .range([plotHeight, 0]);
   
// Place plots:
const plots = g.selectAll(null)
  .data(grouped)
  .enter()
  .append("g")
  .attr("transform", function(d,i) {
     return "translate("+[i*(padding+plotWidth)+padding,padding]+")";
   })
   
//Optional plot background:
plots.append("rect")
  .attr("width",plotWidth)
  .attr("height",plotHeight)
  .attr("fill","#ddd");
   

// Plot actual data
plots.selectAll(null)
     .data(d=>d[1])
     .enter()
     .append("circle")
     .attr("r", 4)
     .attr("cy", d=>yScale(d.ratio))
     .attr("cx", d=>xScale(d.run))

// Plot line if needed:
plots.append("path")
  .attr("d", function(d) {
    return d3.line()
               .x(d=>xScale(d.run))
               .y(d=>yScale(d.ratio))
               (d[1])
   })
   .attr("stroke", "#333")
   .attr("stroke-width", 1)
   .attr("fill","none")
   
// Plot names if needed:
plots.append("text")
  .attr("x", plotWidth/2)
  .attr("y", -10)
  .text(function(d) {
    return d[1][0].name;
  })
  .attr("text-anchor","middle");
   
// Plot axes     
 plots.append("g")
   .attr("transform","translate("+[0,plotHeight]+")")
   .call(d3.axisBottom(xScale).ticks(4));


 g.append("g")
   .attr("transform","translate("+[0,padding]+")")
   .call(d3.axisLeft(yScale))
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.1.0/d3.min.js"></script>

应该产生什么:

enter image description here

关于javascript - 如何在 d3.js 中创建 'facetplot'?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65499073/

相关文章:

javascript - 列表项上的单击事件没有任何响应或控制台输出

javascript - 如何禁用 D3.js 中的缩放功能?

javascript - 如何在 d3js 中查找关联日期(x 轴)与最大值(y 轴)

r - 如何解释 corrplot 的输出?

javascript - 我应该使用哪种 D3 布局来显示关系?

d3.js - 在 D3.js 中绘制 17K x 1K 数据点热图的建议

javascript - 如何将每行的值添加到td?

javascript - 如何使用jquery.validate执行验证?

javascript - 脚本在 safari 中不起作用

javascript - 用 D3 和 Leaflet 重复 SVG