使用序数比例尺的nvd3散点图

6

我相对来说比较新于d3和nvd3,想要创建一个简单的散点图,就像这个例子一样,但是y轴是序数型的。因此y轴的值是分类字符串。这是我认为我需要做的:

var xfun = function (d) { return d.Pos }    //simple ints
  , yfun = function (d) { return d.Title }  //the ordinal values

var chart = nv.models.scatterChart()
    .showDistX(true)
    .showDistY(true)
    .color(d3.scale.category10().range())
    .margin({ top: 30, right: 20, bottom: 50, left: 130 })
    .tooltips(false)
    .x(xfun)   
    .y(yfun);

// create an ordinal scale with some test values
var ys = d3.scale.ordinal()
    .domain(["this","is","an","ordinal","scale"])
    .range(5);

// tell nvd3 to use it
chart.yAxis.scale(ys);

// add to the page
nv.addGraph(function () {
    d3.select(selector).datum(data).transition().duration(500).call(chart);
    nv.utils.windowResize(chart.update);
    return chart;
});

然而,没有运气:

Error: Invalid value for <circle> attribute cy="NaN" d3.js:690
Error: Invalid value for <line> attribute y1="NaN" d3.js:690
Error: Invalid value for <line> attribute y2="NaN" d3.js:690
Error: Invalid value for <circle> attribute cy="NaN" d3.js:7532
Uncaught TypeError: Cannot read property '0' of undefined 
..

而y轴只是显示了从-1到1的线性轴。有趣的是,在y = -1和y = 1(极端值)处绘制了一些圆。

为了手动强制cy的正确值,我尝试在调用(chart)后添加:

d3.selectAll("#mychart circle").attr("cy", function(d){
        return = ys(yfun(d));
        });

但仍然出现相同的错误。我如何使序数比例尺正常工作?请注意,当我使用nvd3图例切换数据系列(其中包含不同的x/y数据)时,我也需要它正确更新。
在github上有一个相关问题,但没有解决方案。
更新:经过一些调试,我尝试用chart.scatter.y(ys)替换chart.yAxis.scale(ys),这样就可以消除错误。我也可以放弃手动选择selectAll
然而,y轴仍然显示从0.99到1.01的线性刻度,并且所有点都绘制在y=1处。所以离有序比例尺还有一步之遥。

你是否检查过原始散点图?你看到的圆不是真正的圆形,它们是以圆形绘制的路径 :) - user1737842
@Dom:可能是这样,但它仍然抱怨svg<cirlce>元素,而我肯定可以在dom中看到它们(但所有的cy值都为NaN)。 - dgorissen
3个回答

6

如果有人偶然遇到这个问题,以下是我成功解决的方法:不要试图强制将序数刻度应用于轴(即我的X轴),而是使用线性刻度,并提供一个自定义的tickFormat函数来返回所需的标签。

chart.xAxis.tickFormat(function(d){
            return labelValues[d];
        });
用于将数值与所需的标签之间进行映射。

是的,这可行。现在我需要想办法将标签旋转为垂直。 - subsci
chart.xAxis.rotateLabels(90) 可以旋转标签,但是只有每个第 n 个标签被渲染出来了。这取决于库的标签隐藏算法来避免重叠。在我的情况下,每七个标签才被渲染出来。 - subsci
你能提供一个“数值和所需标签之间的映射”的例子吗? - Miguel Vazq
@MiguelVazq 试试我的,也许能帮到你。 - Keval Bhatt

2

有一个简单的解决方案,由@robinfhu提供。

不要使用序数比例尺!

相反,将您的x函数设置为返回元素的索引:

chart.x(function(d,i){ return i;})

并将您的tickFormat函数设置为读取正确的标签:

tickFormat(function(d) {return that.data[0].values[d].x;})

工作示例: Plunker

复制并粘贴:

  nv.models.lineChart()
    .x(function(d, i) {
      return i;
    }).xAxis
    .tickFormat(function(d) {
      return that.data[0].values[d].x;
    });

1
我没有使用过nvd3.js,但从我的d3经验来看,错误似乎在于您的比例尺范围。对于序数比例尺,您需要定义一个相应值的数组来映射域。例如,我可以设置以下比例尺:
var myScale = d3.scale.ordinal()
    .domain(['this','is','an','ordinal','scale'])
    .range([1,2,3,4,5])

这个尺度将把我的领域映射到1:5的数字范围内,其中'this' -> 1,'is' -> 2等等。如果我不想逐个设置每个点,我也可以使用rangePoints定义我的范围,如下所示:
var myScale = d3.scale.ordinal()
    .domain(['this','is','an','ordinal','scale'])
    .rangePoints([1,5])

rangePoints会从最小值到最大值给我一个均匀间隔的点范围,因此这将生成相同的范围。

我制作了一些圆形来说明如何做到这一点在这里。

当你切换到不同的数据系列时,你需要更新你的域。由于range对应于你的点映射到页面上的位置,你不需要更新它,除非nvd3.js应用了二次映射并进行了ordinalDomain ->整数 ->点位置的两个步骤。

另一个选择是在函数定义中应用比例尺。

yfun = function (d) { return ys(d.Title) } 

1
谢谢。我也尝试过了,但没有成功。请看上面的编辑。问题似乎比这更早,即首先让nvd3实际使用序数比例尺。至于动态系列切换,nvd3在multibar示例中提供了此功能(也适用于序数数据)。因此,我想它应该也适用于这里的散点图。 - dgorissen
1
我明白你在说nvd3的问题。一个可能的解决方案是在原始函数中使用ys来更改值,然后再传递给nvd3。我已经在上面的帖子中添加了如何实现这一点的说明。 - ckersch
1
实际上,在这种情况下,我会隐藏nvd3的序数刻度并返回一些整数。这确实有效,这就是我现在拥有的。然后,我可以尝试手动覆盖刻度标记,但所有这些都感觉像是一个巨大的hack :) 我已经在nvd3方面创建了一个github问题 https://github.com/novus/nvd3/issues/177 - dgorissen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接