查找数字列表的“形状”(直线/凹/凸,几个波峰)

3 投票
2 回答
3162 浏览
提问于 2025-04-20 05:11

这有点难以解释。我有一个整数列表。比如说,[1, 2, 4, 5, 8, 7, 6, 4, 1],如果把这些数字画成图,跟元素的编号对比起来,会像一个凸起的曲线。那我该怎么从这个列表中提取出这种“形状”的特征呢?不需要特别准确,只要大致的形状,比如一个凸起的有一个隆起,或者凹下去的有两个隆起,或者是一条直线等等,这样就可以了。

我可以为每种可能的形状使用条件判断:例如,如果在某个索引之前,斜率是正的,而在之后是负的,那就是一个斜坡,倾斜的程度可以根据index/list_size来决定。

有没有更聪明、更通用的方法呢?我想这可能是一个分类问题,但没有机器学习的话,这样做可能行不行呢?

谢谢。

2 个回答

0

如果你对数据进行差分处理(也就是用 x[i+1] - x[i] 这样的方式),一直重复这个过程,直到所有的结果都是同一个符号,这样怎么样呢?举个例子,如果你差分两次,结果都是非负的,那就说明这个数据是凸的。要是结果不一样,那就再差分一次,看看符号。如果你觉得差分次数太多,比如超过10次,就可以认为这个序列太复杂,没法简单描述了。否则,你的数据形状就可以通过你差分的次数和最后的符号来判断。

9
numpy.diff 

一阶差分的计算方式是:out[n] = a[n+1] - a[n]

https://docs.scipy.org/doc/numpy-1.10.1/reference/generated/numpy.diff.html

import numpy as np

data = [1, 2, 4, 5, 8, 7, 6, 4, 1]
data = np.array(data, dtype=float)
velocity = np.diff(data)
acceleration = np.diff(velocity)
jerk = np.diff(acceleration)
jounce = np.diff(jerk)

print data
print velocity
print acceleration
print jerk
print jounce

>>>
[ 1.  2.  4.  5.  8.  7.  6.  4.  1.]

# positive numbers = rising
[ 1.  2.  1.  3. -1. -1. -2. -3.]

# positive numbers = concave up
[ 1. -1.  2. -4.  0. -1. -1.]

# positive numbers = curling up
[-2.  3. -6.  4. -1.  0.]

# positive numbers = snapping up
[  5.  -9.  10.  -5.   1.]

https://en.wikipedia.org/wiki/Velocity

https://en.wikipedia.org/wiki/Acceleration

https://en.wikipedia.org/wiki/Jerk_(physics)

https://en.wikipedia.org/wiki/Jounce

我的习惯是先把一阶导数(速度)用移动平均值来除,然后乘以100来转换成百分比变化率;有时候加速度也很重要;曲率... 你越往后计算“抖动”或“冲击”,数据就会变得越随机和嘈杂。

你也可以计算每个值的平均:

print np.mean(data)
print np.mean(velocity)
print np.mean(acceleration)

以便对这个样本集的形状进行概括:

>>>
4.22222222222     # average value
0.0               # generally sideways; no trend
-0.571428571429   # concave mostly down

然后计算平均相对标准差

import numpy as np
data = [1, 2, 4, 5, 8, 7, 6, 4, 1]
coef_variance = np.std(data) / np.mean(data)
print coef_variance

>>>0.566859453383

我会称之为“相当波动”;但并不是极端的波动;通常大于1就被认为是“高度变异”。

https://en.wikipedia.org/wiki/Coefficient_of_variation

如果我们绘制图表:

import matplotlib.pyplot as plt
import numpy as np

data = [1, 2, 4, 5, 8, 7, 6, 4, 1]
x = range(9)

plt.plot(x,data,c='red',ms=2)

plt.show()

我们可以看到这基本上很好地描述了我们发现的情况:

enter image description here

没有整体的上升或下降趋势,相当波动,向下凹;平均值略高于4

你还可以进行多项式拟合:

import matplotlib.pyplot as plt
import numpy as np

data = [1, 2, 4, 5, 8, 7, 6, 4, 1]
x = range(9)
plt.plot(x,data,c='red',ms=2)
poly = np.polyfit(x,data,2)
z = []
for x in range(9):
    z.append(poly[0]*x*x + poly[1]*x + poly[2])
x = range(9)
plt.plot(x,z,c='blue',ms=2)
print poly
plt.show()

这会返回:

[-0.37445887  3.195671   -0.07272727]

换句话说:

-0.374x^2 +  3.195x - 0.072

这会绘制出:

enter image description here

从这里你可以计算平方和,以查看你的模型有多准确。

在numpy/scipy中计算平方和差异(SSD)

你还可以重复多项式拟合的过程,每次增加多项式的次数。

np.polyfit(x,data,degree)

直到你得到一个足够低的SSD,以满足你的需求;这会告诉你你的数据更像是x^2、x^3、x^4等等。

while ssd > your_desire:               
   poly_array = polyfit()
   ssd = sum_squares(poly_array, data)
   degree +=1

撰写回答