viseda 1.0.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.
- viseda/__init__.py +20 -0
- viseda/cli.py +144 -0
- viseda/core/__init__.py +3 -0
- viseda/core/base.py +69 -0
- viseda/hyperspectral/__init__.py +1 -0
- viseda/hyperspectral/eda.py +1849 -0
- viseda/image/__init__.py +1 -0
- viseda/image/eda.py +1840 -0
- viseda/pointcloud/__init__.py +3 -0
- viseda/pointcloud/eda.py +1167 -0
- viseda/report/__init__.py +3 -0
- viseda/report/html_report.py +213 -0
- viseda/text/__init__.py +3 -0
- viseda/text/eda.py +1613 -0
- viseda/utils/__init__.py +27 -0
- viseda/utils/helpers.py +120 -0
- viseda/video/__init__.py +3 -0
- viseda/video/eda.py +587 -0
- viseda-1.0.0.dist-info/METADATA +266 -0
- viseda-1.0.0.dist-info/RECORD +24 -0
- viseda-1.0.0.dist-info/WHEEL +5 -0
- viseda-1.0.0.dist-info/entry_points.txt +2 -0
- viseda-1.0.0.dist-info/licenses/LICENSE +21 -0
- viseda-1.0.0.dist-info/top_level.txt +1 -0
viseda/__init__.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""
|
|
2
|
+
VisEDA — Visual Exploratory Data Analysis
|
|
3
|
+
==========================================
|
|
4
|
+
|
|
5
|
+
A Python library for performing rich EDA on image datasets,
|
|
6
|
+
hyperspectral data, point clouds, videos, and text/NLP datasets.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
__version__ = "1.0.0"
|
|
10
|
+
__author__ = "Isaac Osei Agyemang"
|
|
11
|
+
__email__ = "agyemangisaac45@gmail.com"
|
|
12
|
+
__license__ = "MIT"
|
|
13
|
+
|
|
14
|
+
from viseda.image import ImageEDA
|
|
15
|
+
from viseda.hyperspectral import HyperspectralEDA
|
|
16
|
+
from viseda.pointcloud import PointCloudEDA
|
|
17
|
+
from viseda.video import VideoEDA
|
|
18
|
+
from viseda.text import TextEDA
|
|
19
|
+
|
|
20
|
+
__all__ = ["ImageEDA", "HyperspectralEDA", "PointCloudEDA", "VideoEDA", "TextEDA"]
|
viseda/cli.py
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"""viseda.cli - Command-line interface for VisEDA."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
import argparse
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _cmd_image(args):
|
|
7
|
+
from viseda import ImageEDA
|
|
8
|
+
eda = ImageEDA(verbose=True, max_images=args.max)
|
|
9
|
+
eda.load(args.path, label_from_parent=args.label_from_parent)
|
|
10
|
+
_print_summary(eda.summary())
|
|
11
|
+
if args.report: eda.report(args.report)
|
|
12
|
+
if args.plot: eda.plot(save_path=args.save_plot)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _cmd_hyper(args):
|
|
16
|
+
import numpy as np
|
|
17
|
+
from viseda import HyperspectralEDA
|
|
18
|
+
wl = np.load(args.wavelengths) if args.wavelengths else None
|
|
19
|
+
eda = HyperspectralEDA(verbose=True, wavelengths=wl)
|
|
20
|
+
eda.load(args.path, label_from_parent=args.label_from_parent)
|
|
21
|
+
_print_summary(eda.summary())
|
|
22
|
+
if args.report: eda.report(args.report)
|
|
23
|
+
if args.plot:
|
|
24
|
+
eda.plot_dataset(save_path=args.save_plot) if args.dataset_plot else eda.plot(save_path=args.save_plot)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _cmd_cloud(args):
|
|
28
|
+
from viseda import PointCloudEDA
|
|
29
|
+
eda = PointCloudEDA(verbose=True, max_clouds=args.max_clouds, max_points_per_cloud=args.max_points)
|
|
30
|
+
eda.load(args.path, label_from_parent=args.label_from_parent)
|
|
31
|
+
_print_summary(eda.summary())
|
|
32
|
+
if args.report: eda.report(args.report)
|
|
33
|
+
if args.plot: eda.plot_dataset(save_path=args.save_plot)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _cmd_video(args):
|
|
37
|
+
from viseda import VideoEDA
|
|
38
|
+
eda = VideoEDA(
|
|
39
|
+
verbose=True,
|
|
40
|
+
max_videos=args.max,
|
|
41
|
+
frame_sample_rate=args.frame_sample_rate,
|
|
42
|
+
max_frames_per_video=args.max_frames_per_video,
|
|
43
|
+
blur_threshold=args.blur_threshold,
|
|
44
|
+
motion_blur_threshold=args.motion_blur_threshold,
|
|
45
|
+
)
|
|
46
|
+
eda.load(args.path, label_from_parent=args.label_from_parent)
|
|
47
|
+
_print_summary(eda.summary())
|
|
48
|
+
if args.report: eda.report(args.report)
|
|
49
|
+
if args.plot:
|
|
50
|
+
eda.plot(video_index=0, save_path=args.save_plot) if args.single_plot else eda.plot_dataset(save_path=args.save_plot)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _cmd_text(args):
|
|
54
|
+
from viseda import TextEDA
|
|
55
|
+
eda = TextEDA(
|
|
56
|
+
verbose=True,
|
|
57
|
+
max_documents=args.max,
|
|
58
|
+
lowercase=not args.preserve_case,
|
|
59
|
+
min_token_length=args.min_token_length,
|
|
60
|
+
short_document_words=args.short_words,
|
|
61
|
+
long_document_words=args.long_words,
|
|
62
|
+
encoding=args.encoding,
|
|
63
|
+
)
|
|
64
|
+
eda.load(
|
|
65
|
+
args.path,
|
|
66
|
+
label_from_parent=args.label_from_parent,
|
|
67
|
+
text_field=args.text_field,
|
|
68
|
+
label_field=args.label_field,
|
|
69
|
+
)
|
|
70
|
+
_print_summary(eda.summary())
|
|
71
|
+
if args.report:
|
|
72
|
+
eda.report(args.report)
|
|
73
|
+
if args.plot:
|
|
74
|
+
if args.single_plot:
|
|
75
|
+
eda.plot(document_index=args.document_index, save_path=args.save_plot)
|
|
76
|
+
else:
|
|
77
|
+
eda.plot_dataset(save_path=args.save_plot)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _print_summary(s):
|
|
81
|
+
import json
|
|
82
|
+
print(json.dumps(s, indent=2, default=str))
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def main():
|
|
86
|
+
parser = argparse.ArgumentParser(prog="viseda", description="VisEDA – Visual Exploratory Data Analysis")
|
|
87
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
88
|
+
|
|
89
|
+
p = sub.add_parser("image", help="EDA on an image dataset")
|
|
90
|
+
p.add_argument("path"); p.add_argument("--max", type=int, default=None)
|
|
91
|
+
p.add_argument("--label-from-parent", action="store_true"); p.add_argument("--report", default=None)
|
|
92
|
+
p.add_argument("--plot", action="store_true"); p.add_argument("--save-plot", default=None)
|
|
93
|
+
|
|
94
|
+
p = sub.add_parser("hyper", help="EDA on a hyperspectral cube or dataset")
|
|
95
|
+
p.add_argument("path"); p.add_argument("--wavelengths", default=None)
|
|
96
|
+
p.add_argument("--label-from-parent", action="store_true"); p.add_argument("--report", default=None)
|
|
97
|
+
p.add_argument("--plot", action="store_true"); p.add_argument("--dataset-plot", action="store_true")
|
|
98
|
+
p.add_argument("--save-plot", default=None)
|
|
99
|
+
|
|
100
|
+
p = sub.add_parser("cloud", help="EDA on a point cloud dataset")
|
|
101
|
+
p.add_argument("path"); p.add_argument("--max-clouds", type=int, default=None)
|
|
102
|
+
p.add_argument("--max-points", type=int, default=1_000_000)
|
|
103
|
+
p.add_argument("--label-from-parent", action="store_true"); p.add_argument("--report", default=None)
|
|
104
|
+
p.add_argument("--plot", action="store_true"); p.add_argument("--save-plot", default=None)
|
|
105
|
+
|
|
106
|
+
p = sub.add_parser("video", help="EDA on a single video or video dataset")
|
|
107
|
+
p.add_argument("path"); p.add_argument("--max", type=int, default=None)
|
|
108
|
+
p.add_argument("--frame-sample-rate", type=int, default=5)
|
|
109
|
+
p.add_argument("--max-frames-per-video", type=int, default=300)
|
|
110
|
+
p.add_argument("--blur-threshold", type=float, default=80.0)
|
|
111
|
+
p.add_argument("--motion-blur-threshold", type=float, default=80.0)
|
|
112
|
+
p.add_argument("--label-from-parent", action="store_true"); p.add_argument("--report", default=None)
|
|
113
|
+
p.add_argument("--plot", action="store_true"); p.add_argument("--single-plot", action="store_true")
|
|
114
|
+
p.add_argument("--save-plot", default=None)
|
|
115
|
+
|
|
116
|
+
p = sub.add_parser("text", help="EDA on text and NLP datasets")
|
|
117
|
+
p.add_argument("path", help="Text file or dataset directory")
|
|
118
|
+
p.add_argument("--max", type=int, default=None, help="Maximum documents to analyse")
|
|
119
|
+
p.add_argument("--text-field", default=None, help="Text column/key for CSV/TSV/JSON")
|
|
120
|
+
p.add_argument("--label-field", default=None, help="Label column/key for CSV/TSV/JSON")
|
|
121
|
+
p.add_argument("--label-from-parent", action="store_true", help="Use parent folder as label")
|
|
122
|
+
p.add_argument("--encoding", default=None, help="Preferred input encoding")
|
|
123
|
+
p.add_argument("--preserve-case", action="store_true", help="Do not lowercase tokens")
|
|
124
|
+
p.add_argument("--min-token-length", type=int, default=1)
|
|
125
|
+
p.add_argument("--short-words", type=int, default=5, help="Very-short document threshold")
|
|
126
|
+
p.add_argument("--long-words", type=int, default=1000, help="Very-long document threshold")
|
|
127
|
+
p.add_argument("--report", default=None, help="Save HTML report")
|
|
128
|
+
p.add_argument("--plot", action="store_true", help="Show/save a dashboard")
|
|
129
|
+
p.add_argument("--single-plot", action="store_true", help="Plot one document instead of dataset")
|
|
130
|
+
p.add_argument("--document-index", type=int, default=0)
|
|
131
|
+
p.add_argument("--save-plot", default=None)
|
|
132
|
+
|
|
133
|
+
args = parser.parse_args()
|
|
134
|
+
{
|
|
135
|
+
"image": _cmd_image,
|
|
136
|
+
"hyper": _cmd_hyper,
|
|
137
|
+
"cloud": _cmd_cloud,
|
|
138
|
+
"video": _cmd_video,
|
|
139
|
+
"text": _cmd_text,
|
|
140
|
+
}[args.command](args)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
if __name__ == "__main__":
|
|
144
|
+
main()
|
viseda/core/__init__.py
ADDED
viseda/core/base.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""
|
|
2
|
+
viseda.core.base
|
|
3
|
+
----------------
|
|
4
|
+
Abstract base class shared by all EDA modules.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import abc
|
|
10
|
+
import time
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any, Dict, List, Optional
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class BaseEDA(abc.ABC):
|
|
16
|
+
"""Abstract base for all VisEDA analysers."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, verbose: bool = True):
|
|
19
|
+
self.verbose = verbose
|
|
20
|
+
self._results: Dict[str, Any] = {}
|
|
21
|
+
self._timing: Dict[str, float] = {}
|
|
22
|
+
|
|
23
|
+
# ------------------------------------------------------------------
|
|
24
|
+
# Public interface every subclass must implement
|
|
25
|
+
# ------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
@abc.abstractmethod
|
|
28
|
+
def load(self, source: Any) -> "BaseEDA":
|
|
29
|
+
"""Load data from *source* (path, directory, array, …)."""
|
|
30
|
+
|
|
31
|
+
@abc.abstractmethod
|
|
32
|
+
def summary(self) -> Dict[str, Any]:
|
|
33
|
+
"""Return a high-level summary dict."""
|
|
34
|
+
|
|
35
|
+
@abc.abstractmethod
|
|
36
|
+
def plot(self, **kwargs) -> None:
|
|
37
|
+
"""Produce the main visualisation panel."""
|
|
38
|
+
|
|
39
|
+
# ------------------------------------------------------------------
|
|
40
|
+
# Shared helpers
|
|
41
|
+
# ------------------------------------------------------------------
|
|
42
|
+
|
|
43
|
+
def _log(self, msg: str) -> None:
|
|
44
|
+
if self.verbose:
|
|
45
|
+
print(f"[viseda] {msg}")
|
|
46
|
+
|
|
47
|
+
def _time(self, key: str):
|
|
48
|
+
"""Context-manager-like timing helper (use as decorator or manually)."""
|
|
49
|
+
return _Timer(key, self._timing)
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def results(self) -> Dict[str, Any]:
|
|
53
|
+
return self._results
|
|
54
|
+
|
|
55
|
+
def _store(self, key: str, value: Any) -> None:
|
|
56
|
+
self._results[key] = value
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class _Timer:
|
|
60
|
+
def __init__(self, key: str, store: Dict[str, float]):
|
|
61
|
+
self.key = key
|
|
62
|
+
self.store = store
|
|
63
|
+
|
|
64
|
+
def __enter__(self):
|
|
65
|
+
self._start = time.perf_counter()
|
|
66
|
+
return self
|
|
67
|
+
|
|
68
|
+
def __exit__(self, *_):
|
|
69
|
+
self.store[self.key] = time.perf_counter() - self._start
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from viseda.hyperspectral.eda import *
|