【问题标题】:Protovis Scale Interaction Line Chart exampleProtovis 比例交互折线图示例
【发布时间】:2011-07-19 17:12:44
【问题描述】:

我正在关注比例交互示例@http://mbostock.github.com/protovis/docs/invert.html,我正在尝试绘制 2 线系列图表。

我的JSON文件如下:

var psSeriesData =
    [{"Dates":["1-10","2-10","3-10","4-10","5-10","6-10","7-10","8-10"],"ScoresForA":    
    [78.78,79.79,78.78,78.78,78.78,79.79,79.79,76.92],"ScoresForB":
    [78.78,79.79,78.78,78.78,78.78,79.79,79.79,76.92]}]

我打算使用 Dates 绘制 x 轴,并分别使用 ScoresForA 和 ScoresForB 绘制 2 折线图,但经过多次调整后我很困惑如何做到这一点。

我的代码如下:

                var data = pv.range(2).map(function(i) {
                    return pv.range(0, 10, .1).map(function(x) { 
                        return {x: psSeriesData.Dates, y: psSeriesData.ScoresForA,ScoresForB  };
                    });
                });

                /* Chart dimensions and scales. */
                var w = 400,
                h = 200,
                x = pv.Scale.linear(0, 9.9).range(0, w),
                y = pv.Scale.linear(0, 10).range(0, h),
                i = -1;

                /* The root panel. */
                var vis = new pv.Panel()
                .width(w)
                .height(h)
                .bottom(20)
                .left(20)
                .right(10)
                .top(5);

                /* Y-ticks. */
                vis.add(pv.Rule)
                .data(pv.range(100))
                .visible(function() !(this.index % 2))
                .bottom(function(d) Math.round(y(d)) - .5)
                .strokeStyle(function(d) d ? "#eee" : "#000")
                .anchor("left").add(pv.Label)
                .text(function(d) (d * 10).toFixed(0) );

                /* X-ticks. */
                vis.add(pv.Rule)
                .data(x.ticks())
                .visible(function(d) d > 0)
                .left(function(d) Math.round(x(d)) - .5)
                .strokeStyle(function(d) d ? "#eee" : "#000")
                .anchor("bottom").add(pv.Label)
                .text(function(d) d.toFixed());

                /* A panel for each data series. */
                var panel = vis.add(pv.Panel)
                .data(data);

                /* The line. */
                var line = panel.add(pv.Line)
                .data(function(d) d)
                .left(function(d) x(d.x))
                .bottom(function(d) y(d.y))
                .lineWidth(3);

                /* The mouseover dots and label. */
                line.add(pv.Dot)
                .visible(function() i >= 0)
                .data(function(d) [d[i]])
                .fillStyle(function() line.strokeStyle())
                .strokeStyle("#000")
                .size(20)
                .lineWidth(1)
                .add(pv.Dot)
                .left(10)
                .bottom(function() this.parent.index * 12 + 10)
                .anchor("right").add(pv.Label)
                .text(function(d) (d.y * 10).toFixed(5));

                /* An invisible bar to capture events (without flickering). */
                vis.add(pv.Bar)
                .fillStyle("rgba(0,0,0,.001)")
                .event("mouseout", function() {
                    i = -1;
                    return vis;
                })
                .event("mousemove", function() {
                    var mx = x.invert(vis.mouse().x);
                    i = pv.search(data[0].map(function(d) d.x), mx);
                    i = i < 0 ? (-i - 2) : i;
                    return vis;
                });



                vis.render();

我做错了什么?

nrabinowitz 给出输入后:

     var psSeriesData = {
  "Dates": ["1/10","2/10","3/10","4/10","5/10","6/10","7/10","8/10"],
  "ScoresForA": [78.78,79.79,78.78,78.78,78.78,79.79,79.79,76.92],
  "ScoresForB": [78.78,79.79,78.78,78.78,78.78,79.79,79.79,76.92]
};

                // start by iterating over the two keys for your time series data
                var data = ["ScoresForA","ScoresForB"].map(function(seriesKey) {
                    // use pv.range to walk through the indexes of the
                    // date array (basically the same as a for loop)
                    return pv.range(0, psSeriesData.Dates.length)
                    // map these indexes to an array of objects
                    .map(function(dateIndex) {
                        // now return an object with the date index
                        // and series value for that index
                        return {
                            x: dateIndex,
                            y: psSeriesData[seriesKey][dateIndex]
                        }
                    });
                });


                /* Chart dimensions and scales. */
                var w = 400,
                h = 200,
                x = pv.Scale.linear(0, 9.9).range(0, w),
                y = pv.Scale.linear(0, 10).range(0, h),
                i = -1;

                /* The root panel. */
                var vis = new pv.Panel()
                .width(w)
                .height(h)
                .bottom(20)
                .left(20)
                .right(10)
                .top(5);

                /* Y-ticks. */
                vis.add(pv.Rule)
                .data(pv.range(100))
                .visible(function() !(this.index % 2))
                .bottom(function(d) Math.round(y(d)) - .5)
                .strokeStyle(function(d) d ? "#eee" : "#000")
                .anchor("left").add(pv.Label)
                .text(function(d) (d * 10).toFixed(0) );

                /* X-ticks. */
                vis.add(pv.Rule)
                //.data(function(d) [d[i].Dates])
                .data(pv.range(0, psSeriesData.Dates.length).map(function(a) (psSeriesData[a].Dates)))
                .visible(function(d) d > 0)
                .left(function(d) Math.round(x(d)) - .5)
                .strokeStyle(function(d) d ? "#eee" : "#000")
                .anchor("bottom").add(pv.Label)
                .text(function(d) (d).toFixed());

                /* A panel for each data series. */
                var panel = vis.add(pv.Panel)
                .data(data);

                /* The line. */
                var line = panel.add(pv.Line)
                .data(function(d) d)
                .left(function(d) x(d.x))
                .bottom(function(d) y(d.y))
                .lineWidth(3);

                /* The mouseover dots and label. */
                line.add(pv.Dot)
                .visible(function() i >= 0)
                .data(function(d) [d[i]])
                .fillStyle(function() line.strokeStyle())
                .strokeStyle("#000")
                .size(20)
                .lineWidth(1)
                .add(pv.Dot)
                .left(10)
                .bottom(function() this.parent.index * 12 + 10)
                .anchor("right").add(pv.Label)
                .text(function(d) (d.y ).toFixed(5));

                /* An invisible bar to capture events (without flickering). */
                vis.add(pv.Bar)
                .fillStyle("rgba(0,0,0,.001)")
                .event("mouseout", function() {
                    i = -1;
                    return vis;
                })
                .event("mousemove", function() {
                    var mx = x.invert(vis.mouse().x);
                    i = pv.search(data[0].map(function(d) d.x), mx);
                    i = i < 0 ? (-i - 2) : i;
                    return vis;
                });



                vis.render();

日期仍然没有显示为 x 轴,即使我使用了 map 函数和数组引用。读取“日期”属性似乎有问题。任何建议

错误:TypeError:无法读取未定义的属性“日期”

【问题讨论】:

  • 那么出了什么问题?你有任何错误吗?也许制作一个JSFiddle 会帮助你得到答案。

标签: javascript visualization protovis


【解决方案1】:

在处理这样的可视化时(尤其是在遵循 Protovis 示例时)要做的第一件事是确保您的数据采用您需要的格式。我还没有完成你所有的代码都在这里,但是你在前面的数据上有一些明显的问题:

  • 为什么你的初始数据在一个数组中?是否有任何理由包含封闭的直括号(即psSeriesData = [{ ... }] 中的外括号)?正如您所提供的那样,我在代码中没有看到任何原因,而且只会使事情变得混乱(例如,psSeriesData.Dates 未定义 - 您需要引用 psSeriesData[0].Dates)。

  • 我完全不清楚你在初始数据设置代码中做了什么,但我很确定它没有给你想要的东西 - 它看起来像一个盲目的剪辑和 -从示例中粘贴,即使它不适用。该示例使用pv.range 生成假数据 - 你不需要这个,你有 真实 数据,你可以通过这个来代替。

从这里开始的最佳方法是了解数据应该是什么样子。在示例中,数据的生成方式如下:

data = pv.range(3).map(function(i) {
    return pv.range(0, 10, .1).map(function(x) {
        return {x: x, y: i + Math.sin(x) + Math.random() * .5 + 2};
    });  
});

在控制台中运行,你会看到生成的数据是这样的:

[
    [
        {
            x: 0.1,
            y: 2.34
        },
        // ...
    ],
    // ...
]

外部数组保存不同的时间序列;每个时间序列都是一个对象数组,例如{x:0.1, y:2.34}。如果您的数据看起来不是这样,则它不适用于示例代码。

您的初始数据应该可以正常工作,但您需要将其转换为正确的格式。这里的一个问题是日期列表 - 这些是字符串,而不是数字,除非您将它们转换为 Date 对象,否则您将无法将它们用作数据(这是一个真正的痛苦,如果可能,请避免使用它)或将它们映射到数字 - 后者在这里很容易,因为它们是常规系列。 (如果您有不均匀的日期间隔,这一切都会更加复杂,但我们暂时忘记这一点。)您可以将日期的 index 用作 x 值,然后使用您的两个系列作为y 值。

将所有这些放在一起,您可以像这样格式化数据:

// note - no enclosing array
var psSeriesData = {
  "Dates": ["1-10","2-10","3-10","4-10","5-10","6-10","7-10", "8-10"], 
  "ScoresForA": [78.78,79.79,78.78,78.78,78.78,79.79,79.79,76.92],
  "ScoresForB": [78.78,79.79,78.78,78.78,78.78,79.79,79.79,76.92]
};

// start by iterating over the two keys for your time series data
var data = ["ScoresForA","ScoresForB"].map(function(seriesKey) {
        // use pv.range to walk through the indexes of the
        // date array (basically the same as a for loop)
        return pv.range(0, psSeriesData.Dates.length)
            // map these indexes to an array of objects
            .map(function(dateIndex) {
                // now return an object with the date index 
                // and series value for that index
                return {
                    x: dateIndex,
                    y: psSeriesData[seriesKey][dateIndex]
                }
            });
});

还有很多其他方法可以做到这一点,但重点是你会得到一个像这样的数组:[[{x:0, y:79.79}, ...], ...]。我还没有查看您的其余代码,但是现在您的数据格式正确,您应该能够用代码中的真实数据替换示例中的假数据,并使整个工作正常预期(尽管您需要更改示例中关于xy 的预期最大值和最小值的任何假设)。

【讨论】:

  • 您好 nrabinowitz,感谢您对这个问题的投入。是的,我正在尝试学习 Protovis,并希望尝试使用他们的示例。所以,我尝试了你的方法,并被困在为什么 x 轴没有显示日期。正如您之前提到的,它必须是数字而不是字符串,因此,我将日期更改为“日期”:[110,210,310,410,510,610,710,810]。同样,这是出于测试目的,一旦我开始工作,我打算按照你的建议转换它们。
  • 这就是我所做的:/* X-ticks. */ vis.add(pv.Rule) .data(function(d) [d[i].Dates]) 另一个问题是折线图没有显示,但是当我将鼠标悬停在画布上时,会显示点和值分别出来。请指教。
  • 很难说没有完整的例子,但你可能没有为你的数据正确设置 x 比例 - 范围应该从 0 到你的最大值(上面例子中的 810)。如果我是你,我只会使用索引作为数据(例如pv.range(0, psSeriesData.Dates.length)),然后将它们映射到标签的日期字符串。
  • 您好 nrabinowitz,我已编辑并将整个代码库放在最上面。请看一下并提供任何可能有帮助的建议。谢谢。
  • @Queryer - 通常,你会为此提出一个新问题 - SO 格式对于这种长格式来回运行并不是那么好。但是:没有像psSeriesData[a].Dates 这样的数据——只有psSeriesData.Dates。我会尽快写一些更长的东西
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-04
  • 2021-02-18
  • 1970-01-01
  • 2011-06-13
  • 1970-01-01
  • 2015-10-10
相关资源
最近更新 更多