nff-normalized-factor-flow 0.1.0__tar.gz

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.
@@ -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,111 @@
1
+ # NFF — Normalized Factor Flow
2
+
3
+ Python package migration scaffold for [Elxrth/NFF-normalized-factor-flow](https://github.com/Elxrth/NFF-normalized-factor-flow).
4
+
5
+ > **Important status:** this archive provides the packaging layout, installation metadata,
6
+ > tests, command-line entry point, and reusable data utilities. It does **not** contain the
7
+ > original `main.py` analysis implementation. The source file could not be retrieved while
8
+ > preparing this ZIP, so no NUA, normalization, signature, or ORDER formula has been recreated
9
+ > or guessed. Follow [`INTEGRATION.md`](INTEGRATION.md) to integrate the actual engine before
10
+ > publishing a functional NFF release to PyPI.
11
+
12
+ ## Requirements
13
+
14
+ - Python 3.10 or newer
15
+ - NumPy 1.24 or newer
16
+ - PyTorch is an optional extra in this scaffold; install it when integrating/running the
17
+ original PyTorch engine.
18
+
19
+ ## Install for development
20
+
21
+ ```bash
22
+ git clone https://github.com/Elxrth/NFF-normalized-factor-flow.git
23
+ cd NFF-normalized-factor-flow
24
+ python -m venv .venv
25
+ ```
26
+
27
+ Windows PowerShell:
28
+
29
+ ```powershell
30
+ .venv\Scripts\Activate.ps1
31
+ python -m pip install --upgrade pip
32
+ python -m pip install -e ".[dev,engine]"
33
+ ```
34
+
35
+ Linux / macOS:
36
+
37
+ ```bash
38
+ source .venv/bin/activate
39
+ python -m pip install --upgrade pip
40
+ python -m pip install -e '.[dev,engine]'
41
+ ```
42
+
43
+ For a CUDA-enabled PyTorch build, install the build matching your CUDA/runtime from the
44
+ [official PyTorch install selector](https://pytorch.org/get-started/locally/) before installing
45
+ this project with `--no-deps`, or adjust the project extras to your supported CUDA index.
46
+
47
+ ## Current utility API
48
+
49
+ ```python
50
+ import nff
51
+ import numpy as np
52
+
53
+ flow = nff.Flow(np.sin(np.linspace(0, 10, 2_000)), name="sensor", sampling_rate=100)
54
+ containers = nff.create_containers(flow.data, size=256, stride=128)
55
+ normalized = nff.normalize(containers[0], method="zscore")
56
+
57
+ print(len(flow))
58
+ print(normalized[:5])
59
+ ```
60
+
61
+ Available utilities:
62
+
63
+ | API | Purpose |
64
+ | --- | --- |
65
+ | `nff.Flow(data)` | Validated one-dimensional numeric flow |
66
+ | `nff.create_containers(data, size, stride=...)` | Split a stream into temporal windows |
67
+ | `nff.normalize(data, method=...)` | Generic z-score, min-max, or robust scaling |
68
+ | `nff.compare_signatures(a, b, metric=...)` | Generic cosine / Euclidean / Manhattan comparison |
69
+ | `nff.load_csv(path, column=0)` | Load a numeric CSV column |
70
+ | `nff.load_numpy(path)` | Load a `.npy` array |
71
+ | `nff.export_json(data, path)` | Export structured data to JSON |
72
+ | `nff.export_csv(data, path)` | Export a numeric vector to CSV |
73
+ | `nff.NFFEngine(...).analyze(flow)` | Reserved for the original NFF engine; pending integration |
74
+
75
+ The generic normalization and comparison helpers are convenience utilities. They are not claims
76
+ about the project's canonical NFF normalization or signature-comparison definitions.
77
+
78
+ ## CLI
79
+
80
+ ```bash
81
+ nff --version
82
+ nff info
83
+ nff doctor
84
+ ```
85
+
86
+ ## Tests
87
+
88
+ ```bash
89
+ python -m pytest
90
+ ```
91
+
92
+ ## Build distributions
93
+
94
+ ```bash
95
+ python -m pip install --upgrade build twine
96
+ python -m build
97
+ twine check dist/*
98
+ ```
99
+
100
+ Do not upload this scaffold to PyPI as a finished NFF release until the original algorithm is
101
+ integrated, tests cover its documented outputs, and the license metadata has been verified.
102
+
103
+ ## PyPI publication
104
+
105
+ 1. Confirm that the distribution name `nff-normalized-factor-flow` is available on PyPI.
106
+ 2. Integrate the original engine and complete the tests (see `INTEGRATION.md`).
107
+ 3. Verify the real license and author metadata.
108
+ 4. Build and validate the distributions with the commands above.
109
+ 5. Test on TestPyPI, then publish with `twine upload dist/*` or configure PyPI Trusted Publishing.
110
+
111
+ Project source and theory: <https://github.com/Elxrth/NFF-normalized-factor-flow>
@@ -0,0 +1,51 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "nff-normalized-factor-flow"
7
+ version = "0.1.0"
8
+ description = "Python package scaffold for Normalized Factor Flow (NFF)."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ authors = [{ name = "Elxrth" }]
12
+ keywords = ["data-streams", "signal-analysis", "time-series", "normalized-factor-flow"]
13
+ classifiers = [
14
+ "Development Status :: 2 - Pre-Alpha",
15
+ "Intended Audience :: Developers",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3.10",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Programming Language :: Python :: 3.13",
21
+ "Topic :: Scientific/Engineering :: Information Analysis",
22
+ ]
23
+ dependencies = ["numpy>=1.24"]
24
+
25
+ [project.optional-dependencies]
26
+ engine = ["torch>=2.0"]
27
+ plot = ["matplotlib>=3.7"]
28
+ serial = ["pyserial>=3.5"]
29
+ all = ["torch>=2.0", "matplotlib>=3.7", "pyserial>=3.5"]
30
+ dev = ["pytest>=8.0", "build>=1.2", "twine>=5.0", "ruff>=0.6"]
31
+
32
+ [project.scripts]
33
+ nff = "nff.cli:main"
34
+
35
+ [project.urls]
36
+ Homepage = "https://github.com/Elxrth/NFF-normalized-factor-flow"
37
+ Repository = "https://github.com/Elxrth/NFF-normalized-factor-flow"
38
+ Issues = "https://github.com/Elxrth/NFF-normalized-factor-flow/issues"
39
+
40
+ [tool.setuptools]
41
+ package-dir = {"" = "src"}
42
+
43
+ [tool.setuptools.packages.find]
44
+ where = ["src"]
45
+
46
+ [tool.pytest.ini_options]
47
+ testpaths = ["tests"]
48
+
49
+ [tool.ruff]
50
+ line-length = 100
51
+ target-version = "py310"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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
+ ]
@@ -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())
@@ -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'.")
@@ -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
@@ -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
+ )
@@ -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
@@ -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
@@ -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)
@@ -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,18 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/nff/__init__.py
4
+ src/nff/cli.py
5
+ src/nff/comparison.py
6
+ src/nff/containers.py
7
+ src/nff/core.py
8
+ src/nff/data.py
9
+ src/nff/exporters.py
10
+ src/nff/normalization.py
11
+ src/nff/results.py
12
+ src/nff_normalized_factor_flow.egg-info/PKG-INFO
13
+ src/nff_normalized_factor_flow.egg-info/SOURCES.txt
14
+ src/nff_normalized_factor_flow.egg-info/dependency_links.txt
15
+ src/nff_normalized_factor_flow.egg-info/entry_points.txt
16
+ src/nff_normalized_factor_flow.egg-info/requires.txt
17
+ src/nff_normalized_factor_flow.egg-info/top_level.txt
18
+ tests/test_utilities.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ nff = nff.cli:main
@@ -0,0 +1,21 @@
1
+ numpy>=1.24
2
+
3
+ [all]
4
+ torch>=2.0
5
+ matplotlib>=3.7
6
+ pyserial>=3.5
7
+
8
+ [dev]
9
+ pytest>=8.0
10
+ build>=1.2
11
+ twine>=5.0
12
+ ruff>=0.6
13
+
14
+ [engine]
15
+ torch>=2.0
16
+
17
+ [plot]
18
+ matplotlib>=3.7
19
+
20
+ [serial]
21
+ pyserial>=3.5
@@ -0,0 +1,39 @@
1
+ import numpy as np
2
+ import pytest
3
+
4
+ import nff
5
+
6
+
7
+ def test_flow_validates_input():
8
+ flow = nff.Flow([1, 2, 3], name="demo", sampling_rate=10)
9
+ assert len(flow) == 3
10
+ assert flow.name == "demo"
11
+ with pytest.raises(ValueError):
12
+ nff.Flow([[1, 2], [3, 4]])
13
+
14
+
15
+ def test_normalize_zscore():
16
+ result = nff.normalize([1, 2, 3])
17
+ assert np.isclose(result.mean(), 0)
18
+ assert np.isclose(result.std(), 1)
19
+
20
+
21
+ def test_normalize_constant():
22
+ np.testing.assert_array_equal(nff.normalize([5, 5, 5]), [0, 0, 0])
23
+
24
+
25
+ def test_create_containers():
26
+ chunks = nff.create_containers(np.arange(8), size=3, stride=2)
27
+ assert [len(chunk) for chunk in chunks] == [3, 3, 3, 2]
28
+ np.testing.assert_array_equal(chunks[1], [2, 3, 4])
29
+
30
+
31
+ def test_compare_signatures():
32
+ assert nff.compare_signatures([1, 0], [1, 0]) == pytest.approx(1.0)
33
+ assert nff.compare_signatures([1, 0], [0, 1], metric="euclidean") == pytest.approx(2**0.5)
34
+
35
+
36
+ def test_engine_does_not_fake_nff_algorithm():
37
+ engine = nff.NFFEngine()
38
+ with pytest.raises(NotImplementedError, match="canonical NFF engine"):
39
+ engine.analyze([1, 2, 3])