在使用 Excel 的时候, 发现它的 "智能填充" 功能非常有趣, 能够智能地分析我当前的内容, 然后准确预测出我期望得到的值. 排除了 AI 的加成, 发现这个功能其实也可以通过数学理论和简单代码来实现. 经过一番折腾, 终于用 JS 实现了大致的功能, 然后我把它名为 smart-predictor.
项目地址: https://github.com/jrainlau/smart-predictor
什么是 "智能填充"?
首先我们来看两张 gif 图:
是不是很神奇? 假设我有一组给定的数据
[1, 3, 'aaa1', 'bbb2']
,Excel 的智能填充能够给我返回
[5, 7, 'aaa2', 'bbb3', 9, 11 'aaa3', 'bbb4']
这一组数据.
更厉害的是, 智能填充不是简单地对数据进行递增, 而是会对数据进行分组, 每个分组按照自己的规则去进行递增, 就比如说我们可以从 [1, 2, 'x', 3] 得到[3, 4, 'x', 4].
在明白这些结论之后, 我们就可以去讨论它到底是怎么实现的.
Separator
我们用数组
[1, 2, 'a1c', 'a2c']
作为例子. 当我们拿到这样一个数组的时候, 第一步是要对其进行分析, 分析数组内每个元素到底是一个数字, 一段字符串, 还是别的什么东西. 分析完了, 就要给他们都标注更详细的信息, 然后把这些信息都组合起来.
比如数组元素 1, 可以被处理成下面这个样子:
- {
- realValue: 1,
- numericValue: 1,
- splitParts: 'Number',
- index: 0
- }
而数组元素 a1c, 则可以处理成这样:
- {
- realValue: 'a1c',
- numericValue: 1,
- splitParts: ['a', 'c'],
- index: 2
- }
代码请戳: https://github.com/jrainlau/smart-predictor/blob/master/src/separator.js
可以注意到, 我会提取每一个元素的纯数字部分出来, 然后把其余部分通过一个数组储存起来. 这一切就是 Separator 所做的工作, 我们最终会得到一个富含信息的新数组, 然后继续我们的工作吧!
Classifier
智能填充的最小单位是 "组". 当我们通过上一步得到一个富含信息的新数组之后, 接下来就应该对它们进行合理的分组. 分组的动作包含了两个细节:
同一组的数据应该拥有一致的 "类型", 这里我们使用 splitParts 属性去实现.
同一组的数据应该是连续的, 否则的话就要把不连续的数据扔到一个新的组去.
假设有一个数组
[1, 2, 'a1c', 'a2c', 6, 8]
, 元素 1 和 2 就应该被分配到名为 Number 的组去, a1c 和 a2c 则会被分配到名为 ac 的组里面, 而
6
和
8
则会被另外分配到名为 Number1 的新组里面去, 最后结果如下:
- {
- 'Number': [{
- realValue: '1',
- ...
- }, {
- realValue: '2',
- ...
- }],
- 'ac': [{
- realValue: 'a1c',
- ...
- }, {
- realValue: 'a2c',
- ...
- }],
- 'Number1': [{
- realValue: '6',
- ...
- }, {
- realValue: '8',
- ...
- }]
- }
代码请戳: https://github.com/jrainlau/smart-predictor/blob/master/src/classifier.js
通过上述步骤, 我们成功把数据进行分组, 组与组之间的元素并不会相互干扰. 接下来我们需要实现一个专门做 "线性回归" 的方法, 有了这个方法我们才能对数据进行 "预测".
Linear regression
"线性回归" 是一个数学理论, 详情请自己 google 之, 这里我直接使用线性回归的二元一次公式去求得回归直线的斜率:
- y = ax + b
- a = (xx')(yy') / (xx')(xx')
其中 x'是所有点 x 坐标的平均数, 同样的, y'是所有点 y 坐标的平均数.
代码请戳: https://github.com/jrainlau/smart-predictor/blob/master/src/linearRegression.js
通过这条公式, 我们可以轻易得到数组 [1, 3] 的斜率和偏移量为{ a: 2, b:1 }, 然后就可以知道以后的数据走向将会是[5, 7, 9, ...].
这就是整一个 "智能填充" 的核心原理, 接下来我们就可以依靠这个原理去实现数据的预测了.
Predictor
借助线性回归的力量, 我们可以通过设置预测的次数, 挨个挨个地对每一个分组数据进行预测, 然后再把它们组合到一起形成一个新的结果数组.
以上文 Classifier 中的分组数据为例, 对它预测一次, 结果如下:
- {
- 'Number': [{
- realValue: '1',
- index: 0,
- ...
- }, {
- realValue: '2',
- index: 1,
- ...
- }, {
- realValue: '3',
- index: 6,
- ...
- }, {
- realValue: '4',
- index: 7,
- ...
- }],
- 'ac': [{
- realValue: 'a1c',
- index: 2,
- ...
- }, {
- realValue: 'a2c',
- index: 3,
- ...
- }, {
- realValue: 'a3c',
- index: 8,
- ...
- }, {
- realValue: 'a4c',
- index: 9,
- ...
- }],
- 'Number1': [{
- realValue: '6',
- index: 4,
- ...
- }, {
- realValue: '8',
- index: 5
- ...
- }, {
- realValue: '10',
- index: 10,
- ...
- }, {
- realValue: '12',
- index: 11
- ...
- }]
- }
代码请戳: https://github.com/jrainlau/smart-predictor/blob/master/src/predictor.js
由于我们知道每一个数据的下标, 所以我们可以简单又准确地把它们放到正确的位置去, 最后输出如下:
[1, 2, 'a1c', 'a2c', 6, 8, 3, 4, 'a3c', 'a4c', 10, 12]
接下来我们可以来看看测试用例对比 Excel 表现:
More
当前的 smart-predictor 仍然不够 "smart", 它只能预测自然数字, 或者自然数字与字符串的结合, 但仍然不支持对日期格式, 字母列表等数据的预测. 如果各位读者有兴趣, 也非常环境大家来贡献脑洞, 让 smart-predicotr 变得更加智能.
来源: https://segmentfault.com/a/1190000015951750