在学习别人做的报表的时候,我有看到如下图所示的效果。
我个人觉得这个趋势图比较有意思,于是就研究了一下是如何画出来的。
经研究发现,这个就是用SVG画出来的。
前面介绍SVG的时候提到过,SVG可以用来画一些自定义的图表。
这里我们主要就学习如何用SVG画折线图。
图中所示的折线图的含义是:每类电影票房趋势的变化(by year)。
即:X轴——year,Y轴——每年该类电影票房总值。
由于是缩略图,我们肯定不能按照真实的year和票房来展现X、Y轴。
而需要对X、Y轴进行一些处理,让缩略图显示得更加美观。
对X、Y轴的处理主要就是:计算相对位置(当前值在整个区间内的比例)。
X轴:以最小年份作为X轴的起点,最大年份作为X轴的终点,然后计算当前年份在整个区间内的百分比位置。Y轴同理。
比如X轴:假设某电影的上映年限为2000年-2020年,如果当前年份为2010年,那么2010年就应该在整个X轴的50%的位置。
Y轴同理:假设某电影的票房区间(最大值-最小值)(by year)为一亿,而当前年份的票房高出最低票房三千万,则该年的Y坐标就应该在整个Y轴的30%的位置。
注:必须理解 “相对位置” 的概念,才能知道图是何如画出来的。
折线图的简单示例:
<svg xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 500 500'>
<polyline
fill='none'
stroke='red'
stroke-width='4'
points='0,40 40,40 40,80 80,80 80,120 120,120 120,160'/>
</svg>
画简单的折线图,我们只需要用到两个标签。
标签属性说明:
- xmlns:说明这段代码所遵从的标准;
- viewBox:设置可视窗口的大小;
- fill:设置图形填充颜色;
- stroke:设置图形边框的颜色;
- stroke-width:设置图形边框的宽度;
- points:画折线图的点位信息,每个点位用空格隔开,SVG会按顺序将各个点连接起来;
如:points=‘0,40 40,40 40,80 80,80 80,120 120,120 120,160’
代表依次从点1(0,40)到点2(40,40)到点3(40,80)…,直到最后一个点,绘制折线图。
可以在 SVG图形展示 中查看SVG的图形形状 (将代码粘贴到文本框中,然后运行即可)。
前面的SVG代码就可以绘制出下面的折线图:
为了整个图片的呈现美观,我们需要先对SVG图标的大小进行设计。
这里,我们将整个SVG图标的大小设计成 100*100 的大小(如果没有特殊要求,我们一般采用100*100的大小)。
X轴:
① 求出年份的最大值和最小值;
② 计算 (当前年份-最小年份) 占整个年份区间内的比例;
如:年份区间为:2000至2020,当前年份为2010。
[(2010-2000)/(2020-2000)]*100%=50%。
则当前年份的X坐标应该在整个X轴的50%的位置。
③ 对②中计算出来的比例值乘以100放大,并以此值作为X坐标;(因为②中得到的比例范围为0-1,乘以100,刚好是0-100的范围)
如:②中举例年份的X坐标为50。
Y轴:
Y轴和X轴的步骤一样。
① 求出该类电影票房的最大值和最小值(by year);
② 计算 (当前年份票房-最小票房) 占整个票房区间的比例;
③ 对②中计算出来的值乘以100放大,并以此值作为Y坐标。
box_office_sum = sum('movie_metadata'[box office])
VAR XMinDate = MIN('movie_metadata'[year])
VAR XMaxDate = MAX('movie_metadata'[year])
VAR YMinValue = MINX(VALUES('movie_metadata'[year]),CALCULATE([box_office_sum]))
VAR YMaxValue = MAXX(VALUES('movie_metadata'[year]),CALCULATE([box_office_sum]))
VAR SparklineTable = ADDCOLUMNS(
SUMMARIZE('movie_metadata','movie_metadata'[year]),
"X",INT(100 * DIVIDE('movie_metadata'[year]- XMinDate , XMaxDate - XMinDate)),
"Y",INT(100 * DIVIDE([box_office_sum] - YMinValue,YMaxValue - YMinValue)))
从前面的polyline标签的points属性的介绍中,我们知道,points属性是由多个点组成的,每个点用空格隔开。
即points属性的格式如下:
points='0,40 40,40 40,80 80,80 80,120 120,120 120,160'
要将第4步中的表格数据格式转换成points属性需要的文本格式。
我们可以使用下面的语句:
VAR Lines = CONCATENATEX(SparklineTable,[X] & "," & 100-[Y]," ", [year])
使用concatenatex()函数将X、Y值用逗号连接起来,然后将多行的数据用空格来隔开,并用 [year] 来正序排列X、Y值。
注:使用“100-[Y]”的原因:使用SVG画图时,它的坐标系是从左上角开始的;而我们一般画图时的坐标系是从左下角开始的,所以Y值需要用整个画布的大小,减去原来的Y值,得到新的Y值,作为SVG画图的Y坐标(相当于将图片" 上下颠倒 ")。
SVG画图时的坐标系:
注:concatenatex()函数详解:理解 CONCATENATEX
box_office_sum = sum('movie_metadata'[box office])
Box_office_sparkline =
// Static line color - use %23 instead of # for Firefox compatibility
VAR LineColor = "%234A588A"
// "Date" field used in this example along the X axis
VAR XMinDate = MIN('movie_metadata'[year])
VAR XMaxDate = MAX('movie_metadata'[year])
// Obtain overall min and overall max measure values when evaluated for each date
VAR YMinValue = MINX(VALUES('movie_metadata'[year]),CALCULATE([box_office_sum]))
VAR YMaxValue = MAXX(VALUES('movie_metadata'[year]),CALCULATE([box_office_sum]))
// Build table of X & Y coordinates and fit to 100 x 100 viewbox
VAR SparklineTable = ADDCOLUMNS(
SUMMARIZE('movie_metadata','movie_metadata'[year]),
"X",INT(100 * DIVIDE('movie_metadata'[year]- XMinDate , XMaxDate - XMinDate)),
"Y",INT(100 * DIVIDE([box_office_sum] - YMinValue,YMaxValue - YMinValue)))
// Concatenate X & Y coordinates to build the sparkline
VAR Lines = CONCATENATEX(SparklineTable,[X] & "," & 100-[Y]," ", [year])
// Add to SVG, and verify Data Category is set to Image URL for this measure
VAR SVGImageURL = IF(HASONEVALUE('movie_metadata'[type]),
"data:image/svg+xml;utf8," &
" &
" ",
BLANK())
RETURN if(isblank(sum('movie_metadata'[box office])),blank(),SVGImageURL)
写完度量值后,还需要将度量值的数据类型改成“图像 URL”,否则只能显示SVG源代码,无法解析成图片。
注:原始数据格式如下:
想要自己练习的小伙伴可以按照这个格式,生成测试数据来练习。
或者**直接私信我,要原始数据文档和 .pbix 文件。**