查找数字列表的“形状”(直线/凹/凸,几个波峰)
这有点难以解释。我有一个整数列表。比如说,[1, 2, 4, 5, 8, 7, 6, 4, 1]
,如果把这些数字画成图,跟元素的编号对比起来,会像一个凸起的曲线。那我该怎么从这个列表中提取出这种“形状”的特征呢?不需要特别准确,只要大致的形状,比如一个凸起的有一个隆起,或者凹下去的有两个隆起,或者是一条直线等等,这样就可以了。
我可以为每种可能的形状使用条件判断:例如,如果在某个索引之前,斜率是正的,而在之后是负的,那就是一个斜坡,倾斜的程度可以根据index/list_size
来决定。
有没有更聪明、更通用的方法呢?我想这可能是一个分类问题,但没有机器学习的话,这样做可能行不行呢?
谢谢。
2 个回答
如果你对数据进行差分处理(也就是用 x[i+1] - x[i]
这样的方式),一直重复这个过程,直到所有的结果都是同一个符号,这样怎么样呢?举个例子,如果你差分两次,结果都是非负的,那就说明这个数据是凸的。要是结果不一样,那就再差分一次,看看符号。如果你觉得差分次数太多,比如超过10次,就可以认为这个序列太复杂,没法简单描述了。否则,你的数据形状就可以通过你差分的次数和最后的符号来判断。
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()
我们可以看到这基本上很好地描述了我们发现的情况:
没有整体的上升或下降趋势,相当波动,向下凹;平均值略高于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
这会绘制出:
从这里你可以计算平方和,以查看你的模型有多准确。
你还可以重复多项式拟合的过程,每次增加多项式的次数。
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