Power BI内置条形图、柱形图一般是这样的:

但其实也可以是这样:

这样:

这样:

以上所有效果不依赖任何第三方视觉对象,完全使用Power BI内置图表生成,交互功能一个不少(比如工具提示),数据标签,条件格式也完全支持。这是怎么办到的?

答案是:矩阵
下图是常见的一个矩阵样式,总共有10列,每个格子填充了一个数字1。如果填充的内容不是1,而是图案,则可以实现文章开始的各种效果。

如果一个店铺业绩最高,为10万,条形图填充西红柿,可以想到,它需要10个格子都填充,如果是9万,则填充9个西红柿。现实生活不会这么整齐,如果是9.3万呢?可以想到前面9个格子需要完整的西红柿,最后一个格子只需要西红柿的30%。大家可以看到,下方两个店铺,数据仅微差,最后一个西红柿像素级精确切割。

图案的来源可以是emoji库(比如这个https://unicode-table.com/cn/emoji/),Power BI支持直接显示emoji,或者使用UNICHAR函数链接相应编码。

如果是完全的整数,事情将会变得简单,但世界总是有余数,最后一个格子如何按余数切割图案?答案是我也不知道。切割很麻烦,可以逆向思维,把多余的部分覆盖掉。覆盖反而很简单,在度量值使用SVG中的矩形(rect)可以精确覆盖。比如7.6万这个业绩,前面7格完整的西红柿,第8格还是完整的西红柿,只是西红柿的右边40%用矩形进行遮挡。
2. 实现过程
新建一个索引表,这里有两种方式,一种是DAX使用GENERATESERIES函数新建,第二种是Power Query直接={1..9},索引的最大值反映了你想要显示图案的密集程度,越密集最大值设置的越高即可,一般建议10个以内,示例是9个。

新建条形图度量值,如下,度量值标记为图像URL,其中的业绩替换为你的指标进行复用。
矩阵条形图-Emoji =
VAR MaxValue =
MAXX ( ALLSELECTED ( '店铺资料'[店铺名称] ), [M.销售业绩] )
VAR N =
MAXX ( ALL ( '索引表' ), '索引表'[Value] )
VAR Width_ALL = 48 * N * [M.销售业绩] / MaxValue // N个格子
VAR Width_N =
INT ( Width_ALL / 48 )
VAR Width_MOD =
MOD ( Width_ALL, 48 )
VAR Fill_Emoji =
"<text font-size='35' x='24' y='24' text-anchor='middle' dominant-baseline='middle'>"
& UNICHAR (127813 ) & "</text>"
VAR Chart =
"data:image/svg+xml;utf8,
<svg xmlns='http://www.w3.org/2000/svg' width='48' height='48' viewbox='0 0 48 48'> "
& IF (
SELECTEDVALUE ( '索引表'[Value] ) <= Width_N,
Fill_Emoji,
IF (
SELECTEDVALUE ( '索引表'[Value] ) = Width_N + 1,
Fill_Emoji & "
<rect x='"
& Width_MOD &
"' y='0' height='48' width='"
&
48
- Width_MOD &
"' fill='white' />"
,
BLANK ()
)
) & "
</svg>"
VAR LABEL =
"data:image/svg+xml;utf8,
<svg xmlns='http://www.w3.org/2000/svg' width='48' height='48' >
<text font-size='25' x='24' y='24' text-anchor='middle' dominant-baseline='middle'>"
& FORMAT ( [M.销售业绩] / 1000, "#,#" ) & "</text>
</svg>"
RETURN
IF ( HASONEVALUE ( '索引表'[Value] ), Chart, Label )拖拽一个矩阵,字段如下设置:

此时,你看到的是如下的样式,本度量值充分利用总计功能充当数据标签,如果你不需要数据标签,关掉矩阵的总计即可。

矩阵上方的索引号可以设置为白色颜色或者使用方框进行遮挡。如果想要替换图案,将度量值中的UNICHAR换为任意表情符号(输入法一般自带,或者使用上方推荐的emoji列表)

以上是条形图的示例,柱形图道理一样,起初你进行翻转时,样式可能如下,数据标签在下方,因为矩阵的总计在下方,这时需要把度量值中text的内容换为维度内容。维度可能存在文字过长的问题,处理起来还需要额外的功夫。

如果emoji表情库那么多图案你都不想用,一定要自定义,那就不得不上SVG中的path了,万物皆path。以下是魔方的例子:
矩阵条形图-Path-魔方 =
VAR MaxValue =
MAXX ( ALLSELECTED ( '店铺资料'[店铺名称]), [M.销售业绩] )
VAR N =
MAXX ( ALL ( '索引表' ), '索引表'[Value] )
VAR Width_ALL = 48 * N * [M.销售业绩] / MaxValue // N个格子
VAR Width_N =
INT ( Width_ALL / 48 )
VAR Width_MOD =
MOD ( Width_ALL, 48 )
VAR Fill_Path=
"<path d='M15.5 9L7 14V24V34L15.5 39L24 44L32.5001 39L41 34V24V14L32.5001 9L24 4L15.5 9Z' stroke='#333' stroke-width='2' stroke-linecap='round' fill='none' stroke-linejoin='round'/><path d='M41 14L24 24' stroke='#333' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/><path d='M7 14L24 24' stroke='#333' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/><path d='M24 44V24' stroke='#333' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/><path d='M32 19L32 39' stroke='#333' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/><path d='M41 24L24 34' stroke='#333' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/><path d='M24 34L7 24' stroke='#333' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/><path d='M16 39L16 19' stroke='#333' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/><path d='M32 9L16 19' stroke='#333' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/><path d='M32 19L16 9' stroke='#333' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/>"
VAR Chart =
"data:image/svg+xml;utf8,
<svg xmlns='http://www.w3.org/2000/svg' width='48' height='48' viewbox='0 0 48 48'> "
& IF (
SELECTEDVALUE ( '索引表'[Value] ) <= Width_N,
Fill_Path,
IF (
SELECTEDVALUE ( '索引表'[Value] ) = Width_N + 1,
Fill_Path & "
<rect x='" & Width_MOD & "' y='0' height='48' width='" & 48 - Width_MOD & "' fill='white' />",
BLANK ()
)
) & "
</svg>"
VAR LABEL =
"data:image/svg+xml;utf8,
<svg xmlns='http://www.w3.org/2000/svg' width='48' height='48' >
<text font-size='25' x='24' y='24' text-anchor='middle' dominant-baseline='middle'>"
& FORMAT ( [M.销售业绩] / 1000, "#,#" ) & "</text>
</svg>"
RETURN
IF ( HASONEVALUE ( '索引表'[Value] ), Chart, Label )度量值中Fill_Path是自定义的图案内容,图案代码可以在阿里(https://www.iconfont.cn/)或者字节(https://iconpark.oceanengine.com/official)的图标库查询,也可以自己用PPT画一个,另存为SVG,再用记事本打开复制里面的代码到Fill_Path。
以上演示每个条形柱子均相同,如需图案不一样可以使用SWITCH函数切换,如需颜色不一样可以将度量值中的fill(填充色)或者stroke(边框色)设置条件进行变换。
以上讲解的差不多了,因为图表寄生在矩阵上,因此交互能力和原生图表完全一致。有读者最后问了句,这种图能下钻吗?当然能,请对度量值中的最大值进行SWITCH层级切换处理。