生成商业图表的应用程序





2.00/5 (1投票)
B4A 生成商业图表的应用程序
引言
通过图表可视化数据可以更清晰地理解现象,前提是我们选择合适的图表,并且对于某些类型,我们不期望精确的对应关系。
一个图表可以包含一个或多个数值数据集,这些数据集可以被视为一个坐标,而另一个坐标则是一个隐含的递增数字,对应于一个枚举或时间点。
某些图表可以接受多个数据集,如果它们具有兼容的比例,就可以一起绘制。两个非同质的数据集可以在散点图中排列,其中第一个数据集生成的点成为一个气泡,其尺寸与第二个数据集中的相应数据成比例。
如果我们有一个分区数据集,或者说,一个百分比数据集,那么最佳显示方式是饼图或环形图。
当然,这个简要的检查并不详尽,它涵盖了附带文档中可能找到的所有图表或变体,但它足以说明所开发的程序。
下表是一个总结。
图表类型 | 数据类型 | |
Area | 具有相同比例的数据集 | 线性现象的值 |
Bar | 具有相同比例的数据集 | 时间或枚举数据 |
Bubble | 具有不同比例的数据集 | 气泡代替点,尺寸与第二个数据集成比例 |
Line | 具有相同比例的数据集 | 通常在固定点采集的现象的值 |
差值 | 相同比例数据集的组合 | 两个数据集的差值 |
饼图、环形图 | 数据集的分区 | 可能的第二个数据集创建可变扇区 |
Point | 具有相同比例的数据集 | 通常在固定点采集的现象的值 |
维恩图 | 两个数据集及其交集 |
使用程序
该程序是一个“沙盒”,允许使用我的 B4A 表单生成器 来尝试脚本 Chart.bas
,该生成器用于输入生成图表所需的数据。Chart.bas
是一个活动模块,它包含两个函数,每个函数都有一个参数,即一个描述图表的 string
。
CallSubDelayed2(Charts,"startChart",param) ' for portrait chart
CallSubDelayed2(Charts,"changeOrientation",param) ' for landscape chart
程序的工作原理
图表的创建基于一个描述,其中组成图表的对象后面跟着它们的属性以及可能的值;如果一个属性有多个值,例如,颜色或标签列表,则必须用撇号括起来。对象的顺序不重要;但是,如果存在多种图表类型,某些属性将取自第一个图表。图表描述使用正则表达式进行解析(有关机械原理的解释,请参阅 正则表达式使用示例 段),解析后,使用 B4A 对象 canvas
生成图表。
生成图表的算法没有任何特别的困难,除了创建饼图切片和表示维恩图。您可以在附带文档的 **技术参考** 章节中找到涉及的数学知识。
饼图切片
在开发此程序所使用的 B4A 版本中,无法绘制弧线,只能绘制圆形,因此可以通过裁剪适当的区域来获得切片,绘制圆形并移除裁剪区域。
在上图右侧,虚线围合了用于获得切片的裁剪区域。裁剪区域需要以笛卡尔坐标表示的点,这意味着对于点 B,需要计算圆和直线 y = tan(θ)*x 的交点,而点 A 是直线
- y = tan(α+θ)*x
- y = (-1/ tan(θ))*x
相反,使用极坐标,任务就容易多了,事实上,这三个点的坐标是
- C(0, 0)
- B(R, θ)
- A(R*SQRT(1+tan(α)* tan(α))), θ+α) (AB 的长度是 R*tan(α),根据勾股定理 CA2 = R2 + R*tan(α)2)
通过关系式:x = ρ * cos(φ)
和 y = ρ * sin(φ)
,可以轻松地将它们转换为笛卡尔坐标。
![]() | 在左侧的图像中,有一个带有爆炸的切片的雷达饼图示例。生成图表的脚本是:Chart Pie color 'Silver' Border labels 'Medicine Letters Mathematics Physics' Data 'Graduate distribution' '100 110 20 77,6.8 4.1 7.9 3.1' Color 'Olive aqua orange purple' Title 'Pie Radar' color Olive Align CENTER explode '2' show '%' 。Sub drawSlice(Canvas As Canvas, center() As Int, _
Radius As Int, startDegree As Float, _
sliceAngle As Float, Color As Int, explode As Int)
Dim cx As Int = center(0)
Dim cy As Int = center(1)
' explode contains 0 or 10dip if the
' slice must be exploded
cx = cx + explode * CosD(startDegree+0.5*sliceAngle)
cy = cy + explode * SinD(startDegree+0.5*sliceAngle)
Dim p As Path
Do While sliceAngle > 0
Dim slDg As Float = 72
If slDg > sliceAngle Then slDg = sliceAngle
p.Initialize(cx, cy)
p.LineTo(cx+Radius*CosD_
(startDegree),cy+Radius*SinD(startDegree))
Dim ip As Float = Radius*Sqrt_
(1+TanD(slDg)*TanD(slDg)) ' hypotenuse
p.LineTo(cx+ip*CosD(slDg+startDegree),_
cy+ip*SinD(slDg+startDegree))
p.lineTo(cx, cy)
Canvas.ClipPath(p)
Canvas.DrawCircle_
(cx, cy, Radius, Color, True, 0)
Canvas.RemoveClip
sliceAngle = sliceAngle - slDg
startDegree = startDegree + slDg
Loop
End Sub
|
维恩图
维恩图可以被看作是数字到图形表示的类比对应,就像饼图中的扇形与相对百分比成正比一样。
因此,在维恩图中,这种比例关系仅在存在两个数据集及其交集的情况下成立;对于三个数据集,这种对应关系仅适用于所有数据集及其两两交集,而不适用于三者交集。在某些情况下,上述比例关系是不可能的。
在下面的片段中,交集区域用于查找两个中心的距离,这是通过二分法完成的
...
Dim intersectArea As Float = itemdata(0).data(2)
Dim x1 As Float = r1+r2 ' at end contains the distance
Dim x2 As Float = 0
Do While Abs(intersectArea - a(0)) > 0.001
Log(intersectArea - a(0))
Dim d As Float = 0.5*(x1+x2)
a = areaOfIntersection(r1,d, r2) ' returns area and ascissa of
' intersection of circles
If a(0) < intersectArea Then x1 = d ' the distance must be decreased
If a(0) >= intersectArea Then x2 = d ' the distance must be augmented
Loop
...
private Sub areaOfIntersection(r1 As Float, d As Float, r2 As Float) As Float()
' first circle is on origin, all center are on abscisse returns area and
' center of intersection of circles
Dim rr1 As Float = r1 * r1
Dim rr2 As Float = r2 * r2
Dim d2 As Float = d * d
Dim phi As Float = 2 * (ACos((rr1 + d2 - rr2) / (2 * r1 * d))) ' cos(α) = (a2+b2-c2)/(2*a*b)
Dim theta As Float = 2 * (ACos((rr2 + d2 - rr1) / (2 * r2 * d)))
Dim a(2) As Float
a(0) = 0.5 * (rr1 * (theta - Sin(theta)) + rr2 * (phi - Sin(phi)))
a(1) = r1 * Cos(phi * 0.5) ' center of intersection
Return a
End Sub
正则表达式使用示例
在网站 https://regex101.com/ 上,您可以尝试您的正则表达式。
B4A 具有 Matcher
对象,其 Match
属性包含与搜索匹配的字符 string
。
解析颜色列表
颜色可以是符号名称之一,即 Colors
对象名称加上一些其他名称,后面可能跟着透明度值或十六进制值,此外,颜色可以重复,例如饼图切片的颜色:color 'red 127 green x7f00007f'
,即透明红色、绿色和透明暗蓝色。
正则表达式是:([xX][0-9a-fA-F]{1,8})|([a-zA-Z]+\s+\d{1,3})|([a-zA-Z]+)
;符号 |
分隔颜色选项,(...)
允许记住找到的值
([xX][0-9a-fA-F]{1,8})
- 这捕获一个十六进制值,最多 8 位数字,前提是它以x
开头([a-zA-Z]+\s+\d{1,3})
- 这捕获一个后跟最多三位数字的单词([a-zA-Z]+) -
这捕获一个单词
请注意,顺序对于捕获不同的颜色编码很重要,并且在提取后,必须验证值。上面有一个用于提取(可能的)值的函数。
Public Sub splitColors(c As String) As List ' contains the list of colors
Dim Match As Matcher = Regex.Matcher(reSplitColors,c)
Dim l1 As List
l1.initialize
Do While Match.Find
l1.Add(interpColor(Match.Match))
Loop
Return l1
End Sub
使用正则表达式 reSplitColors
匹配 string
c
的结果是一个列表,其中包含所有匹配的颜色。
解析标签列表
标签可以用空格分隔,如果标签包含空格,则分隔符是逗号;因此,正则表达式是 \s*([^,]+)\s*,|([^\s,]+)
。
这种情况与前一种情况的处理方式略有不同,因为识别模式可能包含一个不需要提取的分隔符。提取的 string
(即括号内的内容)存储在 Group(n)
属性中,其中 n
是正则表达式的第 n 个替代项。
...
Dim reSplitTokens As String = "\s*([^,]+)\s*,|([^\s,]+)" ' split tokens
'on space or comma \s*([^,]+)\s*,|([^\s,]+)
...
itemData.labels = getItems(replace(getItem("labels","")),reSplitTokens)
...
Sub getItems(s As String, re As String) As String() ' return an array of item with
' removed possibly delimiters
Dim list As List
list.Initialize
Dim Mtch As Matcher
Mtch = Regex.Matcher2(re,Regex.CASE_INSENSITIVE,s)
Do While Mtch.Find
For i=1 To Mtch.GroupCount
If Mtch.Group(i) <> Null Then list.Add(removeDelimiters(Mtch.Group(i)))
Next
Loop
Return list2Array(list)
End Sub
注释
JavaScript 和 B4A Canvas
JavaScript canvas 是一个丰富的对象,其核心是变换矩阵[1]。
在编写此软件时,我与 B4A canvas 的限制进行了斗争,即
注意事项
该程序仍处于初期阶段,除了显而易见的和简单的实现之外,并非所有数据都得到了完美的控制,因此请谨慎使用(自担风险),并严厉批评。显然,欢迎提出建议。
最后,该程序不提供广告,也不读取您的个人数据。
- ^请参阅 JavaScript 课程章节。
- ^在 B4A 的最新版本中,可以将裁剪区域设置为扇区。