【发布时间】:2017-08-29 21:16:15
【问题描述】:
我刚开始使用 dc.js,正在查看主站点上的 NASDAQ 示例:https://dc-js.github.io/dc.js/
我创建了a Fiddle,其中包含一些示例虚拟数据以及该问题的两个相关图表。
与 NASDAQ 示例类似,我想要一个气泡图,其中 Y 轴是由不同图表中的画笔控制的时间跨度内的值变化百分比。纳斯达克示例的代码执行以下操作:
var yearlyPerformanceGroup = yearlyDimension.group().reduce(
/* callback for when data is added to the current filter results */
function (p, v) {
++p.count;
p.absGain += v.close - v.open;
p.fluctuation += Math.abs(v.close - v.open);
p.sumIndex += (v.open + v.close) / 2;
p.avgIndex = p.sumIndex / p.count;
p.percentageGain = p.avgIndex ? (p.absGain / p.avgIndex) * 100 : 0;
p.fluctuationPercentage = p.avgIndex ? (p.fluctuation / p.avgIndex) * 100 : 0;
return p;
},
/* callback for when data is removed from the current filter results */
function (p, v) {
--p.count;
p.absGain -= v.close - v.open;
p.fluctuation -= Math.abs(v.close - v.open);
p.sumIndex -= (v.open + v.close) / 2;
p.avgIndex = p.count ? p.sumIndex / p.count : 0;
p.percentageGain = p.avgIndex ? (p.absGain / p.avgIndex) * 100 : 0;
p.fluctuationPercentage = p.avgIndex ? (p.fluctuation / p.avgIndex) * 100 : 0;
return p;
},
/* initialize p */
function () {
return {
count: 0,
absGain: 0,
fluctuation: 0,
fluctuationPercentage: 0,
sumIndex: 0,
avgIndex: 0,
percentageGain: 0
};
}
);
我目前将其解释为对所有数据求和(关闭-打开)并除以平均每日指数的平均值。但这不是我熟悉的百分比变化公式。 (例如(新旧)/旧 x 100)
虽然它似乎适用于 NASDAQ 示例,但我的数据更像是以下内容:
country_id,received_week,product_type,my_quantity,my_revenue,country_other_quantity
3,2017-04-02,1,1,361,93881
1,2017-04-02,4,45,140,93881
2,2017-04-02,4,2,30,93881
3,2017-04-02,3,1,462,93881
2,2017-04-02,3,48,497,93881
等.. 多月和 product_types。
假设我对计算特定国家的百分比变化感兴趣。如何获取给定国家/地区的开始和结束数量,以便将变化计算为 end-start/start * 100?
我正在考虑以下内容(假设我设置了正确的尺寸和所有内容)
var country_dim = ndx.dimension(function (d) { return d['country_id']; })
var received_day_dim = ndx.dimension(function (d) { return d['received_day']; })
var date_min = received_day_dim.bottom(1)[0]['received_day']
var date_max = received_day_dim.top(1)[0]['received_day']
然后在我的自定义reduce函数中,目前在示例的脉络中(错误):
var statsByCountry = country_dim.group().reduce(
function (p, v) {
++p.count;
p.units += +v["my_units"];
p.example_rate = +v['my_units']/(v['quantity_unpacked']*90) //place holder for total units per day per country
p.sumRate += p.opp_buy_rate;
p.avgRate = p.opp_buy_rate/p.count;
p.percentageGain = p.avgRate ? (p.opp_buy_rate / p.avgRate) * 100 : 0;
p.dollars += +v["quantity_unpacked"]/2;
// p.max_date = v['received_week'].max();
// p.min_date
//dateDimension.top(Infinity)[dateDimension.top(Infinity).length - 1]['distance'] - dateDimension.top(Infinity)[0]['distance']
return p;
},
function (p, v) {
--p.count;
if (v.region_id > 2) {
p.test -= 100;
}
p.units -= +v["quantity_unpacked"];
p.opp_buy_rate = +v['quantity_unpacked']/(v['quantity_unpacked']*90) //place holder for total units per day per country
p.sumRate -= p.opp_buy_rate;
p.avgRate = p.count ? p.opp_buy_rate/p.count : 0;
p.percentageGain = p.avgRate ? (p.opp_buy_rate / p.avgRate) * 100 : 0;
p.dollars -= +v["quantity_unpacked"]/2;
// p.max_date = v['received_week'].max();
return p;
},
function () {
return {quantity_unpacked: 0,
count: 0,
units: 0,
opp_buy_rate: 0,
sumRate: 0,
avgRate: 0,
percentageGain: 0,
dollars: 0,
test: 0
};//, dollars: 0}
}
);
还有我的图表:
country_bubble
.width(990)
.height(250)
.margins({top:10, right: 50, bottom: 30, left:80})
.dimension(country_dim)
.group(statsByCountry)
.keyAccessor(function (p) {
return p.value.units;
})
.valueAccessor(function (p) { //y alue
return p.value.percentageGain;
})
.radiusValueAccessor(function (p) { //radius
return p.value.dollars/10000000;
})
.maxBubbleRelativeSize(0.05)
.elasticX(true)
.elasticY(true)
.elasticRadius(true)
.x(d3.scale.linear())
.y(d3.scale.linear())
// .x(d3.scale.linear().domain([0, 1.2*bubble_xmax]))
// .y(d3.scale.linear().domain([0, 10000000]))
.r(d3.scale.linear().domain([0, 10]))
.yAxisPadding('25%')
.xAxisPadding('15%')
.renderHorizontalGridLines(true)
.renderVerticalGridLines(true)
.on('renderlet', function(chart, filter){
chart.svg().select(".chart-body").attr("clip-path",null);
});
本来想在statsbycountry里有类似下面的东西:
if (v.received_day == date_min) {
p.start_value += v.my_quantity;
}
if (v.received_day == date_max) {
p.end_value += v.my_quantity;
}
这似乎有点笨拙?但是如果我这样做,我认为这不会随着其他过滤器的变化(比如时间或产品)而不断更新? Ethan 建议我使用假组,但我有点迷茫。
【问题讨论】:
-
Crossfilter 在这种计算上不会很擅长,因此尝试将其嵌入 Crossfilter 范式中并不值得。进行过滤,然后使用
dimension.top(Infinity)提取当前过滤器中的数据,然后使用该数据计算您在时间跨度内的变化。如果您需要在 dc.js 图表中显示此内容,请使用假组 (github.com/dc-js/dc.js/wiki/FAQ#fake-groups) -
@Ethan,所以你是在建议我不要使用你对 dimension.top(infinity) 的建议吗?那么有什么替代方法来进行计算,以便在我更改时间范围时它会更新?抱歉,我有点糊涂了。
-
我是说使用维度而不是组。请改用假组。抱歉,这几周我不太在线,希望其他人可以帮忙
-
感谢 Ethan 的帮助,不幸的是,通过不乏您的尝试,我仍然不明白我的自定义 reduce 函数、我原来的 country_dim 维度、statsByCountry 和我的假组是如何组合在一起的以及我放了什么在哪里。
-
@Ethan,也许您可以再次提供一些快速指导。再考虑一下,我想我的主要问题是如何按国家/地区分组,但按日期排序,以便 top(Infinity)[0] 和 top(Infinity)[array.length - 1] 给我想要的结果?我首先需要在最早日期和最晚日期获取每个国家/地区的单位。例如开始日期, 1, 100| end_date, 1, 200|开始日期, 2, 50| start_date,2,50 才能开始计算。
标签: javascript d3.js dc.js crossfilter