跳到主要内容

范例:AdventureWorks Sales - 8. 动态参数与联动分析

· 阅读需 15 分钟
Tiven Wang

欢迎大家回到《元数分析云》范例:AdventureWorks Sales 系列!今天,我们将继续探索故事仪表板的高级功能,着重介绍动态 参数 功能和组件之间的联动分析。这些功能将使您能够更深入地探索数据,发现隐藏的洞察,并将您的分析提升到一个全新的水平。

在许多情况下,我们需要关注销售额最高的前 N 个产品,以便重点关注最具价值的产品。通过元数分析云的参数功能,您可以轻松地实现动态 帕累托分析

产品帕累托分析
产品帕累托分析

通过元数分析云的关联切片器功能,您可以将不同组件之间的筛选条件关联起来。

产品利润率分析
产品利润率分析

故事仪表板实际效果:

帕累托分析

帕累托图(Pareto Chart) 又称排列图法主次因素分析法,是一种条形图和折线图的组合,为品质管理上经常使用的一种图表方法。

信息

帕雷托根据“关键的少数和次要的多数”的原理而制作,其结构为两个纵坐标和一个横坐标,合并长条图及折线图所构成。左侧纵坐标表示频率,右侧纵坐标则表示累计频率(以百分比表示),横坐标表示影响质量的各种因素之名称,按影响大小顺序排列,直方形高度表示相应的因素的影响程度(即出现频率为多少),上方之折线则表示累计频率线(又称帕雷托曲线)。这些图表基于“80/20”法则。即:大约 80% 的问题是由 20% 的原因导致的。“80/20”法则也被称为“至关重要的少数和微不足道的多数”。也就是说,您可以关注导致问题发生的少数至关重要的根本原因,忽略微不足道的多数原因。

在我们对 AdventureWorks Sales 模型数据分析中,了解哪些产品对销售额做出了最大贡献是至关重要的。通过使用帕累托图,可以更好地识别那些具有显著销售额的顶尖产品,从而指导战略决策。想要计算帕累托分析需要提供以下几个因素:

  • 影响因素(横坐标) 使用 产品 Product 维度
  • 影响程度(直方形高度) 使用 销售额 Sales Amount 度量
  • 累计影响程度(折线) 使用 累计销售额除以总销售额 Sum(Sales Amount)
  • 基准线 (标记线)使用动态参数 ParetoBenchmark,默认值为 80

接下来看一下如何在故事仪表板中实现以上因素的帕累托图形。

图形配置

首先我们创建一个图形组件,选择自定义图形类型(因为我们将使用自定义逻辑脚本计算累计销售额占比),并配置三个度量字段和一个维度产品。

  1. 销售额
  2. 全部产品总销售额,用于计算累计销售额占比
  3. 基准线,用于绘制标记线
  4. 产品维度,使用产品维度的产品分类层次结构下的制程层级
帕累托图配置
帕累托图配置

其中全部产品总销售额使用 受限度量 类型的计算度量,使用了产品分类为度的 All 成员去限定了销售额度量, 配置如下图

产品总销售额计算度量
产品总销售额计算度量

参数配置

对于需要动态设置的基准线,我们需要创建一个 计算公式 类型的计算度量,并且使用一个数字参数作为计算表达式。如下图所示

基准线计算度量
基准线计算度量

点击参数中的创建按钮新建一个基准线 参数,用于设置基准线的数值,这里我们设置默认值为 80。

基准线参数
基准线参数

然后将参数拖至计算编辑器中使用,这样在使用此计算度量时将直接得到此参数的常量值。

前多少的产品

想要计算所有产品中的前多少名的产品,需要用到组件选择条件中的 高级切片器 功能。在组件选择条件中添加高级切片器。

图形选择条件
图形选择条件

高级切片器配置如下,其中上下文条件应该选择与图形维度一致的产品分类层次结构。

操作符使用 前 N 的TopCount 函数, 并且设置 N 的值为参数 TopNSales (创建方式同上述参数的方式),这样就可以动态的设置前多少名的产品。

TopN高级切片器
TopN 高级切片器
提示

这里 汇总其他项 指是否将 Top N 之外的其他数据项汇总为一个 Other 项,如果开启在后续处理数据时需要注意它。

参数输入控制器

每当新增参数时故事都会自动创建相应的参数输入控制器组件以绑定此参数进行输入控制。对于我们的类型为数字的参数,可以使用数字输入控制器或者滑杆输入控制器,如下图所示

参数输入框
参数输入框
参数滑杆输入
参数滑杆输入
提示

用户还可以手动创建输入控制器,并选择控制器的字段为相应参数即可。注意同一故事仪表板中不能为同一个参数创建多个输入控制器。

自定义逻辑

对于累计值的比例计算和图形绘制我们使用自定义逻辑脚本的方式,代码如下

// queryResult: QueryReturn<any>, chartAnnotation: ChartAnnotation, entityType: EntityType, locale: string, chartsInstance: ECharts
const measure = chartAnnotation.measures?.[0];
const pareto = chartAnnotation.measures?.[1];
const benchmark = chartAnnotation.measures?.[2];
const dimension = chartAnnotation.dimensions?.[0];
const {
echarts,
getEntityHierarchy,
getPropertyCaption,
assignDeepOmitBlank,
formatting,
} = utils;

if (!dimension || !measure) {
return {};
}

// 获取维度的层次结构字段信息
const hierarchy = getEntityHierarchy(entityType, dimension);
// 获取维度成员的显示名称字段名
const caption = getPropertyCaption(hierarchy);

const source = [...queryResult.data];
source.sort((a, b) => b[measure.measure] - a[measure.measure]);
// 计算度量值范围
const min = source[source.length - 1][measure.measure];
const max = source[0][measure.measure];
const options = {
dataset: [
{
source: source.map((item, index) => ({
...item,
pareto:
(source
.slice(0, index + 1)
.reduce((acc, item) => acc + item[measure.measure], 0) /
item[pareto.measure]) *
100,
})),
},
],
visualMap: {
min,
max,
calculable: true,
type: "piecewise",
orient: "horizontal",
left: "center",
bottom: "10",
precision: 1,
dimension: measure.measure,
// Measure palette colors as visualMap color
inRange: measure.palette?.colors
? {
color: measure.palette?.colors,
}
: null,
formatter: (low, high) => {
return formatting(low, measure.formatting) + ' ~ ' + formatting(high, measure.formatting)
}
},
grid: [
{
containLabel: true,
bottom: 30,
},
],
series: [
assignDeepOmitBlank(
{
id: measure.measure,
name: "销售额",
type: "bar",
measure: measure.measure,
datasetIndex: 0,
selectedMode: "single",
yAxisIndex: 0,
encode: {
x: caption,
y: measure.measure,
tooltip: [],
},
tooltip: {},
xAxisIndex: 0,
},
measure.chartOptions?.seriesStyle,
5
),
{
id: "pareto",
name: "销售额累计%",
type: "line",
measure: "pareto",
datasetIndex: 0,
selectedMode: "single",
yAxisIndex: 1,
encode: {
x: caption,
y: "pareto",
tooltip: [],
},
tooltip: {},
xAxisIndex: 0,
markLine: {
data: [
{
yAxis: source[0][benchmark.measure],
name: "Benchmark",
lineStyle: benchmark.chartOptions?.seriesStyle?.lineStyle,
},
],
},

...(pareto.chartOptions?.seriesStyle ?? {}),
},
],

tooltip: [
{
trigger: "axis",
valueFormatter: (value) => {
return formatting(value, measure.formatting, locale);
},
},
],
legend: [{ show: true }],
dataZoom: [],
xAxis: [
assignDeepOmitBlank({
type: "category",
axisPointer: {
label: {},
},
axisLabel: {
formatter: (value, index) => {
return source[index][caption];
},
},
orient: "xAxis",
gridIndex: 0,
}, dimension.chartOptions?.axis, 5),
],
yAxis: [
{
type: "value",
axisLabel: {},
gridIndex: 0,
axisLabel: {
formatter: (value) => {
return formatting(value, measure.formatting, locale);
},
},
},
{
type: "value",
axisLabel: {},
gridIndex: 0,
axisLabel: {
formatter: "{value}%",
},
},
],
};

return {
options,
};
提示

有关自定义图形逻辑的更多信息,请参阅之前的博文 范例:AdventureWorks Sales - 5. 故事自定义图形

帕累托分析图形微件最终效果:

在文本中使用参数度量和指标

接下来,我们将在文本组件和组件标题文本中使用参数、度量和指标,以更生动的方式呈现数据的计算结果。如下图所示在文本组件中使用参数和度量,参数格式为 [@name] 度量(同指标)格式为 [#name]

富文本组件中使用参数和度量
富文本组件中使用参数和度量

用户可以手动插入参数和度量,也可以使用右侧的参数和度量列表进行拖拽插入。参数和度量的计算结果将在预览或查看模式下展示。

除了文本组件和富文本组件外,图形组件、表格组件、关键指标组件的标题文本内容也可以使用参数和度量,使用方式与文本组件相同。

关联分析

关联分析 是故事仪表板单个页面内组件之间使用切片器相互过滤的联动分析功能。除了通常的过滤器栏和输入控制器组件能够实现对其他组件的联动分析外,各组件之间也可以进行联动分析。

对于图形组件来说,用户点击图形的数据点会弹出菜单:联动筛选、层级下钻和下钻,用户选择联动筛选后将使用此数据点对应的切片器对其他组件进行联动过滤。

图形的联动分析菜单
图形的联动分析菜单
提示

还可以通过设置图形卡片组件选项中的 实时联动分析禁用上下文菜单 做到无需弹出菜单而是点击图形实时进行联动分析。

对于表格组件来说,选中某个单元格后将使用此单元格对应的维度成员对其他组件进行联动过滤,如果此单元格是度量值,那么将使用当前行上对应的所有维度成员去联动过滤其他组件。

表格的联动分析
表格的联动分析

用户可以在图形和表格组件的工具栏中找到切片器按钮,打开切片器面板,可以看到影响当前组件的所有切片器,用户还可以在临时添加更多切片器,但这里添加的切片器不会被保存。

影响组件的过滤器
影响组件的过滤器

总结

总结一下,元数分析云的动态参数和关联切片器功能为您提供了更灵活和深入的数据探索方式。您可以根据不同的参数和选择,快速生成洞察,展示结果,并在不同组件之间进行有意义的联动分析。故事仪表板将成为您的数据驱动决策的有力工具,帮助您更好地了解业务情况,制定有效的战略。

感谢您继续关注我们的《元数分析云》系列博文!敬请期待下一篇文章,我们将继续分享更多关于高级功能的实用技巧。如果您有任何问题或建议,请随时与我们联系。

附录