Tableau作为国外知名BI产品,其LOD表达式可让用户轻松计算不在可视化详细级别层面上的聚合,做到沉浸式分析,但是LOD又并非能轻易学会。为解决上手问题,官方博客提供了15个经典案例供用户学习,每一个都是经典的高级计算场景,实现能在一个视图中包含多个层次的跨层次分析,为业务分析师提供高级分析思路。
而FineBI作为目前国内帆软旗下领先的BI产品,以BI分析三大件:数据编辑、主题模型、DEF函数作为产品亮点,其中DEF函数(def、def-add、def-sub和earlier函数)则是其专门突破解决复杂计算场景的自研函数。
由于工作需要,老李我经常需要处理复杂计算场景,且近期公司有国产化需求,因此想借本篇文章,来直观测评两款BI产品在实现复杂计算场景的表现情况。
本文将详细展示如何分别使用Tableau及FineBI完成这15个案例,并从中对比、总结出各自的优劣势。
先给结论:
通过本次测评,我们发现针对这15个案例,相较于Tableau的LOD表达式,FineBI的DEF函数在85%情况下能完成相同效果,无需写sql完成,易用性属于中上水平。但FineBI在部分场景下的易用性还稍显不足,例如书写复杂及过滤优先级问题,不过据FineBI产品团队透露,这些问题将在下半年得到解决。
总的来说,通过此次测评可看出FineBI的复杂计算体系已初步构建完成,能满足大部分复杂计算场景,但在各类功能细节处还需不断优化迭代,二者具体优劣势总结可直接见文末测评总结。
场景01:客户订单频率
场景描述:
找出每个客户订购的订单数相对简单,但是如果我们想了解订购过一个订单、两个订单、三个订单(依次类推)的客户数目,该怎么办?要生成相应视图,我们必须按订购的订单数划分客户数。这是一个简单的问题,但是如果没有详细级别表达式,按照某一度量划分另一度量将非常困难。
想一想每个订单包含多个项目的超市销售数据库,各个客户的单独订单数指出了每个客户订购的订单数,只需采用简单的详细级别表达式,即可将订单数转变为按客户数划分的维度。
可视化关键:
不同购买频次的顾客数量
LOD范例:
每个顾客的购买次数:{ FIXED [Customer ID]:COUNTD([Order ID])}
FineBI复刻:
每个顾客的购买次数:DEF(COUNTD_AGG(${Order ID}),[${Customer Name}])
点评:
在此场景,二者无明显差异。
场景02:阵列分析
场景描述:
合作间越长的客户对销售额的贡献越大吗?下面的视图按照客户首次购买的年份将客户分组,以便对比各个阵列的年度销售贡献额。每个客户的最早订单日期将体现出首次购买日期。不过,由于视图中的数据未按客户显示,我们必须使用详细级别表达式来确定每个客户的最早订单日期。
可视化关键:
每年,各年新增顾客群的贡献度
LOD范例:
每个顾客的首次购买日期:{ FIXED [Customer ID]:MIN([Order Date])}
FineBI复刻:
每个顾客的首次购买日期:DEF(MIN_AGG(${Order Date}),${Customer ID})
点评:
在此场景,Tableau里的 FIXED 是不被过滤条件影响,但FineBI里的结果过滤器是明细过滤,所以当需要结果过滤的时候,应该把字段拖到细粒度里,然后用细粒度过滤就是结果过滤了,过滤优先级问题需要改善。
场景03:每日利润 KPI
场景描述:
我们确实可以查看利润随时间的变化趋势,但如果我们想按照每个工作日的总利润来衡量成功,该怎么办?
我们可能想要了解每个月或每年的盈利天数,尤其是在我们想了解季节影响时。下面的视图显示了在以交易级别记录基础数据的情况下,我们如何利用详细级别表达式轻松根据聚合数据创建分级(例如每日利润)。
可视化关键:
每月,三个盈利标签下的天数
LOD范例:
根据每天的利润总额,给每天标记一个利润标签。
每一天的盈利额:{FIXED [Order Date]:SUM([Profit])}
盈利标签:IF [每一天的盈利额] > 2000 THEN "高盈利" ELSEIF [每一天的盈利额] <= 0 THEN "亏损" ELSE "盈利" END
FineBI复刻:
每一天的盈利额:DEF(SUM_AGG(${Profit}),[${Order Date}])
盈利标签:IF(${每一天的盈利额}>2000,"高盈利",IF(${每一天的盈利额}<0,"亏损","盈利"))
点评:
在此场景,二者无明显差异。
场景04:总额百分比
场景描述:
每个国家/地区的收入对全球销售额有何贡献?如果我们按照贡献百分比着色,即可看出美国对全球销售收入的贡献最大。但是,我们可能想重点关注欧盟等在绝对值上相对贡献较小的市场。如果不采用详细级别表达式,对某市场进行筛选会导致总额百分比的重新计算,从而显示每个国家/地区对其市场的贡献。利用简单的详细级别表达式,我们可对某市场进行筛选,同时仍可衡量全球贡献。
可视化关键:
每个区域的销售额,占总的额度占比
LOD范例:
总的销售额,不随筛选器变化
所有国家的销售额的总和:{ sum([sales]) }
FineBI复刻:
所有国家的销售额总和:DEF(SUM_AGG(${Sales}),[])
点评:
在此场景,FineBI同2场景(阵列分析)一样,还是过滤优先级问题,如果想要占比不受过滤器影响的话,只能放到这几个结果过滤的地方,但这样其实不方便查看用户操作。
场景05:新客户争取率
场景描述:
各个市场总客户争取率的每日趋势如何?了解这一数据趋势后,可帮助我们了解地区营销和销售组织在发展新业务方面的表现。线越陡,则争取率趋势越好。如果线逐渐变平,则必须采取一些措施来增加潜在客户流。
详细级别表达式可确保回头客不会误计入新客户,这是因为虽然数据按照市场和天数直观显示,但是必须在客户层面上进行评估。
可视化关键:
每个市场每天的新客户数量
LOD范例:
给每个客户一个标签,标记在当前日期下的状态
首次购买日期:{FIXED [Customer ID]:MIN([Order Date])}
根据购买日期是否等于首次购买日期判断新老客户:IF [Order Date] = [首次购买日期] THEN "新客户" ELSE "老客户" END
通过快速表计算,计算汇总累计客户数。
FineBI复刻:
IF(${Order Date}=${首次购买日期-1},"新客户","老客户")累计客户数,目前没有类似Tableau的表计算,通过函数实现累计求和:
DEF_ADD(${客户数},[],[${Order Date}<=EARLIER(${Order Date}),${Market}=EARLIER(${Market})])点评:
在此场景,FineBI性能问题略显劣势,老李我在用户交流时曾给产品团队反馈过,预计年底会发布windows窗口函数,可以解决性能卡慢问题。
场景06:对比销售额分析
场景描述:
找出与平均值之间的差异相对简单,但是如果想找出与选定类别的差异,该怎么办?首先,必须隔离选定类别的销售额。然后,需要使用 EXCLUDE 表达式在所有其他类别中重复该值。之后,便可轻松了解每个类别的销售额与其他类别的差异。
可视化关键:
每个分类的销售减去选择的分类的销售额
LOD范例:
把选择的产品销售锁定为全局可用:{EXCLUDE [Category] : SUM([选定的category的销售额]) }
FineBI复刻:
引入额外背景条件:DEF_SUB(SUM_AGG(${选定category的销售额}),${Category})
点评:
在此场景,二者无明显差异。
场景07:各个销售代表的平均最大额交易数额
场景描述:
每个销售代表达成的最大交易额是多少?然后想一想,对于各个销售代表达成的最大额交易数额,按国家/地区计算的平均值是多少?
利用详细级别表达式,即使数据按照国家/地区级别直观显示,我们也可以向下查看销售代表详细级别。在下面的视图中,销售代表的平均最大额交易数额在颜色为蓝色的国家/地区较高,而在颜色为橙色的国家/地区较低。我们可以使用这些信息指导对从国家/地区到销售代表的深入分析。
可视化关键:
不同地区的“最大订单金额”的平均值
LOD范例:
计算各销售代表的最大订单金额:{INCLUDE [Sales Rep]:MAX([Sales])}
FineBI复刻:
每个销售代表的最大订单金额:DEF_ADD(MAX_AGG(${Sales}),${Sales Rep})
点评:
在此场景,二者无明显差异。
场景08:实际对比目标
场景描述:
在本可视化视图中,我们描述了每个州连锁咖啡厅的实际利润与目标利润之间的差异。在顶部视图中,我们可以清楚地看出哪些州超额完成目标,哪些州未完成目标。但是采用这种聚合方式,我们会漏掉重要的细微差别。某些州超出目标是因为在该州销售的每项产品都超出目标,另外一些州超出目标是因为单项产品超额完成其目标,足以弥补未完成目标的所有其他产品。我们可以使用详细级别表达式,确定某州内销售的产品中超出目标产品所占百分比。
可视化关键:
每个州,销售超过目标计划的商品占总数占比
LOD范例:
商品详细级别,销售超过目标的商品数量
每个产品的利润和目标利润差异:{INCLUDE [Product]:SUM([Difference Between Actual and Target Profit])}
该产品是否达到目标利润:IIF([每个产品的利润和目标利润差异]>0,1,0)
FineBI复刻:
每个产品的利润和目标利润差异:DEF_ADD(SUM_AGG(${Difference Between Actual and Target Profit}),${Product})
该产品是否达到目标利润:IF(${每个产品的利润和目标利润差异}>0,1,0)
点评:
在此场景,点击区域可以联动显示该区域哪些商品达到了目标利润,不过由于FineBI的参考线不如Tableau的灵活,所以这里修改了数据结构,做了一个类似子弹图的效果。
场景09:周期最后一天的价值
场景描述:
表示具体某天状态的数据(如库存数、员工实际人数或存货的日清算值)需要与可以聚合的指标(例如销售额或利润)区别对待。处理这些数据时,可能希望显示日历月最后一天的值。此外,我们可能希望从每月下钻到每周后,视图能够更新显示每周最后一天的值。
在下面的示例中,我们记录了每天多个时间点的库存数据。该视图对比了平均日清算值和周期最后一天的清算值。使用简单的详细级别表达式,我们可以向下查看每日级别,虽然数据直观显示的是较高级别。
可视化关键:
视图中日期维度(上面为月份)最后一天的闭市价格(close value)
LOD范例:
可视化日期详细级别中中,最后一天的闭市价格。
最后一天:{ INCLUDE:MAX([Date])}
最后一天的闭市价格:IF { INCLUDE:MAX([Date])}=[Date] THEN [Adj Close] ELSE 0 END
FineBI复刻:
最后一天:DEF(MAX_AGG(${Date}),[YEAR(${Date}),MONTH(${Date})])
缺点:DEF_ADD不能max日期也是我没想到的……
最后一天的闭市价格:IF(${Date}=${最后一天},${Adj Close},0)
点评:
在此场景,二者无明显差异。
场景10:各个阵列的回头客
场景描述:
争取新客户的成本可能非常高,因此我们希望能够确保现有客户会重复购买。在一个、两个、三个、N 个季度重复购买的客户数目是多少?从未重复购买的客户数目是多少?如果按季度阵列划分重复购买行为,会怎么样?我们可以使用 FIXED 表达式找到每个客户的第一次和第二次购买日期,并从中计算出重复购买的季度数。
可视化关键:
订单日期与顾客两次购买间隔,对应的顾客数量
LOD范例:
顾客两次购买的时间间隔。
首次购买日期:{FIXED [Customer ID]:MIN([Order Date])}
剔除首次购买日期:IIF([Order Date]> [首次购买日期],[Order Date],NULL)
第二次购买日期:{ FIXED [Customer ID]: MIN([剔除首次购买日期])}
计算两次消费日期间隔:DATEDIFF('quarter',[首次购买日期],[第二次购买日期])
FineBI复刻:
DEF(MIN_AGG(${剔除首次购买日期}),${Customer ID})计算两次消费日期间隔:
DATEDIF(${第一次购买日期},${第二次购买日期},"M")IF(${第二次购买日期}=NULL,NULL,DATEDIF(${第一次购买日期},${第二次购买日期},"M"))点评:
在此场景,FineBI计算时间差没有季度参数,所以这里用月份差代替,以及DEF的结果转换为维度后,必须再次转回指标才可以进行修改,不太方便。
场景11:一系列平均值的百分比差异
场景描述:
示例 6 说明了如何对比单个选定项,但是如果您想对比一系列值,该怎么办?例如,在影响所关注行业的重大事件发生之前,您可能想对比存货的日清算值与平均日清算值。
可视化关键:
无
LOD范例:
额外引用的背景数据:指定ticker的选定日期区间内的平均闭市价格
新建两个参数:开始日期、结束日期返回日期区间的闭市价格数值:IIF([Date]>= [开始日期] and [Date]<= [结束日期],[Adj Close],null)计算选定日期区间内的平均值:{FIXED [Ticker]: AVG([选定日期区间内的闭市价格])}百分比差异:([闭市价格]-[计算选定日期区间内的平均值])/[计算选定日期区间内的平均值]FineBI复刻:
新建两个参数:开始日期、结束日期返回日期区间的闭市价格数值:IF(AND(${Date}>=${开始日期},${Date}<=${结束日期}),${Adj Close},null)返回日期区间的闭市价格的平均值:DEF(AVG_AGG(${返回日期区间的闭市价格数值}),${Ticker})百分比差异:(${Adj Close}-${日期区间的闭市价格的平均值})/${日期区间的闭市价格的平均值}点评:
在此场景,由于FineBI中折线图限制了维度+指标,而不能给维度加参考线,所以做不了Tableau里用参考区间表达日期区间的可视化效果明显
场景12:相对周期筛选
场景描述:
与前一年的年初至今和月初至今进行对比是分析业绩的常用指标。通过筛选相对于今天,我们能够轻松得出分析,但如果数据每周都刷新一次,这时该怎么办?假设您最近一次刷新是在 3 月 1 日,但当前日期是 3 月 7 日。月初至今对比会显示前一年 3 月 1 日到 3 月 7 日与今年的 3 月 1 日的对比。这会导致出现严重警报,但这种情况下不该出现警报!我们可以采用简单的详细级别表达式找出数据集中的最晚日期。
可视化关键:
今年同比去年的利润
LOD范例:
缺少去年YTD的截止日期,需要从今年的max日期中做比照。
最大的日期:{ max ([order date]) }
筛选
dayofyear = DATEPART(‘dayofyear’, {MAX([Order Date])} ) 258天筛选条件:[dayofyear] >= DATEPART(‘dayofyear’, [Order Date] )计算2013年和2014年的累计汇总利润,以及2013年和2014年累计利润的差异,直接通过表计算可视化界面配置即可
FineBI复刻:
步骤:在自助数据集里获取年份、周数,然后按照年月、周数分组汇总,再按年累计汇总,最后行转列得到2014年和2013年的两列累计利润值,最后把累计值为空值的行过滤掉即可
组件里直接计算差异:${2014-按年累计汇总}-${2013-按年累计汇总}
点评:
不能像Tableau里用表计算直接算,略麻烦
场景13:用户登录频率
场景描述:
每月一次、每两月一次、每三月一次(依次类推)登录网站或应用程序的用户数目是多少?平均登录率是多少?这一平均值周围的分布偏斜如何?数据粒度为每个用户 ID 的登录日期。也就是说,用户登录的每一天都记录为一行。生成此视图需要按照登录率划分客户数,即我们必须按照某个度量划分另一个度量。我们在示例 1 中了解了如何使用详细级别表达式轻松进行此类分析。
可视化关键:
首次和二次登录的间隔,对应的用户数量
LOD范例:
首次和二次登录间隔,以及每个用户的登录次数。
首次登录日期+最近登录日期, fixed+min/max函数:{FIXED [User ID]:MIN([Log in Date])}、{FIXED [User ID]:MAX([Log in Date])}计算上述两个日期的间隔,用datediff函数,同时参数定为month:DATEDIFF('month',[首次登录日期],[最近登录日期])计算每个用户的登录次数,fixed +countd:{FIXED[User ID]:COUNTD([Log in Date])}计算每个用户的平均登录周期,间隔/次数:ROUND([间隔日期]/[每个客户的登录次数])参考线,所有用户的平均登录周期:{EXCLUDE [平均登录周期]:AVG([平均登录周期])}柱子的渐变颜色:AVG([平均登录周期])-AVG([所有用户的平均登录周期])FineBI复刻:
首次登录日期+最近登录日期计算两个日期的间隔月份:DATEDIF(${首次登录日期},${最近登录日期},"M")计算每个用户的登录次数:DEF(COUNTD_AGG(${Log in Date}),${User ID})计算每个用户的平均登录周期ROUND(${两个日期的间隔月份}/${每个用户的登录次数},0)复制一个指标,其中一个转维度,用于图表,另一个保留参与后续计算所有用户的平均登录周期:DEF_SUB(AVG_AGG(${每个用户的登录周期1}),${每个用户的登录周期1})柱子的渐变颜色:AVG_AGG(${每个用户的登录周期1})-AVG_AGG(${所有用户的平均登录周期})点评:
由于图表的限制,FineBI中柱形图图表只能是维度+指标,所以做不了竖着的参考线。同时指标转换成维度后,无法继续当做数值计算,所以需要复制一个值计算所有用户的平均登录周期。
场景14:成比例笔刷
场景描述:
任何分析中最基本的问题都是:“对比对象是什么?” 在筛选时,我们有时会需要对比所选内容和总量,而不是简单地向下筛选所选内容。这种技巧称为按比例笔刷。
可视化关键:
国家在每个分类的销售的占比
LOD范例:
全部的销售数量(聚合级别更高)
该子分类在所有国家的销售额:{Fixed [Sub-Category] : Sum([Sales]) }
占比:SUM([sales])/SUM([该子分类在所有国家的销售额])
FineBI复刻:
该子分类在所有国家的销售额:DEF(SUM_AGG(${Sales}),${Sub-Category})
占比:SUM_AGG(${Sales})/SUM_AGG(${该子分类在所有国家的销售额})
点评:
不能完全做到Tableau的效果,图表联动是明细过滤,所以不能像Tableau一样通过点击地图,动态显示每个国家的情况。同时,由于两个柱子不堆积重叠显示,另一个柱子单独显示也做不到,所以用了两个柱子并列+点图的组合图
场景15:各个客户阵列的年度购买频率
场景描述:
如果合作时间按争取到客户的年份度量,忠诚度按年度购买频率度量,那么合作时间越长的客户越忠诚吗?
从示例 1 中,我们可以精确了解购买过一次、两次(依次类推)的客户数目。然而,营销人员极少需要精确确定购买过五次产品的所有客户。相比之下,了解至少购买过五次产品的客户数目更有用。
另外,我们从示例 2 中了解到,2011 年争取到的客户最多,而 2014 年争取到的客户最少。查看客户的绝对数目只能再次了解同样的趋势。因此,我们应该将每个阵列的客户总额百分比作为度量忠诚度的标准,这样会更意义。
所以最初问题更具体的表达是,在每个阵列中在一年中购买过至少一次、两次、三次、N 次产品的客户所占的百分比是多少?
本示例结合了示例 1 中订单数详细级别表达式的变体、示例 2 中的阵列表达式和示例 4 中的总额百分比表达式的变体。
可视化关键:
筛选器:年度客户矩阵的累计合计的百分比,以购买频次为轴
LOD范例:
额外引用的背景数据:至少购买1次、2次、3次、n次的顾客数量、以及阵列(即顾客的获客年份)信息
每个客户最早的购买年份:{FIXED [Customer ID]: MIN(Year([Order Date]))}每年每个客户的购买次数:{FIXED [Customer ID], [Order Date (Years)]: COUNTD([Order ID])}至少购买1、2、n次的客户数:WINDOW_SUM( COUNTD([Customer ID]), 0, LAST())每年的获客总人数:{ FIXED [每个客户最早的购买年份]:COUNTD([Customer ID])}占比:[至少购买1、2、n次的客户数]/SUM([每年的获客总人数])排除100%,因为当年购买的客户肯定至少购买过1次,这样可视化效果不受100%的干扰
FineBI复刻:
每个客户最早的购买年份:DEF( MIN_AGG(YEAR(${Order Date})),${Customer ID})每年每个客户的购买次数:DEF(COUNTD_AGG(${Order ID}),[YEAR(${Order Date}),${Customer ID}])至少购买1、2、n次的客户数:DEF_ADD(COUNTD_AGG(${Customer ID}),[],[${每年每个客户的购买次数1}>=EARLIER(${每年每个客户的购买次数1}),${每个客户最早的购买年份1}=EARLIER(${每个客户最早的购买年份1}),${Order Date (Years)}=EARLIER(${Order Date (Years)})])每年的获客总人数:DEF(COUNTD_AGG(${Customer ID}),${每个客户最早的购买年份1})占比:${组内累计}/${每年的获客总人数}点评:
FineBI指标和维度的转换,指标计算完成后转换成维度,但是维度无法编辑,也不能参与计算,导致需要保留一个计算指标 + 一个指标转换后的维度,略显麻烦。
测评总结
作为BI产品资深用户,通过这次测评,老李我能明显发现,相较于目前国内其余BI产品,FineBI 6.0 的 DEF 函数在仪表板计算方面表现优秀,功能丰富且强大。
值得一提的是,FineBI内置了数据集功能,这不仅弥补了 DEF 函数在表处理方面的不足,还进一步增强了数据整理和准备能力。与之相对,Tableau 的 FIXED 函数虽然具有一定的计算能力,但缺少过滤能力,同时需要借助另一款名为 Tableau Prep 的工具来完成表处理操作,这带来了一定的阻碍。
不过正如开头所说,FineBI在各类功能细节处还需不断优化迭代,建议FineBI团队仍需踏实研究产品逻辑及用户习惯,期间还是有很多可改进及优化的地方,例如:
图表的灵活性:和Tableau对比,无法完成特殊的组合图和一些参考线的效果。复杂函数书写: 目前,在涉及组内排序、组内累计、跨行计算等场景下,函数的书写较为繁琐,需要将其封装成快速计算,以便业务人员更便捷使用。DEF计算优先级问题: 在仪表板中,DEF函数存在优先级问题。例如,在过滤组件和图表联动方面,明细过滤的机制使得部分结果过滤需在组件中配置。这会导致查看用户无法轻松地进行修改和调整。指标计算转换为维度问题: 在柱形图和折线图等情境中,指标计算完成后需要将其转换为维度。然而,维度却无法被编辑,因此再次转换回指标进行编辑。同时,维度也无法参与公式计算,这在某些情况下需要保留一个维度加一个指标的组合。NULL处理问题: 目前,在计算过程中,NULL值被视为当前日期,而非空值。这可能引发一些不准确的计算结果。基础函数问题: 部分基础函数存在缺陷,例如计算时间差缺乏季度参数,导致无法直接计算季度差,限制了一些时间相关计算的可能性。此外,在FineBI组件内部,周数的计算结果与某些函数所得到的周数结果不一致。DEF_ADD不支持MAX_AGG日期等。最后,说一些心里话
由于前司工作原因,老李我很早就在使用FineBI了,因此与FineBI团队也有过数次用户研究及共创。因此,在文章发布前,我曾将文章草稿给到帆软FineBI产品团队共同研讨,他们表示:
“Tableau作为全球领先的BI软件,积累了丰富的经验和强大的功能集,深受众多企业的信赖和喜爱。而FineBI作为国产BI软件,与Tableau相比,我们FineBI团队清楚地认识到还存在一些技术和功能上的差距。但是,我们也并没有被差距所阻挡,我们反而将这些差距视为前进的动力,致力于持续地提升产品性能、用户体验和功能完整性。通过不断地听取用户的反馈意见,努力弥补产品中的不足之处,引入创新功能,优化性能,以便更好地满足用户的需求。”
而针对此次测评,我目前了解到的是FineBI团队已针对2、3、4问题完成设计优化,且其他问题产品也在持续关注中,听说在年末或明年年初能解决上述问题,这段期间不妨让我们共同静候国产BI的成长,给这个认真做产品的团队一些信心。
最后,如果你想体验FineBI的DEF函数功能,文末指路免费试用FineBI。