Сравнение временных рядов
Занимаюсь определением накрутки просмотров в Telegram. При сборе данных заметил, что почти все обычные ряды просмотров под постами имеют похожие графики, только масштабы различные. Таким образом я хочу, построив эталонный временной ряд, определять накрутку.
На данный момент вижу две проблемы:
- Составление эталонного графика для всех постов, то есть чтобы масштаб не играл роли
- Способ сравнения эталонного графика с произвольным
Какие методы можете предложить для решения данных проблем?
P.S. под рядом просмотров подразумеваю временной ряд, характеризующий зависимость прироста кол-ва просмотров под постом за час
Привожу пример возможных графиков:
Нормальный пост:
Данные(первое число - временная метка, второе - приращение просмотров за час):
[(0, 257), (1, 84), (2, 28), (3, 21), (4, 20), (5, 19), (6, 11), (7, 10), (8, 10), (9, 11), (10, 5), (11, 3), (12, 1), (13, 2), (14, 0), (15, 2), (16, 3), (17, 4), (18, 6), (19, 7), (20, 4), (21, 2), (22, 9), (23, 7), (24, 4), (25, 1), (26, 3), (27, 5), (28, 2), (29, 2), (30, 6), (31, 3), (32, 1), (33, 5), (34, 1), (35, 0), (36, 1), (37, 1), (38, 0), (39, 0), (40, 0), (41, 4), (42, 0), (43, 2), (44, 4), (45, 4), (46, 2), (47, 1), (48, 1), (49, 2), (50, 2), (51, 6), (52, 33), (53, 9), (54, 10), (55, 1), (56, 6), (57, 1), (58, 3), (59, 0), (60, 1), (61, 3), (62, 1), (63, 1), (64, 3), (65, 2), (66, 2), (67, 3), (68, 2), (69, 2), (70, 1), (71, 2), (72, 0), (73, 5), (74, 1), (75, 1), (76, 2), (77, 2), (78, 2), (79, 1), (80, 4), (81, 4), (82, 2), (83, 0), (84, 1), (85, 1), (86, 1), (87, 1), (88, 1), (89, 1), (90, 0), (91, 1), (92, 4), (93, 1), (94, 2), (95, 2), (96, 2), (97, 4), (98, 1), (99, 0), (100, 2), (101, 0), (102, 2), (103, 2), (104, 0), (105, 2), (106, 1), (107, 2), (108, 0), (109, 1), (110, 0), (111, 4), (112, 1), (113, 2), (114, 4), (115, 3), (116, 3), (117, 0), (118, 3), (119, 0), (120, 0), (121, 2), (122, 6), (123, 3), (124, 2), (125, 2), (126, 3), (127, 2), (128, 0)]
Накрученные просмотры:
Данные:
[(0, 13089), (1, 161), (2, 125), (3, 81), (4, 43), (5, 32), (6, 17), (7, 15), (8, 19), (9, 17), (10, 31), (11, 33), (12, 32), (13, 36), (14, 22), (15, 12), (16, 24), (17, 27), (18, 39), (19, 19), (20, 12), (21, 15), (22, 14), (23, 34), (24, 13), (25, 12), (26, 10), (27, 9), (28, 5), (29, 1), (30, 3), (31, 1), (32, 2), (33, 4), (34, 3), (35, 6), (36, 4), (37, 8), (38, 2), (39, 3), (40, 4), (41, 28), (42, 8), (43, 7), (44, 9), (45, 3), (46, 2), (47, 2), (48, 62), (49, 32), (50, 40), (51, 0), (52, 8), (53, 10), (54, 0), (55, 5), (56, 1), (57, 2), (58, 4), (59, 10), (60, 6), (61, 3), (62, 19), (63, 16), (64, 19), (65, 37), (66, 7), (67, 8), (68, 20), (69, 7), (70, 4), (71, 9), (72, 33)]
Данные:
[(0, 719), (1, 629), (2, 607), (3, 609), (4, 605), (5, 608), (6, 620), (7, 610), (8, 620), (9, 636), (10, 619), (11, 635), (12, 601), (13, 563), (14, 558), (15, 548), (16, 557), (17, 570), (18, 550), (19, 557), (20, 564), (21, 572), (22, 593), (23, 593), (24, 427), (25, 623), (26, 638), (27, 503), (28, 597), (29, 11), (30, 0), (31, 0), (32, 2), (33, 0), (34, 2), (35, 14), (36, 5), (37, 3), (38, 1), (39, 3), (40, 6), (41, 8), (42, 5), (43, 6), (44, 1), (45, 2), (46, 2), (47, 1), (48, 2), (49, 2), (50, 1), (51, 0), (52, 0), (53, 1), (54, 0), (55, 0), (56, 0), (57, 0), (58, 2), (59, 1), (60, 3), (61, 2), (62, 0), (63, 1), (64, 3), (65, 2), (66, 0), (67, 3), (68, 2), (69, 2), (70, 0), (71, 1), (72, 1), (73, 3), (74, 1), (75, 3), (76, 0), (77, 1), (78, 3), (79, 2), (80, 1), (81, 4), (82, 2), (83, 4), (84, 0), (85, 9), (86, 2), (87, 1), (88, 5), (89, 1), (90, 2), (91, 4), (92, 2), (93, 5)]
Ещё один пример:
Ответы (3 шт):
Ну вот например простая эвристика.
data = [
[(0, 257), (1, 84), (2, 28), (3, 21), (4, 20), (5, 19), (6, 11), (7, 10), (8, 10), (9, 11), (10, 5), (11, 3), (12, 1), (13, 2), (14, 0), (15, 2), (16, 3), (17, 4), (18, 6), (19, 7), (20, 4), (21, 2), (22, 9), (23, 7), (24, 4), (25, 1), (26, 3), (27, 5), (28, 2), (29, 2), (30, 6), (31, 3), (32, 1), (33, 5), (34, 1), (35, 0), (36, 1), (37, 1), (38, 0), (39, 0), (40, 0), (41, 4), (42, 0), (43, 2), (44, 4), (45, 4), (46, 2), (47, 1), (48, 1), (49, 2), (50, 2), (51, 6), (52, 33), (53, 9), (54, 10), (55, 1), (56, 6), (57, 1), (58, 3), (59, 0), (60, 1), (61, 3), (62, 1), (63, 1), (64, 3), (65, 2), (66, 2), (67, 3), (68, 2), (69, 2), (70, 1), (71, 2), (72, 0), (73, 5), (74, 1), (75, 1), (76, 2), (77, 2), (78, 2), (79, 1), (80, 4), (81, 4), (82, 2), (83, 0), (84, 1), (85, 1), (86, 1), (87, 1), (88, 1), (89, 1), (90, 0), (91, 1), (92, 4), (93, 1), (94, 2), (95, 2), (96, 2), (97, 4), (98, 1), (99, 0), (100, 2), (101, 0), (102, 2), (103, 2), (104, 0), (105, 2), (106, 1), (107, 2), (108, 0), (109, 1), (110, 0), (111, 4), (112, 1), (113, 2), (114, 4), (115, 3), (116, 3), (117, 0), (118, 3), (119, 0), (120, 0), (121, 2), (122, 6), (123, 3), (124, 2), (125, 2), (126, 3), (127, 2), (128, 0)],
[(0, 13089), (1, 161), (2, 125), (3, 81), (4, 43), (5, 32), (6, 17), (7, 15), (8, 19), (9, 17), (10, 31), (11, 33), (12, 32), (13, 36), (14, 22), (15, 12), (16, 24), (17, 27), (18, 39), (19, 19), (20, 12), (21, 15), (22, 14), (23, 34), (24, 13), (25, 12), (26, 10), (27, 9), (28, 5), (29, 1), (30, 3), (31, 1), (32, 2), (33, 4), (34, 3), (35, 6), (36, 4), (37, 8), (38, 2), (39, 3), (40, 4), (41, 28), (42, 8), (43, 7), (44, 9), (45, 3), (46, 2), (47, 2), (48, 62), (49, 32), (50, 40), (51, 0), (52, 8), (53, 10), (54, 0), (55, 5), (56, 1), (57, 2), (58, 4), (59, 10), (60, 6), (61, 3), (62, 19), (63, 16), (64, 19), (65, 37), (66, 7), (67, 8), (68, 20), (69, 7), (70, 4), (71, 9), (72, 33)],
[(0, 719), (1, 629), (2, 607), (3, 609), (4, 605), (5, 608), (6, 620), (7, 610), (8, 620), (9, 636), (10, 619), (11, 635), (12, 601), (13, 563), (14, 558), (15, 548), (16, 557), (17, 570), (18, 550), (19, 557), (20, 564), (21, 572), (22, 593), (23, 593), (24, 427), (25, 623), (26, 638), (27, 503), (28, 597), (29, 11), (30, 0), (31, 0), (32, 2), (33, 0), (34, 2), (35, 14), (36, 5), (37, 3), (38, 1), (39, 3), (40, 6), (41, 8), (42, 5), (43, 6), (44, 1), (45, 2), (46, 2), (47, 1), (48, 2), (49, 2), (50, 1), (51, 0), (52, 0), (53, 1), (54, 0), (55, 0), (56, 0), (57, 0), (58, 2), (59, 1), (60, 3), (61, 2), (62, 0), (63, 1), (64, 3), (65, 2), (66, 0), (67, 3), (68, 2), (69, 2), (70, 0), (71, 1), (72, 1), (73, 3), (74, 1), (75, 3), (76, 0), (77, 1), (78, 3), (79, 2), (80, 1), (81, 4), (82, 2), (83, 4), (84, 0), (85, 9), (86, 2), (87, 1), (88, 5), (89, 1), (90, 2), (91, 4), (92, 2), (93, 5)]
]
for d in data:
y = [i[1] for i in d]
m = max(max(a, b) / (min(a, b) + 1) for a, b in zip(y, y[1:]))
print(f'max div = {m}')
Вывод:
max div = 5.0
max div = 80.79629629629629
max div = 49.75
Что я собственно смотрю - насколько большой был скачок вверх или вниз, т.е. ищу тот самый "порожек", который я ранее заприметил. Я беру xi и xi+1, делю большее из них на меньшее и ищу максимум этой величины по всем i. Какое выбрать значение для отсечения накруток от не накруток - смотрите по остальным данным. По предоставленным данным можно и 10
взять и 20
, например. Если меньше этого числа, то считаем, что без накруток, если больше - с накрутками.
Но это очень простая эвристика, можно придумать что-то сложнее, но нужно больше данных для этого.
Задача очень интересная. (Конечно, сравнивать надо не графики, а сами временные ряды). Мне кажется, она сводиться к задачам, которыми занимаются в разделах классификации (это если у вас имеются все или большинство шаблоны аномального поведения значений ряда) или кластеризации временных рядов. Там в общем-то разработано несколько интересных решений. Интересно было бы даже посмотреть как они сработают на вашей задаче.
Для примера, можно ознакомиться с более-менее текущим положением дел в этих областях. Там прикладные задачи конечно другие. Но методы переносятся в вашу прикладную область достаточно прозрачно.
https://towardsdatascience.com/k-nn-based-time-series-classification-e5d761d01ea2
https://www.storybench.org/how-to-use-hierarchical-cluster-analysis-on-time-series-data/
https://pub.towardsai.net/highly-comparative-time-series-analysis-a-paper-review-5b51d14a291c
https://habr.com/ru/companies/otus/articles/721026/
P.S. А данные у вас свои или откуда-то из открытых источников получены?
На данный момент вижу две проблемы:
Составление эталонного графика для всех постов, то есть чтобы масштаб не играл роли
Способ сравнения эталонного графика с произвольным Какие методы можете предложить для решения данных проблем?
для п.1 - можно стандартизировать/нормализовать данные
для п.2 - вычесть среднее, чтобы сравнивать тренд и дисперсию по остаткам