PowerBI实战-制作带有同比及趋势线的双柱状图

一、引言

今天的PowerBI报表的制作相对有一点复杂,我们直接根据最终展示图来讲解:

可以看到,我们今天要制作的图像需要包括以下几点:时间维度的趋势、两种不同维度的数据对比、不同数据标签的展示、不同年份间环比的标签展示以及上方趋势线以及趋势标签的制作。

最终清晰展现交互性的时间趋势环比双柱状图。

二、具体教学

那么该如何制作这样一张表呢?

我们的基础数据源需要用到三列数据:

【日期列】(图中年份数据)、【双柱状图数据】(图中浅蓝色为中国部分的数据,橙色为Global部分数据)

首先我们需要用到的可视化对象:

接着我们需要构建在两个基础度量值,分别计算当前计算到的中国部分数据总和以及Global部分数据总和:

Current_year_CNPVO_value = sum('PVO&CNI_BI'[Total CHN PVO])

Current_year_GlobalPVO_value = sum('PVO&CNI_BI'[Global PVO])

接着我们绘制我们的双柱状图的代码:

Bar_PVO =
// 'PVO&CNI_BI'[YEAR] ), [000 Current_year_CNPVO_value], [000 Current_year_GlobalPVO_value]

VAR Width_Bar =80 //每个柱形空间宽度

VAR Width_BarAct =35 //每个柱形实际宽度(减小宽度以容纳两个柱子)

VAR GapBetweenBars = 3 //两个柱子之间的间距

VAR Height_ItemLabel = 20 //类别标签高度

VAR Height_Bar = 200 //柱形最大高度

VAR FontSize = 14 //字号

VAR SpaceAboveRect = 100 //柱形上方留一定空间

VAR Height_Total = Height_ItemLabel + Height_Bar + SpaceAboveRect

VAR MaxValueCN = MAXX( ALLSELECTED( 'PVO&CNI_BI'[YEAR] ), [000 Current_year_CNPVO_value] )

VAR MaxValueGlobal = MAXX( ALLSELECTED( 'PVO&CNI_BI'[YEAR] ), [000 Current_year_GlobalPVO_value] )

VAR MaxValue = MAX(MaxValueCN, MaxValueGlobal) //取两个指标的最大值

VAR Chart ="

        <!-- CN PVO 柱子 -->

        <rect

                x='" & (Width_Bar - (Width_BarAct * 2 + GapBetweenBars)) / 2 & "'

                y='" & Height_Total - Height_ItemLabel - Height_Bar * [000 Current_year_CNPVO_value] /                 MaxValue & "'

                height='" & Height_Bar * [000 Current_year_CNPVO_value] / MaxValue & "'

                width='" & Width_BarAct & "'

                fill='lightblue' />

        <!-- Global PVO 柱子 -->

        <rect

                x='" & (Width_Bar - (Width_BarAct * 2 + GapBetweenBars)) / 2 + Width_BarAct + GapBetweenBars & "'

                y='" & Height_Total - Height_ItemLabel - Height_Bar * [000 Current_year_GlobalPVO_value] / MaxValue & "'

                height='" & Height_Bar * [000 Current_year_GlobalPVO_value] / MaxValue & "'

                width='" & Width_BarAct & "'

                fill='orange' />

        <!-- 数值标签 -->

        <text

                x='" & (Width_Bar - (Width_BarAct * 2 + GapBetweenBars)) / 2 + Width_BarAct / 2 & "'

                y='" & Height_Total - Height_ItemLabel - Height_Bar * [000 Current_year_CNPVO_value] /                 MaxValue - 5 & "' text-anchor='middle'

                font-size='" & FontSize * 0.7 & "'

                fill='deepskyblue'

        >" & "</text>

        <text

                x='" & (Width_Bar - (Width_BarAct * 2 + GapBetweenBars)) / 2 + Width_BarAct * 1.5 + GapBetweenBars & "'

                y='" & Height_Total - Height_ItemLabel - Height_Bar * [000 Current_year_GlobalPVO_value] / MaxValue - 5 & "'

                text-anchor='middle'

                font-size='" & FontSize * 0.7 & "'

                fill='orange'

>" & "</text>

        <!-- 年份标签 -->

        <text

                x='" & Width_Bar / 2 & "' y='" & Height_Total - FontSize / 2 & "' text-anchor='middle'

                dominant-baseline='middle'

                font-size='" & FontSize & "'

                font-weight='bold' >" & SELECTEDVALUE('PVO&CNI_BI'[YEAR]) & "</text>"

RETURN

        "<svg xmlns='http://www.w3.org/2000/svg' id='" &         SELECTEDVALUE('PVO&CNI_BI'[YEAR]) & "'

        width='" & Width_Bar & "'

        height='" & Height_Total & "'>"

        & Chart & "

</svg>"

然后是将对应数据与上面绘制的图像相结合:

Stylesheet = 
// 'PVO&CNI_BI'[YEAR] ), [000 Current_year_CNPVO_value], [000 Current_year_GlobalPVO_value]
VAR n = COUNTROWS( ALLSELECTED( 'PVO&CNI_BI'[YEAR] ))
VAR Width_Bar = 80 //每个柱形空间宽度
VAR Width_BarAct_CNPVO = 35 //CNPVO柱形实际宽度
VAR Width_BarAct_Global = 21 //GlobalPVO柱形实际宽度(CNPVO宽度的60%)
VAR Width_Total = Width_Bar * n
VAR Height_ItemLabel = 20 //类别标签高度
VAR Height_Bar = 200 //柱形最大高度
VAR FontSize = 12 //字号
VAR SpaceAboveRect = 100 //柱形上方留一定空间
 

// 分别计算CNPVO和GlobalPVO的最大值
VAR MaxValue_CNPVO =
    MAXX ( ALLSELECTED ( 'PVO&CNI_BI'[YEAR] ), [000 Current_year_CNPVO_value] )
VAR MaxValue_GlobalPVO =
    MAXX ( ALLSELECTED ( 'PVO&CNI_BI'[YEAR] ), [000 Current_year_GlobalPVO_value] )

// 分别计算CNPVO和GlobalPVO的总高度
VAR Height_Total_CNPVO = Height_ItemLabel + Height_Bar + SpaceAboveRect
VAR Height_Total_Global = Height_ItemLabel + Height_Bar + SpaceAboveRect

// 修正FirstValue和LastValue的计算
VAR FirstValue =
    CALCULATE(
        [000 Current_year_CNPVO_value],
        TOPN(1, ALLSELECTED('PVO&CNI_BI'[YEAR]), 'PVO&CNI_BI'[YEAR], ASC)
    )
VAR LastValue =
    CALCULATE(
        [000 Current_year_CNPVO_value],
        TOPN(1, ALLSELECTED('PVO&CNI_BI'[YEAR]), 'PVO&CNI_BI'[YEAR], DESC)
    )
VAR Gap = DIVIDE(LastValue-FirstValue,FirstValue)
 

// 获取第一个和最后一个柱子的中心X坐标
VAR FirstBarCenterX = Width_Bar / 4          // 调整gap线的首端位置
VAR LastBarCenterX = Width_Total - Width_Bar*(3/4) // 调整gap线的尾端位置
 

// 创建横向排列的柱形
VAR HorizontalBars = 
CONCATENATEX(
    ALLSELECTED('PVO&CNI_BI'[YEAR]),
    VAR CurrentValue = [000 Current_year_CNPVO_value]
    VAR GlobalValue = [000 Current_year_GlobalPVO_value]
    
    // 分别计算CNPVO和GlobalPVO的柱子高度
    VAR CNPVO_BarHeight = Height_Bar * CurrentValue / MaxValue_CNPVO
    VAR Global_BarHeight = Height_Bar * GlobalValue / MaxValue_GlobalPVO
    
    // 分别计算CNPVO和GlobalPVO的柱子Y位置
    VAR CNPVO_PositionY = Height_Total_CNPVO - Height_ItemLabel - CNPVO_BarHeight
    VAR Global_PositionY = Height_Total_Global - Height_ItemLabel - Global_BarHeight
    
    VAR PositionX = (RANKX(ALLSELECTED('PVO&CNI_BI'[YEAR]), 'PVO&CNI_BI'[YEAR],,ASC) - 1) * Width_Bar

    
    // 分别计算CNPVO和GlobalPVO的X位置
    VAR CNPVO_PositionX = PositionX + (Width_Bar - Width_BarAct_CNPVO) / 2
    VAR Global_PositionX = PositionX + (Width_Bar - Width_BarAct_Global) / (1.2)
    
    VAR YearValue = SELECTEDVALUE('PVO&CNI_BI'[YEAR])
    
    // 分别计算CNPVO和GlobalPVO柱子中心X坐标
    VAR CNPVO_BarCenterX = CNPVO_PositionX + Width_BarAct_CNPVO / 2
    VAR Global_BarCenterX = Global_PositionX + Width_BarAct_Global / 2
    
    // 计算百分比标签的X坐标
    VAR MonthIndex = RANKX(ALLSELECTED('PVO&CNI_BI'[YEAR]), 'PVO&CNI_BI'[YEAR],,ASC)
    VAR PercentPositionX = (MonthIndex - 1) * Width_Bar + (Width_Bar - Width_BarAct_CNPVO) / 5.5
    VAR PercentBarCenterX = PercentPositionX + Width_BarAct_CNPVO / 4
    
    // 计算百分比标签的Y坐标,并向上移动10个单位
    VAR PercentBarHeight = Height_Bar * CurrentValue / MaxValue_CNPVO * 0.3
    VAR PercentPositionY = Height_Total_CNPVO - Height_ItemLabel - PercentBarHeight - 45 - 10
    
    // 计算CNPVO数值标签的Y坐标,位于百分比标签下方
    VAR CNPVO_ValueLabelY = PercentPositionY + 20


RETURN
    // CNPVO柱形
    "<rect x='" & CNPVO_PositionX & "' y='" & CNPVO_PositionY & 
    "' width='" & Width_BarAct_CNPVO & "' height='" & CNPVO_BarHeight & 
    "' fill='transparent' rx='2'/>" &
    
    // GlobalPVO柱形
    "<rect x='" & Global_PositionX & "' y='" & Global_PositionY & 
    "' width='" & Width_BarAct_Global & "' height='" & Global_BarHeight & 
    "' fill='transparent' opacity='0.6' rx='2'/>" &
    
    // 年份标签
    "<text x='" & CNPVO_BarCenterX & "' y='" & (Height_Total_CNPVO - 5) & 
    "' text-anchor='middle' font-size='" & FontSize * 0.8 & "'>" & YearValue & "</text>" &
    
    // CNPVO数值标签
    "<text x='" & PercentBarCenterX & "' y='" & CNPVO_ValueLabelY &
    "' text-anchor='middle' font-size='" & FontSize * 0.8 & "'>" & FORMAT(CurrentValue, "#,##0.0") & "</text>" &
    
    // GlobalPVO数值标签(使用Global柱子的中心X坐标)
    "<text x='" & Global_BarCenterX & "' y='" & (Global_PositionY - 5) &
    "' text-anchor='middle' font-size='" & FontSize * 0.7 & "' fill='black'>" & FORMAT(GlobalValue, "#,##0.0") & "</text>"
)


// LineBtw保持不变(百分比标签的位置计算),但需要向上移动10个单位
VAR LineBtw = ADDCOLUMNS(ALLSELECTED('PVO&CNI_BI'[YEAR]),"line",
VAR CurrentYear = SELECTEDVALUE('PVO&CNI_BI'[YEAR])
VAR PreviousYearIndex = RANKX(ALLSELECTED('PVO&CNI_BI'[YEAR]), 'PVO&CNI_BI'[YEAR],,ASC) - 1
VAR PreviousValue = 
    IF(PreviousYearIndex >= 1,
        CALCULATE(
            [000 Current_year_CNPVO_value],
            WINDOW(
                -1,
                REL,
                -1,
                REL,
                ALLSELECTED('PVO&CNI_BI'[YEAR]),
                ORDERBY('PVO&CNI_BI'[YEAR])
            )
        )
    )

VAR YoY = IF(ISBLANK(PreviousValue) || PreviousValue = 0, BLANK(), DIVIDE([000 Current_year_CNPVO_value] - PreviousValue, PreviousValue))
VAR MonthIndex = RANKX(ALLSELECTED('PVO&CNI_BI'[YEAR]), 'PVO&CNI_BI'[YEAR],,ASC)
VAR CurrentValue = [000 Current_year_CNPVO_value]
VAR BarHeight = Height_Bar * CurrentValue / MaxValue_CNPVO * 0.3 // YoY显示在柱子上的高度
VAR PositionX = (MonthIndex - 1) * Width_Bar + (Width_Bar - Width_BarAct_CNPVO) / 5.5
VAR BarCenterX = PositionX + Width_BarAct_CNPVO / 4  // 柱子中心X坐标
VAR PositionY = Height_Total_CNPVO - Height_ItemLabel - BarHeight - 45 - 10  // 向上移动10个单位
RETURN
IF(NOT ISBLANK(YoY),
    "<text x='" & BarCenterX & "' y='" & PositionY & 
    "' font-size='" & FontSize * 0.8 & "' text-anchor='middle' fill='" & 
    IF(YoY>0,"green","red") & "'>" & FORMAT(YoY,"▲0.0%;▼0.0%;-") & "</text>"
))

// 使用CNPVO的总高度作为SVG容器的高度
VAR _CSS = 
"body { background-image: url(""data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='" &  Width_Total & "' height='" & Height_Total_CNPVO & "'><path d='M" & FirstBarCenterX & " " & Height_Total_CNPVO - Height_ItemLabel - Height_Bar * FirstValue / MaxValue_CNPVO & "L" & FirstBarCenterX & " " & SpaceAboveRect * 0.2 & " " &  LastBarCenterX & " " & SpaceAboveRect * 0.2 & " " & LastBarCenterX & " " & Height_Total_CNPVO - Height_ItemLabel - Height_Bar * LastValue / MaxValue_CNPVO & "' fill='none' stroke='lightgrey' stroke-width='1.5' stroke-dasharray='5 2'/><path d='M" & Width_Total / 2 - Width_Bar  & " " & SpaceAboveRect * 0.2  & "L" & Width_Total / 2 & " " & SpaceAboveRect * 0.1 & " " &  Width_Total / 2 + Width_Bar   & " " & SpaceAboveRect * 0.2 & " " & Width_Total / 2 & " " & SpaceAboveRect * 0.3 & "' fill='" & IF (Gap>0,"green","tomato") &"'/><text x='" & Width_Total / 2 & "' y='" & SpaceAboveRect * 0.25 & "' font-size='" & FontSize & "' text-anchor='middle' fill='snow'>" & FORMAT(Gap,"+0.0%;-0.0%;-") & "</text><text x='" & LastBarCenterX & "' y='" & Height_Total_CNPVO - Height_ItemLabel - Height_Bar * LastValue / MaxValue_CNPVO - 15 & "' font-size='12' text-anchor='middle' fill='grey'>▼</text>" & HorizontalBars & CONCATENATEX(LineBtw,[line]) & "</svg>"");background-repeat: no-repeat; }" & "
#htmlContent {
    display: flex; 
    flex-wrap: nowrap; 
    overflow-x: auto;
    white-space: nowrap; 
}"
RETURN IF(n>1,_CSS)

代码部分确实有一点复杂,大家可以参照代码后面所标的注释一同理解,如果大家想要套用,只需修改表以及列的相关变量即可。

接着,我们拖入刚刚创建的第一个长度量值名称为【Bar_PVO】到Values部分,Year为表格中日期维度。

然后把第二个长度量值【Style Sheet】拖入到该视觉对象Format下的【Style Sheet】中

接着我们就得到了所想要制作的效果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/98191.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/98191.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

物联网智能网关配置教程:实现注塑机数据经基恩士PLC上传至云平台

一、项目背景随着制造业向智能化、信息化方向快速发展&#xff0c;注塑车间作为塑料制品制造的核心环节&#xff0c;面临着设备协议多样、数据孤岛严重、系统集成困难等问题。某大型注塑企业计划对其老旧车间进行数字化改造&#xff0c;实现设备数据采集、远程监控与MES系统对接…

【实战】预警算法--噪声添加机制

1. 背景 在多变量自联想预测或异常检测场景中&#xff0c;我们常使用带噪自编码器&#xff08;Denoising AutoEncoder&#xff0c;DAE&#xff09;来训练模型&#xff0c;使模型能够从带噪输入中重构原始数据。噪声的添加方式对训练效果、稳定性以及模型用途有显著影响。 2. 两…

ChromaDB探索

关于 ChromaDB、向量与 RAG 系统的核心知识问答总结 ​​Q1: ChromaDB 是什么&#xff1f;它在数据库领域中扮演什么角色&#xff1f;​​​​A:​​ ChromaDB 是一款开源的​​向量数据库​​。它的核心角色是专门为 AI 应用&#xff08;如语义搜索、推荐系统、RAG&#xff09…

C# 基于halcon的视觉工作流-章33-矩状测量

C# 基于halcon的视觉工作流-章33-矩状测量 本章目标&#xff1a; 一、gen_measure_rectangle2准备提取垂直于矩形的直边&#xff1b; 二、measure_pos 提取垂直于矩形或环形弧的直线边缘&#xff1b; 三、measure_pairs提取垂直于矩形或环形弧长轴的直边对&#xff1b; 四、匹配…

Day05_苍穹外卖——Redis店铺营业状态设置

目录1.1 Redis简介1.2 Redis下载与安装1.2.1 Redis下载1.2.2 Redis安装1.3 Redis服务启动与停止1.3.1 服务启动命令1.3.2 客户端连接命令1.3.3 修改Redis配置文件1.3.4 Redis客户端图形工具2. Redis数据类型2.1 五种常用数据类型介绍2.2 各种数据类型特点3. Redis常用命令3.1 字…

双指针:字符串

题目&#xff1a;字符串 题目概述&#xff1a;找包含所有小写字母的最短字符串。 重点思路&#xff1a; right是 < len-1字符 - ‘26’转换成整形再判断&#xff08;写字符a也可以&#xff0c;更准确&#xff09;。 #include <iostream> #include <algorithm>…

HarmonyOS 应用开发深度实践:精通 Stage 模型与 UIAbility 生命周期

好的&#xff0c;请看这篇关于 HarmonyOS Stage 模型与 UIAbility 深度实践的技术文章。 HarmonyOS 应用开发深度实践&#xff1a;精通 Stage 模型与 UIAbility 生命周期 引言 随着 HarmonyOS 4、5 的广泛部署和 HarmonyOS NEXT (API 12) 的发布&#xff0c;华为的分布式操作系…

DEDECMS 小程序插件简介 2.0全新上线

网上有很多的dedecms的小程序插件&#xff0c;但是有的依赖他们第三方、有的需要一定php或sql基础、有的插件免费但是小程序源码价格昂贵&#xff0c;这也是促使我开发dedecms小程序插件的一大原因。2025年9月4日 dedecms小程序插件2.0版本正式上线&#xff0c;由于使用人数减少…

Flink 1.17.2 集群安装部署

Flink集群的安装 1. 集群规划 Ip host Server Note 192.168.10.101 node01 jobManager、TaskManagerRunner 老大和小弟服务 192.168.10.102 node02 TaskManagerRunner 小弟 192.168.10.103 node03 TaskManagerRunner 小弟 注意&#xff1a;本次使用jdk-1.8.0…

[vue.js] 树形结点多选框选择

vue.js前端代码&#xff1a; <template><div><el-tree:data"treeData"node-key"id"show-checkboxref"tree"check-change"handleCheckChange"/><el-button click"getSelectedNodes">获取选中的节点&…

Web 服务器基本工作流程

这是一个关于 ​​Web 服务器基本工作流程​​ 的全面解释。我们以最经典的 ​​客户端-服务器-后端​​ 三层架构为例&#xff0c;并结合你之前遇到的 Nginx 场景进行说明。​​核心角色​​​​客户端 (Client)​​&#xff1a; 通常是 ​​Web 浏览器​​ (Chrome, Firefox)…

IDEA 连接MySQL数据库

一、 连接数据库1、打开连接2、建立连接3、输入用户名和密码二、操作数据库1、选择数据库2、New| Query Console 查询控制台3、写查询语句4、New| SQL Script| sql Generator 生成这个数据库表的SQL结构New | SQL Script | Generate DDL to Query Console 在查询控制台生成…

江协科技STM32课程笔记(二)—外部中断EXTI

二、外部中断EXTI中断&#xff1a;在主程序运行过程中&#xff0c;出现了特定的中断触发条件&#xff08;中断源&#xff09;&#xff0c;使得CPU暂停当前正在运行的程序&#xff0c;转而去处理中断程序&#xff0c;处理完成后又返回原来被暂停的位置继续运行。1、stm32中断简介…

Java常见排序算法实现

以下是Java中几种常见排序算法的实现&#xff0c;包括冒泡排序、选择排序、插入排序、快速排序和归并排序。 各排序算法特点说明&#xff1a;冒泡排序&#xff1a; 原理&#xff1a;重复比较相邻元素&#xff0c;将大的元素逐步"冒泡"到数组末尾特点&#xff1a;稳定…

Python爬虫实战:研究Pandas,构建地理信息数据采集和分析系统

1. 引言 1.1 研究背景 地理数据作为描述地球表面空间要素的数据,包含了丰富的空间位置、分布特征和属性信息,在城市规划、环境监测、商业分析等众多领域发挥着不可替代的作用。随着 "数字地球"、"智慧城市" 等概念的提出和发展,地理数据的重要性日益凸…

nvm安装node后出现报错: “npm 不是内部或外部命令,也不是可运行的程序 或批处理文件”

一、问题描述 使用nvm安装node后&#xff0c;使用npm命令报错如下 报错1&#xff1a;npm : 无法将“npm”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写&#xff0c;如果包括路径&#xff0c;请确保路径正确&#xff0c;然后再试一次。报错2&#xf…

【高等数学】第十二章 无穷级数——第二节 常数项级数的审敛法

上一节&#xff1a;【高等数学】第十二章 无穷级数——第一节 常数项级数的概念和性质 总目录&#xff1a;【高等数学】 目录 文章目录1. 正项级数及其审敛法1. 正项级数及其审敛法 正项级数 各项都是正数或零的级数称为正项级数正项级数收敛 正项级数 ∑n1∞un\displaystyle\…

图观 流渲染场景编辑器

一、 产品简介图观 流渲染场景编辑器&#xff0c;以编辑器插件形式&#xff0c;在Unreal Engine中轻松编辑并发布数字孪生场景。支持 GIS 全球/局部 数字孪生场景构建&#xff0c;并预置 图观技术架构工程模板&#xff0c;支持对 场景效果、镜头视野&#xff0c;环境时间气象、…

Visual Studio 函数头显示引用个数

在visual studio 里面有自带的显示引用方案 codeLens

数据结构的哈希表冲突解决方法

哈希表是一种高效的数据结构,通过哈希函数将键映射到存储位置。但由于哈希函数可能将不同键映射到相同位置(称为哈希冲突),需要有效的方法来解决冲突。以下是常见的冲突解决策略,每种方法都有其原理、优缺点和适用场景。我将逐步解释这些方法,确保内容清晰可靠。 1. 开放…