Python数据分析实验 - DBSCAN

上个星期接到一个任务,是分析一段期货数据。因为没有接触过金融数据的分析,就单纯地从以前学过的内容出发,想到了用DBSCAN的方法,试图找出大幅偏离主题数值的点。DBSCAN 是一个聚类算法,可以在没有事先设定聚类数目和平均值等信息的情况下完成分类。这个算法本身基于密度分类的思路在我看来很吸引人。但是实际使用的时候却发现由于交易数据不连续(并非24小时开市),DBSCAN的聚类将每一天的数据分作一组。这个时间断层过于significant,导致数据本身含有的特殊点被忽略。并且由于这个方法没有普遍应用于time series analysis,implement的过程十分波折。其中,datetime的数据类型不能直接input到DBSCAN里,我不得不将其转换成“自数据中最小年份的1月1日开始经过的秒数”,然后standard_scale,再输入到DBSCAN里。其实这个过程中对数据的细节造成了多大的扭曲和损失都未可知。因此放弃了这个方案,开始研究正统的time series analysis怎么做。

dbscan.png

Fig 1. DBSCAN generated figure

从Fig1中可以看到每日数据之间的断层远超过数据点之间的波动。

代码记录:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
from matplotlib import dates
import os
import time
import pandas as pd
from datetime import datetime

from sklearn.cluster import DBSCAN
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import normalize
from sklearn.decomposition import PCA

os.chdir(os.path.dirname(__file__))

# read data and parse the date data
df = pd.read_csv('sum.csv', parse_dates=['date'])
df['date'] = pd.to_datetime(df.date, format='%Y-%m-%d %H:%M:%S')
df = df.dropna()

# make a copy of the original data before changing the datetime into number of seconds
df_scaled = df.copy()
oldest = min(df_scaled['date']).year

#count the number of seconds since '2018-1-1'
def toseconds(t):
return int((t-datetime(2018,1,1)).total_seconds())
df_scaled['date'] = df['date'].apply(lambda x :toseconds(x))

# number of data points plotted
num = 60000

#DBSCAN
#首先尝试DBSCAN,试图找出大幅偏离主题数值的点
#DBSCAN 试图找出偏离主线的点
#参考 https://blog.csdn.net/qq_32618817/article/details/81172103
t0 = time.time()
scaler = StandardScaler()
df_scaled = pd.DataFrame(scaler.fit_transform(df_scaled),columns = df_scaled.columns)

dbscan = DBSCAN(eps=.03,min_samples=30).fit(df_scaled.iloc[0:num,[1,7]])

fig = plt.figure(figsize=(10,10))
hfmt = dates.DateFormatter('%m-%d %H:%M')
ax = fig.add_subplot(111)
ax.xaxis.set_major_formatter(hfmt)

t = time.time()-t0

print(dbscan.labels_)

plt.scatter(df.iloc[0:num,1], df_scaled.iloc[0:num,7] , c=dbscan.labels_, alpha = 0.6,s = 0.5)
plt.title('time : %f'%t)
plt.autoscale(True)
plt.show()
#DBSCAN 是一个聚类算法,但是实际使用聚类算法的时候却发现,
# 由于交易数据不连续(并非24小时开市),DBSCAN的聚类会倾
# 向于将一天的数据分作一组,这个时间上的断层不好处理