Python, как кластеризовать многомерные временные ряды? - PullRequest
0 голосов
/ 22 марта 2020

У меня есть временный ряд игрушечных временных рядов в этом формате:

>>> df
                 dtime dev        sw1        sw2
0  2020-01-01 00:00:00  A1   5.496714   5.593792
1  2020-01-01 00:15:00  A1   5.417291   6.385936
2  2020-01-01 00:30:00  A1   6.758800   6.056747
3  2020-01-01 00:45:00  A1   8.189697   7.862034
4  2020-01-01 01:00:00  A1   6.988069   6.595961
5  2020-01-01 01:15:00  A1   7.543641   6.080126
6  2020-01-01 01:30:00  A1   9.912546  10.208666
7  2020-01-01 01:45:00  A1   9.656324   9.917379
8  2020-01-01 02:00:00  A1   8.974970   8.980084
9  2020-01-01 02:15:00  A1  10.542560  10.307973
0  2020-01-01 00:00:00  B1   4.536582   3.121212
1  2020-01-01 00:15:00  B1   5.089826   4.669180
2  2020-01-01 00:30:00  B1   6.353073   6.010359
3  2020-01-01 00:45:00  B1   4.753386   3.951109
4  2020-01-01 01:00:00  B1   5.497304   5.336019
..                 ...  ..        ...        ...
5  2020-01-01 01:15:00  H3   3.044125   3.456906
6  2020-01-01 01:30:00  H3   1.753714   2.575774
7  2020-01-01 01:45:00  H3   0.812104   2.708897
8  2020-01-01 02:00:00  H3   0.647316   0.401928
9  2020-01-01 02:15:00  H3  -1.987569  -2.741305
0  2020-01-01 00:00:00  I3   4.780328   3.890814
1  2020-01-01 00:15:00  I3   4.801557   3.985747
2  2020-01-01 00:30:00  I3   5.366783   5.289681
3  2020-01-01 00:45:00  I3   2.815063   3.156215
4  2020-01-01 01:00:00  I3   1.969284   2.245975
5  2020-01-01 01:15:00  I3   1.720465   2.547648
6  2020-01-01 01:30:00  I3   2.582069   2.595071
7  2020-01-01 01:45:00  I3   1.439862   2.893396
8  2020-01-01 02:00:00  I3   0.025795  -0.238861
9  2020-01-01 02:15:00  I3   0.513267   3.233437

[90 rows x 4 columns]

В каждой строке представлены время (устройство), устройство и положение двух переключателей устройства. Я должен сгруппировать этот временной ряд по положению переключателей. Таким образом, аналогично установленные коммутаторы (с минимальным расстоянием) должны образовывать кластер.

Dataframe создается таким образом, что устройства A1, B1 и C1 должны формировать кластер 1, устройства D2, E2, F2 должны формировать кластер 2, и устройства G3, H3, I3 должны образовывать кластер 3.

Код, которым строятся временные ряды, выглядит следующим образом:

import pandas as pd
import numpy as np

nper = 10 # number of periods
dtime_fr = '2020-01-01' # datetime from
freq = '15T'
dtime_range = pd.date_range(dtime_fr, periods=nper, freq=freq) # timestamps creation

df = pd.DataFrame()

# reising baseline
baseline_start = 5
baseline_stop = 10
baseline_linspace = np.linspace(baseline_start, baseline_stop, nper)
ts = baseline_linspace + np.random.normal(size=nper)
df = pd.concat([df, pd.DataFrame({'dtime' : dtime_range, 'dev': 'A1', 'sw1': ts, 'city':'C1'})], sort=False)
ts = baseline_linspace + np.random.normal(size=nper)
df = pd.concat([df, pd.DataFrame({'dtime' : dtime_range, 'dev': 'B1', 'sw1': ts, 'city':'C1'})], sort=False)
ts = baseline_linspace + np.random.normal(size=nper)
df = pd.concat([df, pd.DataFrame({'dtime' : dtime_range, 'dev': 'C1', 'sw1': ts, 'city':'C1'})], sort=False)

# steady baseline
baseline_start = 5
baseline_stop = 5
baseline_linspace = np.linspace(baseline_start, baseline_stop, nper)
ts = baseline_linspace + np.random.normal(size=nper)
df = pd.concat([df, pd.DataFrame({'dtime' : dtime_range, 'dev': 'D2', 'sw1': ts, 'city':'C1'})], sort=False)
ts = baseline_linspace + np.random.normal(size=nper)
df = pd.concat([df, pd.DataFrame({'dtime' : dtime_range, 'dev': 'E2', 'sw1': ts, 'city':'C1'})], sort=False)
ts = baseline_linspace + np.random.normal(size=nper)
df = pd.concat([df, pd.DataFrame({'dtime' : dtime_range, 'dev': 'F2', 'sw1': ts, 'city':'C1'})], sort=False)


# falling baseline
baseline_start = 5
baseline_stop = 0
baseline_linspace = np.linspace(baseline_start, baseline_stop, nper)
ts = baseline_linspace + np.random.normal(size=nper)
df = pd.concat([df, pd.DataFrame({'dtime' : dtime_range, 'dev': 'G3', 'sw1': ts, 'city':'C1'})], sort=False)
ts = baseline_linspace + np.random.normal(size=nper)
df = pd.concat([df, pd.DataFrame({'dtime' : dtime_range, 'dev': 'H3', 'sw1': ts, 'city':'C1'})], sort=False)
ts = baseline_linspace + np.random.normal(size=nper)
df = pd.concat([df, pd.DataFrame({'dtime' : dtime_range, 'dev': 'I3', 'sw1': ts, 'city':'C1'})], sort=False)

df.insert(3, 'sw2', df.sw1 + np.random.normal(size=len(df)))

df

Если у меня только один переключатель (sw1), Я бы сделал кластеризацию следующим образом:

df_sw1 = df.pivot(index='dev', values='sw1', columns='dtime')
dist = pdist(df_sw1, metric='euclidean')
Z = scipy.cluster.hierarchy.linkage(dist)

fig, ax = plt.subplots(figsize=(20,15))
scipy.cluster.hierarchy.dendrogram(Z, labels=df_sw1.index, orientation='top');

from sklearn.cluster import AgglomerativeClustering

hclust = AgglomerativeClustering(n_clusters=3)
hclust.fit(df_sw1)

hclust.labels_

array([0, 0, 0, 2, 2, 2, 1, 1, 1], dtype=int64)

enter image description here

В случае, когда мне нужно рассмотреть оба коммутатора, если два устройства похожи, sw1 будет иметь (почти) то же значение, что и sw1 другого устройства, и в то же время sw2 будет (почти) иметь то же значение, что и sw2 другого устройства.

Я не уверен, как это сделать.

Какой формат должен иметь формат данных, чтобы рассчитать pdist? Как сделать кластеризацию, если я должен рассмотреть оба коммутатора?

...