数组折线图压缩

背景

想一想,图片压缩我们已经司空见惯了,好比哔哩哔哩的视频就有1080P, 720P,已经480P和360P。不同的视频他的分辨率不一样。
那么我们考虑一个新鲜的东西,我们用来画图的数据怎么来进行压缩呢?好比下面的曲线:
随机生成的数据曲线
上面的数据呢是我随即生成了10000个数据点绘制出来的,那么,问题来了,如果想对上面的数据进行压缩要怎么办呢?

思路

思考一下视频的压缩,图表数据的压缩应该要做到下面的两点:

  1. 要像图片那样,压缩后像素点个数要降低,也就是说压缩以后呢数据图表的数据点个数相应也要降低
  2. 压缩后的图片不应该 失真严重,也就是说压缩前是什么形状的,你压缩后也应该是什么形状的

基于上面的两点我们考虑这个要怎么来实现。其实这里要做的就一个任务:将数据中的整体无关的冗余信息去除即可,那么那些属于冗余信息呢?

对于折线图来说,如果好几个点连接成的是一条直线,而我们知道,画一条直线只需要连个点就可以画成一条直线,那么这两个点之间的数据就属于冗余信息了,在上面的图中就是下面圈出来的部分,当然下面图片中只摘取了一部分
可以摘要的点
好了,那么接下来的步骤就是怎么找到这些类似于直线的部分了
数学里面有一个工具对于找到数据中的直线部分(其实就是导数不变)特别有用,那就是二次导数。利用二次导数我们可以很轻松的找到函数中的直线部分。

当然还有一个不同的一点是,我们程序中使用的数据都是离散的数据,即它们x的取值是无法做到无限小的,那么怎么对这些离散的值进行求导呢?

很幸运在python中,numpy给我们提供了一个diff函数,可以简单给离散值求导,当然自己实现也很简单,就是让数组后面一个值减去前面一个值。

实现思路

经过上面的分析,大致的实现思路如下:

  1. 利用numpy的diff函数对离散数组进行两次运算,求得离散数组的二次导数,再对结果取绝对值(因为我们只要二次倒数接近0的值,因此绝对值方便运算)
  2. 筛选二次导数数组,对二次倒数小于某个临界值的值从原数组中去除

就这么简单,下面是实现的代码

生成随机曲线的代码

生成用于测试的曲线,这个不赘述

def random_arr(lens, grad_hold, grad):
    arr = []
    for i in range(int(lens / grad_hold)):
        grade = random.randint(-grad, grad)
        for j in range(grad_hold):
            if i == 0 and j == 0:
                arr.append(random.random() * grade)
            else:
                arr.append(arr[i * grad_hold + j - 1] + (random.random()) * grade)
    return np.array(arr)

对数组求二次导数并取绝对值

很简单就是利用numpy做简单的计算

def dff_abs(arr):
    return np.abs(np.diff(np.diff(arr)))

剔除二次倒数在临界值一下的值

这里呢我做了一点小小的改动,这里面多了一个weight的参数,它用来控制临界值,这里weight的作用是,假如说我们只想保留原数组中的50%的数据点,我们将weight=0.5即可
实现思路就是先将二次倒数数组排序,找到weight所在百分比的二次导数值,将这个二次导数值作为临界值,这样就能保证小于百分之weight*100的值被省略了。

def reduce_arr(arr, dff_arr, weight):
    sorted_dff_arr = np.sort(dff_arr)
    reduce_index = sorted_dff_arr[int((len(dff_arr) - 1) * (1. - weight))]
    x = [0, 1]
    y = [arr[0], arr[1]]
    for i in range(len(dff_arr)):
        if dff_arr[i] > reduce_index:
            x.append(i + 2)
            y.append(arr[i + 2])
    return x, y

测试结果

当然,一个问题就是临界值怎么选,上面的实现方式就换成了这个weight怎么取值的问题,很明显取小了会有大量的值被忽略掉从而出现严重失真,取大了会有一些数据冗余。怎么取值呢?还是试验出真知吧。
下面是数据取值从2%-100%的数据压缩情况:
测试结果
从上面可以看到,当点数较少的时候数据会严重失真,当点数慢慢变多的时候数会慢慢变得和原始图像一致了,而且(经过反复测试)最小在点数占比为37%的时候,图像几乎和原始图像一致,而且很不凑巧的是这个37%恰恰是最优停止理论中的最优停止值 1 e \frac{1}{e} e1, 这里是不是就这么凑巧呢?咱也不知道。其实可以看到在采样为百分之十几的时候已经能做到大部分能贴合原始曲线了(尤其是那些类似于直线的部分最容易贴合),而最难的就是那些拐角的地方,必须采样更多的点来进行贴合。

这里不知道各位还有什么好的方法没有?什么你问我这个工作中什么地方能用到吗?说实话暂时还真没有发现那里可以用到。就是感觉挺好玩的,之前也尝试过使用指数平均来对原始图像计算后再计算二次导数,发现没什么作用,还是借用数学之美里面的思想来结尾吧:正确的数学模型往往都是简单的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值