72 lines
3.0 KiB
Python
72 lines
3.0 KiB
Python
import numpy as np
|
|
from datetime import datetime
|
|
|
|
class SOIPredictionService:
|
|
"""학습형 시계열 예측 및 피처 추출 엔진"""
|
|
|
|
@staticmethod
|
|
def get_historical_soi(cursor, project_id):
|
|
"""DB에서 프로젝트의 과거 SOI 히스토리를 시퀀스로 추출"""
|
|
cursor.execute("""
|
|
SELECT crawl_date, file_count, recent_log
|
|
FROM projects_history
|
|
WHERE project_id = %s
|
|
ORDER BY crawl_date ASC
|
|
""", (project_id,))
|
|
return cursor.fetchall()
|
|
|
|
@staticmethod
|
|
def extract_vitality_features(history):
|
|
"""딥러닝 학습을 위한 4대 핵심 피처 추출 (Feature Engineering)"""
|
|
if len(history) < 2:
|
|
return {"velocity": 0, "acceleration": 0, "consistency": 0.5, "density": 0.1}
|
|
|
|
# 실제 데이터 구조에 맞게 보정
|
|
counts = []
|
|
for h in history:
|
|
try:
|
|
val = int(h['file_count']) if h['file_count'] is not None else 0
|
|
counts.append(val)
|
|
except:
|
|
counts.append(0)
|
|
|
|
# 1. 활동 속도 (Velocity)
|
|
velocity = np.diff(counts).mean() if len(counts) > 1 else 0
|
|
|
|
# 2. 활동 가속도 (Acceleration): 최근 활동이 빨라지는지 느려지는지
|
|
acceleration = np.diff(np.diff(counts)).mean() if len(counts) > 2 else 0
|
|
|
|
# 3. 로그 밀도 (Density): 전체 기간 대비 실제 로그 발생 비율
|
|
logs = [h['recent_log'] for h in history if h['recent_log'] and h['recent_log'] != "데이터 없음"]
|
|
density = len(logs) / len(history) if len(history) > 0 else 0
|
|
|
|
# 4. 관리 일관성 (Consistency): 업데이트 간격의 표준편차 (낮을수록 좋음)
|
|
# (현재 데이터는 일일 크롤링이므로 로그 텍스트 변화 시점을 기준으로 간격 계산 가능)
|
|
|
|
return {
|
|
"velocity": float(velocity),
|
|
"acceleration": float(acceleration),
|
|
"density": float(density),
|
|
"sample_count": len(history)
|
|
}
|
|
|
|
@staticmethod
|
|
def predict_future_soi(current_soi, history, days_ahead=14):
|
|
"""기존 점수와 시계열 피처를 결합하여 미래 점수 예측"""
|
|
if not history or len(history) < 2:
|
|
return round(max(0, min(100, current_soi - (0.05 * days_ahead))), 1)
|
|
|
|
features = SOIPredictionService.extract_vitality_features(history)
|
|
|
|
# 기준점을 현재의 실제 SOI 점수로 설정 (핵심 수정)
|
|
current_val = float(current_soi)
|
|
|
|
# 활동 모멘텀 계산: 파일 증가 속도와 로그 밀도 반영
|
|
momentum_factor = (features['velocity'] * 0.2) + (features['density'] * 2.0)
|
|
|
|
# 예측 로직: 현재값 + 모멘텀 - 자연 감쇄
|
|
decay_constant = 0.05
|
|
predicted = current_val + momentum_factor - (decay_constant * days_ahead)
|
|
|
|
return round(max(0, min(100, predicted)), 1)
|