修复显示的轴比例范围

Posted

技术标签:

【中文标题】修复显示的轴比例范围【英文标题】:Fixing the axis-scale range that is presented 【发布时间】:2020-10-06 12:18:15 【问题描述】:

我的值是我在时间序列中呈现的总分钟数。虽然分钟用于绘图,但我想更改轴刻度标签以显示此数据最接近的小时数。我解决这个问题的方法是添加一个.tickFormat(),将分钟转换为小时,然后返回d3.timeFormat(),小时格式和一个新日期设置为0,设置小时。虽然轴刻度标签很接近,但它们不正确。似乎有轻微的偏移、值的重复和比例不是从 0 开始(从 12 开始)。我应该使用不同的函数调用来更正比例吗?

这是有问题的代码:

svg.append("g")
        .call(d3.axisLeft()
            .scale(y)
            .tickFormat((d, i) => 
                var hours = Math.floor(d / 60);
                console.log(hours)
                console.log(new Date(0).setMinutes(hours))
                return d3.timeFormat("%I")( new Date(0).setHours(hours))
            ));

<meta charset="utf-8">

<!-- Load d3.js -->
<script src="https://d3js.org/d3.v4.js"></script>

<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>

<style>
div.tooltip    
    position: absolute;         
    text-align: center;         
    width: 100px;                   
    height: 30px;                   
    padding: 2px;               
    font: 12px sans-serif;      
    background: lightsteelblue; 
    border: 0px;        
    border-radius: 8px;         
    pointer-events: none;           

</style>

<script>

    var data = [
         "x": "2020-04-26", "y": 461.0, "label": "7:41" ,
         "x": "2020-04-27", "y": 421.0, "label": "7:01" ,
         "x": "2020-04-28", "y": 519.0, "label": "8:39" ,
         "x": "2020-04-29", "y": 502.0, "label": "8:22" ,
         "x": "2020-04-30", "y": 511.0, "label": "8:31" ,
         "x": "2020-05-01", "y": 513.0, "label": "8:33" ,
         "x": "2020-05-02", "y": 496.0, "label": "8:16" ,
         "x": "2020-05-03", "y": 480.0, "label": "8:00" ,
         "x": "2020-05-04", "y": 364.0, "label": "6:04" ,
         "x": "2020-05-05", "y": 498.0, "label": "8:18" ,
         "x": "2020-05-06", "y": 467.0, "label": "7:47" ,
         "x": "2020-05-07", "y": 477.0, "label": "7:57" ,
         "x": "2020-05-08", "y": 431.0, "label": "7:11" ,
         "x": "2020-05-09", "y": 419.0, "label": "6:59" ,
         "x": "2020-05-10", "y": 471.0, "label": "7:51" ,
         "x": "2020-05-11", "y": 391.0, "label": "6:31" ,
         "x": "2020-05-12", "y": 481.0, "label": "8:01" ,
         "x": "2020-05-13", "y": 494.0, "label": "8:14" ,
         "x": "2020-05-14", "y": 506.0, "label": "8:26" ,
         "x": "2020-05-15", "y": 464.0, "label": "7:44" ,
         "x": "2020-05-16", "y": 474.0, "label": "7:54" ,
         "x": "2020-05-17", "y": 383.0, "label": "6:23" ,
         "x": "2020-05-18", "y": 385.0, "label": "6:25" ,
         "x": "2020-05-19", "y": 470.0, "label": "7:50" ,
         "x": "2020-05-20", "y": 465.0, "label": "7:45" ,
         "x": "2020-05-21", "y": 574.0, "label": "9:34" ,
         "x": "2020-05-22", "y": 473.0, "label": "7:53" ,
         "x": "2020-05-23", "y": 431.0, "label": "7:11" ,
         "x": "2020-05-24", "y": 497.0, "label": "8:17" ,
         "x": "2020-05-26", "y": 482.0, "label": "8:02" ,
         "x": "2020-05-27", "y": 492.0, "label": "8:12" ,
         "x": "2020-05-28", "y": 494.0, "label": "8:14" ,
         "x": "2020-05-29", "y": 469.0, "label": "7:49" ,
         "x": "2020-05-30", "y": 395.0, "label": "6:35" ,
         "x": "2020-05-31", "y": 427.0, "label": "7:07" ,
         "x": "2020-06-01", "y": 346.0, "label": "5:46" ,
         "x": "2020-06-02", "y": 416.0, "label": "6:56" ,
         "x": "2020-06-03", "y": 461.0, "label": "7:41" ,
         "x": "2020-06-04", "y": 486.0, "label": "8:06" ,
         "x": "2020-06-05", "y": 451.0, "label": "7:31" ,
         "x": "2020-06-06", "y": 533.0, "label": "8:53" ,
         "x": "2020-06-08", "y": 462.0, "label": "7:42" ,
         "x": "2020-06-09", "y": 461.0, "label": "7:41" ,
         "x": "2020-06-10", "y": 477.0, "label": "7:57" ,
         "x": "2020-06-11", "y": 458.0, "label": "7:38" ,
         "x": "2020-06-12", "y": 484.0, "label": "8:04" ,
         "x": "2020-06-13", "y": 389.0, "label": "6:29" ,
         "x": "2020-06-15", "y": 472.0, "label": "7:52" ,
         "x": "2020-06-16", "y": 462.0, "label": "7:42" ,
         "x": "2020-06-17", "y": 486.0, "label": "8:06" ,
         "x": "2020-06-18", "y": 489.0, "label": "8:09" ,
         "x": "2020-06-19", "y": 483.0, "label": "8:03" ,
         "x": "2020-06-20", "y": 426.0, "label": "7:06" ,
         "x": "2020-06-21", "y": 453.0, "label": "7:33" ,
         "x": "2020-06-22", "y": 489.0, "label": "8:09" ,
         "x": "2020-06-23", "y": 467.0, "label": "7:47" ,
         "x": "2020-06-24", "y": 474.0, "label": "7:54" ,
         "x": "2020-06-25", "y": 451.0, "label": "7:31" ,
         "x": "2020-06-26", "y": 450.0, "label": "7:30" ,
         "x": "2020-06-27", "y": 470.0, "label": "7:50" ,
         "x": "2020-06-29", "y": 247.0, "label": "4:07" ,
         "x": "2020-06-30", "y": 502.0, "label": "8:22" ,
         "x": "2020-07-01", "y": 464.0, "label": "7:44" 
    ]
    
    // D3 date parser
    for (var i=0; i < data.length; i++)
        var parser = d3.timeParse("%Y-%m-%d")
        data[i].date = parser(data[i].x);
    

    var margin =  top: 10, right: 30, bottom: 30, left: 60  
    var width = 800 - margin.left - margin.right;
    var height = 800 - margin.top - margin.bottom;


    // Define the div for the tooltip
    var div = d3.select("body").append("div")   
        .attr("class", "tooltip")               
        .style("opacity", 0);

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

    var x = d3.scaleTime()
        .domain([d3.min(data, function(d)  return d.date ), d3.max(data, function(d)  return d.date )])
        .range([0, width]);
    
    svg.append("g")
        .attr("transform", "translate(" + 0 + "," + height + ")")
        .call(d3.axisBottom(x));

    // text label for the x axis
    svg.append("text")             
        .attr("transform",
                "translate(" + (width/2) + " ," + (height + margin.top + 20) + ")")
        .style("text-anchor", "middle")
        .text("Date");

    var y = d3.scaleLinear()
        .domain([0, d3.max(data, function(d) return +d.y )])
        .range([height, 0]);
    
    svg.append("g")
        .call(d3.axisLeft()
            .scale(y)
            .tickFormat((d, i) => 
                var hours = Math.floor(d / 60);
                console.log(hours)
                console.log(new Date(0).setMinutes(hours))
                return d3.timeFormat("%I")( new Date(0).setHours(hours))
            ));

    // text label for the y axis
    svg.append("text")
        .attr("transform", "rotate(-90)")
        .attr("y", 0 - margin.left)
        .attr("x",0 - (height / 2))
        .attr("dy", "1em")
        .style("text-anchor", "middle")
        .text("Time Asleep (Minutes)");

    // Add line path
    svg.append("path")
        .datum(data)
        .attr("fill", "none")
        .attr("stroke", "steelblue")
        .attr("stroke-width", 1.5)
        .attr("d", d3.line()
            .x(function(d)  return x(d.date) )
            .y(function(d)  return y(d.y) )
        );

    // Add the scatterplot (data points)
    svg.selectAll("dot")
        .data(data)
    .enter().append("circle")
        .attr("r", 3)
        // Add tooltip on hover
        .on("mouseover", function(d) 
            div.transition()
                .duration(200)
                .style("opacity", .9);
            div.html(d.x + "<br/>" + d.label)
                .style("left", (d3.event.pageX) + "px")
                .style("top", (d3.event.pageY - 30) + "px")
        )
        // Remove tooltip after hover
        .on("mouseout", function(d) 
            div.transition()
                .duration(500)
                .style("opacity", 0);
        )
        .transition()
        .delay(function(d,i) return (i*3))
        .duration(2000)
        .attr("cx", function(d) return x(d.date) )
        .attr("cy", function(d) return y(d.y) );
    

</script>

【问题讨论】:

【参考方案1】:

虽然您的数据是时间,但它不是日期。强迫它成为一个约会会给你带来许多令人讨厌的并发症。我刚刚超越了 d3 和 Date 逻辑,得到了一个更简单的解决方案:

var data = [
    "x": "2020-04-26",
    "y": 461.0,
    "label": "7:41"
  ,
  
    "x": "2020-04-27",
    "y": 421.0,
    "label": "7:01"
  ,
  
    "x": "2020-04-28",
    "y": 519.0,
    "label": "8:39"
  ,
  
    "x": "2020-04-29",
    "y": 502.0,
    "label": "8:22"
  ,
  
    "x": "2020-04-30",
    "y": 511.0,
    "label": "8:31"
  ,
  
    "x": "2020-05-01",
    "y": 513.0,
    "label": "8:33"
  ,
  
    "x": "2020-05-02",
    "y": 496.0,
    "label": "8:16"
  ,
  
    "x": "2020-05-03",
    "y": 480.0,
    "label": "8:00"
  ,
  
    "x": "2020-05-04",
    "y": 364.0,
    "label": "6:04"
  ,
  
    "x": "2020-05-05",
    "y": 498.0,
    "label": "8:18"
  ,
  
    "x": "2020-05-06",
    "y": 467.0,
    "label": "7:47"
  ,
  
    "x": "2020-05-07",
    "y": 477.0,
    "label": "7:57"
  ,
  
    "x": "2020-05-08",
    "y": 431.0,
    "label": "7:11"
  ,
  
    "x": "2020-05-09",
    "y": 419.0,
    "label": "6:59"
  ,
  
    "x": "2020-05-10",
    "y": 471.0,
    "label": "7:51"
  ,
  
    "x": "2020-05-11",
    "y": 391.0,
    "label": "6:31"
  ,
  
    "x": "2020-05-12",
    "y": 481.0,
    "label": "8:01"
  ,
  
    "x": "2020-05-13",
    "y": 494.0,
    "label": "8:14"
  ,
  
    "x": "2020-05-14",
    "y": 506.0,
    "label": "8:26"
  ,
  
    "x": "2020-05-15",
    "y": 464.0,
    "label": "7:44"
  ,
  
    "x": "2020-05-16",
    "y": 474.0,
    "label": "7:54"
  ,
  
    "x": "2020-05-17",
    "y": 383.0,
    "label": "6:23"
  ,
  
    "x": "2020-05-18",
    "y": 385.0,
    "label": "6:25"
  ,
  
    "x": "2020-05-19",
    "y": 470.0,
    "label": "7:50"
  ,
  
    "x": "2020-05-20",
    "y": 465.0,
    "label": "7:45"
  ,
  
    "x": "2020-05-21",
    "y": 574.0,
    "label": "9:34"
  ,
  
    "x": "2020-05-22",
    "y": 473.0,
    "label": "7:53"
  ,
  
    "x": "2020-05-23",
    "y": 431.0,
    "label": "7:11"
  ,
  
    "x": "2020-05-24",
    "y": 497.0,
    "label": "8:17"
  ,
  
    "x": "2020-05-26",
    "y": 482.0,
    "label": "8:02"
  ,
  
    "x": "2020-05-27",
    "y": 492.0,
    "label": "8:12"
  ,
  
    "x": "2020-05-28",
    "y": 494.0,
    "label": "8:14"
  ,
  
    "x": "2020-05-29",
    "y": 469.0,
    "label": "7:49"
  ,
  
    "x": "2020-05-30",
    "y": 395.0,
    "label": "6:35"
  ,
  
    "x": "2020-05-31",
    "y": 427.0,
    "label": "7:07"
  ,
  
    "x": "2020-06-01",
    "y": 346.0,
    "label": "5:46"
  ,
  
    "x": "2020-06-02",
    "y": 416.0,
    "label": "6:56"
  ,
  
    "x": "2020-06-03",
    "y": 461.0,
    "label": "7:41"
  ,
  
    "x": "2020-06-04",
    "y": 486.0,
    "label": "8:06"
  ,
  
    "x": "2020-06-05",
    "y": 451.0,
    "label": "7:31"
  ,
  
    "x": "2020-06-06",
    "y": 533.0,
    "label": "8:53"
  ,
  
    "x": "2020-06-08",
    "y": 462.0,
    "label": "7:42"
  ,
  
    "x": "2020-06-09",
    "y": 461.0,
    "label": "7:41"
  ,
  
    "x": "2020-06-10",
    "y": 477.0,
    "label": "7:57"
  ,
  
    "x": "2020-06-11",
    "y": 458.0,
    "label": "7:38"
  ,
  
    "x": "2020-06-12",
    "y": 484.0,
    "label": "8:04"
  ,
  
    "x": "2020-06-13",
    "y": 389.0,
    "label": "6:29"
  ,
  
    "x": "2020-06-15",
    "y": 472.0,
    "label": "7:52"
  ,
  
    "x": "2020-06-16",
    "y": 462.0,
    "label": "7:42"
  ,
  
    "x": "2020-06-17",
    "y": 486.0,
    "label": "8:06"
  ,
  
    "x": "2020-06-18",
    "y": 489.0,
    "label": "8:09"
  ,
  
    "x": "2020-06-19",
    "y": 483.0,
    "label": "8:03"
  ,
  
    "x": "2020-06-20",
    "y": 426.0,
    "label": "7:06"
  ,
  
    "x": "2020-06-21",
    "y": 453.0,
    "label": "7:33"
  ,
  
    "x": "2020-06-22",
    "y": 489.0,
    "label": "8:09"
  ,
  
    "x": "2020-06-23",
    "y": 467.0,
    "label": "7:47"
  ,
  
    "x": "2020-06-24",
    "y": 474.0,
    "label": "7:54"
  ,
  
    "x": "2020-06-25",
    "y": 451.0,
    "label": "7:31"
  ,
  
    "x": "2020-06-26",
    "y": 450.0,
    "label": "7:30"
  ,
  
    "x": "2020-06-27",
    "y": 470.0,
    "label": "7:50"
  ,
  
    "x": "2020-06-29",
    "y": 247.0,
    "label": "4:07"
  ,
  
    "x": "2020-06-30",
    "y": 502.0,
    "label": "8:22"
  ,
  
    "x": "2020-07-01",
    "y": 464.0,
    "label": "7:44"
  
]

// D3 date parser
for (var i = 0; i < data.length; i++) 
  var parser = d3.timeParse("%Y-%m-%d")
  data[i].date = parser(data[i].x);


var margin = 
  top: 10,
  right: 30,
  bottom: 30,
  left: 60

var width = 800 - margin.left - margin.right;
var height = 800 - margin.top - margin.bottom;


// Define the div for the tooltip
var div = d3.select("body").append("div")
  .attr("class", "tooltip")
  .style("opacity", 0);

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

var x = d3.scaleTime()
  .domain([d3.min(data, function(d) 
    return d.date
  ), d3.max(data, function(d) 
    return d.date
  )])
  .range([0, width]);

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

// text label for the x axis
svg.append("text")
  .attr("transform",
    "translate(" + (width / 2) + " ," + (height + margin.top + 20) + ")")
  .style("text-anchor", "middle")
  .text("Date");

var y = d3.scaleLinear()
  .domain([0, d3.max(data, function(d) 
    return +d.y
  )])
  .range([height, 0]);

svg.append("g")
  .call(d3.axisLeft()
    .scale(y)
    .tickFormat((d, i) => 
      var hours = Math.floor(d / 60);
      var minutes = d - hours * 60;
      return hours.toString().padStart(2, 0) + ":" + minutes.toString().padStart(2, 0).padStart(2, 0);
    ));

// text label for the y axis
svg.append("text")
  .attr("transform", "rotate(-90)")
  .attr("y", 0 - margin.left)
  .attr("x", 0 - (height / 2))
  .attr("dy", "1em")
  .style("text-anchor", "middle")
  .text("Time Asleep (Minutes)");

// Add line path
svg.append("path")
  .datum(data)
  .attr("fill", "none")
  .attr("stroke", "steelblue")
  .attr("stroke-width", 1.5)
  .attr("d", d3.line()
    .x(function(d) 
      return x(d.date)
    )
    .y(function(d) 
      return y(d.y)
    )
  );

// Add the scatterplot (data points)
svg.selectAll("dot")
  .data(data)
  .enter().append("circle")
  .attr("r", 3)
  // Add tooltip on hover
  .on("mouseover", function(d) 
    div.transition()
      .duration(200)
      .style("opacity", .9);
    div.html(d.x + "<br/>" + d.label)
      .style("left", (d3.event.pageX) + "px")
      .style("top", (d3.event.pageY - 30) + "px")
  )
  // Remove tooltip after hover
  .on("mouseout", function(d) 
    div.transition()
      .duration(500)
      .style("opacity", 0);
  )
  .transition()
  .delay(function(d, i) 
    return (i * 3)
  )
  .duration(2000)
  .attr("cx", function(d) 
    return x(d.date)
  )
  .attr("cy", function(d) 
    return y(d.y)
  );
div.tooltip 
  position: absolute;
  text-align: center;
  width: 100px;
  height: 30px;
  padding: 2px;
  font: 12px sans-serif;
  background: lightsteelblue;
  border: 0px;
  border-radius: 8px;
  pointer-events: none;
<meta charset="utf-8">

<!-- Load d3.js -->
<script src="https://d3js.org/d3.v4.js"></script>

<!-- Create a div where the graph will take place -->
<div id="my_dataviz"></div>

【讨论】:

这很有意义。比我预期的要容易得多。谢谢!

以上是关于修复显示的轴比例范围的主要内容,如果未能解决你的问题,请参考以下文章

如何调整plot的坐标轴的显示位置

ArcGIS10全图功能,比例尺很小,如何调整全图功能下的比例尺缩放范围

R语言ggplot2可视化分面图(faceting): ggplot2可视化分面图(facet_wrap)并设置不同的分面使用不同的坐标轴数值范围以及不同的轴标签断点间隔breaks

如何修复 JSONException:索引 0 超出范围 [0..0)?

固定y轴的比例/大小

问: win10页面与xp、win7的不一样,打印范围或纸张比例大小不一