nff-normalized-factor-flow 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nff/__init__.py +28 -0
- nff/cli.py +48 -0
- nff/comparison.py +37 -0
- nff/containers.py +42 -0
- nff/core.py +87 -0
- nff/data.py +38 -0
- nff/exporters.py +47 -0
- nff/normalization.py +40 -0
- nff/results.py +24 -0
- nff_normalized_factor_flow-0.1.0.dist-info/METADATA +147 -0
- nff_normalized_factor_flow-0.1.0.dist-info/RECORD +14 -0
- nff_normalized_factor_flow-0.1.0.dist-info/WHEEL +5 -0
- nff_normalized_factor_flow-0.1.0.dist-info/entry_points.txt +2 -0
- nff_normalized_factor_flow-0.1.0.dist-info/top_level.txt +1 -0
nff/__init__.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""NFF — Normalized Factor Flow.
|
|
2
|
+
|
|
3
|
+
This archive contains the package structure and reusable data utilities.
|
|
4
|
+
The original NFF analysis engine still needs to be integrated; see INTEGRATION.md.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .comparison import compare_signatures
|
|
8
|
+
from .containers import create_containers
|
|
9
|
+
from .core import Flow, NFFEngine
|
|
10
|
+
from .data import load_csv, load_numpy
|
|
11
|
+
from .normalization import normalize
|
|
12
|
+
from .results import AnalysisResult
|
|
13
|
+
from .exporters import export_csv, export_json
|
|
14
|
+
|
|
15
|
+
__version__ = "0.1.0"
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"AnalysisResult",
|
|
19
|
+
"Flow",
|
|
20
|
+
"NFFEngine",
|
|
21
|
+
"compare_signatures",
|
|
22
|
+
"create_containers",
|
|
23
|
+
"export_csv",
|
|
24
|
+
"export_json",
|
|
25
|
+
"load_csv",
|
|
26
|
+
"load_numpy",
|
|
27
|
+
"normalize",
|
|
28
|
+
]
|
nff/cli.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Command-line interface for the NFF package."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
|
|
7
|
+
from . import __version__
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
11
|
+
parser = argparse.ArgumentParser(prog="nff", description="Normalized Factor Flow utilities")
|
|
12
|
+
parser.add_argument("--version", action="version", version=f"NFF {__version__}")
|
|
13
|
+
sub = parser.add_subparsers(dest="command")
|
|
14
|
+
sub.add_parser("info", help="Display package and integration status")
|
|
15
|
+
sub.add_parser("doctor", help="Check optional runtime dependencies")
|
|
16
|
+
return parser
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def main() -> int:
|
|
20
|
+
args = build_parser().parse_args()
|
|
21
|
+
if args.command == "doctor":
|
|
22
|
+
try:
|
|
23
|
+
import torch # noqa: F401
|
|
24
|
+
print("PyTorch: available")
|
|
25
|
+
except ImportError:
|
|
26
|
+
print("PyTorch: not installed (install with: pip install 'nff-normalized-factor-flow[engine]')")
|
|
27
|
+
try:
|
|
28
|
+
import matplotlib # noqa: F401
|
|
29
|
+
print("Matplotlib: available")
|
|
30
|
+
except ImportError:
|
|
31
|
+
print("Matplotlib: not installed (optional)")
|
|
32
|
+
try:
|
|
33
|
+
import serial # noqa: F401
|
|
34
|
+
print("PySerial: available")
|
|
35
|
+
except ImportError:
|
|
36
|
+
print("PySerial: not installed (optional)")
|
|
37
|
+
return 0
|
|
38
|
+
if args.command == "info":
|
|
39
|
+
print(f"NFF {__version__}")
|
|
40
|
+
print("Package scaffold: original NFF analysis engine not yet integrated.")
|
|
41
|
+
print("See INTEGRATION.md before publishing a functional release.")
|
|
42
|
+
return 0
|
|
43
|
+
build_parser().print_help()
|
|
44
|
+
return 0
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
if __name__ == "__main__":
|
|
48
|
+
raise SystemExit(main())
|
nff/comparison.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Generic signature comparison metrics."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Literal
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
Metric = Literal["cosine", "euclidean", "manhattan"]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def compare_signatures(left: object, right: object, *, metric: Metric = "cosine") -> float:
|
|
13
|
+
"""Compare two equally-sized numeric vectors.
|
|
14
|
+
|
|
15
|
+
Cosine returns similarity in [-1, 1]. Euclidean and Manhattan return distances.
|
|
16
|
+
This is a generic vector comparison utility, not an NFF-defined distance unless
|
|
17
|
+
the project specification explicitly adopts it.
|
|
18
|
+
"""
|
|
19
|
+
a = np.asarray(left, dtype=np.float64)
|
|
20
|
+
b = np.asarray(right, dtype=np.float64)
|
|
21
|
+
if a.ndim != 1 or b.ndim != 1:
|
|
22
|
+
raise ValueError("Both signatures must be one-dimensional.")
|
|
23
|
+
if a.shape != b.shape:
|
|
24
|
+
raise ValueError("Signatures must have identical dimensions.")
|
|
25
|
+
if not np.isfinite(a).all() or not np.isfinite(b).all():
|
|
26
|
+
raise ValueError("Signatures must contain only finite values.")
|
|
27
|
+
|
|
28
|
+
if metric == "cosine":
|
|
29
|
+
denom = float(np.linalg.norm(a) * np.linalg.norm(b))
|
|
30
|
+
if denom == 0:
|
|
31
|
+
return 1.0 if np.array_equal(a, b) else 0.0
|
|
32
|
+
return float(np.dot(a, b) / denom)
|
|
33
|
+
if metric == "euclidean":
|
|
34
|
+
return float(np.linalg.norm(a - b))
|
|
35
|
+
if metric == "manhattan":
|
|
36
|
+
return float(np.abs(a - b).sum())
|
|
37
|
+
raise ValueError("metric must be 'cosine', 'euclidean', or 'manhattan'.")
|
nff/containers.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Utilities to split a continuous flow into temporal containers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Iterable
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def create_containers(
|
|
11
|
+
data: Iterable[float] | np.ndarray,
|
|
12
|
+
size: int,
|
|
13
|
+
*,
|
|
14
|
+
stride: int | None = None,
|
|
15
|
+
drop_remainder: bool = False,
|
|
16
|
+
) -> list[np.ndarray]:
|
|
17
|
+
"""Split one-dimensional data into possibly overlapping windows.
|
|
18
|
+
|
|
19
|
+
Parameters are generic segmentation controls; they do not impose or infer
|
|
20
|
+
the canonical NFF Container Delay or NUA configuration.
|
|
21
|
+
"""
|
|
22
|
+
values = np.asarray(data, dtype=np.float64)
|
|
23
|
+
if values.ndim != 1:
|
|
24
|
+
raise ValueError("data must be one-dimensional.")
|
|
25
|
+
if values.size == 0:
|
|
26
|
+
raise ValueError("data cannot be empty.")
|
|
27
|
+
if size <= 0:
|
|
28
|
+
raise ValueError("size must be greater than zero.")
|
|
29
|
+
step = size if stride is None else stride
|
|
30
|
+
if step <= 0:
|
|
31
|
+
raise ValueError("stride must be greater than zero.")
|
|
32
|
+
|
|
33
|
+
result: list[np.ndarray] = []
|
|
34
|
+
for start in range(0, values.size, step):
|
|
35
|
+
window = values[start : start + size]
|
|
36
|
+
if window.size < size and drop_remainder:
|
|
37
|
+
break
|
|
38
|
+
if window.size:
|
|
39
|
+
result.append(window.copy())
|
|
40
|
+
if start + size >= values.size:
|
|
41
|
+
break
|
|
42
|
+
return result
|
nff/core.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""Public NFF objects. The project's original analysis engine is integrated separately."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Any, Iterable
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
from .results import AnalysisResult
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(slots=True)
|
|
14
|
+
class Flow:
|
|
15
|
+
"""A one-dimensional numeric data flow.
|
|
16
|
+
|
|
17
|
+
Parameters
|
|
18
|
+
----------
|
|
19
|
+
data:
|
|
20
|
+
Iterable of numeric samples or a NumPy array.
|
|
21
|
+
name:
|
|
22
|
+
Optional human-readable flow name.
|
|
23
|
+
sampling_rate:
|
|
24
|
+
Optional sampling frequency in samples per second.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
data: np.ndarray
|
|
28
|
+
name: str = "flow"
|
|
29
|
+
sampling_rate: float | None = None
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
data: Iterable[float] | np.ndarray,
|
|
34
|
+
name: str = "flow",
|
|
35
|
+
sampling_rate: float | None = None,
|
|
36
|
+
) -> None:
|
|
37
|
+
values = np.asarray(data, dtype=np.float64)
|
|
38
|
+
if values.ndim != 1:
|
|
39
|
+
raise ValueError("Flow data must be one-dimensional.")
|
|
40
|
+
if values.size == 0:
|
|
41
|
+
raise ValueError("Flow data cannot be empty.")
|
|
42
|
+
if not np.isfinite(values).all():
|
|
43
|
+
raise ValueError("Flow data must contain only finite numeric values.")
|
|
44
|
+
if sampling_rate is not None and sampling_rate <= 0:
|
|
45
|
+
raise ValueError("sampling_rate must be greater than zero.")
|
|
46
|
+
self.data = values
|
|
47
|
+
self.name = str(name)
|
|
48
|
+
self.sampling_rate = sampling_rate
|
|
49
|
+
|
|
50
|
+
def __len__(self) -> int:
|
|
51
|
+
return int(self.data.size)
|
|
52
|
+
|
|
53
|
+
def __array__(self, dtype: Any = None) -> np.ndarray:
|
|
54
|
+
return np.asarray(self.data, dtype=dtype)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class NFFEngine:
|
|
58
|
+
"""Entry point reserved for the original NFF analysis implementation.
|
|
59
|
+
|
|
60
|
+
The public repository's main.py could not be downloaded in the archive-building
|
|
61
|
+
environment. This class deliberately does not substitute a made-up ORDER or
|
|
62
|
+
NUA formula. Integrate the original algorithm as described in INTEGRATION.md.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
def __init__(self, *, device: str = "auto", dtype: str = "float64") -> None:
|
|
66
|
+
if device not in {"auto", "cpu", "cuda"}:
|
|
67
|
+
raise ValueError("device must be 'auto', 'cpu', or 'cuda'.")
|
|
68
|
+
if dtype not in {"float32", "float64"}:
|
|
69
|
+
raise ValueError("dtype must be 'float32' or 'float64'.")
|
|
70
|
+
self.device = device
|
|
71
|
+
self.dtype = dtype
|
|
72
|
+
|
|
73
|
+
def analyze(self, flow: Flow | Iterable[float] | np.ndarray) -> AnalysisResult:
|
|
74
|
+
"""Run the canonical NFF algorithm.
|
|
75
|
+
|
|
76
|
+
Raises
|
|
77
|
+
------
|
|
78
|
+
NotImplementedError
|
|
79
|
+
Until the original engine from the repository's main.py is integrated.
|
|
80
|
+
"""
|
|
81
|
+
if not isinstance(flow, Flow):
|
|
82
|
+
flow = Flow(flow)
|
|
83
|
+
raise NotImplementedError(
|
|
84
|
+
"The canonical NFF engine has not been integrated into this package scaffold. "
|
|
85
|
+
"See INTEGRATION.md and add the original repository's main.py implementation "
|
|
86
|
+
"before publishing a functional NFF release."
|
|
87
|
+
)
|
nff/data.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Input helpers for common numeric data formats."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def load_csv(path: str | Path, *, column: int = 0, delimiter: str = ",", skip_header: int = 0) -> np.ndarray:
|
|
12
|
+
"""Load one numeric column from a CSV file."""
|
|
13
|
+
if column < 0:
|
|
14
|
+
raise ValueError("column must be zero or greater.")
|
|
15
|
+
table = np.genfromtxt(path, delimiter=delimiter, skip_header=skip_header, dtype=np.float64)
|
|
16
|
+
if table.size == 0:
|
|
17
|
+
raise ValueError("The CSV file contains no numeric data.")
|
|
18
|
+
if table.ndim == 1:
|
|
19
|
+
if column != 0:
|
|
20
|
+
raise IndexError("Requested column does not exist in this one-column CSV.")
|
|
21
|
+
values = table
|
|
22
|
+
else:
|
|
23
|
+
if column >= table.shape[1]:
|
|
24
|
+
raise IndexError(f"column {column} is outside CSV width {table.shape[1]}.")
|
|
25
|
+
values = table[:, column]
|
|
26
|
+
values = np.asarray(values, dtype=np.float64).reshape(-1)
|
|
27
|
+
if not np.isfinite(values).all():
|
|
28
|
+
raise ValueError("Selected CSV column contains missing or non-finite values.")
|
|
29
|
+
return values
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def load_numpy(path: str | Path, *, allow_pickle: bool = False) -> np.ndarray:
|
|
33
|
+
"""Load an array from a .npy file."""
|
|
34
|
+
data: Any = np.load(path, allow_pickle=allow_pickle)
|
|
35
|
+
values = np.asarray(data, dtype=np.float64)
|
|
36
|
+
if values.size == 0 or not np.isfinite(values).all():
|
|
37
|
+
raise ValueError("NumPy file must contain non-empty finite numeric data.")
|
|
38
|
+
return values
|
nff/exporters.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Export numeric arrays and structured results."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import csv
|
|
6
|
+
import json
|
|
7
|
+
from dataclasses import asdict, is_dataclass
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any, Mapping
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _jsonable(value: Any) -> Any:
|
|
15
|
+
if isinstance(value, np.ndarray):
|
|
16
|
+
return value.tolist()
|
|
17
|
+
if isinstance(value, np.generic):
|
|
18
|
+
return value.item()
|
|
19
|
+
if is_dataclass(value) and not isinstance(value, type):
|
|
20
|
+
return _jsonable(asdict(value))
|
|
21
|
+
if isinstance(value, Mapping):
|
|
22
|
+
return {str(k): _jsonable(v) for k, v in value.items()}
|
|
23
|
+
if isinstance(value, (list, tuple)):
|
|
24
|
+
return [_jsonable(v) for v in value]
|
|
25
|
+
return value
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def export_json(data: Any, path: str | Path, *, indent: int = 2) -> Path:
|
|
29
|
+
"""Write a result or mapping as UTF-8 JSON and return the output path."""
|
|
30
|
+
target = Path(path)
|
|
31
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
32
|
+
target.write_text(json.dumps(_jsonable(data), indent=indent, ensure_ascii=False), encoding="utf-8")
|
|
33
|
+
return target
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def export_csv(data: object, path: str | Path, *, header: str = "value") -> Path:
|
|
37
|
+
"""Write a one-dimensional numeric sequence to CSV."""
|
|
38
|
+
values = np.asarray(data)
|
|
39
|
+
if values.ndim != 1:
|
|
40
|
+
raise ValueError("CSV export expects one-dimensional data.")
|
|
41
|
+
target = Path(path)
|
|
42
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
43
|
+
with target.open("w", newline="", encoding="utf-8") as stream:
|
|
44
|
+
writer = csv.writer(stream)
|
|
45
|
+
writer.writerow([header])
|
|
46
|
+
writer.writerows([[value.item() if isinstance(value, np.generic) else value] for value in values])
|
|
47
|
+
return target
|
nff/normalization.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""General-purpose normalization helpers (not a replacement for NFF's own method)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Literal
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
Method = Literal["zscore", "minmax", "robust"]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def normalize(data: object, method: Method = "zscore", *, axis: int | None = None) -> np.ndarray:
|
|
13
|
+
"""Normalize numeric data using z-score, min-max, or robust scaling.
|
|
14
|
+
|
|
15
|
+
Constant ranges are returned as zeros. This utility is independent from the
|
|
16
|
+
canonical NFF normalization pipeline, which must be ported from the source engine.
|
|
17
|
+
"""
|
|
18
|
+
values = np.asarray(data, dtype=np.float64)
|
|
19
|
+
if values.size == 0:
|
|
20
|
+
raise ValueError("data cannot be empty.")
|
|
21
|
+
if not np.isfinite(values).all():
|
|
22
|
+
raise ValueError("data must contain only finite numeric values.")
|
|
23
|
+
|
|
24
|
+
if method == "zscore":
|
|
25
|
+
center = np.mean(values, axis=axis, keepdims=axis is not None)
|
|
26
|
+
scale = np.std(values, axis=axis, keepdims=axis is not None)
|
|
27
|
+
elif method == "minmax":
|
|
28
|
+
center = np.min(values, axis=axis, keepdims=axis is not None)
|
|
29
|
+
scale = np.max(values, axis=axis, keepdims=axis is not None) - center
|
|
30
|
+
elif method == "robust":
|
|
31
|
+
center = np.median(values, axis=axis, keepdims=axis is not None)
|
|
32
|
+
q75 = np.percentile(values, 75, axis=axis, keepdims=axis is not None)
|
|
33
|
+
q25 = np.percentile(values, 25, axis=axis, keepdims=axis is not None)
|
|
34
|
+
scale = q75 - q25
|
|
35
|
+
else:
|
|
36
|
+
raise ValueError("method must be 'zscore', 'minmax', or 'robust'.")
|
|
37
|
+
|
|
38
|
+
safe_scale = np.where(np.asarray(scale) == 0, 1.0, scale)
|
|
39
|
+
result = (values - center) / safe_scale
|
|
40
|
+
return np.where(np.asarray(scale) == 0, 0.0, result)
|
nff/results.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Result container for NFF analyses."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(slots=True)
|
|
12
|
+
class AnalysisResult:
|
|
13
|
+
"""Structured result returned by the NFF analysis engine."""
|
|
14
|
+
|
|
15
|
+
order: float
|
|
16
|
+
signature: np.ndarray
|
|
17
|
+
metrics: dict[str, Any] = field(default_factory=dict)
|
|
18
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
19
|
+
|
|
20
|
+
def __post_init__(self) -> None:
|
|
21
|
+
self.order = float(self.order)
|
|
22
|
+
self.signature = np.asarray(self.signature, dtype=np.float64)
|
|
23
|
+
if self.signature.ndim != 1:
|
|
24
|
+
raise ValueError("signature must be one-dimensional.")
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nff-normalized-factor-flow
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python package scaffold for Normalized Factor Flow (NFF).
|
|
5
|
+
Author: Elxrth
|
|
6
|
+
Project-URL: Homepage, https://github.com/Elxrth/NFF-normalized-factor-flow
|
|
7
|
+
Project-URL: Repository, https://github.com/Elxrth/NFF-normalized-factor-flow
|
|
8
|
+
Project-URL: Issues, https://github.com/Elxrth/NFF-normalized-factor-flow/issues
|
|
9
|
+
Keywords: data-streams,signal-analysis,time-series,normalized-factor-flow
|
|
10
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
Requires-Dist: numpy>=1.24
|
|
21
|
+
Provides-Extra: engine
|
|
22
|
+
Requires-Dist: torch>=2.0; extra == "engine"
|
|
23
|
+
Provides-Extra: plot
|
|
24
|
+
Requires-Dist: matplotlib>=3.7; extra == "plot"
|
|
25
|
+
Provides-Extra: serial
|
|
26
|
+
Requires-Dist: pyserial>=3.5; extra == "serial"
|
|
27
|
+
Provides-Extra: all
|
|
28
|
+
Requires-Dist: torch>=2.0; extra == "all"
|
|
29
|
+
Requires-Dist: matplotlib>=3.7; extra == "all"
|
|
30
|
+
Requires-Dist: pyserial>=3.5; extra == "all"
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
33
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
34
|
+
Requires-Dist: twine>=5.0; extra == "dev"
|
|
35
|
+
Requires-Dist: ruff>=0.6; extra == "dev"
|
|
36
|
+
|
|
37
|
+
# NFF — Normalized Factor Flow
|
|
38
|
+
|
|
39
|
+
Python package migration scaffold for [Elxrth/NFF-normalized-factor-flow](https://github.com/Elxrth/NFF-normalized-factor-flow).
|
|
40
|
+
|
|
41
|
+
> **Important status:** this archive provides the packaging layout, installation metadata,
|
|
42
|
+
> tests, command-line entry point, and reusable data utilities. It does **not** contain the
|
|
43
|
+
> original `main.py` analysis implementation. The source file could not be retrieved while
|
|
44
|
+
> preparing this ZIP, so no NUA, normalization, signature, or ORDER formula has been recreated
|
|
45
|
+
> or guessed. Follow [`INTEGRATION.md`](INTEGRATION.md) to integrate the actual engine before
|
|
46
|
+
> publishing a functional NFF release to PyPI.
|
|
47
|
+
|
|
48
|
+
## Requirements
|
|
49
|
+
|
|
50
|
+
- Python 3.10 or newer
|
|
51
|
+
- NumPy 1.24 or newer
|
|
52
|
+
- PyTorch is an optional extra in this scaffold; install it when integrating/running the
|
|
53
|
+
original PyTorch engine.
|
|
54
|
+
|
|
55
|
+
## Install for development
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
git clone https://github.com/Elxrth/NFF-normalized-factor-flow.git
|
|
59
|
+
cd NFF-normalized-factor-flow
|
|
60
|
+
python -m venv .venv
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Windows PowerShell:
|
|
64
|
+
|
|
65
|
+
```powershell
|
|
66
|
+
.venv\Scripts\Activate.ps1
|
|
67
|
+
python -m pip install --upgrade pip
|
|
68
|
+
python -m pip install -e ".[dev,engine]"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Linux / macOS:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
source .venv/bin/activate
|
|
75
|
+
python -m pip install --upgrade pip
|
|
76
|
+
python -m pip install -e '.[dev,engine]'
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
For a CUDA-enabled PyTorch build, install the build matching your CUDA/runtime from the
|
|
80
|
+
[official PyTorch install selector](https://pytorch.org/get-started/locally/) before installing
|
|
81
|
+
this project with `--no-deps`, or adjust the project extras to your supported CUDA index.
|
|
82
|
+
|
|
83
|
+
## Current utility API
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
import nff
|
|
87
|
+
import numpy as np
|
|
88
|
+
|
|
89
|
+
flow = nff.Flow(np.sin(np.linspace(0, 10, 2_000)), name="sensor", sampling_rate=100)
|
|
90
|
+
containers = nff.create_containers(flow.data, size=256, stride=128)
|
|
91
|
+
normalized = nff.normalize(containers[0], method="zscore")
|
|
92
|
+
|
|
93
|
+
print(len(flow))
|
|
94
|
+
print(normalized[:5])
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Available utilities:
|
|
98
|
+
|
|
99
|
+
| API | Purpose |
|
|
100
|
+
| --- | --- |
|
|
101
|
+
| `nff.Flow(data)` | Validated one-dimensional numeric flow |
|
|
102
|
+
| `nff.create_containers(data, size, stride=...)` | Split a stream into temporal windows |
|
|
103
|
+
| `nff.normalize(data, method=...)` | Generic z-score, min-max, or robust scaling |
|
|
104
|
+
| `nff.compare_signatures(a, b, metric=...)` | Generic cosine / Euclidean / Manhattan comparison |
|
|
105
|
+
| `nff.load_csv(path, column=0)` | Load a numeric CSV column |
|
|
106
|
+
| `nff.load_numpy(path)` | Load a `.npy` array |
|
|
107
|
+
| `nff.export_json(data, path)` | Export structured data to JSON |
|
|
108
|
+
| `nff.export_csv(data, path)` | Export a numeric vector to CSV |
|
|
109
|
+
| `nff.NFFEngine(...).analyze(flow)` | Reserved for the original NFF engine; pending integration |
|
|
110
|
+
|
|
111
|
+
The generic normalization and comparison helpers are convenience utilities. They are not claims
|
|
112
|
+
about the project's canonical NFF normalization or signature-comparison definitions.
|
|
113
|
+
|
|
114
|
+
## CLI
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
nff --version
|
|
118
|
+
nff info
|
|
119
|
+
nff doctor
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Tests
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
python -m pytest
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Build distributions
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
python -m pip install --upgrade build twine
|
|
132
|
+
python -m build
|
|
133
|
+
twine check dist/*
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Do not upload this scaffold to PyPI as a finished NFF release until the original algorithm is
|
|
137
|
+
integrated, tests cover its documented outputs, and the license metadata has been verified.
|
|
138
|
+
|
|
139
|
+
## PyPI publication
|
|
140
|
+
|
|
141
|
+
1. Confirm that the distribution name `nff-normalized-factor-flow` is available on PyPI.
|
|
142
|
+
2. Integrate the original engine and complete the tests (see `INTEGRATION.md`).
|
|
143
|
+
3. Verify the real license and author metadata.
|
|
144
|
+
4. Build and validate the distributions with the commands above.
|
|
145
|
+
5. Test on TestPyPI, then publish with `twine upload dist/*` or configure PyPI Trusted Publishing.
|
|
146
|
+
|
|
147
|
+
Project source and theory: <https://github.com/Elxrth/NFF-normalized-factor-flow>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
nff/__init__.py,sha256=MCoPvzgJhN0gNGyzjB3tkzRy7JLVXSP0mnIaNenVGsY,703
|
|
2
|
+
nff/cli.py,sha256=oUjEfFzgpnBZgtZcHzzlHzJYI1S1arzcU3psvF9DNfE,1624
|
|
3
|
+
nff/comparison.py,sha256=9bRAr1cuWdzowc_yZo0sg3Jag35HysmJnvv8mreqmw8,1412
|
|
4
|
+
nff/containers.py,sha256=fa8-AAlilbcjpTm-TbN2xPaFwivDmv1vACxJifFag_4,1291
|
|
5
|
+
nff/core.py,sha256=IQ6eZGvDHjTzAn2p242pzoMc8CAhGsimxKDJLDCoZI0,2950
|
|
6
|
+
nff/data.py,sha256=3k18_HRUTqGPi1j01xB2beWgYl-3t5wgGZJ_5bQUups,1507
|
|
7
|
+
nff/exporters.py,sha256=tlDGPzvYFV467oX2YSxb18gal-PPswbDHDajeX499xU,1665
|
|
8
|
+
nff/normalization.py,sha256=fLniGr4mPI4xEOGAdCp6MxZElWhqKtGyGyXG7l8VbGc,1681
|
|
9
|
+
nff/results.py,sha256=hqaME-WS1dGqbewVZYsmrrBgtps2EZ5paPrN0JV2XjE,686
|
|
10
|
+
nff_normalized_factor_flow-0.1.0.dist-info/METADATA,sha256=ekOGRD_1l9fTjZKC2YeT6OWJXSWT-ixnMDsK-nLxaYk,5348
|
|
11
|
+
nff_normalized_factor_flow-0.1.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
12
|
+
nff_normalized_factor_flow-0.1.0.dist-info/entry_points.txt,sha256=KcTJ9_Z6qqK-1OUdv7GJmkn8bPVkPWcjHe3Juq4COBE,37
|
|
13
|
+
nff_normalized_factor_flow-0.1.0.dist-info/top_level.txt,sha256=K5kgEY0GFqztWlM65sgCppDsnNPsQFUkt0F7gRA56t0,4
|
|
14
|
+
nff_normalized_factor_flow-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
nff
|