-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathemergence_detector.py
More file actions
158 lines (128 loc) · 5.36 KB
/
Copy pathemergence_detector.py
File metadata and controls
158 lines (128 loc) · 5.36 KB
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
"""
Emergence detection for identifying rapidly growing topics.
"""
import numpy as np
import pandas as pd
from typing import List
from scipy import stats
from scipy.signal import find_peaks
from sklearn.preprocessing import StandardScaler
class EmergenceDetector:
"""
Detect emerging topics using statistical methods including:
- CUSUM change point detection
- Growth rate analysis
- Trend strength calculation
- Persistence measurement
"""
def __init__(self, significance_level: float = 0.05, min_persistence: int = 3):
"""
Initialize the emergence detector.
Parameters:
-----------
significance_level : float
Statistical significance level for tests
min_persistence : int
Minimum number of time points a topic should persist to be considered emerging
"""
self.significance_level = significance_level
self.min_persistence = min_persistence
self.scaler = StandardScaler()
def detect_change_points(self, topic_frequencies: np.ndarray) -> List[int]:
"""
Detect change points in topic frequency using CUSUM algorithm.
Parameters:
-----------
topic_frequencies : np.ndarray
Array of topic frequencies over time
Returns:
--------
List[int]
Indices of detected change points
"""
mean_freq = np.mean(topic_frequencies)
cusum_pos = np.maximum.accumulate(np.maximum(0,
np.cumsum(topic_frequencies - mean_freq)))
cusum_neg = np.maximum.accumulate(np.maximum(0,
np.cumsum(mean_freq - topic_frequencies)))
threshold = 3 * np.std(topic_frequencies)
changes_pos = find_peaks(cusum_pos, height=threshold)[0]
changes_neg = find_peaks(cusum_neg, height=threshold)[0]
return sorted(list(changes_pos) + list(changes_neg))
def calculate_emergence_score(self,
topic_frequencies: np.ndarray,
time_window: int = 5) -> float:
"""
Calculate emergence score for a topic based on growth rate, acceleration,
and persistence.
Parameters:
-----------
topic_frequencies : np.ndarray
Array of topic frequencies over time
time_window : int
Number of recent time points to consider
Returns:
--------
float
Emergence score (higher = more emerging)
"""
if len(topic_frequencies) < time_window:
return 0.0
recent = topic_frequencies[-time_window:]
historical = topic_frequencies[:-time_window] if len(topic_frequencies) > time_window else np.array([0])
growth_rate = (np.mean(recent) - np.mean(historical)) / (np.mean(historical) + 1e-6)
if len(topic_frequencies) >= 3:
acceleration = np.mean(np.diff(np.diff(topic_frequencies)))
else:
acceleration = 0
persistence = len([x for x in recent if x > np.mean(historical)]) / len(recent)
emergence_score = (0.4 * growth_rate +
0.3 * acceleration +
0.3 * persistence)
return max(0, emergence_score)
def detect_emerging_topics(self, topics_over_time_df: pd.DataFrame,
top_n: int = 10) -> pd.DataFrame:
"""
Detect emerging topics from topics over time data.
Parameters:
-----------
topics_over_time_df : pd.DataFrame
DataFrame with columns: Topic, Timestamp, Frequency
top_n : int
Number of top emerging topics to return
Returns:
--------
pd.DataFrame
DataFrame of emerging topics sorted by emergence score
"""
emerging_topics = []
unique_topics = [t for t in topics_over_time_df['Topic'].unique() if t != -1]
for topic in unique_topics:
topic_data = topics_over_time_df[topics_over_time_df['Topic'] == topic].sort_values('Timestamp')
frequencies = topic_data['Frequency'].values
if len(frequencies) < 3:
continue
emergence_score = self.calculate_emergence_score(frequencies)
change_points = self.detect_change_points(frequencies)
if len(frequencies) > 1:
slope, _, r_value, _, _ = stats.linregress(range(len(frequencies)), frequencies)
trend_strength = r_value ** 2
else:
slope = 0
trend_strength = 0
recent_avg = np.mean(frequencies[-3:]) if len(frequencies) >= 3 else frequencies[-1]
historical_avg = np.mean(frequencies[:-3]) if len(frequencies) > 3 else frequencies[0]
growth_ratio = recent_avg / (historical_avg + 1e-6)
emerging_topics.append({
'Topic': topic,
'Emergence_Score': emergence_score,
'Growth_Ratio': growth_ratio,
'Trend_Strength': trend_strength,
'Slope': slope,
'Change_Points': len(change_points),
'Recent_Avg_Freq': recent_avg,
'Historical_Avg_Freq': historical_avg
})
emerging_df = pd.DataFrame(emerging_topics)
emerging_df = emerging_df.sort_values('Emergence_Score', ascending=False).head(top_n)
return emerging_df