echoss-timeseries
echoss AI Bigdata Center 시계열 데이터/모델 기반 라이브러리.
여러 도메인 프로젝트가 함께 쓰는 공통 패키지다. 통계 / 기계학습 / 딥러닝 / 트랜스포머 계열 시계열 모델을 한 구조 위에서 다루는 것을 목표로 한다.
도메인 프로젝트가 TSData / TSModel 을 상속해서 사용하는 것을 전제로 설계되었다.
데이터 로딩·리샘플링·시계열 피처 엔지니어링·진단은 기반 클래스가 담당하고,
도메인 고유의 전처리와 학습 로직만 자식 클래스에 구현한다.
설치
pip install echoss-timeseries # 데이터 · 피처 · 진단 · 모델 골격
pip install 'echoss-timeseries[plot]' # 플롯까지
요구사항
- Python >= 3.12
기본 설치:
| 패키지 | 버전 | 용도 |
|---|---|---|
| echoss-fileformat | >=1.3.2 | logger, config/파일 IO |
| pandas | >=3.0.0 | DataFrame |
| numpy | >=2.5.3 | 수치 연산 |
| scipy | >=1.18.1 | BoxCox, 통계 |
| scikit-learn | >=1.9.0 | scaler, encoder, metric |
| statsmodels | >=0.15.0 | STL, ADF/KPSS, AutoReg |
| holidays | >=0.104 | 공휴일 피처 |
| window_ops | >=0.0.15 | seasonal rolling 연산 |
| numba | >=0.68.0 | window_ops 실행 (0.67 이하는 numpy 2.5 거부) |
[plot] extras:
| 패키지 | 버전 | 용도 |
|---|---|---|
| bokeh | >=3.10.0 | 인터랙티브 플롯 |
| matplotlib | >=3.11.1 | 정적 플롯 |
[forecast] extras — darts 예측 모델 (naive_seasonal, exponential_smoothing, theta, auto_arima, lightgbm):
| 패키지 | 버전 | 용도 |
|---|---|---|
| darts | >=0.47.0 | 예측 모델 (0.46.x 의 Theta 는 statsmodels 0.15 에서 실패) |
| statsforecast | >=2.0.1 | darts AutoARIMA (2.1.0 부터 pandas<3 을 요구해 pandas 3 과 함께 설치되지 않음) |
| lightgbm | >=4.7.0 | darts LightGBM |
판매 없는 날(0)이 있는 자료에서 theta 는 hyperparameters: {season_mode: additive} 로 쓴다 (darts 기본값인 곱셈은 0 을 거부한다). 계절성·추세 방식은 additive / multiplicative / none 문자열로 지정한다. exponential_smoothing 은 계절 주기를 자료 간격으로 정한다(일 7, 주 52, 월 12). 7일처럼 배수 간격이거나, 분 단위처럼 주기를 정할 수 없는 간격이거나, 자료가 그 주기의 두 바퀴보다 짧으면 학습 전에 멈추므로, seasonal_periods 를 지정하거나 seasonal: none 으로 쓴다.
extras 없이 플롯·예측 모델을 쓰면 무엇을 설치해야 하는지 알려주는 오류가 난다.
하한은 pandas 3.0.5 조합에서 시험 통과와 청정 환경 설치를 확인한 값이며, 각 패키지의 최소 요구 버전을 조사한 값이 아니다. pandas 2.x 환경에는 설치되지 않는다.
필요한 것만 적재된다. import echoss_timeseries 는 이름을 실제로 쓸 때까지 하위 모듈을
읽지 않는다. 진단 함수 하나만 쓰면 플롯 라이브러리는 올라오지 않는다.
기능
| 모듈 | 주요 export |
|---|---|
echoss_tsdata |
TSData — 데이터 로딩/캐시, split, 리샘플링, 피처 추가 진입점 |
echoss_tsmodel |
TSModel — run_models() 파이프라인 골격, forecast/classification 스코어 |
tsdiag |
stationarity_tests, lag_diagnostics, try_autoreg, naive_cv_scores, seasonal_strength, is_constant_series, analyze_series, ensure_month_start_col, validate_month_ms, ensure_datetime_freq |
autoregressive_features |
add_lag_features, add_rolling_features, add_seasonal_rolling_features, add_ewma_features |
temporal_features |
add_temporal_features, add_fourier_features, bulk_add_fourier_features |
decomposition |
BaseDecomposition, MultiSeasonalDecomposition, FourierDecomposition, DecomposeResult |
outlier |
detect_outlier_sd, detect_outlier_iqr, detect_outlier_isolation_forest |
echoss_plot_bokeh [plot] |
plot_lines, plot_datetime, plot_forecast, plot_train_test_forecast 등 |
echoss_plot_matplot [plot] |
plot_sales_intensity_heatmap |
engines |
ModelEngine, DataShape, EngineRegistry, StatsmodelsArimaEngine, SklearnTabularEngine, DartsSeriesEngine, HoldoutComparison, Ensemble, WeightedEnsemble, Screener, Candidate |
사용법
1. TSData 상속 — 도메인 데이터 레이어
from echoss_timeseries import TSData, EventDayFeatures
class BakeryData(TSData):
"""제과점 판매 데이터. 월별 Excel 원천 -> 일별 dense DataFrame."""
def __init__(self, data_path='data', config='config/bakery_data.yaml', use_cache=False):
super().__init__(data_path, config, use_cache=use_cache)
self.id_col = self.config.get('id_col')
self.time_col = self.config.get('time_col')
self.target_col = self.config.get('target_col')
def load_raw(self):
"""도메인 원천(Excel/DB) 파싱. 기반 클래스의 add_data() 로 등록한다."""
raw_df = ... # 도메인 파싱
self.add_data(raw_df, data_key='raw_df')
def prepare_xy_data(self):
"""기반 클래스의 피처 메서드와 EventDayFeatures 를 조합한다. 각 호출은 (df, 추가된_컬럼명) 을 반환한다."""
df = self.get_dataframe('raw_df')
df = self.fill_to_frequency(df, 'D', time_col=self.time_col, id_col=self.id_col)
df, _ = self.add_temporal_features(df, time_col=self.time_col, frequency='D')
df, _ = EventDayFeatures(country="KR").add_features(df, time_col=self.time_col)
df, _ = self.add_lag_features(df, lags=[1, 7, 14], column=self.target_col, ts_id=self.id_col)
df, _ = self.add_rolling_features(df, rolls=[7, 28], column=self.target_col, ts_id=self.id_col)
self.add_data(df, data_key='daily_dense_df')
데이터는 항상 cache 에 쓰고, use_cache=True 일 때만 cache 에서 읽는다. cache 는 cache_dir(기본 cached, 실행 폴더 기준)에
{config 파일명}_{data_key} 로 저장되며, 같은 cache_dir 의 인스턴스는 cache 를 공유한다(config 가 dict 이면 이름은 cache).
나누려면 cache_dir 나 cache_suffix 를 지정한다.
2. TSModel 상속 — 도메인 모델 레이어
run_models() 가 prepare_dataset() -> training_model() -> evaluation_model() 순서로
자식 클래스 구현을 호출한다. 단계 간 데이터 전달은 self.runtime_dict 를 사용한다.
from echoss_timeseries import TSModel
class BakeryModel(TSModel):
"""2단계 예측: has_sales 분류 -> 수량 회귀."""
def prepare_dataset(self, df):
# split 은 데이터 레이어 책임 — self.ts_data 를 통해 호출한다
train_df, test_df = self.ts_data.split_data(df, split_date=self.config.get('split_date'))
self.runtime_dict['train_df'] = train_df
self.runtime_dict['test_df'] = test_df
def training_model(self):
train_df = self.runtime_dict['train_df']
# 도메인 학습 엔진 호출 (AutoGluon, pycaret 등)
self.runtime_dict['predictor'] = ...
return self.runtime_dict
def evaluation_model(self):
test_df = self.runtime_dict['test_df']
pred_df = self.runtime_dict['pred_df']
return self.calculate_forecast_score(
test_df[self.config['target_col']], pred_df['pred_y'],
)
ts_data = BakeryData(data_path='data', config='config/bakery_data.yaml')
ts_data.load_raw()
ts_data.prepare_xy_data()
model = BakeryModel(ts_data, config='config/bakery_model.yaml', result_save_path='results/')
model.run_models(data='daily_dense_df', training_kwargs={'year_month': '2026-05'})
인스턴스는 원하는 만큼 만들 수 있다. TSData 와 TSModel 의 가변 상태(data_map,
runtime_dict, scaler·encoder 맵 등)는 인스턴스마다 분리된다.
show_plot 같은 불변 플래그만 클래스 레벨에 남아 있어, 자식 클래스에서 클래스 레벨로
기본값을 바꿀 수 있다.
3. 시계열 진단 (tsdiag)
상속 없이 모듈 함수로 바로 사용한다.
from echoss_timeseries import analyze_series, stationarity_tests, naive_cv_scores
result = analyze_series(ts, freq='D', seasonal_period=7, max_ar=12, horizon=14)
print(result['predictability_score'], result['seasonal_strength'])
print(stationarity_tests(ts)) # adf_pvalue, kpss_pvalue
print(naive_cv_scores(ts, horizon=14, seasonal_period=7))
도메인 컬럼명은 라이브러리 기본값으로 두지 않는다. validate_month_ms(df, time_col='날짜')
처럼 호출부가 명시 전달한다.
알려진 한계 (1.0.0)
고치지 않고 공개한 동작이다. 고치면 동작이 바뀌므로 다음 major 에서 다룬다.
| 대상 | 한계 | 우회 |
|---|---|---|
TSData.set_dataframe |
등록되지 않은 data_key 로 부르면 예외 없이 지나가고 cache 에만 쓴다 |
새 키 등록은 add_data, set_dataframe 은 기존 키 갱신에만 쓴다 |
EventDayFeatures.add_features |
넘겨받은 DataFrame 에 컬럼을 직접 더한다 | 원본을 지켜야 하면 df.copy() 를 넘긴다 |
HoldoutComparison.run |
시계열 하나라도 결측이 있으면 비교 전체를 ValueError 로 거부한다 |
결측을 미리 채우거나 시계열마다 따로 run() 한다 |
analyze_series 의 predictability_score |
실측 판매 자료(상품 71개)에서 실제 예측 오차와 반대 방향으로 움직였다 | 모델 선택이나 예측 난이도 판정에 쓰지 않는다 |
MultiSeasonalDecomposition('fourier', ...) |
요일 주기(day_of_week) 성분의 세기가 항상 0 으로 나온다 |
요일 주기는 STL 이나 averages 방식으로 분해한다 |
seasonal_esd |
원 알고리즘(S-H-ESD)대로 추세를 빼지 않아 추세가 있는 자료에서 이상치를 놓친다 | 추세를 먼저 빼고 넘긴다 |
is_constant_series |
넘파이 불리언을 돌려준다 | is True 대신 진리값으로 비교한다 |
문서
| 문서 | 내용 |
|---|---|
prior-work.jsonl |
작업원장. 이전 작업의 결론과 다시 하지 말 것, 백로그 |
docs/known-issues.md |
사용자용. 알려진 문제와 우회 방법 |
docs/backlog.md |
개발자용. 결함 대장(B4 |
docs/design/engine-abstraction.md |
학습 엔진 추상화 ABC 설계 (구현 전) |
앞으로
알고리즘을 교체하고 결합할 수 있는 계층을 만들고 있다 — 후보 알고리즘을 추리는 선별기, 알고리즘 하나를 감싸는 모델 엔진, 여러 모델을 합치는 앙상블 셋으로 나눈다.
모델 엔진과 앙상블은 동작한다. 선별기는 ABC 만 있다 — AutoML 어댑터는 아직 없다.
from echoss_timeseries import EngineRegistry, WeightedEnsemble
# 알고리즘은 config 로 고른다. 이름은 알고리즘 이름이다 (라이브러리 이름이 아니다)
models = EngineRegistry.build_models({"models": [
{"name": "arima", "hyperparameters": {"order": [1, 1, 1]}},
{"name": "random_forest", "problem_type": "regression", "target_col": "y"},
]})
engine = models[0].fit(series)
pred = engine.predict(future_index) # 예측 길이는 넘긴 인덱스의 길이로 정해진다
# 여러 모델을 검증오차 역수로 가중 평균한다
ensemble = WeightedEnsemble().fit(fitted_models, (valid_x, valid_y))
print(ensemble.weights()) # 어느 모델이 얼마나 기여했는지 설명된다
config 에 모르는 이름이나 키를 적으면 예외가 난다. 조용히 무시하면 그 설정이 적용되지 않은 채 실행되고, 결과만 보고는 원인을 찾을 수 없다.
설계는 docs/design/engine-abstraction.md 에 있다.
License
Apache-2.0
Metadata
Release files for echoss-timeseries 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| echoss_timeseries-1.0.0.tar.gz | 83.9 kB | Without provenance | Details for echoss_timeseries-1.0.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| echoss_timeseries-1.0.0-py3-none-any.whl | Python 3 | none | any | Details for echoss_timeseries-1.0.0-py3-none-any.whl |
Total release size: 173.5 kB
Release files / echoss_timeseries-1.0.0.tar.gz
| Download URL | echoss_timeseries-1.0.0.tar.gz |
|---|---|
| Size | 83.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
666fec0577c7e1c4a314a123432a31b276414aa15050d8682b2c526c833ad3f4
|
|
BLAKE2b-256 checksum How to use checksums |
14ea282702d5c896e1b67beec3978e3f55e306d4adcb7622f3720017ccd399b3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|
Release files / echoss_timeseries-1.0.0-py3-none-any.whl
| Download URL | echoss_timeseries-1.0.0-py3-none-any.whl |
|---|---|
| Size | 89.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
56d4ba61d458a55c0ecf86895ee4ef95fb9b373962ec72068e703f3ef6475fe1
|
|
BLAKE2b-256 checksum How to use checksums |
110dcbc207270e03ee4f4467606eea41de02be2147a8ebfd7849d9fa1e52856f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|
Security for echoss-timeseries 1.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.