Ejemplo n.º 1
0
def test_dtw_subseq1():
    with util_numpy.test_uses_numpy() as np:
        query = np.array([1., 2, 0])
        series = np.array([1., 0, 1, 2, 1, 0, 2, 0, 3, 0, 0])
        sa = subsequence_alignment(query, series)
        mf = sa.matching_function()
        # print(f'{mf=}')
        match = sa.best_match()
        # print(match)
        # print(f'Segment={match.segment}')
        # print(f'Path={match.path}')
        if not dtwvis.test_without_visualization():
            try:
                import matplotlib.pyplot as plt
            except ImportError:
                raise MatplotlibException("No matplotlib available")
            if directory:
                plt.plot(mf)
                plt.savefig(directory / "subseq_matching.png")
                dtwvis.plot_warpingpaths(query,
                                         series,
                                         sa.warping_paths(),
                                         match.path,
                                         filename=directory /
                                         "subseq_warping.png")
                plt.close()
        best_k = sa.kbest_matches(k=3)
        assert match.path == [(0, 2), (1, 3), (2, 4)]
        assert [m.segment for m in best_k] == [[2, 4], [5, 7], [0, 1]]
Ejemplo n.º 2
0
def test_dtw_subseqsearch_eeg():
    with util_numpy.test_uses_numpy() as np:
        data_fn = Path(__file__).parent / 'rsrc' / 'EEGRat_10_1000.txt'
        data = np.loadtxt(data_fn)
        series = np.array(data[1500:1700])
        query = np.array(data[1331:1352])
        # print(f'{len(series)=}')

        k = 3
        s = []
        s_idx = []
        w = 22  # window size
        ws = int(np.floor(w / 2))  # shift size
        wn = int(np.floor((len(series) - (w - ws)) / ws))
        si, ei = 0, w
        for i in range(wn):
            s.append(series[si:ei])
            s_idx.append(si)
            si += ws
            ei += ws
        tic = time.perf_counter()
        sa = subsequence_search(query, s)
        best = sa.kbest_matches(k=k)
        toc = time.perf_counter()
        print(f"Searching performed in {toc - tic:0.4f} seconds")
        # print(sa.distances)
        # print(best)

        if directory and not dtwvis.test_without_visualization():
            try:
                import matplotlib.pyplot as plt
                from matplotlib import gridspec
            except ImportError:
                raise MatplotlibException("No matplotlib available")
            fn = directory / "test_dtw_subseqsearch_eeg.png"
            ymin, ymax = np.min(series), np.max(series)
            fig = plt.figure()
            if k is None:
                k = len(s)
            gs = gridspec.GridSpec(3, k, wspace=0.5, hspace=1)
            ax = fig.add_subplot(gs[0, 0])
            ax.plot(query)
            ax.set_title('Query')
            ax.set_ylim((ymin, ymax))
            for idx, match in enumerate(best):
                ax = fig.add_subplot(gs[1, idx])
                if idx == 0:
                    ax.set_title(f'Best {k} windows')
                ax.set_ylim((ymin, ymax))
                ax.plot(s[match.idx])
            ax = fig.add_subplot(gs[2, :])
            ax.set_ylim((ymin, ymax))
            ax.set_title(f'Series with windows')
            for idx in s_idx:
                ax.vlines(idx, ymin, ymax, color='grey', alpha=0.4)
            ax.plot(series)
            for idx, match in enumerate(best):
                ax.vlines(s_idx[match.idx], ymin, ymax, color='red')
            plt.savefig(fn)
            plt.close(fig)
Ejemplo n.º 3
0
def test_dtw_localconcurrences_short():
    with util_numpy.test_uses_numpy() as np:
        series = np.array([0, -1, -1, 0, 1, 2, 1, 0, 0, 0, 1, 3, 2, 1, 0, 0, 0, -1, 0])

        gamma = 1
        threshold_tau = 70
        delta = -2 * np.exp(-gamma * np.percentile(series, threshold_tau))  # -len(series)/2  # penalty
        delta_factor = 0.5
        tau = np.exp(-gamma * np.percentile(series, threshold_tau))  # threshold
        # print(f'{tau=}, {delta=}')
        buffer = 10
        minlen = 3
        lc = local_concurrences(series, gamma=gamma, tau=tau, delta=delta, delta_factor=delta_factor, penalty=1)
        matches = []
        for match in lc.kbest_matches(k=100, minlen=minlen, buffer=buffer):
            if match is None:
                break
            matches.append(match)

        assert [(m.row, m.col) for m in matches] == [(10, 17), (4, 19)]

        if directory and not dtwvis.test_without_visualization():
            try:
                import matplotlib.pyplot as plt
            except ImportError:
                raise MatplotlibException("No matplotlib available")
            fn = directory / "test_dtw_localconcurrences_short.png"
            fig = plt.figure()
            fig, ax = dtwvis.plot_warpingpaths(series, series, lc.wp, path=-1, figure=fig)
            for match in matches:
                dtwvis.plot_warpingpaths_addpath(ax, match.path)
            plt.savefig(fn)
            plt.close(fig)
Ejemplo n.º 4
0
def test_trace():
    with util_numpy.test_uses_numpy() as np:
        rsrc_fn = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'rsrc', 'Trace_TRAIN.txt')
        data = np.loadtxt(rsrc_fn)
        labels = data[:, 0]
        # series = data[:, 1:]
        series = data[labels == 1, 1:][:2,:].copy()
        # c = series[0, :]
        print(type(series))
        print(series.shape)

        tic = time.perf_counter()
        avg = dtw_barycenter.dba_loop(series, c=None, max_it=100, thr=0.000001,
                                      nb_initial_samples=4, use_c=True)
        toc = time.perf_counter()
        print(f'DBA: {toc - tic:0.4f} sec')

        if directory and not dtwvis.test_without_visualization():
            try:
                import matplotlib.pyplot as plt
            except ImportError:
                raise MatplotlibException("No matplotlib available")
            fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(10,4))
            fn = directory / "test_trace_barycenter.png"

            for serie in series:
                ax[0].plot(serie, alpha=0.5)
            ax[1].plot(avg)

            fig.savefig(str(fn))
            plt.close()
Ejemplo n.º 5
0
def test_dtw_localconcurrences_eeg():
    with util_numpy.test_uses_numpy() as np:
        data_fn = Path(__file__).parent / 'rsrc' / 'EEGRat_10_1000.txt'
        data = np.loadtxt(data_fn)
        series = np.array(data[1500:1700])

        gamma = 1
        # domain = 2 * np.std(series)
        # affinity = np.exp(-gamma * series)
        # print(f'Affinity in [{np.min(affinity)}, {np.max(affinity)}]\n'
        #       f'             {np.mean(affinity)} +- {np.std(affinity)}\n'
        #       f'             {np.exp(-gamma * np.mean(series))} +- {np.exp(-gamma * np.std(series))}\n'
        #       f'             {np.exp(-gamma * np.percentile(series, 75))} / {np.exp(-gamma * np.median(series))} / {np.exp(-gamma * np.percentile(series, 25))}\n')
        tau_stddev = 0.40
        diffp = tau_stddev * np.std(series)
        delta = -2 * np.exp(-gamma * diffp**2)  # -len(series)/2  # penalty
        delta_factor = 0.5
        tau = np.exp(-gamma * diffp**2)  # threshold
        print(f'{tau=}, {delta=}')
        # tau=0.8532234738897421, delta=-1.7064469477794841
        buffer = 10
        minlen = 20
        lc = local_concurrences(series,
                                gamma=gamma,
                                tau=tau,
                                delta=delta,
                                delta_factor=delta_factor)
        print(f'{lc.tau=}, {lc.delta=}')
        matches = []
        for match in lc.kbest_matches(k=100, minlen=minlen, buffer=buffer):
            if match is None:
                break
            matches.append(match)
        print([(m.row, m.col) for m in matches])
        # assert [(m.row, m.col) for m in matches] == [(84, 95), (65, 93), (50, 117), (117, 200), (32, 180),
        #                                              (160, 178), (96, 139), (138, 181), (71, 200), (71, 117),
        #                                              (73, 137), (52, 138), (12, 117), (117, 178), (117, 160),
        #                                              (30, 160), (32, 52), (30, 117), (117, 135), (160, 200),
        #                                              (178, 200), (11, 52), (71, 160), (134, 160), (135, 200),
        #                                              (30, 200), (50, 200), (11, 73), (50, 160), (12, 33), (11, 137),
        #                                              (36, 143), (11, 179), (88, 160), (66, 178), (11, 93)]

        if directory and not dtwvis.test_without_visualization():
            try:
                import matplotlib.pyplot as plt
            except ImportError:
                raise MatplotlibException("No matplotlib available")
            fn = directory / "test_dtw_localconcurrences.png"
            fig = plt.figure()
            fig, ax = dtwvis.plot_warpingpaths(series,
                                               series,
                                               lc.wp,
                                               path=-1,
                                               figure=fig)
            for match in matches:
                dtwvis.plot_warpingpaths_addpath(ax, match.path)
            plt.savefig(fn)
            plt.close(fig)
Ejemplo n.º 6
0
def test_pair():

    with util_numpy.test_uses_numpy() as np:
        s = np.array([[0.5, 1, 2, 3, 2.0, 2.1, 1.0, 0, 0, 0],
                      [0.4, 0, 1, 1.5, 1.9, 2.0, 0.9, 1, 0, 0]])
        # s = np.array([
        #     [5.4407042e-01, 6.5786304e-01, 6.1123908e-01, 5.4541312e-01, 5.3215608e-01, 5.9301252e-01, 5.9528021e-01, 5.3827698e-01, 5.6342901e-01, 5.9595647e-01, 6.3291485e-01, 5.8464636e-01, 6.2318725e-01, 6.0595466e-01, 5.9037231e-01, 6.2702137e-01, 6.1348560e-01, 6.2305276e-01, 5.4601808e-01, 6.4113615e-01, 5.4939481e-01, 6.1069654e-01, 5.7797370e-01, 6.3566551e-01, 5.7421817e-01, 6.0636056e-01, 6.3805334e-01, 6.4059697e-01, 6.0581139e-01, 6.5486549e-01, 6.2933728e-01, 6.7354635e-01, 6.4083011e-01, 6.7106754e-01, 5.9539175e-01, 5.8942609e-01, 6.1587013e-01, 6.6703825e-01, 6.0380075e-01, 6.3386067e-01, 6.4719272e-01, 6.4482981e-01, 6.4368627e-01, 6.2349955e-01, 5.6237133e-01, 6.1063165e-01, 6.0773194e-01, 6.4240876e-01, 5.7791595e-01, 6.8620718e-01, 6.1297880e-01, 1.0087818e+00, 2.3082535e+00, 3.4865161e+00, 3.8273665e+00, 3.8177745e+00, 1.0119979e+00, -1.2480659e+00, -1.8793527e+00, -1.9483470e+00, -1.9013292e+00, -1.9191206e+00, -1.9221467e+00, -1.9814806e+00, -1.9719053e+00, -1.9875261e+00, -1.8793638e+00, -1.9005814e+00, -1.8825048e+00, -1.8736002e+00, -1.8876155e+00, -1.9502441e+00, -1.8904222e+00, -1.8784054e+00, -1.8606558e+00, -1.9099004e+00, -1.8661716e+00, -1.8816032e+00, -1.8314015e+00, -1.7828515e+00, -1.7619036e+00, -1.8435448e+00, -1.8210940e+00, -1.7934786e+00, -1.7384911e+00, -1.7141641e+00, -1.7810861e+00, -1.7017178e+00, -1.7586494e+00, -1.6361580e+00, -1.6761555e+00, -1.6352849e+00, -1.6648909e+00, -1.6537056e+00, -1.5992698e+00, -1.5857011e+00, -1.5706887e+00, -1.5565333e+00, -1.5257045e+00, -1.5343949e+00, -1.5144240e+00, -1.4585244e+00, -1.4340508e+00, -1.3994088e+00, -1.4395239e+00, -1.3883845e+00, -1.3141078e+00, -1.2873144e+00, -1.2437876e+00, -1.2662198e+00, -1.2174011e+00, -1.1404096e+00, -1.0779149e+00, -1.0360995e+00, -1.0235959e+00, -9.4192680e-01, -9.6460812e-01, -9.2692898e-01, -8.0692488e-01, -8.1502916e-01, -8.0227687e-01, -7.7763514e-01, -7.1609000e-01, -7.1871558e-01, -5.9853322e-01, -6.2256531e-01, -6.4287218e-01, -6.1775796e-01, -5.5449064e-01, -5.1017090e-01, -5.0237049e-01, -3.8755927e-01, -4.0832809e-01, -3.6083551e-01, -3.3675164e-01, -2.7108165e-01, -2.3104480e-01, -2.5503431e-01, -1.9585961e-01, -1.3165326e-01, -9.1146626e-02, -1.9168974e-02, -1.3373473e-01, -7.6407845e-02, -6.3702210e-02, -5.8617472e-02, -1.0619203e-02, 7.1343850e-02, 2.1105735e-02, 1.0268053e-01, 1.1936396e-01, 1.1838773e-01, 1.5021707e-01, 1.4664151e-01, 1.4904700e-01, 1.6199376e-01, 2.4104677e-01, 2.3775815e-01, 2.7550593e-01, 3.1824048e-01, 3.7817589e-01, 3.7694541e-01, 3.0430703e-01, 3.5849188e-01, 2.9257194e-01, 3.3785668e-01, 3.7219016e-01, 3.7010148e-01, 4.0480926e-01, 3.7049106e-01, 4.1597004e-01, 4.0382502e-01, 4.3679156e-01, 4.0612548e-01, 4.0152885e-01, 4.7647383e-01, 4.4169304e-01, 4.4384292e-01, 3.8545218e-01, 4.6372423e-01, 4.4630043e-01, 4.4484963e-01, 4.8374203e-01, 4.8585592e-01, 5.3424775e-01, 5.3889814e-01, 4.6434472e-01, 5.1374644e-01, 4.9446973e-01, 5.3983401e-01, 5.2213212e-01, 5.1881301e-01, 5.2453233e-01, 5.6395751e-01, 5.5313703e-01, 6.0116005e-01, 5.5151348e-01, 5.4741656e-01, 5.4669622e-01, 5.8659053e-01, 5.2656235e-01, 5.4569127e-01, 5.7221179e-01, 5.2088122e-01, 5.8197830e-01, 5.7022402e-01, 5.6669003e-01, 5.7069312e-01, 5.6999052e-01, 5.4572128e-01, 5.9778768e-01, 5.7268841e-01, 5.7180281e-01, 6.3555668e-01, 5.0059640e-01, 5.5195177e-01, 5.9130039e-01, 5.5103119e-01, 5.7287569e-01, 5.5867501e-01, 5.9223111e-01, 5.8432069e-01, 5.7091733e-01, 6.0559221e-01, 6.5693579e-01, 5.6511058e-01, 5.5240516e-01, 5.6491058e-01, 5.8311869e-01, 6.3398184e-01, 5.7997700e-01, 6.2535219e-01, 5.8981952e-01, 5.7572385e-01, 6.0549305e-01, 5.3555521e-01, 5.4941540e-01, 5.5352685e-01, 5.9538265e-01, 5.3455956e-01, 5.3054529e-01, 6.1195102e-01, 5.8604957e-01, 5.4530951e-01, 5.5091084e-01, 5.6394117e-01, 6.0954467e-01, 5.3654038e-01, 5.9055258e-01, 5.5313359e-01, 5.8818908e-01, 5.3334192e-01, 5.8794384e-01, 5.8296638e-01, 5.9814783e-01, 6.0032051e-01, 5.6657579e-01, 6.7193951e-01, 6.2128079e-01, 5.5514667e-01, 5.8124140e-01, 6.7463871e-01, 5.7606997e-01, 6.3720999e-01, 5.7822404e-01, 5.8822587e-01, 5.7788588e-01, 5.9806934e-01, 5.8369488e-01, 6.0287742e-01, 5.5426348e-01, 5.1414728e-01, 6.0377304e-01, 5.9633124e-01, 5.8322427e-01],
        #     [5.4137206e-01, 5.7190786e-01, 5.6214670e-01, 5.2095091e-01, 5.2749375e-01, 6.0404306e-01, 5.4052105e-01, 5.8038292e-01, 5.0058210e-01, 5.9781245e-01, 5.5531829e-01, 5.0399817e-01, 4.8721503e-01, 5.2919197e-01, 5.7732146e-01, 5.8306679e-01, 5.6387021e-01, 5.1736838e-01, 5.5365038e-01, 6.3540910e-01, 5.2845206e-01, 5.6114973e-01, 5.0572623e-01, 6.4666921e-01, 5.6247629e-01, 5.6586631e-01, 5.7505220e-01, 6.1367060e-01, 5.9094675e-01, 5.4806871e-01, 5.3635967e-01, 5.8288635e-01, 6.2479649e-01, 5.4263060e-01, 5.3727177e-01, 5.4420961e-01, 4.6516908e-01, 5.1660881e-01, 5.6632066e-01, 5.8851816e-01, 5.5663496e-01, 5.3749507e-01, 6.2990795e-01, 5.2720783e-01, 5.8189400e-01, 6.1033741e-01, 5.6246110e-01, 5.9735452e-01, 5.8936942e-01, 6.2465823e-01, 5.7547395e-01, 5.7821937e-01, 6.3206557e-01, 5.9249883e-01, 5.6148297e-01, 5.9005716e-01, 5.7541260e-01, 6.4415288e-01, 6.1059152e-01, 6.0653983e-01, 6.8802537e-01, 1.0147607e+00, 2.3145332e+00, 3.5409618e+00, 3.8384987e+00, 3.8225459e+00, 3.1880896e+00, -1.3082260e+00, -1.9498738e+00, -1.9228263e+00, -2.0425930e+00, -1.9294838e+00, -2.0234096e+00, -1.9554894e+00, -1.9346159e+00, -1.9389827e+00, -1.9903105e+00, -1.9996996e+00, -1.9364940e+00, -1.9481327e+00, -1.9478419e+00, -1.9529609e+00, -1.8885611e+00, -1.9274720e+00, -1.9067925e+00, -1.8492225e+00, -1.8022711e+00, -1.8571755e+00, -1.8731676e+00, -1.7942706e+00, -1.8352960e+00, -1.8129494e+00, -1.8213582e+00, -1.7435004e+00, -1.7834703e+00, -1.6825393e+00, -1.6991139e+00, -1.6781857e+00, -1.7190823e+00, -1.6429358e+00, -1.5756199e+00, -1.6127048e+00, -1.6271342e+00, -1.5669044e+00, -1.5274913e+00, -1.5603284e+00, -1.5284419e+00, -1.4536210e+00, -1.4746732e+00, -1.4013397e+00, -1.3672767e+00, -1.4367848e+00, -1.3940244e+00, -1.3391982e+00, -1.3087654e+00, -1.2541147e+00, -1.2509465e+00, -1.1480202e+00, -1.1584576e+00, -1.0661748e+00, -1.0838297e+00, -1.0605984e+00, -1.0545557e+00, -9.8054161e-01, -9.7516375e-01, -9.0965554e-01, -9.3395351e-01, -8.3288487e-01, -8.7299559e-01, -7.9129969e-01, -7.9793562e-01, -7.7723834e-01, -6.8966425e-01, -6.8607855e-01, -6.2673536e-01, -6.0840173e-01, -5.7696618e-01, -5.3084902e-01, -5.2748290e-01, -4.3871482e-01, -4.9210561e-01, -4.3981229e-01, -3.2472595e-01, -3.3753481e-01, -2.8367354e-01, -2.7158593e-01, -2.1485086e-01, -2.3176371e-01, -1.5325167e-01, -1.9440216e-01, -1.0415893e-01, -2.4629932e-02, -4.5354485e-02, -3.6301485e-02, 2.0418909e-02, 1.0027844e-01, 8.8132967e-02, 1.2071415e-01, 2.1155213e-02, 9.2120760e-02, 9.1224315e-02, 1.5286046e-01, 1.8740585e-01, 2.0764562e-01, 2.2897012e-01, 1.6721991e-01, 2.4065580e-01, 3.4694248e-01, 1.9462176e-01, 3.1338695e-01, 2.7861734e-01, 3.0417210e-01, 2.9476327e-01, 3.3008070e-01, 3.3258469e-01, 3.5294907e-01, 3.9032386e-01, 3.7415578e-01, 3.7775534e-01, 3.8227615e-01, 4.1445601e-01, 3.7363077e-01, 4.1116205e-01, 4.1717600e-01, 3.9964844e-01, 4.0182791e-01, 3.9896304e-01, 4.6554637e-01, 4.3096288e-01, 4.7565329e-01, 4.7633896e-01, 4.7028428e-01, 4.7656642e-01, 4.2424559e-01, 5.8662101e-01, 4.7144488e-01, 5.0448089e-01, 5.3749633e-01, 4.8611158e-01, 4.8701654e-01, 4.3499943e-01, 4.9157679e-01, 4.8762772e-01, 4.5786105e-01, 5.5207149e-01, 5.5383578e-01, 5.1621105e-01, 5.3562938e-01, 5.3433077e-01, 4.8763878e-01, 5.7401483e-01, 5.9950807e-01, 5.0169837e-01, 5.7199426e-01, 5.7622109e-01, 4.9258166e-01, 5.6401024e-01, 5.8920374e-01, 5.3761485e-01, 5.5162361e-01, 5.0071393e-01, 5.1108397e-01, 5.0957378e-01, 5.0310495e-01, 5.3417705e-01, 5.6347038e-01, 5.5544608e-01, 5.0517001e-01, 5.4170490e-01, 5.5768119e-01, 6.1605762e-01, 5.2684445e-01, 5.6026650e-01, 4.9761039e-01, 5.7826229e-01, 4.9039267e-01, 5.5444683e-01, 5.5327922e-01, 5.7359620e-01, 5.3077316e-01, 5.3897197e-01, 5.1796237e-01, 5.2855358e-01, 5.1671037e-01, 5.9622412e-01, 6.1819121e-01, 5.7296705e-01, 5.5145456e-01, 5.0723387e-01, 5.5503681e-01, 5.4870379e-01, 5.1063454e-01, 5.0387017e-01, 5.2631419e-01, 5.6185307e-01, 5.5370395e-01, 5.5088067e-01, 5.0169928e-01, 5.5612542e-01, 5.0657189e-01, 5.2409481e-01, 5.2284798e-01, 5.9391410e-01, 5.6873552e-01, 5.4726018e-01, 5.8600009e-01, 6.2647159e-01, 6.0961628e-01, 5.5428459e-01, 5.3946531e-01, 5.3351539e-01, 5.5775245e-01, 5.0782548e-01, 5.1376850e-01, 5.2813152e-01]
        #     # [6.4599204e-01, 5.8009481e-01, 6.1606415e-01, 6.1071098e-01, 6.1859521e-01, 6.1274416e-01, 5.6404645e-01, 6.2055753e-01, 5.3794194e-01, 6.3609172e-01, 6.0945935e-01, 5.5504533e-01, 5.6271107e-01, 5.5380374e-01, 6.0596144e-01, 5.6616977e-01, 6.2227118e-01, 5.9801028e-01, 6.3691294e-01, 6.5189082e-01, 5.6928840e-01, 5.9739031e-01, 6.2144372e-01, 5.5423491e-01, 6.1966872e-01, 5.9997056e-01, 5.9309788e-01, 5.9097654e-01, 6.6743532e-01, 6.2585670e-01, 6.4283290e-01, 6.1084533e-01, 6.0601246e-01, 6.5786714e-01, 6.1675659e-01, 5.8428597e-01, 5.7911015e-01, 6.1814446e-01, 6.5467570e-01, 6.2327132e-01, 6.1820943e-01, 6.7392847e-01, 6.0636712e-01, 6.4118791e-01, 6.1282180e-01, 5.8978860e-01, 6.7797797e-01, 7.1699196e-01, 1.0117528e+00, 2.2726181e+00, 3.4698210e+00, 3.8304702e+00, 3.8196334e+00, 3.2619192e+00, 1.0205306e+00, -1.2214097e+00, -1.7390308e+00, -1.8675370e+00, -1.8828808e+00, -1.9184427e+00, -1.8976587e+00, -1.8388410e+00, -1.9245830e+00, -1.8400097e+00, -1.8427256e+00, -1.8667457e+00, -1.7722318e+00, -1.8766957e+00, -1.8453581e+00, -1.7864935e+00, -1.8080930e+00, -1.8282433e+00, -1.7986122e+00, -1.8028363e+00, -1.7642486e+00, -1.7948663e+00, -1.7103064e+00, -1.7080497e+00, -1.7272006e+00, -1.7985161e+00, -1.7146590e+00, -1.6627331e+00, -1.7296342e+00, -1.6556524e+00, -1.6513342e+00, -1.6131169e+00, -1.7312787e+00, -1.6266636e+00, -1.5899808e+00, -1.5773010e+00, -1.5944990e+00, -1.5456211e+00, -1.5613456e+00, -1.5233710e+00, -1.4942540e+00, -1.4886351e+00, -1.4576059e+00, -1.4864930e+00, -1.4694330e+00, -1.3648873e+00, -1.3591080e+00, -1.3182067e+00, -1.2946644e+00, -1.3112402e+00, -1.3143085e+00, -1.3002664e+00, -1.2457161e+00, -1.1826555e+00, -1.1478405e+00, -1.1393425e+00, -1.1818354e+00, -1.0905381e+00, -1.1134195e+00, -1.0568539e+00, -1.0096635e+00, -9.9456280e-01, -9.4615698e-01, -9.6722803e-01, -8.9581465e-01, -8.5329021e-01, -8.0415690e-01, -7.8231323e-01, -7.5747249e-01, -6.9181889e-01, -6.6083636e-01, -7.1778461e-01, -6.3738983e-01, -6.5927655e-01, -5.6273476e-01, -5.4926631e-01, -4.5594428e-01, -4.7428247e-01, -4.5613309e-01, -3.5143676e-01, -3.8044406e-01, -2.8879577e-01, -2.9708038e-01, -3.0873051e-01, -2.5243644e-01, -1.8748736e-01, -1.2681093e-01, -2.0259857e-01, -1.0690861e-01, -1.5195203e-01, -9.5526833e-02, -1.2628887e-01, -3.7242313e-02, -2.3766929e-02, 4.9910492e-02, -8.1735932e-03, 2.0913314e-02, 4.7210633e-02, 6.9717876e-02, 9.9995072e-02, 1.4030909e-01, 1.7352288e-01, 1.3181121e-01, 2.1229484e-01, 1.8366278e-01, 2.1271492e-01, 2.5346393e-01, 1.9855797e-01, 2.6804769e-01, 3.0556837e-01, 3.1518281e-01, 2.9710565e-01, 3.0082077e-01, 3.3285803e-01, 3.6951418e-01, 4.2511496e-01, 3.9051354e-01, 3.3237006e-01, 3.5956342e-01, 3.1772785e-01, 3.8804607e-01, 5.0094976e-01, 4.0347956e-01, 4.5050806e-01, 4.6945845e-01, 4.2596043e-01, 4.1725340e-01, 4.5567870e-01, 4.5973729e-01, 5.3553104e-01, 5.1218158e-01, 4.8497334e-01, 4.8960637e-01, 4.9621481e-01, 4.2483759e-01, 5.0125449e-01, 5.9456671e-01, 5.3640062e-01, 5.0674797e-01, 4.9412346e-01, 5.2386933e-01, 5.6600273e-01, 5.3306648e-01, 5.2529226e-01, 5.1029361e-01, 4.9331903e-01, 5.0693239e-01, 5.1490854e-01, 5.6811201e-01, 5.7341867e-01, 5.7849439e-01, 5.2703544e-01, 5.5386148e-01, 5.8900738e-01, 5.2480575e-01, 5.6596905e-01, 5.2460600e-01, 5.7997364e-01, 6.0908832e-01, 5.8077073e-01, 5.3383030e-01, 6.1826030e-01, 5.1965089e-01, 5.9340016e-01, 5.7225953e-01, 5.7117192e-01, 5.9511521e-01, 5.1910997e-01, 6.1595223e-01, 5.4929296e-01, 5.6567802e-01, 5.6199596e-01, 5.3658150e-01, 5.3073311e-01, 5.0845425e-01, 5.2633724e-01, 6.2759642e-01, 6.0729269e-01, 6.2850204e-01, 5.5503403e-01, 6.4417801e-01, 5.9421709e-01, 5.4251513e-01, 6.3298142e-01, 6.3688128e-01, 5.5519935e-01, 6.1786844e-01, 6.0465278e-01, 5.7177749e-01, 6.0314338e-01, 5.4351271e-01, 6.1584186e-01, 5.4914328e-01, 6.5036590e-01, 5.6558653e-01, 6.4837370e-01, 5.4398120e-01, 5.8070264e-01, 6.1290281e-01, 6.1060894e-01, 5.5958162e-01, 6.4370850e-01, 5.5792888e-01, 6.4400948e-01, 6.3940254e-01, 5.1763647e-01, 6.1151425e-01, 6.1301439e-01, 5.7474884e-01, 5.9040337e-01, 5.9723567e-01, 5.7600326e-01, 6.1296410e-01, 5.3629869e-01, 6.3200031e-01, 6.4605875e-01, 6.6053779e-01, 6.1220579e-01, 5.8413897e-01, 6.4785446e-01, 5.9291996e-01]
        # ])

        t = s.shape[1]
        # c = np.array([0.0, 0.5, 1.5, 2.5, 2, 2, 1, 0.5, 0, 0])
        # c = np.zeros((s.shape[1],))
        c = s[0, :]
        max_it = 1
        avgs = [c]

        tic = time.perf_counter()
        avg, avgs = dtw_barycenter.dba_loop(s,
                                            c,
                                            max_it=max_it,
                                            thr=0.0001,
                                            keep_averages=True,
                                            use_c=False)
        toc = time.perf_counter()
        print(f'DBA_loop: {toc - tic:0.4f} sec')
        print(avg)

        # tic = time.perf_counter()
        # for it in range(max_it):
        #     avg = dtw_barycenter.dba(s, c, use_c=True)
        #     avgs.append(avg)
        #     c = avg
        # toc = time.perf_counter()
        # print(f'DBA: {toc - tic:0.4f} sec')

        if directory and not dtwvis.test_without_visualization():
            try:
                import matplotlib.pyplot as plt
            except ImportError:
                raise MatplotlibException("No matplotlib available")
            fig, ax = plt.subplots(nrows=max_it, ncols=1)
            fn = directory / "test_pair_barycenter.png"
            for it in range(len(avgs)):
                dtwvis.plot_average(s[0, :],
                                    s[1, :],
                                    avgs[it],
                                    None,
                                    None,
                                    ax=ax[it])
                ax[it].set_title(f'Iteration {it}')
            fig.savefig(str(fn))
            plt.close()
Ejemplo n.º 7
0
def test_dtw_subseq_eeg():
    with util_numpy.test_uses_numpy() as np:
        data_fn = Path(__file__).parent / 'rsrc' / 'EEGRat_10_1000.txt'
        data = np.loadtxt(data_fn)
        series = np.array(data[1500:1700])
        query = np.array(data[1331:1352])

        sa = subsequence_alignment(query, series)
        match = sa.best_match()
        kmatches = list(sa.kbest_matches(k=15, overlap=0))
        segments = [m.segment for m in kmatches]
        segments_sol = [[38, 56], [19, 37], [167, 185], [124, 143], [84, 100],
                        [59, 77], [150, 162], [101, 121], [0, 15]]

        assert segments == segments_sol

        if directory and not dtwvis.test_without_visualization():
            try:
                import matplotlib.pyplot as plt
            except ImportError:
                raise MatplotlibException("No matplotlib available")

            fn = directory / "test_dtw_subseq_eeg1.png"
            fig = plt.figure(figsize=(20, 30))
            dtwvis.plot_warpingpaths(query,
                                     series,
                                     sa.warping_paths(),
                                     match.path,
                                     figure=fig)
            plt.savefig(fn)
            plt.close(fig)

            fn = directory / "test_dtw_subseq_eeg2.png"
            startidx, endidx = match.segment
            fig = plt.figure()
            plt.plot(query, label='query')
            plt.plot(series[startidx:endidx], label='best match')
            plt.legend()
            plt.savefig(fn)
            plt.close(fig)

            fn = directory / "test_dtw_subseq_eeg3.png"
            fig = plt.figure(figsize=(20, 10))
            fig, ax = dtwvis.plot_warpingpaths(query,
                                               series,
                                               sa.warping_paths(),
                                               path=-1,
                                               figure=fig)
            print(f'plotting {len(kmatches)} matches')
            for kmatch in kmatches:
                dtwvis.plot_warpingpaths_addpath(ax, kmatch.path)
            plt.savefig(fn)
            plt.close(fig)
Ejemplo n.º 8
0
def test_nparray_kmeans():
    with util_numpy.test_uses_numpy() as np:
        k = 4
        max_it = 10
        max_dba_it = 20

        series = np.array([[0., 0, 1, 2, 1, 0, 1, 0, 0],
                           [0., 1, 2, 0, 0, 0, 0, 0, 0],
                           [1., 2, 0, 0, 0, 0, 0, 1, 1],
                           [0., 0, 1, 2, 1, 0, 1, 0, 0],
                           [0., 1, 2, 0, 0, 0, 0, 0, 0],
                           [1., 2, 0, 0, 0, 0, 0, 1, 1]])
        print(type(series))
        print(series.shape)
        window = int(series.shape[1] * 1.0)

        # Perform k-means
        tic = time.perf_counter()
        model = KMeans(k=k,
                       max_it=max_it,
                       max_dba_it=max_dba_it,
                       dists_options={"window": window},
                       initialize_with_kmedoids=False,
                       initialize_with_kmeanspp=True)
        cluster_idx, performed_it = model.fit(series,
                                              use_c=True,
                                              use_parallel=False)
        toc = time.perf_counter()
        print(f'DBA ({performed_it} iterations: {toc - tic:0.4f} sec')

        if directory and not dtwvis.test_without_visualization():
            try:
                import matplotlib.pyplot as plt
            except ImportError:
                raise MatplotlibException("No matplotlib available")
            fig, ax = plt.subplots(nrows=k,
                                   ncols=2,
                                   figsize=(10, 4),
                                   sharex='all',
                                   sharey='all')
            fn = directory / "test_nparray_barycenter.png"

            all_idx = set()
            for ki in range(k):
                ax[ki, 0].plot(model.means[ki])
                for idx in cluster_idx[ki]:
                    ax[ki, 1].plot(series[idx], alpha=0.3)
                    if idx in all_idx:
                        raise Exception(f'Series in multiple clusters: {idx}')
                    all_idx.add(idx)
            assert (len(all_idx) == len(series))
            fig.savefig(str(fn))
            plt.close()
Ejemplo n.º 9
0
def test_pair():

    with util_numpy.test_uses_numpy() as np:
        s = np.array([[0.5, 1, 2, 3, 2.0, 2.1, 1.0, 0, 0, 0],
                      [0.4, 0, 1, 1.5, 1.9, 2.0, 0.9, 1, 0, 0]])

        t = s.shape[1]
        # c = np.array([0.0, 0.5, 1.5, 2.5, 2, 2, 1, 0.5, 0, 0])
        # c = np.zeros((s.shape[1],))
        c = s[0, :]
        max_it = 1
        avgs = [c]

        tic = time.perf_counter()
        avg, avgs = dtw_barycenter.dba_loop(s,
                                            c,
                                            max_it=max_it,
                                            thr=0.0001,
                                            keep_averages=True,
                                            use_c=False)
        toc = time.perf_counter()
        print(f'DBA_loop: {toc - tic:0.4f} sec')
        print(avg)

        # tic = time.perf_counter()
        # for it in range(max_it):
        #     avg = dtw_barycenter.dba(s, c, use_c=True)
        #     avgs.append(avg)
        #     c = avg
        # toc = time.perf_counter()
        # print(f'DBA: {toc - tic:0.4f} sec')

        if directory and not dtwvis.test_without_visualization():
            try:
                import matplotlib.pyplot as plt
            except ImportError:
                raise MatplotlibException("No matplotlib available")
            fig, ax = plt.subplots(nrows=max_it, ncols=1)
            fn = directory / "test_pair_barycenter.png"
            for it in range(len(avgs)):
                dtwvis.plot_average(s[0, :],
                                    s[1, :],
                                    avgs[it],
                                    None,
                                    None,
                                    ax=ax[it])
                ax[it].set_title(f'Iteration {it}')
            fig.savefig(str(fn))
            plt.close()
Ejemplo n.º 10
0
def test_trace_kmeans():
    with util_numpy.test_uses_numpy() as np:
        k = 4
        max_it = 10
        max_dba_it = 20
        rsrc_fn = os.path.join(os.path.abspath(os.path.dirname(__file__)),
                               'rsrc', 'Trace_TRAIN.txt')
        data = np.loadtxt(rsrc_fn)
        labels = data[:, 0]
        series = data[:, 1:]
        mask = np.full((len(labels), ), False, dtype=bool)
        mask[:] = (labels == 1)
        # c = series[0, :]
        print(type(series))
        print(series.shape)
        window = int(series.shape[1] * 1.0)

        # Z-normalize sequences
        series = (series -
                  series.mean(axis=1)[:, None]) / series.std(axis=1)[:, None]
        # Align start and/or end values
        # avg_start = series[:, :20].mean(axis=1)
        # avg_end = series[:, 20:].mean(axis=1)
        # series = (series - avg_end[:, None])

        # Perform k-means
        tic = time.perf_counter()
        model = KMeans(k=k,
                       max_it=max_it,
                       max_dba_it=max_dba_it,
                       drop_stddev=2,
                       dists_options={"window": window},
                       initialize_with_kmedoids=False,
                       initialize_with_kmeanspp=True)
        try:
            cluster_idx, performed_it = model.fit(series,
                                                  use_c=True,
                                                  use_parallel=False)
        except PyClusteringException:
            return
        toc = time.perf_counter()
        print(f'DBA ({performed_it} iterations: {toc - tic:0.4f} sec')

        if directory and not dtwvis.test_without_visualization():
            try:
                import matplotlib.pyplot as plt
            except ImportError:
                raise MatplotlibException("No matplotlib available")
            fig, ax = plt.subplots(nrows=k,
                                   ncols=2,
                                   figsize=(10, 4),
                                   sharex='all',
                                   sharey='all')
            fn = directory / "test_trace_barycenter.png"

            all_idx = set()
            for ki in range(k):
                ax[ki, 0].plot(model.means[ki])
                for idx in cluster_idx[ki]:
                    ax[ki, 1].plot(series[idx], alpha=0.3)
                    if idx in all_idx:
                        raise Exception(f'Series in multiple clusters: {idx}')
                    all_idx.add(idx)
            assert (len(all_idx) == len(series))
            fig.savefig(str(fn))
            plt.close()

            fig, ax = plt.subplots(nrows=k,
                                   ncols=1,
                                   figsize=(5, 4),
                                   sharex='all',
                                   sharey='all')
            fn = directory / "test_trace_barycenter_solution.png"
            for i in range(len(labels)):
                ax[int(labels[i]) - 1].plot(series[i], alpha=0.3)
            fig.savefig(str(fn))
            plt.close()
Ejemplo n.º 11
0
def test_trace_kmeans_differencing():
    with util_numpy.test_uses_numpy() as np, util_numpy.test_uses_scipy() as scipy:
        k = 4
        max_it = 10
        max_dba_it = 20
        nb_prob_samples = 0
        use_c = True
        rsrc_fn = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'rsrc', 'Trace_TRAIN.txt')
        data = np.loadtxt(rsrc_fn)
        labels = data[:, 0]
        series = data[:, 1:]
        mask = np.full((len(labels),), False, dtype=bool)
        mask[:] = (labels == 1)
        # c = series[0, :]
        print(type(series))
        print(series.shape)
        window = int(series.shape[1] * 0.5)

        # Differencing
        # The baseline differences are not relevant thus we cluster based
        # on the result of differencing.
        # Also the high-freq noise dominates the local differences, thus
        # we apply a low-pass filter first.
        signal = scipy.import_signal()
        series_orig = series.copy()
        series = np.diff(series, n=1, axis=1)
        fs = 100  # sample rate, Hz
        cutoff = 10  # cut off frequency, Hz
        nyq = 0.5 * fs  # Nyquist frequency
        b, a = signal.butter(2, cutoff / nyq, btype='low', analog=False, output='ba')
        series = signal.filtfilt(b, a, series, axis=1)

        # Perform k-means
        tic = time.perf_counter()
        model = KMeans(k=k, max_it=max_it, max_dba_it=max_dba_it, drop_stddev=1,
                       nb_prob_samples=nb_prob_samples,
                       dists_options={"window": window},
                       initialize_with_kmedoids=False,
                       initialize_with_kmeanspp=True)
        try:
            cluster_idx, performed_it = model.fit(series, use_c=use_c, use_parallel=False)
        except PyClusteringException:
            return
        toc = time.perf_counter()
        print(f'DBA ({performed_it} iterations: {toc - tic:0.4f} sec')

        if directory and not dtwvis.test_without_visualization():
            try:
                import matplotlib.pyplot as plt
            except ImportError:
                raise MatplotlibException("No matplotlib available")
            fig, ax = plt.subplots(nrows=k, ncols=3, figsize=(10,4),
                                   sharex='all', sharey='all')
            fn = directory / "test_trace_barycenter.png"

            all_idx = set()
            mask = np.full((k, len(series_orig)), False, dtype=bool)
            for ki in range(k):
                ax[ki, 0].plot(model.means[ki])
                for idx in cluster_idx[ki]:
                    ax[ki, 2].plot(series_orig[idx], alpha=0.3)
                    mask[ki, idx] = True
                    if idx in all_idx:
                        raise Exception(f'Series in multiple clusters: {idx}')
                    all_idx.add(idx)

            series_orig = (series_orig - series_orig.mean(axis=1)[:, None]) / series_orig.std(axis=1)[:, None]
            for ki, mean in enumerate(model.means):
                # dba = dba_loop(series_orig, c=None, mask=mask[ki, :],
                #                max_it=max_it, thr=None, use_c=use_c,
                #                nb_prob_samples=nb_prob_samples)
                print(mean.shape)
                dba = np.r_[0, mean].cumsum()
                ax[ki, 1].plot(dba)
            assert(len(all_idx) == len(series))
            ax[0, 0].set_title("DBA Differencing + LP")
            ax[0, 1].set_title("DBA Original series")
            ax[0, 2].set_title("Clustered series")
            fig.savefig(str(fn))
            plt.close()

            fig, ax = plt.subplots(nrows=k, ncols=1, figsize=(5, 4),
                                   sharex='all', sharey='all')
            fn = directory / "test_trace_barycenter_solution.png"
            for i in range(len(labels)):
                ax[int(labels[i]) - 1].plot(series_orig[i], alpha=0.3)
            fig.savefig(str(fn))
            plt.close()
Ejemplo n.º 12
0
    def plot(self,
             filename=None,
             axes=None,
             ts_height=10,
             bottom_margin=2,
             top_margin=2,
             ts_left_margin=0,
             ts_sample_length=1,
             tr_label_margin=3,
             tr_left_margin=2,
             ts_label_margin=0,
             show_ts_label=None,
             show_tr_label=None,
             cmap='viridis_r',
             ts_color=None):
        """Plot the hierarchy and time series.

        :param filename: If a filename is passed, the image is written to this file.
        :param axes: If a axes array is passed the image is added to this figure.
            Expects axes[0] and axes[1] to be present.
        :param ts_height: Height of a time series
        :param bottom_margin: Margin on bottom
        :param top_margin: Margin on top
        :param ts_left_margin: Margin on left of time series image
        :param ts_sample_length: Space between two points in the time series
        :param tr_label_margin: Margin between tree split and label
        :param tr_left_margin: Left margin for tree
        :param ts_label_margin: Margin between start of series and label
        :param show_ts_label: Show label indices. Boolean, callable or subscriptable object.
            If it is a callable object, the index of the time series will be given and the
            return string will be printed.
        :param show_tr_label: Show tree distances. Boolean, callable or subscriptable object.
            If it is a callable object, the index of the time series will be given and the
            return string will be printed.
        :param cmap: Matplotlib colormap name
        :param ts_color: function that takes the index and returns a color
            (compatible with the matplotlib.color color argument)
        """
        # print('linkage')
        # for l in self.linkage:
        #     print(l)
        if np is None:
            raise NumpyException(
                "The plot function requires Numpy to be installed.")
        try:
            from matplotlib import pyplot as plt
            from matplotlib.lines import Line2D
            import matplotlib.colors as colors
            import matplotlib.cm as cmx
        except ImportError:
            raise MatplotlibException(
                "The plot function requires Matplotlib to be installed.")

        if show_ts_label is True:
            show_ts_label = lambda idx: str(int(idx))
        elif show_ts_label is False or show_ts_label is None:
            show_ts_label = lambda idx: ""
        elif callable(show_ts_label):
            pass
        elif hasattr(show_ts_label, "__getitem__"):
            show_ts_label_prev = show_ts_label
            show_ts_label = lambda idx: show_ts_label_prev[idx]
        else:
            raise AttributeError(
                "Unknown type for show_ts_label, expecting boolean, subscriptable or callable, "
                "got {}".format(type(show_ts_label)))
        if show_tr_label is True:
            show_tr_label = lambda dist: "{:.2f}".format(dist)
        elif show_tr_label is False or show_tr_label is None:
            show_tr_label = lambda dist: ""
        elif callable(show_tr_label):
            pass
        elif hasattr(show_tr_label, "__getitem__"):
            show_tr_label_prev = show_tr_label
            show_tr_label = lambda idx: show_tr_label_prev[idx]
        else:
            raise AttributeError(
                "Unknown type for show_ts_label, expecting boolean, subscriptable or callable, "
                "got {}".format(type(show_ts_label)))

        self._series_y = [0] * len(self.series)

        max_dist = 0
        for _, _, d, _ in self.linkage:
            if not np.isinf(d):
                max_dist = max(max_dist, d)

        node_props = dict()

        max_y = self.series.get_max_y()
        self.ts_height_factor = (ts_height / max_y) * 0.9

        def count(node, height):
            # print('count({},{})'.format(node, height))
            maxheight = None
            maxcumdist = None
            curdepth = None
            cnt = 0
            left_cnt = None
            right_cnt = None
            if node < len(self.series):
                # Leaf
                cnt += 1
                maxheight = height
                maxcumdist = 0
                curdepth = 0
                left_cnt = 0
                right_cnt = 0
            else:
                # Inner node
                child_left, child_right, dist, cnt2 = self.get_linkage(
                    int(node))
                child_left, child_right, cnt2 = int(child_left), int(
                    child_right), int(cnt2)
                if child_left == child_right:
                    raise Exception(
                        "Row in linkage contains same node as left and right child: {}-{}"
                        .format(child_left, child_right))
                if np.isinf(dist):
                    dist = 1.5 * max_dist
                # Left
                nc, nmh, ncd, nmd = count(child_left, height + 1)
                cnt += nc
                maxheight = nmh
                maxcumdist = nmd + dist
                curdepth = ncd + 1
                left_cnt = nc
                # Right
                nc, nmh, ncd, nmd = count(child_right, height + 1)
                cnt += nc
                maxheight = max(maxheight, nmh)
                maxcumdist = max(maxcumdist, nmd + dist)
                curdepth = max(curdepth, ncd + 1)
                right_cnt = nc
                # if cnt != cnt2:
                #     raise Exception("Count in linkage not correct")
            # print('c', node, c)
            node_props[int(node)] = (cnt, curdepth, left_cnt, right_cnt,
                                     maxcumdist)
            # print('count({},{}) = {}, {}, {}, {}'.format(node, height, cnt, maxheight, curdepth, maxcumdist))
            return cnt, maxheight, curdepth, maxcumdist

        cnt, maxheight, curdepth, maxcumdist = count(self.maxnode, 0)
        # for node, props in node_props.items():
        #     print("{:<3}: {}".format(node, props))

        if axes is None:
            fig, ax = plt.subplots(nrows=1, ncols=2, frameon=False)
        else:
            fig, ax = None, axes
        ax[0].set_axis_off()
        # ax[0].set_xlim(left=0, right=curdept)
        ax[0].set_xlim(left=0, right=tr_left_margin + maxcumdist + 0.05)
        ax[0].set_ylim(bottom=0,
                       top=bottom_margin + ts_height * len(self.series) +
                       top_margin)
        # ax[0].plot([0,1],[1,2])
        # ax[0].add_line(Line2D((0,1),(2,2), lw=2, color='black', axes=ax[0]))

        ax[1].set_axis_off()
        max_length = max(len(s) for s in self.series)
        ax[1].set_xlim(left=0,
                       right=ts_left_margin + ts_sample_length * max_length)
        ax[1].set_ylim(bottom=0,
                       top=bottom_margin + ts_height * len(self.series) +
                       top_margin)

        if type(cmap) == str:
            cmap = plt.get_cmap(cmap)
        else:
            pass
        line_colors = cmx.ScalarMappable(norm=colors.Normalize(vmin=0,
                                                               vmax=max_dist),
                                         cmap=cmap)

        cnt_ts = 0

        def plot_i(node, depth, cnt_ts, prev_lcnt, ax, left):
            # print('plot_i', node, depth, cnt_ts, prev_lcnt)
            pcnt, pdepth, plcnt, prcnt, pcdist = node_props[node]
            # px = maxheight - pdepth
            px = tr_left_margin + maxcumdist - pcdist
            py = prev_lcnt * ts_height
            if node < len(self.series):
                # Plot series
                # print('plot series y={}'.format(ts_bottom_margin + ts_height * cnt_ts + self.ts_height_factor))
                self._series_y[int(node)] = bottom_margin + ts_height * cnt_ts
                serie = self.series[int(node)]
                ax[1].text(ts_left_margin + ts_label_margin,
                           bottom_margin + ts_height * cnt_ts + ts_height / 2,
                           show_ts_label(int(node)),
                           ha='left',
                           va='center')
                if ts_color:
                    curcolor = ts_color(int(node))
                else:
                    curcolor = None
                ax[1].plot(ts_left_margin +
                           ts_sample_length * np.arange(len(serie)),
                           bottom_margin + ts_height * cnt_ts +
                           self.ts_height_factor * serie,
                           color=curcolor)
                cnt_ts += 1

            else:
                child_left, child_right, dist, _ = self.get_linkage(node)
                color = line_colors.to_rgba(dist)
                ax[0].text(px + tr_label_margin,
                           py,
                           show_tr_label(dist),
                           ha='left',
                           va='center',
                           color=color)

                # Left
                ccnt, cdepth, clcntl, crcntl, clcdist = node_props[child_left]
                # print('left', ccnt, cdepth, clcntl, crcntl)
                # cx = maxheight - cdepth
                cx = tr_left_margin + maxcumdist - clcdist
                cy = (prev_lcnt - crcntl) * ts_height
                if py == cy:
                    cy -= 1 / 2 * ts_height
                # print('plot line', (px, cx), (py, cy))
                # ax[0].add_line(Line2D((px, cx), (py, cy), lw=2, color='black', axes=ax[0]))
                ax[0].add_line(
                    Line2D((px, px), (py, cy), lw=1, color=color, axes=ax[0]))
                ax[0].add_line(
                    Line2D((px, cx), (cy, cy), lw=1, color=color, axes=ax[0]))
                cnt_ts = plot_i(child_left, depth + 1, cnt_ts,
                                prev_lcnt - crcntl, ax, True)

                # Right
                ccnt, cdepth, clcntr, crcntr, crcdist = node_props[child_right]
                # print('right', ccnt, cdepth, clcntr, crcntr)
                # cx = maxheight - cdepth
                cx = tr_left_margin + maxcumdist - crcdist
                cy = (prev_lcnt + clcntr) * ts_height
                if py == cy:
                    cy += 1 / 2 * ts_height
                # print('plot line', (px, cx), (py, cy))
                # ax[0].add_line(Line2D((px, cx), (py, cy), lw=2, color='black', axes=ax[0]))
                ax[0].add_line(
                    Line2D((px, px), (py, cy), lw=1, color=color, axes=ax[0]))
                ax[0].add_line(
                    Line2D((px, cx), (cy, cy), lw=1, color=color, axes=ax[0]))
                cnt_ts = plot_i(child_right, depth + 1, cnt_ts,
                                prev_lcnt + clcntr, ax, False)
            return cnt_ts

        plot_i(self.maxnode, 0, 0, node_props[self.maxnode][2], ax, True)

        if filename:
            if isinstance(filename, Path):
                filename = str(filename)
            plt.savefig(filename, bbox_inches='tight', pad_inches=0)
            plt.close()
            fig, ax = None, None

        return fig, ax