term-plot-mc 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.
- term_plot_mc-0.1.0/LICENSE +21 -0
- term_plot_mc-0.1.0/PKG-INFO +194 -0
- term_plot_mc-0.1.0/README.md +168 -0
- term_plot_mc-0.1.0/pyproject.toml +38 -0
- term_plot_mc-0.1.0/setup.cfg +4 -0
- term_plot_mc-0.1.0/src/term_plot/__init__.py +13 -0
- term_plot_mc-0.1.0/src/term_plot/bar_chart.py +139 -0
- term_plot_mc-0.1.0/src/term_plot/histogram.py +104 -0
- term_plot_mc-0.1.0/src/term_plot/line_chart.py +164 -0
- term_plot_mc-0.1.0/src/term_plot/scatter_plot.py +93 -0
- term_plot_mc-0.1.0/src/term_plot/utils.py +73 -0
- term_plot_mc-0.1.0/src/term_plot_mc.egg-info/PKG-INFO +194 -0
- term_plot_mc-0.1.0/src/term_plot_mc.egg-info/SOURCES.txt +15 -0
- term_plot_mc-0.1.0/src/term_plot_mc.egg-info/dependency_links.txt +1 -0
- term_plot_mc-0.1.0/src/term_plot_mc.egg-info/top_level.txt +1 -0
- term_plot_mc-0.1.0/tests/test_bar_chart.py +86 -0
- term_plot_mc-0.1.0/tests/test_utils.py +53 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mehdi Maleki
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: term-plot-mc
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Draw simple plots and charts in the terminal using ASCII symbols
|
|
5
|
+
Author-email: Mehdi Maleki <mosioc79@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/mosioc/term-plot
|
|
8
|
+
Project-URL: Repository, https://github.com/mosioc/term-plot
|
|
9
|
+
Project-URL: Issues, https://github.com/mosioc/term-plot/issues
|
|
10
|
+
Keywords: terminal,ascii,plot,chart,visualization,cli
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Classifier: Topic :: Terminals
|
|
22
|
+
Requires-Python: >=3.8
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
# Terminal Plot
|
|
28
|
+
|
|
29
|
+
> Draw beautiful plots and charts in your terminal using ASCII and Unicode symbols
|
|
30
|
+
|
|
31
|
+
[](https://opensource.org/licenses/MIT)
|
|
32
|
+
[](https://www.python.org/downloads/)
|
|
33
|
+
|
|
34
|
+
## Features
|
|
35
|
+
|
|
36
|
+
- **Bar Charts** - Horizontal and vertical bar charts for categorical data
|
|
37
|
+
- **Line Charts** - Single and multi-series line plots with automatic scaling
|
|
38
|
+
- **Scatter Plots** - Visualize correlations and relationships
|
|
39
|
+
- **Histograms** - Show data distributions with customizable bins
|
|
40
|
+
- **Dual Modes** - Beautiful Unicode symbols or ASCII for compatibility
|
|
41
|
+
- **Color Support** - Optional ANSI colors for better visualization
|
|
42
|
+
- **Zero Dependencies** - Pure Python, works anywhere
|
|
43
|
+
- **Lightweight** - Fast and minimal resource usage
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
### From PyPI (once published)
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install term-plot
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### From source
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
git clone https://github.com/yourusername/term-plot.git
|
|
57
|
+
cd term-plot
|
|
58
|
+
pip install -e .
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Quick Start
|
|
62
|
+
|
|
63
|
+
### Bar Chart
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from term_plot import BarChart
|
|
67
|
+
|
|
68
|
+
# Simple horizontal bar chart
|
|
69
|
+
data = [23, 45, 12, 67, 34, 89, 56]
|
|
70
|
+
labels = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
|
|
71
|
+
|
|
72
|
+
chart = BarChart(data, labels=labels, title="Daily Sales", width=50)
|
|
73
|
+
print(chart.horizontal())
|
|
74
|
+
|
|
75
|
+
# Vertical bar chart
|
|
76
|
+
chart = BarChart(data, labels=labels, title="Daily Sales", height=15)
|
|
77
|
+
print(chart.vertical())
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Line Chart
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
from term_plot import LineChart
|
|
84
|
+
|
|
85
|
+
# Single series
|
|
86
|
+
data = [1, 3, 7, 4, 8, 6, 9, 12, 10]
|
|
87
|
+
chart = LineChart(data, title="Temperature Over Time", width=60, height=15)
|
|
88
|
+
print(chart.plot())
|
|
89
|
+
|
|
90
|
+
# Multiple series
|
|
91
|
+
data = {
|
|
92
|
+
"Product A": [5, 7, 9, 12, 15, 18, 20],
|
|
93
|
+
"Product B": [3, 5, 8, 10, 12, 15, 17]
|
|
94
|
+
}
|
|
95
|
+
chart = LineChart(data, title="Sales Comparison", width=70, height=20)
|
|
96
|
+
print(chart.plot())
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Scatter Plot
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
from term_plot import ScatterPlot
|
|
103
|
+
|
|
104
|
+
x_data = [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
105
|
+
y_data = [2, 4, 3, 5, 7, 6, 8, 9, 8]
|
|
106
|
+
|
|
107
|
+
chart = ScatterPlot(x_data, y_data, title="Correlation Plot", width=50, height=15)
|
|
108
|
+
print(chart.plot())
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Histogram
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from term_plot import Histogram
|
|
115
|
+
import random
|
|
116
|
+
|
|
117
|
+
# Generate sample data
|
|
118
|
+
data = [random.gauss(50, 15) for _ in range(1000)]
|
|
119
|
+
|
|
120
|
+
chart = Histogram(data, bins=15, title="Test Score Distribution", width=60, height=15)
|
|
121
|
+
print(chart.plot())
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Customization Options
|
|
125
|
+
|
|
126
|
+
### ASCII vs Unicode
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
# Unicode mode (default) - prettier but requires Unicode support
|
|
130
|
+
chart = BarChart(data, labels=labels, use_unicode=True)
|
|
131
|
+
|
|
132
|
+
# ASCII mode - works everywhere
|
|
133
|
+
chart = BarChart(data, labels=labels, use_unicode=False)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Colors
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
# With colors (default)
|
|
140
|
+
chart = BarChart(data, labels=labels, color=True)
|
|
141
|
+
|
|
142
|
+
# Without colors
|
|
143
|
+
chart = BarChart(data, labels=labels, color=False)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Size Control
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
# Custom width and height
|
|
150
|
+
chart = LineChart(data, width=80, height=25)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Examples
|
|
154
|
+
|
|
155
|
+
Check out the `examples/` directory for more:
|
|
156
|
+
|
|
157
|
+
- `bar_chart_example.py` - Various bar chart styles
|
|
158
|
+
- `line_chart_example.py` - Line chart examples including sine waves
|
|
159
|
+
- `demo_all.py` - Comprehensive demo of all chart types
|
|
160
|
+
|
|
161
|
+
Run examples:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
cd examples
|
|
165
|
+
python demo_all.py
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Testing
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
# Install development dependencies
|
|
172
|
+
pip install -e ".[dev]"
|
|
173
|
+
|
|
174
|
+
# Run tests
|
|
175
|
+
pytest
|
|
176
|
+
|
|
177
|
+
# Run tests with coverage
|
|
178
|
+
pytest --cov=term_plot
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Requirements
|
|
182
|
+
|
|
183
|
+
- Python 3.8 or higher
|
|
184
|
+
- No external dependencies!
|
|
185
|
+
|
|
186
|
+
## License
|
|
187
|
+
|
|
188
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
189
|
+
|
|
190
|
+
## Acknowledgments
|
|
191
|
+
|
|
192
|
+
- Inspired by various terminal plotting libraries
|
|
193
|
+
- Unicode block elements from the Unicode standard
|
|
194
|
+
- ANSI color codes for terminal colors
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# Terminal Plot
|
|
2
|
+
|
|
3
|
+
> Draw beautiful plots and charts in your terminal using ASCII and Unicode symbols
|
|
4
|
+
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](https://www.python.org/downloads/)
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- **Bar Charts** - Horizontal and vertical bar charts for categorical data
|
|
11
|
+
- **Line Charts** - Single and multi-series line plots with automatic scaling
|
|
12
|
+
- **Scatter Plots** - Visualize correlations and relationships
|
|
13
|
+
- **Histograms** - Show data distributions with customizable bins
|
|
14
|
+
- **Dual Modes** - Beautiful Unicode symbols or ASCII for compatibility
|
|
15
|
+
- **Color Support** - Optional ANSI colors for better visualization
|
|
16
|
+
- **Zero Dependencies** - Pure Python, works anywhere
|
|
17
|
+
- **Lightweight** - Fast and minimal resource usage
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
### From PyPI (once published)
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install term-plot
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### From source
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
git clone https://github.com/yourusername/term-plot.git
|
|
31
|
+
cd term-plot
|
|
32
|
+
pip install -e .
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
### Bar Chart
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from term_plot import BarChart
|
|
41
|
+
|
|
42
|
+
# Simple horizontal bar chart
|
|
43
|
+
data = [23, 45, 12, 67, 34, 89, 56]
|
|
44
|
+
labels = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
|
|
45
|
+
|
|
46
|
+
chart = BarChart(data, labels=labels, title="Daily Sales", width=50)
|
|
47
|
+
print(chart.horizontal())
|
|
48
|
+
|
|
49
|
+
# Vertical bar chart
|
|
50
|
+
chart = BarChart(data, labels=labels, title="Daily Sales", height=15)
|
|
51
|
+
print(chart.vertical())
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Line Chart
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from term_plot import LineChart
|
|
58
|
+
|
|
59
|
+
# Single series
|
|
60
|
+
data = [1, 3, 7, 4, 8, 6, 9, 12, 10]
|
|
61
|
+
chart = LineChart(data, title="Temperature Over Time", width=60, height=15)
|
|
62
|
+
print(chart.plot())
|
|
63
|
+
|
|
64
|
+
# Multiple series
|
|
65
|
+
data = {
|
|
66
|
+
"Product A": [5, 7, 9, 12, 15, 18, 20],
|
|
67
|
+
"Product B": [3, 5, 8, 10, 12, 15, 17]
|
|
68
|
+
}
|
|
69
|
+
chart = LineChart(data, title="Sales Comparison", width=70, height=20)
|
|
70
|
+
print(chart.plot())
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Scatter Plot
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from term_plot import ScatterPlot
|
|
77
|
+
|
|
78
|
+
x_data = [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
79
|
+
y_data = [2, 4, 3, 5, 7, 6, 8, 9, 8]
|
|
80
|
+
|
|
81
|
+
chart = ScatterPlot(x_data, y_data, title="Correlation Plot", width=50, height=15)
|
|
82
|
+
print(chart.plot())
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Histogram
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from term_plot import Histogram
|
|
89
|
+
import random
|
|
90
|
+
|
|
91
|
+
# Generate sample data
|
|
92
|
+
data = [random.gauss(50, 15) for _ in range(1000)]
|
|
93
|
+
|
|
94
|
+
chart = Histogram(data, bins=15, title="Test Score Distribution", width=60, height=15)
|
|
95
|
+
print(chart.plot())
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Customization Options
|
|
99
|
+
|
|
100
|
+
### ASCII vs Unicode
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
# Unicode mode (default) - prettier but requires Unicode support
|
|
104
|
+
chart = BarChart(data, labels=labels, use_unicode=True)
|
|
105
|
+
|
|
106
|
+
# ASCII mode - works everywhere
|
|
107
|
+
chart = BarChart(data, labels=labels, use_unicode=False)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Colors
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
# With colors (default)
|
|
114
|
+
chart = BarChart(data, labels=labels, color=True)
|
|
115
|
+
|
|
116
|
+
# Without colors
|
|
117
|
+
chart = BarChart(data, labels=labels, color=False)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Size Control
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
# Custom width and height
|
|
124
|
+
chart = LineChart(data, width=80, height=25)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Examples
|
|
128
|
+
|
|
129
|
+
Check out the `examples/` directory for more:
|
|
130
|
+
|
|
131
|
+
- `bar_chart_example.py` - Various bar chart styles
|
|
132
|
+
- `line_chart_example.py` - Line chart examples including sine waves
|
|
133
|
+
- `demo_all.py` - Comprehensive demo of all chart types
|
|
134
|
+
|
|
135
|
+
Run examples:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
cd examples
|
|
139
|
+
python demo_all.py
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Testing
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
# Install development dependencies
|
|
146
|
+
pip install -e ".[dev]"
|
|
147
|
+
|
|
148
|
+
# Run tests
|
|
149
|
+
pytest
|
|
150
|
+
|
|
151
|
+
# Run tests with coverage
|
|
152
|
+
pytest --cov=term_plot
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Requirements
|
|
156
|
+
|
|
157
|
+
- Python 3.8 or higher
|
|
158
|
+
- No external dependencies!
|
|
159
|
+
|
|
160
|
+
## License
|
|
161
|
+
|
|
162
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
163
|
+
|
|
164
|
+
## Acknowledgments
|
|
165
|
+
|
|
166
|
+
- Inspired by various terminal plotting libraries
|
|
167
|
+
- Unicode block elements from the Unicode standard
|
|
168
|
+
- ANSI color codes for terminal colors
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "term-plot-mc"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Draw simple plots and charts in the terminal using ASCII symbols"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
authors = [{ name = "Mehdi Maleki", email = "mosioc79@gmail.com" }]
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Development Status :: 3 - Alpha",
|
|
14
|
+
"Intended Audience :: Developers",
|
|
15
|
+
"License :: OSI Approved :: MIT License",
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Programming Language :: Python :: 3.8",
|
|
18
|
+
"Programming Language :: Python :: 3.9",
|
|
19
|
+
"Programming Language :: Python :: 3.10",
|
|
20
|
+
"Programming Language :: Python :: 3.11",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
23
|
+
"Topic :: Terminals",
|
|
24
|
+
]
|
|
25
|
+
keywords = ["terminal", "ascii", "plot", "chart", "visualization", "cli"]
|
|
26
|
+
requires-python = ">=3.8"
|
|
27
|
+
dependencies = []
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://github.com/mosioc/term-plot"
|
|
31
|
+
Repository = "https://github.com/mosioc/term-plot"
|
|
32
|
+
Issues = "https://github.com/mosioc/term-plot/issues"
|
|
33
|
+
|
|
34
|
+
[tool.setuptools.packages.find]
|
|
35
|
+
where = ["src"]
|
|
36
|
+
|
|
37
|
+
[tool.setuptools.package-data]
|
|
38
|
+
term_plot = ["py.typed"]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""
|
|
2
|
+
term-plot: Draw simple plots and charts in the terminal using ASCII/Unicode symbols.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
__version__ = "0.1.0"
|
|
6
|
+
__author__ = "Mosioc"
|
|
7
|
+
|
|
8
|
+
from .bar_chart import BarChart
|
|
9
|
+
from .line_chart import LineChart
|
|
10
|
+
from .scatter_plot import ScatterPlot
|
|
11
|
+
from .histogram import Histogram
|
|
12
|
+
|
|
13
|
+
__all__ = ["BarChart", "LineChart", "ScatterPlot", "Histogram"]
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Bar chart implementation for terminal.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .utils import normalize_data, truncate_label, Symbols
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BarChart:
|
|
9
|
+
"""Create horizontal or vertical bar charts in the terminal."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, data, labels=None, title="", width=50, height=20,
|
|
12
|
+
use_unicode=True, color=True):
|
|
13
|
+
"""
|
|
14
|
+
Initialize a bar chart.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
data: List of numeric values
|
|
18
|
+
labels: List of labels for each bar (optional)
|
|
19
|
+
title: Chart title
|
|
20
|
+
width: Maximum width of the chart
|
|
21
|
+
height: Maximum height of bars (for vertical charts)
|
|
22
|
+
use_unicode: Use Unicode symbols (prettier) vs ASCII
|
|
23
|
+
color: Use ANSI colors (if supported)
|
|
24
|
+
"""
|
|
25
|
+
self.data = data
|
|
26
|
+
self.labels = labels or [f"Item {i+1}" for i in range(len(data))]
|
|
27
|
+
self.title = title
|
|
28
|
+
self.width = width
|
|
29
|
+
self.height = height
|
|
30
|
+
self.use_unicode = use_unicode
|
|
31
|
+
self.color = color
|
|
32
|
+
|
|
33
|
+
if len(self.data) != len(self.labels):
|
|
34
|
+
raise ValueError("Data and labels must have the same length")
|
|
35
|
+
|
|
36
|
+
def horizontal(self):
|
|
37
|
+
"""Draw a horizontal bar chart."""
|
|
38
|
+
if not self.data:
|
|
39
|
+
return "No data to display"
|
|
40
|
+
|
|
41
|
+
output = []
|
|
42
|
+
|
|
43
|
+
# title
|
|
44
|
+
if self.title:
|
|
45
|
+
output.append(f"\n{self.title}")
|
|
46
|
+
output.append("=" * len(self.title))
|
|
47
|
+
|
|
48
|
+
# find max label length
|
|
49
|
+
max_label_len = max(len(str(label)) for label in self.labels)
|
|
50
|
+
max_label_len = min(max_label_len, 20) # Cap at 20 chars
|
|
51
|
+
|
|
52
|
+
# normalize data to fit width
|
|
53
|
+
bar_width = self.width - max_label_len - 10 # Space for label and value
|
|
54
|
+
normalized = normalize_data(self.data, 0, bar_width)
|
|
55
|
+
|
|
56
|
+
# choose symbols
|
|
57
|
+
if self.use_unicode:
|
|
58
|
+
bar_char = Symbols.UNICODE_FULL_BLOCK
|
|
59
|
+
else:
|
|
60
|
+
bar_char = Symbols.ASCII_BLOCK
|
|
61
|
+
|
|
62
|
+
# draw bars
|
|
63
|
+
for i, (label, value, norm_value) in enumerate(zip(self.labels, self.data, normalized)):
|
|
64
|
+
label_str = truncate_label(label, max_label_len).ljust(max_label_len)
|
|
65
|
+
bar_len = int(norm_value)
|
|
66
|
+
bar = bar_char * bar_len
|
|
67
|
+
|
|
68
|
+
# add color if enabled
|
|
69
|
+
if self.color:
|
|
70
|
+
colors = ['\033[91m', '\033[92m', '\033[93m', '\033[94m', '\033[95m', '\033[96m']
|
|
71
|
+
color_code = colors[i % len(colors)]
|
|
72
|
+
reset_code = '\033[0m'
|
|
73
|
+
bar = f"{color_code}{bar}{reset_code}"
|
|
74
|
+
|
|
75
|
+
output.append(f"{label_str} | {bar} {value:.2f}")
|
|
76
|
+
|
|
77
|
+
return "\n".join(output)
|
|
78
|
+
|
|
79
|
+
def vertical(self):
|
|
80
|
+
"""Draw a vertical bar chart."""
|
|
81
|
+
if not self.data:
|
|
82
|
+
return "No data to display"
|
|
83
|
+
|
|
84
|
+
output = []
|
|
85
|
+
|
|
86
|
+
# title
|
|
87
|
+
if self.title:
|
|
88
|
+
output.append(f"\n{self.title}")
|
|
89
|
+
output.append("=" * len(self.title))
|
|
90
|
+
|
|
91
|
+
# normalize data to height
|
|
92
|
+
normalized = normalize_data(self.data, 0, self.height)
|
|
93
|
+
|
|
94
|
+
# choose symbols
|
|
95
|
+
if self.use_unicode:
|
|
96
|
+
bar_char = Symbols.UNICODE_FULL_BLOCK
|
|
97
|
+
h_line = Symbols.UNICODE_HORIZONTAL
|
|
98
|
+
else:
|
|
99
|
+
bar_char = Symbols.ASCII_BLOCK
|
|
100
|
+
h_line = Symbols.ASCII_HORIZONTAL
|
|
101
|
+
|
|
102
|
+
# draw from top to bottom
|
|
103
|
+
for level in range(self.height, 0, -1):
|
|
104
|
+
line = ""
|
|
105
|
+
for i, norm_value in enumerate(normalized):
|
|
106
|
+
if norm_value >= level:
|
|
107
|
+
char = bar_char * 3
|
|
108
|
+
if self.color:
|
|
109
|
+
colors = ['\033[91m', '\033[92m', '\033[93m', '\033[94m', '\033[95m', '\033[96m']
|
|
110
|
+
color_code = colors[i % len(colors)]
|
|
111
|
+
reset_code = '\033[0m'
|
|
112
|
+
char = f"{color_code}{char}{reset_code}"
|
|
113
|
+
line += char + " "
|
|
114
|
+
else:
|
|
115
|
+
line += " "
|
|
116
|
+
output.append(line)
|
|
117
|
+
|
|
118
|
+
# draw x-axis
|
|
119
|
+
output.append(h_line * (len(self.data) * 4))
|
|
120
|
+
|
|
121
|
+
# draw labels
|
|
122
|
+
label_line = ""
|
|
123
|
+
for label in self.labels:
|
|
124
|
+
label_str = truncate_label(label, 3)
|
|
125
|
+
label_line += label_str.center(4)
|
|
126
|
+
output.append(label_line)
|
|
127
|
+
|
|
128
|
+
# draw values
|
|
129
|
+
value_line = ""
|
|
130
|
+
for value in self.data:
|
|
131
|
+
value_str = f"{value:.1f}"
|
|
132
|
+
value_line += truncate_label(value_str, 3).center(4)
|
|
133
|
+
output.append(value_line)
|
|
134
|
+
|
|
135
|
+
return "\n".join(output)
|
|
136
|
+
|
|
137
|
+
def __str__(self):
|
|
138
|
+
"""Default to horizontal bar chart."""
|
|
139
|
+
return self.horizontal()
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Histogram implementation for terminal.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .utils import Symbols
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Histogram:
|
|
9
|
+
"""Create histograms in the terminal."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, data, bins=10, title="", width=60, height=20, use_unicode=True):
|
|
12
|
+
"""
|
|
13
|
+
Initialize a histogram.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
data: List of numeric values
|
|
17
|
+
bins: Number of bins (default 10)
|
|
18
|
+
title: Chart title
|
|
19
|
+
width: Chart width in characters
|
|
20
|
+
height: Chart height in lines
|
|
21
|
+
use_unicode: Use Unicode symbols vs ASCII
|
|
22
|
+
"""
|
|
23
|
+
self.data = data
|
|
24
|
+
self.bins = bins
|
|
25
|
+
self.title = title
|
|
26
|
+
self.width = width
|
|
27
|
+
self.height = height
|
|
28
|
+
self.use_unicode = use_unicode
|
|
29
|
+
|
|
30
|
+
def plot(self):
|
|
31
|
+
"""Draw the histogram."""
|
|
32
|
+
if not self.data:
|
|
33
|
+
return "No data to display"
|
|
34
|
+
|
|
35
|
+
output = []
|
|
36
|
+
|
|
37
|
+
# title
|
|
38
|
+
if self.title:
|
|
39
|
+
output.append(f"\n{self.title}")
|
|
40
|
+
output.append("=" * len(self.title))
|
|
41
|
+
|
|
42
|
+
# calculate bins
|
|
43
|
+
data_min = min(self.data)
|
|
44
|
+
data_max = max(self.data)
|
|
45
|
+
|
|
46
|
+
if data_min == data_max:
|
|
47
|
+
return "All values are the same, cannot create histogram"
|
|
48
|
+
|
|
49
|
+
bin_width = (data_max - data_min) / self.bins
|
|
50
|
+
bin_counts = [0] * self.bins
|
|
51
|
+
bin_edges = [data_min + i * bin_width for i in range(self.bins + 1)]
|
|
52
|
+
|
|
53
|
+
# count values in each bin
|
|
54
|
+
for value in self.data:
|
|
55
|
+
bin_idx = min(int((value - data_min) / bin_width), self.bins - 1)
|
|
56
|
+
bin_counts[bin_idx] += 1
|
|
57
|
+
|
|
58
|
+
# choose symbols
|
|
59
|
+
if self.use_unicode:
|
|
60
|
+
bar_char = Symbols.UNICODE_FULL_BLOCK
|
|
61
|
+
h_char = Symbols.UNICODE_HORIZONTAL
|
|
62
|
+
else:
|
|
63
|
+
bar_char = Symbols.ASCII_BLOCK
|
|
64
|
+
h_char = Symbols.ASCII_HORIZONTAL
|
|
65
|
+
|
|
66
|
+
# scale to height
|
|
67
|
+
max_count = max(bin_counts) if bin_counts else 1
|
|
68
|
+
|
|
69
|
+
# draw histogram from top to bottom
|
|
70
|
+
for level in range(self.height, 0, -1):
|
|
71
|
+
line = ""
|
|
72
|
+
threshold = (level / self.height) * max_count
|
|
73
|
+
|
|
74
|
+
for count in bin_counts:
|
|
75
|
+
if count >= threshold:
|
|
76
|
+
line += bar_char * 3 + " "
|
|
77
|
+
else:
|
|
78
|
+
line += " "
|
|
79
|
+
output.append(line)
|
|
80
|
+
|
|
81
|
+
# draw x-axis
|
|
82
|
+
output.append(h_char * (self.bins * 4))
|
|
83
|
+
|
|
84
|
+
# draw bin labels
|
|
85
|
+
label_line = ""
|
|
86
|
+
for i in range(self.bins):
|
|
87
|
+
bin_start = bin_edges[i]
|
|
88
|
+
label = f"{bin_start:.1f}"
|
|
89
|
+
if len(label) > 3:
|
|
90
|
+
label = label[:3]
|
|
91
|
+
label_line += label.center(4)
|
|
92
|
+
output.append(label_line)
|
|
93
|
+
|
|
94
|
+
# statistics
|
|
95
|
+
output.append(f"\nTotal values: {len(self.data)}")
|
|
96
|
+
output.append(f"Range: {data_min:.2f} to {data_max:.2f}")
|
|
97
|
+
output.append(f"Mean: {sum(self.data)/len(self.data):.2f}")
|
|
98
|
+
output.append(f"Max frequency: {max_count}")
|
|
99
|
+
|
|
100
|
+
return "\n".join(output)
|
|
101
|
+
|
|
102
|
+
def __str__(self):
|
|
103
|
+
"""Default string representation."""
|
|
104
|
+
return self.plot()
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Line chart implementation for terminal.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .utils import normalize_data, Symbols
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class LineChart:
|
|
9
|
+
"""Create line charts in the terminal."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, data, title="", width=60, height=20, use_unicode=True):
|
|
12
|
+
"""
|
|
13
|
+
Initialize a line chart.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
data: List of numeric values or dict of {label: values} for multiple lines
|
|
17
|
+
title: Chart title
|
|
18
|
+
width: Chart width in characters
|
|
19
|
+
height: Chart height in lines
|
|
20
|
+
use_unicode: Use Unicode symbols vs ASCII
|
|
21
|
+
"""
|
|
22
|
+
# support both single series and multiple series
|
|
23
|
+
if isinstance(data, dict):
|
|
24
|
+
self.data = data
|
|
25
|
+
self.multi_series = True
|
|
26
|
+
else:
|
|
27
|
+
self.data = {"Series 1": data}
|
|
28
|
+
self.multi_series = False
|
|
29
|
+
|
|
30
|
+
self.title = title
|
|
31
|
+
self.width = width
|
|
32
|
+
self.height = height
|
|
33
|
+
self.use_unicode = use_unicode
|
|
34
|
+
|
|
35
|
+
def plot(self):
|
|
36
|
+
"""Draw the line chart."""
|
|
37
|
+
if not self.data:
|
|
38
|
+
return "No data to display"
|
|
39
|
+
|
|
40
|
+
output = []
|
|
41
|
+
|
|
42
|
+
# title
|
|
43
|
+
if self.title:
|
|
44
|
+
output.append(f"\n{self.title}")
|
|
45
|
+
output.append("=" * len(self.title))
|
|
46
|
+
|
|
47
|
+
# create the canvas
|
|
48
|
+
canvas = [[' ' for _ in range(self.width)] for _ in range(self.height)]
|
|
49
|
+
|
|
50
|
+
# choose symbols
|
|
51
|
+
if self.use_unicode:
|
|
52
|
+
point_char = Symbols.UNICODE_DOT
|
|
53
|
+
line_chars = ['•', '○', '◆', '◇', '▪', '▫']
|
|
54
|
+
else:
|
|
55
|
+
point_char = Symbols.ASCII_POINT
|
|
56
|
+
line_chars = ['*', 'o', '+', 'x', '#', '@']
|
|
57
|
+
|
|
58
|
+
# plot each series
|
|
59
|
+
series_idx = 0
|
|
60
|
+
all_values = []
|
|
61
|
+
for values in self.data.values():
|
|
62
|
+
all_values.extend(values)
|
|
63
|
+
|
|
64
|
+
# normalize all data together for consistent scale
|
|
65
|
+
if all_values:
|
|
66
|
+
global_min = min(all_values)
|
|
67
|
+
global_max = max(all_values)
|
|
68
|
+
else:
|
|
69
|
+
return "No data to display"
|
|
70
|
+
|
|
71
|
+
for label, values in self.data.items():
|
|
72
|
+
if not values:
|
|
73
|
+
continue
|
|
74
|
+
|
|
75
|
+
char = line_chars[series_idx % len(line_chars)]
|
|
76
|
+
|
|
77
|
+
# normalize to height
|
|
78
|
+
normalized = normalize_data(values, 0, self.height - 1)
|
|
79
|
+
|
|
80
|
+
# calculate x positions
|
|
81
|
+
if len(values) > 1:
|
|
82
|
+
x_step = (self.width - 1) / (len(values) - 1)
|
|
83
|
+
else:
|
|
84
|
+
x_step = 0
|
|
85
|
+
|
|
86
|
+
# plot points and connect them
|
|
87
|
+
for i, norm_value in enumerate(normalized):
|
|
88
|
+
x = int(i * x_step)
|
|
89
|
+
y = self.height - 1 - int(norm_value)
|
|
90
|
+
|
|
91
|
+
if 0 <= x < self.width and 0 <= y < self.height:
|
|
92
|
+
canvas[y][x] = char
|
|
93
|
+
|
|
94
|
+
# draw line to next point
|
|
95
|
+
if i < len(normalized) - 1:
|
|
96
|
+
next_x = int((i + 1) * x_step)
|
|
97
|
+
next_y = self.height - 1 - int(normalized[i + 1])
|
|
98
|
+
|
|
99
|
+
# simple line drawing between points
|
|
100
|
+
self._draw_line(canvas, x, y, next_x, next_y, char)
|
|
101
|
+
|
|
102
|
+
series_idx += 1
|
|
103
|
+
|
|
104
|
+
# add y-axis
|
|
105
|
+
if self.use_unicode:
|
|
106
|
+
v_char = Symbols.UNICODE_VERTICAL
|
|
107
|
+
else:
|
|
108
|
+
v_char = Symbols.ASCII_VERTICAL
|
|
109
|
+
|
|
110
|
+
for y in range(self.height):
|
|
111
|
+
canvas[y][0] = v_char
|
|
112
|
+
|
|
113
|
+
# convert canvas to string
|
|
114
|
+
for row in canvas:
|
|
115
|
+
output.append(''.join(row))
|
|
116
|
+
|
|
117
|
+
# add x-axis
|
|
118
|
+
if self.use_unicode:
|
|
119
|
+
h_char = Symbols.UNICODE_HORIZONTAL
|
|
120
|
+
else:
|
|
121
|
+
h_char = Symbols.ASCII_HORIZONTAL
|
|
122
|
+
output.append(h_char * self.width)
|
|
123
|
+
|
|
124
|
+
# add legend if multiple series
|
|
125
|
+
if self.multi_series and len(self.data) > 1:
|
|
126
|
+
output.append("\nLegend:")
|
|
127
|
+
for idx, label in enumerate(self.data.keys()):
|
|
128
|
+
char = line_chars[idx % len(line_chars)]
|
|
129
|
+
output.append(f" {char} {label}")
|
|
130
|
+
|
|
131
|
+
# add scale info
|
|
132
|
+
output.append(f"\nScale: {global_min:.2f} to {global_max:.2f}")
|
|
133
|
+
|
|
134
|
+
return "\n".join(output)
|
|
135
|
+
|
|
136
|
+
def _draw_line(self, canvas, x1, y1, x2, y2, char):
|
|
137
|
+
"""Draw a simple line between two points using Bresenham's algorithm."""
|
|
138
|
+
dx = abs(x2 - x1)
|
|
139
|
+
dy = abs(y2 - y1)
|
|
140
|
+
sx = 1 if x1 < x2 else -1
|
|
141
|
+
sy = 1 if y1 < y2 else -1
|
|
142
|
+
err = dx - dy
|
|
143
|
+
|
|
144
|
+
x, y = x1, y1
|
|
145
|
+
|
|
146
|
+
while True:
|
|
147
|
+
if 0 <= x < self.width and 0 <= y < self.height:
|
|
148
|
+
if canvas[y][x] == ' ' or canvas[y][x] in ['│', '|']:
|
|
149
|
+
canvas[y][x] = char
|
|
150
|
+
|
|
151
|
+
if x == x2 and y == y2:
|
|
152
|
+
break
|
|
153
|
+
|
|
154
|
+
e2 = 2 * err
|
|
155
|
+
if e2 > -dy:
|
|
156
|
+
err -= dy
|
|
157
|
+
x += sx
|
|
158
|
+
if e2 < dx:
|
|
159
|
+
err += dx
|
|
160
|
+
y += sy
|
|
161
|
+
|
|
162
|
+
def __str__(self):
|
|
163
|
+
"""Default string representation."""
|
|
164
|
+
return self.plot()
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Scatter plot implementation for terminal.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .utils import normalize_data, Symbols
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ScatterPlot:
|
|
9
|
+
"""Create scatter plots in the terminal."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, x_data, y_data, title="", width=60, height=20, use_unicode=True):
|
|
12
|
+
"""
|
|
13
|
+
Initialize a scatter plot.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
x_data: List of x coordinates
|
|
17
|
+
y_data: List of y coordinates
|
|
18
|
+
title: Chart title
|
|
19
|
+
width: Chart width in characters
|
|
20
|
+
height: Chart height in lines
|
|
21
|
+
use_unicode: Use Unicode symbols vs ASCII
|
|
22
|
+
"""
|
|
23
|
+
if len(x_data) != len(y_data):
|
|
24
|
+
raise ValueError("x_data and y_data must have the same length")
|
|
25
|
+
|
|
26
|
+
self.x_data = x_data
|
|
27
|
+
self.y_data = y_data
|
|
28
|
+
self.title = title
|
|
29
|
+
self.width = width
|
|
30
|
+
self.height = height
|
|
31
|
+
self.use_unicode = use_unicode
|
|
32
|
+
|
|
33
|
+
def plot(self):
|
|
34
|
+
"""Draw the scatter plot."""
|
|
35
|
+
if not self.x_data or not self.y_data:
|
|
36
|
+
return "No data to display"
|
|
37
|
+
|
|
38
|
+
output = []
|
|
39
|
+
|
|
40
|
+
# title
|
|
41
|
+
if self.title:
|
|
42
|
+
output.append(f"\n{self.title}")
|
|
43
|
+
output.append("=" * len(self.title))
|
|
44
|
+
|
|
45
|
+
# create the canvas
|
|
46
|
+
canvas = [[' ' for _ in range(self.width)] for _ in range(self.height)]
|
|
47
|
+
|
|
48
|
+
# choose symbols
|
|
49
|
+
if self.use_unicode:
|
|
50
|
+
point_char = Symbols.UNICODE_DOT
|
|
51
|
+
v_char = Symbols.UNICODE_VERTICAL
|
|
52
|
+
h_char = Symbols.UNICODE_HORIZONTAL
|
|
53
|
+
else:
|
|
54
|
+
point_char = Symbols.ASCII_POINT
|
|
55
|
+
v_char = Symbols.ASCII_VERTICAL
|
|
56
|
+
h_char = Symbols.ASCII_HORIZONTAL
|
|
57
|
+
|
|
58
|
+
# normalize data
|
|
59
|
+
x_normalized = normalize_data(self.x_data, 1, self.width - 1)
|
|
60
|
+
y_normalized = normalize_data(self.y_data, 0, self.height - 1)
|
|
61
|
+
|
|
62
|
+
# plot points
|
|
63
|
+
for x, y in zip(x_normalized, y_normalized):
|
|
64
|
+
x_pos = int(x)
|
|
65
|
+
y_pos = self.height - 1 - int(y)
|
|
66
|
+
|
|
67
|
+
if 0 <= x_pos < self.width and 0 <= y_pos < self.height:
|
|
68
|
+
canvas[y_pos][x_pos] = point_char
|
|
69
|
+
|
|
70
|
+
# add y-axis
|
|
71
|
+
for y in range(self.height):
|
|
72
|
+
if canvas[y][0] == ' ':
|
|
73
|
+
canvas[y][0] = v_char
|
|
74
|
+
|
|
75
|
+
# convert canvas to string
|
|
76
|
+
for row in canvas:
|
|
77
|
+
output.append(''.join(row))
|
|
78
|
+
|
|
79
|
+
# add x-axis
|
|
80
|
+
output.append(h_char * self.width)
|
|
81
|
+
|
|
82
|
+
# add scale info
|
|
83
|
+
x_min, x_max = min(self.x_data), max(self.x_data)
|
|
84
|
+
y_min, y_max = min(self.y_data), max(self.y_data)
|
|
85
|
+
output.append(f"\nX: {x_min:.2f} to {x_max:.2f}")
|
|
86
|
+
output.append(f"Y: {y_min:.2f} to {y_max:.2f}")
|
|
87
|
+
output.append(f"Points: {len(self.x_data)}")
|
|
88
|
+
|
|
89
|
+
return "\n".join(output)
|
|
90
|
+
|
|
91
|
+
def __str__(self):
|
|
92
|
+
"""Default string representation."""
|
|
93
|
+
return self.plot()
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Utility functions for terminal plotting.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
def normalize_data(data, min_val=0, max_val=100):
|
|
6
|
+
"""Normalize data to a specific range."""
|
|
7
|
+
if not data:
|
|
8
|
+
return []
|
|
9
|
+
|
|
10
|
+
data_min = min(data)
|
|
11
|
+
data_max = max(data)
|
|
12
|
+
|
|
13
|
+
if data_max == data_min:
|
|
14
|
+
return [min_val] * len(data)
|
|
15
|
+
|
|
16
|
+
scale = (max_val - min_val) / (data_max - data_min)
|
|
17
|
+
return [min_val + (x - data_min) * scale for x in data]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def get_terminal_width():
|
|
21
|
+
"""Get the current terminal width."""
|
|
22
|
+
import shutil
|
|
23
|
+
return shutil.get_terminal_size().columns
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_terminal_height():
|
|
27
|
+
"""Get the current terminal height."""
|
|
28
|
+
import shutil
|
|
29
|
+
return shutil.get_terminal_size().lines
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def truncate_label(label, max_length):
|
|
33
|
+
"""Truncate label to max_length with ellipsis if needed."""
|
|
34
|
+
label = str(label)
|
|
35
|
+
if len(label) <= max_length:
|
|
36
|
+
return label
|
|
37
|
+
return label[:max_length-3] + "..."
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class Symbols:
|
|
41
|
+
"""ASCII and Unicode symbols for plotting."""
|
|
42
|
+
|
|
43
|
+
# ASCII symbols
|
|
44
|
+
ASCII_HORIZONTAL = "-"
|
|
45
|
+
ASCII_VERTICAL = "|"
|
|
46
|
+
ASCII_BLOCK = "#"
|
|
47
|
+
ASCII_POINT = "*"
|
|
48
|
+
ASCII_CORNER = "+"
|
|
49
|
+
|
|
50
|
+
# unicode symbols (prettier but may not work on all terminals)
|
|
51
|
+
UNICODE_HORIZONTAL = "─"
|
|
52
|
+
UNICODE_VERTICAL = "│"
|
|
53
|
+
UNICODE_CORNER_TL = "┌"
|
|
54
|
+
UNICODE_CORNER_TR = "┐"
|
|
55
|
+
UNICODE_CORNER_BL = "└"
|
|
56
|
+
UNICODE_CORNER_BR = "┘"
|
|
57
|
+
UNICODE_CROSS = "┼"
|
|
58
|
+
UNICODE_T_DOWN = "┬"
|
|
59
|
+
UNICODE_T_UP = "┴"
|
|
60
|
+
UNICODE_T_RIGHT = "├"
|
|
61
|
+
UNICODE_T_LEFT = "┤"
|
|
62
|
+
|
|
63
|
+
# block elements
|
|
64
|
+
UNICODE_BLOCKS = ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"]
|
|
65
|
+
UNICODE_FULL_BLOCK = "█"
|
|
66
|
+
UNICODE_LIGHT_SHADE = "░"
|
|
67
|
+
UNICODE_MEDIUM_SHADE = "▒"
|
|
68
|
+
UNICODE_DARK_SHADE = "▓"
|
|
69
|
+
|
|
70
|
+
# dots and points
|
|
71
|
+
UNICODE_DOT = "•"
|
|
72
|
+
UNICODE_CIRCLE = "○"
|
|
73
|
+
UNICODE_SQUARE = "■"
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: term-plot-mc
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Draw simple plots and charts in the terminal using ASCII symbols
|
|
5
|
+
Author-email: Mehdi Maleki <mosioc79@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/mosioc/term-plot
|
|
8
|
+
Project-URL: Repository, https://github.com/mosioc/term-plot
|
|
9
|
+
Project-URL: Issues, https://github.com/mosioc/term-plot/issues
|
|
10
|
+
Keywords: terminal,ascii,plot,chart,visualization,cli
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Classifier: Topic :: Terminals
|
|
22
|
+
Requires-Python: >=3.8
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
# Terminal Plot
|
|
28
|
+
|
|
29
|
+
> Draw beautiful plots and charts in your terminal using ASCII and Unicode symbols
|
|
30
|
+
|
|
31
|
+
[](https://opensource.org/licenses/MIT)
|
|
32
|
+
[](https://www.python.org/downloads/)
|
|
33
|
+
|
|
34
|
+
## Features
|
|
35
|
+
|
|
36
|
+
- **Bar Charts** - Horizontal and vertical bar charts for categorical data
|
|
37
|
+
- **Line Charts** - Single and multi-series line plots with automatic scaling
|
|
38
|
+
- **Scatter Plots** - Visualize correlations and relationships
|
|
39
|
+
- **Histograms** - Show data distributions with customizable bins
|
|
40
|
+
- **Dual Modes** - Beautiful Unicode symbols or ASCII for compatibility
|
|
41
|
+
- **Color Support** - Optional ANSI colors for better visualization
|
|
42
|
+
- **Zero Dependencies** - Pure Python, works anywhere
|
|
43
|
+
- **Lightweight** - Fast and minimal resource usage
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
### From PyPI (once published)
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install term-plot
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### From source
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
git clone https://github.com/yourusername/term-plot.git
|
|
57
|
+
cd term-plot
|
|
58
|
+
pip install -e .
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Quick Start
|
|
62
|
+
|
|
63
|
+
### Bar Chart
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from term_plot import BarChart
|
|
67
|
+
|
|
68
|
+
# Simple horizontal bar chart
|
|
69
|
+
data = [23, 45, 12, 67, 34, 89, 56]
|
|
70
|
+
labels = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
|
|
71
|
+
|
|
72
|
+
chart = BarChart(data, labels=labels, title="Daily Sales", width=50)
|
|
73
|
+
print(chart.horizontal())
|
|
74
|
+
|
|
75
|
+
# Vertical bar chart
|
|
76
|
+
chart = BarChart(data, labels=labels, title="Daily Sales", height=15)
|
|
77
|
+
print(chart.vertical())
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Line Chart
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
from term_plot import LineChart
|
|
84
|
+
|
|
85
|
+
# Single series
|
|
86
|
+
data = [1, 3, 7, 4, 8, 6, 9, 12, 10]
|
|
87
|
+
chart = LineChart(data, title="Temperature Over Time", width=60, height=15)
|
|
88
|
+
print(chart.plot())
|
|
89
|
+
|
|
90
|
+
# Multiple series
|
|
91
|
+
data = {
|
|
92
|
+
"Product A": [5, 7, 9, 12, 15, 18, 20],
|
|
93
|
+
"Product B": [3, 5, 8, 10, 12, 15, 17]
|
|
94
|
+
}
|
|
95
|
+
chart = LineChart(data, title="Sales Comparison", width=70, height=20)
|
|
96
|
+
print(chart.plot())
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Scatter Plot
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
from term_plot import ScatterPlot
|
|
103
|
+
|
|
104
|
+
x_data = [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
105
|
+
y_data = [2, 4, 3, 5, 7, 6, 8, 9, 8]
|
|
106
|
+
|
|
107
|
+
chart = ScatterPlot(x_data, y_data, title="Correlation Plot", width=50, height=15)
|
|
108
|
+
print(chart.plot())
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Histogram
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from term_plot import Histogram
|
|
115
|
+
import random
|
|
116
|
+
|
|
117
|
+
# Generate sample data
|
|
118
|
+
data = [random.gauss(50, 15) for _ in range(1000)]
|
|
119
|
+
|
|
120
|
+
chart = Histogram(data, bins=15, title="Test Score Distribution", width=60, height=15)
|
|
121
|
+
print(chart.plot())
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Customization Options
|
|
125
|
+
|
|
126
|
+
### ASCII vs Unicode
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
# Unicode mode (default) - prettier but requires Unicode support
|
|
130
|
+
chart = BarChart(data, labels=labels, use_unicode=True)
|
|
131
|
+
|
|
132
|
+
# ASCII mode - works everywhere
|
|
133
|
+
chart = BarChart(data, labels=labels, use_unicode=False)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Colors
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
# With colors (default)
|
|
140
|
+
chart = BarChart(data, labels=labels, color=True)
|
|
141
|
+
|
|
142
|
+
# Without colors
|
|
143
|
+
chart = BarChart(data, labels=labels, color=False)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Size Control
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
# Custom width and height
|
|
150
|
+
chart = LineChart(data, width=80, height=25)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Examples
|
|
154
|
+
|
|
155
|
+
Check out the `examples/` directory for more:
|
|
156
|
+
|
|
157
|
+
- `bar_chart_example.py` - Various bar chart styles
|
|
158
|
+
- `line_chart_example.py` - Line chart examples including sine waves
|
|
159
|
+
- `demo_all.py` - Comprehensive demo of all chart types
|
|
160
|
+
|
|
161
|
+
Run examples:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
cd examples
|
|
165
|
+
python demo_all.py
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Testing
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
# Install development dependencies
|
|
172
|
+
pip install -e ".[dev]"
|
|
173
|
+
|
|
174
|
+
# Run tests
|
|
175
|
+
pytest
|
|
176
|
+
|
|
177
|
+
# Run tests with coverage
|
|
178
|
+
pytest --cov=term_plot
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Requirements
|
|
182
|
+
|
|
183
|
+
- Python 3.8 or higher
|
|
184
|
+
- No external dependencies!
|
|
185
|
+
|
|
186
|
+
## License
|
|
187
|
+
|
|
188
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
189
|
+
|
|
190
|
+
## Acknowledgments
|
|
191
|
+
|
|
192
|
+
- Inspired by various terminal plotting libraries
|
|
193
|
+
- Unicode block elements from the Unicode standard
|
|
194
|
+
- ANSI color codes for terminal colors
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/term_plot/__init__.py
|
|
5
|
+
src/term_plot/bar_chart.py
|
|
6
|
+
src/term_plot/histogram.py
|
|
7
|
+
src/term_plot/line_chart.py
|
|
8
|
+
src/term_plot/scatter_plot.py
|
|
9
|
+
src/term_plot/utils.py
|
|
10
|
+
src/term_plot_mc.egg-info/PKG-INFO
|
|
11
|
+
src/term_plot_mc.egg-info/SOURCES.txt
|
|
12
|
+
src/term_plot_mc.egg-info/dependency_links.txt
|
|
13
|
+
src/term_plot_mc.egg-info/top_level.txt
|
|
14
|
+
tests/test_bar_chart.py
|
|
15
|
+
tests/test_utils.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
term_plot
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for BarChart class.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
from term_plot import BarChart
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def test_bar_chart_creation():
|
|
10
|
+
"""Test basic bar chart creation."""
|
|
11
|
+
data = [1, 2, 3, 4, 5]
|
|
12
|
+
labels = ['A', 'B', 'C', 'D', 'E']
|
|
13
|
+
|
|
14
|
+
chart = BarChart(data, labels=labels)
|
|
15
|
+
assert chart.data == data
|
|
16
|
+
assert chart.labels == labels
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_bar_chart_mismatched_lengths():
|
|
20
|
+
"""Test that mismatched data and labels raises error."""
|
|
21
|
+
data = [1, 2, 3]
|
|
22
|
+
labels = ['A', 'B']
|
|
23
|
+
|
|
24
|
+
with pytest.raises(ValueError):
|
|
25
|
+
BarChart(data, labels=labels)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_bar_chart_horizontal():
|
|
29
|
+
"""Test horizontal bar chart generation."""
|
|
30
|
+
data = [10, 20, 30]
|
|
31
|
+
labels = ['X', 'Y', 'Z']
|
|
32
|
+
|
|
33
|
+
chart = BarChart(data, labels=labels, title="Test Chart")
|
|
34
|
+
output = chart.horizontal()
|
|
35
|
+
|
|
36
|
+
assert "Test Chart" in output
|
|
37
|
+
assert "X" in output
|
|
38
|
+
assert "Y" in output
|
|
39
|
+
assert "Z" in output
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_bar_chart_vertical():
|
|
43
|
+
"""Test vertical bar chart generation."""
|
|
44
|
+
data = [5, 10, 15]
|
|
45
|
+
labels = ['A', 'B', 'C']
|
|
46
|
+
|
|
47
|
+
chart = BarChart(data, labels=labels)
|
|
48
|
+
output = chart.vertical()
|
|
49
|
+
|
|
50
|
+
assert output is not None
|
|
51
|
+
assert len(output) > 0
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_bar_chart_empty_data():
|
|
55
|
+
"""Test bar chart with empty data."""
|
|
56
|
+
data = []
|
|
57
|
+
labels = []
|
|
58
|
+
|
|
59
|
+
chart = BarChart(data, labels=labels)
|
|
60
|
+
output = chart.horizontal()
|
|
61
|
+
|
|
62
|
+
assert "No data" in output
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_bar_chart_auto_labels():
|
|
66
|
+
"""Test automatic label generation."""
|
|
67
|
+
data = [1, 2, 3]
|
|
68
|
+
|
|
69
|
+
chart = BarChart(data)
|
|
70
|
+
assert len(chart.labels) == len(data)
|
|
71
|
+
assert chart.labels[0] == "Item 1"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_bar_chart_unicode_vs_ascii():
|
|
75
|
+
"""Test Unicode vs ASCII mode."""
|
|
76
|
+
data = [10, 20]
|
|
77
|
+
labels = ['A', 'B']
|
|
78
|
+
|
|
79
|
+
unicode_chart = BarChart(data, labels=labels, use_unicode=True)
|
|
80
|
+
ascii_chart = BarChart(data, labels=labels, use_unicode=False)
|
|
81
|
+
|
|
82
|
+
unicode_output = unicode_chart.horizontal()
|
|
83
|
+
ascii_output = ascii_chart.horizontal()
|
|
84
|
+
|
|
85
|
+
# They should be different
|
|
86
|
+
assert unicode_output != ascii_output
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for utility functions.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
from term_plot.utils import normalize_data, truncate_label, Symbols
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def test_normalize_data():
|
|
10
|
+
"""Test data normalization."""
|
|
11
|
+
data = [0, 50, 100]
|
|
12
|
+
normalized = normalize_data(data, 0, 10)
|
|
13
|
+
|
|
14
|
+
assert normalized[0] == 0
|
|
15
|
+
assert normalized[1] == 5
|
|
16
|
+
assert normalized[2] == 10
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_normalize_data_same_values():
|
|
20
|
+
"""Test normalization with same values."""
|
|
21
|
+
data = [5, 5, 5]
|
|
22
|
+
normalized = normalize_data(data, 0, 10)
|
|
23
|
+
|
|
24
|
+
# all values should be the same (min_val)
|
|
25
|
+
assert all(v == 0 for v in normalized)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_normalize_data_empty():
|
|
29
|
+
"""Test normalization with empty data."""
|
|
30
|
+
data = []
|
|
31
|
+
normalized = normalize_data(data)
|
|
32
|
+
|
|
33
|
+
assert normalized == []
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_truncate_label():
|
|
37
|
+
"""Test label truncation."""
|
|
38
|
+
# short label
|
|
39
|
+
assert truncate_label("Test", 10) == "Test"
|
|
40
|
+
|
|
41
|
+
# song label
|
|
42
|
+
assert truncate_label("ThisIsAVeryLongLabel", 10) == "ThisIs..."
|
|
43
|
+
|
|
44
|
+
# exact length
|
|
45
|
+
assert truncate_label("ExactLen", 8) == "ExactLen"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_symbols_exist():
|
|
49
|
+
"""Test that symbol constants exist."""
|
|
50
|
+
assert hasattr(Symbols, 'ASCII_BLOCK')
|
|
51
|
+
assert hasattr(Symbols, 'UNICODE_FULL_BLOCK')
|
|
52
|
+
assert hasattr(Symbols, 'ASCII_HORIZONTAL')
|
|
53
|
+
assert hasattr(Symbols, 'UNICODE_DOT')
|