diff --git a/.gitignore b/.gitignore index e41c97e..433991f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,20 +3,21 @@ .DS_Store # Python -*.pyc -venv/ .coverage +dist/ htmlcov/ -uv.lock -termgraph.egg-info/ +*.pyc .python-version .ropeproject/ +termgraph.egg-info/ +uv.lock +venv/ # Editors +.claude/ +CLAUDE.md +.gemini/ .vim/ .vimrc .vscode/ -.claude/ -.gemini/ -CLAUDE.md diff --git a/README.md b/README.md index 3bb8796..8313417 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,23 @@ # Termgraph -A command-line tool that draws basic graphs in the terminal, written in Python. +A command-line tool and Python library that draws basic graphs in the terminal. Graph types supported: - - Bar Graphs - Color charts - Multi-variable - Stacked charts - Histograms - Horizontal or Vertical +- Calendar heatmaps - Emoji! +## Quick Start -### Examples +### Command Line Usage ``` -termgraph data/ex1.dat +$ termgraph data/ex1.dat # Reading data from data/ex1.dat @@ -29,6 +30,41 @@ termgraph data/ex1.dat 2014: ▏ 1.00 ``` +### Python Module Usage + +```python +from termgraph import Data, Args, BarChart + +# Create data +data = Data([[10], [25], [50], [40]], ["Q1", "Q2", "Q3", "Q4"]) + +# Configure chart options +args = Args( + title="Quarterly Sales", + width=50, + format="{:.0f}", + suffix="K" +) + +# Create and display chart +chart = BarChart(data, args) +chart.draw() +``` + +Output: +``` +# Quarterly Sales + +Q1: ▇▇▇▇▇▇▇▇▇▇ 10K +Q2: ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 25K +Q3: ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 50K +Q4: ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 40K +``` + +## More Examples + +### Custom Tick Marks + An example using emoji as custom tick: ``` @@ -46,55 +82,37 @@ termgraph data/ex1.dat --custom-tick "🏃" --width 20 --title "Running Data" ``` +### Color Charts -An example using stdin and emoji: - -``` -echo "Label,3,9,1" | termgraph --custom-tick "😀" --no-label - - -😀😀😀 3.00 -😀😀😀😀😀😀😀😀😀 9.00 -😀 1.00 +Note: Color charts use ANSI escape codes, so may not be able to copy/paste from terminal into other uses. +```bash +$ termgraph data/ex4.dat --color {cyan/yellow} --space-between ``` -Most results can be copied and pasted wherever you like, since they use standard block characters. However the color charts will not show, since they use terminal escape codes for color. A couple images to show color examples: - -``` -termgraph data/ex4.dat --color {blue,red} -``` +![Bar chart with multiple variables](/docs/assets/barchart-multivar.svg) -Multi variable bar chart with colors +--- ``` -termgraph data/ex7.dat --color {yellow,magenta} --stacked --title "Stacked Data" +termgraph data/ex7.dat --color {green,magenta} --stacked ``` -Multi variable stacked bar chart with colors +![Stacked Bar Chart](/docs/assets/barchart-stacked.svg) +### Calendar Heatmap Calendar Heatmap, expects first column to be date in yyyy-mm-dd ``` -termgraph --calendar --start-dt 2017-07-01 data/cal.dat +$ termgraph --calendar --start-dt 2017-07-01 data/cal.dat ``` -Calendar Heatmap - - - -### Install - -Requires Python 3.9+, install from [PyPI project](https://pypi.org/project/termgraph/) +![Calendar Heatmap](/docs/assets/cal-heatmap.svg) -``` -python3 -m pip install termgraph -``` -Note: Be sure your PATH includes the pypi install directory, for me it is `~/.local/bin/` -### Usage +## Usage #### Command Line Interface @@ -105,38 +123,7 @@ Note: Be sure your PATH includes the pypi install directory, for me it is `~/.lo * Help: termgraph -h -#### Programmatic API - -Termgraph can also be used as a Python library for creating charts programmatically: - -```python -from termgraph import Data, Args, BarChart - -# Create data -data = Data([[10], [25], [50], [40]], ["Q1", "Q2", "Q3", "Q4"]) - -# Configure chart options -args = Args( - title="Quarterly Sales", - width=50, - format="{:.0f}", - suffix="K" -) - -# Create and display chart -chart = BarChart(data, args) -chart.draw() -``` - -This produces: -``` -# Quarterly Sales - -Q1: ▇▇▇▇▇▇▇▇▇▇ 10K -Q2: ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 25K -Q3: ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 50K -Q4: ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 40K -``` +#### Command Line Arguments ``` usage: termgraph [-h] [options] [filename] @@ -173,8 +160,47 @@ options: --version Display version and exit ``` +### Python API + +All chart types are available as classes: + +```python +from termgraph import ( + Data, Args, + BarChart, StackedChart, VerticalChart, HistogramChart +) + +# Basic setup +data = Data([[10], [20]], ["A", "B"]) +args = Args(title="My Chart") + +# Choose your chart type +chart = BarChart(data, args) # Horizontal bars +# chart = StackedChart(data, args) # Stacked bars +# chart = VerticalChart(data, args) # Vertical bars +# chart = HistogramChart(data, args) # Histogram + +chart.draw() +``` + +**📚 [Complete Python API Documentation](docs/)** + +For comprehensive examples, detailed API reference, and advanced usage patterns, see the complete documentation: +- **[Getting Started Guide](docs/README.md)** - Examples and best practices +- **[Data Class API](docs/data-class.md)** - Data preparation and validation +- **[Chart Classes API](docs/chart-classes.md)** - All chart types with examples +- **[Args Configuration](docs/args-class.md)** - Complete configuration options + +Quick Args options: +- `title`: Chart title +- `width`: Width in characters (default: 50) +- `format`: Number format string (default: "{:<5.2f}") +- `suffix`: Add suffix to all values +- `no_labels`: Don't show labels +- `no_values`: Don't show values +- `colors`: List of color names -### Background +## Background I wanted a quick way to visualize data stored in a simple text file. I initially created some scripts in R that generated graphs but this was a two step process of creating the graph and then opening the generated graph. @@ -186,7 +212,7 @@ All contributions are welcome! For detailed information about the project struct **Quick Start:** - 🐛 **Bug reports** and 🚀 **feature requests**: Use [GitHub Issues](https://github.com/mkaz/termgraph/issues) -- 🔧 **Code contributions**: See our [development workflow](CONTRIBUTING.md#development-workflow) +- 🔧 **Code contributions**: See our [development workflow](CONTRIBUTING.md#development-workflow) - 📚 **Documentation**: Help improve our guides and examples **Code Quality:** We use `ruff` for linting and formatting, `mypy` for type checking, and maintain comprehensive test coverage. diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..e107ef6 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,475 @@ +# Termgraph Python API Documentation + +Termgraph is a Python library for creating terminal-based charts and graphs. This documentation covers using termgraph as a Python module for programmatic chart generation. + +## Quick Start + +```python +from termgraph import Data, BarChart, Args + +# Create data +data = Data([23, 45, 56, 78, 32], ["A", "B", "C", "D", "E"]) + +# Create and display chart +chart = BarChart(data) +chart.draw() +``` + +## Installation + +```bash +pip install termgraph +``` + +## Core Components + +Termgraph consists of three main classes: + +1. **[Data](data-class.md)** - Handles data storage, validation, and normalization +2. **[Chart Classes](chart-classes.md)** - Various chart types (Bar, Stacked, Vertical, Histogram) +3. **[Args](args-class.md)** - Configuration and styling options + +## Basic Usage + +### Simple Bar Chart + +```python +from termgraph import Data, BarChart + +# Sales data +data = Data( + data=[150, 230, 180, 290, 210], + labels=["Jan", "Feb", "Mar", "Apr", "May"] +) + +chart = BarChart(data) +chart.draw() +``` + +Output: +``` +Jan: ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 150.00 +Feb: ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 230.00 +Mar: ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 180.00 +Apr: ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 290.00 +May: ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 210.00 +``` + +### Styled Chart + +```python +from termgraph import Data, BarChart, Args, Colors + +data = Data( + data=[150, 230, 180, 290, 210], + labels=["Jan", "Feb", "Mar", "Apr", "May"] +) + +args = Args( + width=60, + title="Monthly Sales Report", + colors=[Colors.Green], + suffix=" units", + format="{:<6.0f}" +) + +chart = BarChart(data, args) +chart.draw() +``` + +Output: +``` +# Monthly Sales Report + +Jan: ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 150 units +Feb: ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 230 units +Mar: ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 180 units +Apr: ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 290 units +May: ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 210 units +``` + +## Chart Types + +### Bar Chart (Horizontal) + +Best for comparing values across categories. + +```python +from termgraph import Data, BarChart, Args, Colors + +# Single series +data = Data([45, 32, 78, 56, 23], ["Product A", "Product B", "Product C", "Product D", "Product E"]) +chart = BarChart(data, Args(colors=[Colors.Blue])) +chart.draw() +``` + +### Multi-Series Bar Chart + +Compare multiple data series side by side. + +```python +from termgraph import Data, BarChart, Args, Colors + +# Quarterly sales for two products +data = Data( + data=[ + [120, 80], # Q1: Product A, Product B + [150, 95], # Q2: Product A, Product B + [180, 110], # Q3: Product A, Product B + [200, 125] # Q4: Product A, Product B + ], + labels=["Q1", "Q2", "Q3", "Q4"], + categories=["Product A", "Product B"] +) + +args = Args( + title="Quarterly Sales Comparison", + colors=[Colors.Blue, Colors.Red], + width=50 +) + +chart = BarChart(data, args) +chart.draw() +``` + +### Stacked Bar Chart + +Show parts of a whole. + +```python +from termgraph import Data, StackedChart, Args, Colors + +# Budget breakdown +data = Data( + data=[ + [30, 20, 10], # Q1: Marketing, Development, Operations + [35, 25, 15], # Q2: Marketing, Development, Operations + [40, 30, 20], # Q3: Marketing, Development, Operations + [45, 35, 25] # Q4: Marketing, Development, Operations + ], + labels=["Q1", "Q2", "Q3", "Q4"], + categories=["Marketing", "Development", "Operations"] +) + +args = Args( + title="Budget Allocation by Quarter", + colors=[Colors.Green, Colors.Blue, Colors.Yellow], + suffix="K" +) + +chart = StackedChart(data, args) +chart.draw() +``` + +### Vertical Chart (Column Chart) + +Good for time series or when you have many categories. + +```python +from termgraph import Data, VerticalChart, Args, Colors + +data = Data([23, 45, 56, 78, 32, 67, 45], ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]) + +args = Args( + title="Daily Website Visits", + colors=[Colors.Cyan], + width=40 +) + +chart = VerticalChart(data, args) +chart.draw() +``` + +### Histogram + +Show distribution of continuous data. + +```python +from termgraph import Data, HistogramChart, Args, Colors + +# Temperature readings that will be binned +data = Data( + data=[[15.2, 18.7, 22.1, 19.5, 25.3, 28.9, 24.4, 21.8, 26.2, 23.7, 20.1, 17.6]], + labels=["Temperature Data"] +) + +args = Args( + title="Temperature Distribution", + bins=6, + colors=[Colors.Red], + width=50 +) + +chart = HistogramChart(data, args) +chart.draw() +``` + +## Advanced Examples + +### Financial Dashboard + +```python +from termgraph import Data, BarChart, StackedChart, Args, Colors + +# Revenue data +revenue_data = Data( + data=[ + [450, 380, 290], # Q1: Americas, EMEA, APAC + [520, 420, 340], # Q2: Americas, EMEA, APAC + [480, 450, 380], # Q3: Americas, EMEA, APAC + [600, 480, 420] # Q4: Americas, EMEA, APAC + ], + labels=["Q1 2023", "Q2 2023", "Q3 2023", "Q4 2023"], + categories=["Americas", "EMEA", "APAC"] +) + +# Revenue by region (stacked) +print("=== Revenue by Region (Stacked) ===") +stacked_args = Args( + title="Quarterly Revenue by Region", + colors=[Colors.Blue, Colors.Green, Colors.Yellow], + width=60, + suffix="K USD", + format="{:<5.0f}" +) +stacked_chart = StackedChart(revenue_data, stacked_args) +stacked_chart.draw() + +# Regional comparison (side by side) +print("\n=== Regional Performance Comparison ===") +bar_args = Args( + title="Regional Performance by Quarter", + colors=[Colors.Blue, Colors.Green, Colors.Yellow], + width=60, + suffix="K USD", + space_between=True +) +bar_chart = BarChart(revenue_data, bar_args) +bar_chart.draw() +``` + +### Performance Metrics Dashboard + +```python +from termgraph import Data, BarChart, Args, Colors + +# Performance metrics with different scales +performance_data = Data( + data=[ + [95.5, 1250], # Uptime %, Response Time (ms) + [98.2, 980], + [97.1, 1100], + [99.1, 850], + [96.8, 1300] + ], + labels=["Week 1", "Week 2", "Week 3", "Week 4", "Week 5"], + categories=["Uptime (%)", "Response Time (ms)"] +) + +args = Args( + title="System Performance Metrics", + colors=[Colors.Green, Colors.Red], + different_scale=True, # Use different scales for each metric + width=50, + space_between=True +) + +chart = BarChart(performance_data, args) +chart.draw() +``` + +### Project Status Tracking + +```python +from termgraph import Data, BarChart, Args, Colors + +# Project completion percentages +projects_data = Data( + data=[0.85, 0.62, 0.93, 0.78, 0.45, 0.91], + labels=["Website Redesign", "Mobile App", "Database Migration", "API v2", "Documentation", "Testing Suite"] +) + +args = Args( + title="Project Completion Status", + colors=[Colors.Green], + percentage=True, + width=50, + format="{:<3.0f}", + suffix="%" +) + +chart = BarChart(projects_data, args) +chart.draw() +``` + +### Sales Trend Analysis + +```python +from termgraph import Data, VerticalChart, BarChart, Args, Colors + +# Monthly sales trend +monthly_sales = Data( + data=[120, 135, 150, 145, 160, 175, 185, 170, 190, 200, 195, 210], + labels=["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] +) + +# Vertical chart for trend visualization +print("=== Monthly Sales Trend ===") +vertical_args = Args( + title="2023 Sales Trend", + colors=[Colors.Blue], + width=60 +) +vertical_chart = VerticalChart(monthly_sales, vertical_args) +vertical_chart.draw() + +# Horizontal chart for precise values +print("\n=== Detailed Monthly Sales ===") +bar_args = Args( + title="2023 Monthly Sales Details", + colors=[Colors.Green], + width=50, + suffix="K USD", + format="{:<5.0f}" +) +bar_chart = BarChart(monthly_sales, bar_args) +bar_chart.draw() +``` + +## Data Formats + +### Flat Data (Single Series) + +```python +# Simple list of numbers +data = Data([10, 20, 30, 40], ["A", "B", "C", "D"]) +``` + +### Nested Data (Multi-Series) + +```python +# List of lists for multiple data series +data = Data( + data=[ + [10, 15, 20], # Row 1: three values + [25, 30, 35], # Row 2: three values + [40, 45, 50] # Row 3: three values + ], + labels=["Category 1", "Category 2", "Category 3"], + categories=["Series A", "Series B", "Series C"] +) +``` + +### Working with Real Data + +```python +import csv +from termgraph import Data, BarChart, Args, Colors + +# Reading from CSV (example) +def load_csv_data(filename): + """Load data from CSV file.""" + data = [] + labels = [] + + with open(filename, 'r') as f: + reader = csv.reader(f) + next(reader) # Skip header + + for row in reader: + labels.append(row[0]) + data.append(float(row[1])) + + return Data(data, labels) + +# Usage +# data = load_csv_data('sales_data.csv') +# chart = BarChart(data, Args(title="Sales Data", colors=[Colors.Blue])) +# chart.draw() +``` + +## Error Handling + +```python +from termgraph import Data, BarChart + +try: + # This will raise an exception - mismatched dimensions + data = Data([10, 20, 30], ["A", "B"]) + chart = BarChart(data) + chart.draw() +except Exception as e: + print(f"Error creating chart: {e}") + +try: + # This will raise an exception - empty data + data = Data([], []) + chart = BarChart(data) + chart.draw() +except Exception as e: + print(f"Error with empty data: {e}") +``` + +## Best Practices + +### 1. Choose the Right Chart Type + +- **Bar Chart**: Comparing categories or values +- **Stacked Chart**: Showing parts of a whole +- **Vertical Chart**: Time series or many categories +- **Histogram**: Distribution of continuous data + +### 2. Use Appropriate Colors + +```python +# Good: Different colors for different series +args = Args(colors=[Colors.Blue, Colors.Green, Colors.Red]) + +# Good: Single color for single series +args = Args(colors=[Colors.Blue]) + +# Avoid: Too many similar colors +``` + +### 3. Format Values Appropriately + +```python +# For percentages +args = Args(percentage=True, format="{:<3.0f}", suffix="%") + +# For currency +args = Args(format="{:<6.2f}", suffix=" USD") + +# For large numbers +args = Args(suffix="K", format="{:<5.0f}") # Use K, M, etc. +``` + +### 4. Set Appropriate Width + +```python +# Narrow for simple data +args = Args(width=30) + +# Wide for detailed data +args = Args(width=80) + +# Consider terminal width limitations +``` + +## API Reference + +- **[Data Class](data-class.md)** - Data handling and normalization +- **[Chart Classes](chart-classes.md)** - All available chart types +- **[Args Class](args-class.md)** - Configuration options + +## Examples Repository + +For more examples and use cases, see the test files in the repository: +- Basic usage examples +- Complex multi-series charts +- Real-world data scenarios +- Integration patterns + +## Contributing + +If you find issues or want to contribute improvements to the API documentation, please visit the [GitHub repository](https://github.com/mkaz/termgraph). \ No newline at end of file diff --git a/docs/args-class.md b/docs/args-class.md new file mode 100644 index 0000000..70ef135 --- /dev/null +++ b/docs/args-class.md @@ -0,0 +1,426 @@ +# Args Class API Documentation + +The `Args` class manages chart configuration options and arguments. It provides a centralized way to control chart appearance, behavior, and formatting. + +## Constructor + +```python +from termgraph import Args + +# Default args +args = Args() + +# Custom args +args = Args( + width=60, + title="My Chart", + colors=["red", "blue"], + suffix=" units" +) +``` + +### Parameters + +All parameters are optional and provided as keyword arguments. If not specified, default values are used. + +## Configuration Options + +### Chart Dimensions + +#### `width` (int, default: 50) +The width of the chart in characters. + +```python +args = Args(width=80) # Wide chart +args = Args(width=30) # Narrow chart +``` + +### Chart Appearance + +#### `title` (str, default: None) +Chart title displayed at the top. + +```python +args = Args(title="Monthly Sales Report") +``` + +#### `colors` (list, default: None) +List of color codes for chart series. Use color names or ANSI codes. + +```python +from termgraph import Colors + +args = Args(colors=[Colors.Blue, Colors.Green, Colors.Red]) +# Or use color names +args = Args(colors=["blue", "green", "red"]) +``` + +Available color constants: +- `Colors.Black` +- `Colors.Red` +- `Colors.Green` +- `Colors.Yellow` +- `Colors.Blue` +- `Colors.Magenta` +- `Colors.Cyan` + +#### `custom_tick` (str, default: "") +Custom character to use for chart bars instead of the default block character. + +```python +args = Args(custom_tick="*") # Use asterisks +args = Args(custom_tick="=") # Use equals signs +args = Args(custom_tick="█") # Use solid blocks +``` + +### Value Formatting + +#### `format` (str, default: "{:<5.2f}") +Python format string for numeric values. + +```python +args = Args(format="{:<6.1f}") # 6 chars wide, 1 decimal +args = Args(format="{:>8.0f}") # Right-aligned, no decimals +args = Args(format="{:+.2f}") # Always show sign +``` + +#### `suffix` (str, default: "") +Text appended to each value. + +```python +args = Args(suffix=" units") # Append units +args = Args(suffix="K") # Thousands +args = Args(suffix="$") # Currency +``` + +#### `percentage` (bool, default: False) +Format values as percentages. + +```python +# Convert 0.75 to 75% +args = Args(percentage=True) +``` + +#### `no_readable` (bool, default: False) +Disable automatic conversion of large numbers to readable format (e.g., 1000 → 1K). + +```python +args = Args(no_readable=True) # Show raw numbers +``` + +### Label and Value Display + +#### `no_labels` (bool, default: False) +Hide row labels. + +```python +args = Args(no_labels=True) # Hide all labels +``` + +#### `no_values` (bool, default: False) +Hide numeric values next to bars. + +```python +args = Args(no_values=True) # Show only bars +``` + +#### `label_before` (bool, default: False) +Display labels before the bars instead of to the left with colons. + +```python +args = Args(label_before=True) +# Changes "Label: ████" to "Label ████" +``` + +### Chart Layout + +#### `space_between` (bool, default: False) +Add blank lines between chart rows. + +```python +args = Args(space_between=True) # More readable spacing +``` + +#### `vertical` (bool, default: False) +Create vertical/column charts instead of horizontal bars. + +```python +args = Args(vertical=True) # Column chart +``` + +### Multi-Series Options + +#### `different_scale` (bool, default: False) +Use different scales for each data series in multi-series charts. + +```python +# Useful when series have very different ranges +args = Args(different_scale=True) +``` + +#### `stacked` (bool, default: False) +Create stacked bar charts for multi-series data. + +```python +args = Args(stacked=True) # Stack series on top of each other +``` + +### Histogram Options + +#### `histogram` (bool, default: False) +Enable histogram mode. + +```python +args = Args(histogram=True, bins=10) +``` + +#### `bins` (int, default: 5) +Number of bins for histogram charts. + +```python +args = Args(bins=8) # 8 histogram bins +args = Args(bins=15) # Fine-grained histogram +``` + +### Data Input Options + +#### `filename` (str, default: "-") +Input filename (used by CLI, usually not needed for API usage). + +#### `delim` (str, default: "") +Custom delimiter for data parsing. + +### Calendar Options + +#### `calendar` (bool, default: False) +Enable calendar heatmap mode. + +#### `start_dt` (date, default: None) +Start date for calendar charts. + +### Debugging + +#### `verbose` (bool, default: False) +Enable verbose output for debugging. + +```python +args = Args(verbose=True) # Show debug information +``` + +## Methods + +### `get_arg(arg: str) -> Union[int, str, bool, None]` +Get the value of a specific argument. + +```python +args = Args(width=60, title="Test") + +width = args.get_arg("width") # Returns 60 +title = args.get_arg("title") # Returns "Test" +``` + +### `update_args(**kwargs) -> None` +Update multiple arguments after initialization. + +```python +args = Args(width=50) +args.update_args(width=80, title="Updated Chart") +``` + +## Examples + +### Basic Configuration + +```python +from termgraph import Args, Colors + +# Simple configuration +args = Args( + width=50, + title="Sales Data", + colors=[Colors.Blue], + suffix=" units" +) +``` + +### Advanced Formatting + +```python +from termgraph import Args + +# Detailed formatting options +args = Args( + width=70, + title="Financial Performance Q4 2023", + format="{:>8.2f}", # Right-aligned, 2 decimals + suffix="K USD", # Thousands of dollars + space_between=True, # Better readability + no_readable=True # Show exact numbers +) +``` + +### Multi-Series Configuration + +```python +from termgraph import Args, Colors + +# Multi-series with different colors +args = Args( + width=60, + title="Product Comparison", + colors=[Colors.Green, Colors.Blue, Colors.Red], + different_scale=False, # Use same scale + space_between=True +) +``` + +### Stacked Chart Configuration + +```python +from termgraph import Args, Colors + +# Stacked chart setup +args = Args( + width=50, + title="Market Share Evolution", + colors=[Colors.Blue, Colors.Green, Colors.Yellow], + stacked=True, + format="{:<4.0f}", + suffix="%" +) +``` + +### Histogram Configuration + +```python +from termgraph import Args, Colors + +# Histogram settings +args = Args( + width=60, + title="Data Distribution", + histogram=True, + bins=10, + colors=[Colors.Cyan], + format="{:<3.0f}" +) +``` + +### Minimal Bar Chart + +```python +from termgraph import Args + +# Clean, minimal appearance +args = Args( + width=40, + no_values=True, # Hide numbers + custom_tick="▓", # Custom bar character + colors=["green"] +) +``` + +### Percentage Chart + +```python +from termgraph import Args, Colors + +# Percentage display +args = Args( + width=50, + title="Completion Status", + percentage=True, + colors=[Colors.Green], + format="{:<3.0f}", + suffix="%" +) +``` + +### Vertical Chart Configuration + +```python +from termgraph import Args, Colors + +# Column chart setup +args = Args( + width=30, + title="Vertical Sales Chart", + vertical=True, + colors=[Colors.Blue], + no_labels=False +) +``` + +## Default Values Reference + +```python +# All default values +default_args = { + "filename": "-", + "title": None, + "width": 50, + "format": "{:<5.2f}", + "suffix": "", + "no_labels": False, + "no_values": False, + "space_between": False, + "colors": None, + "vertical": False, + "stacked": False, + "histogram": False, + "bins": 5, + "different_scale": False, + "calendar": False, + "start_dt": None, + "custom_tick": "", + "delim": "", + "verbose": False, + "label_before": False, + "percentage": False, + "no_readable": False, +} +``` + +## Error Handling + +The Args class validates argument names: + +```python +from termgraph import Args + +try: + # Invalid argument name + args = Args(invalid_option=True) +except Exception as e: + print(f"Error: {e}") # "Invalid Argument: invalid_option" + +try: + # Invalid argument in get_arg + args = Args() + value = args.get_arg("nonexistent") +except Exception as e: + print(f"Error: {e}") # "Invalid Argument: nonexistent" +``` + +## Integration with Charts + +The Args object is passed to chart constructors: + +```python +from termgraph import Data, BarChart, Args, Colors + +# Create data and args +data = Data([10, 20, 30], ["A", "B", "C"]) +args = Args( + width=40, + title="Sample Chart", + colors=[Colors.Green] +) + +# Create and draw chart +chart = BarChart(data, args) +chart.draw() +``` + +For more information about chart types, see [Chart Classes Documentation](chart-classes.md). +For data preparation, see [Data Class Documentation](data-class.md). \ No newline at end of file diff --git a/docs/assets/barchart-multivar.svg b/docs/assets/barchart-multivar.svg new file mode 100644 index 0000000..d731e77 --- /dev/null +++ b/docs/assets/barchart-multivar.svg @@ -0,0 +1,59 @@ + + + + + + + + + + + + +Boys + +Girls +Math +: +▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ +78 +▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ +82 +Reading: +▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ +84 +▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ +79 +Science: +▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ +81 +▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ +77 +Art +: +▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ +89 +▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ +65 +PE +: +▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ +83 +▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ +96 +Music +: +▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ +71 +▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ +88 + + + + diff --git a/docs/assets/barchart-stacked.svg b/docs/assets/barchart-stacked.svg new file mode 100644 index 0000000..20aa0ae --- /dev/null +++ b/docs/assets/barchart-stacked.svg @@ -0,0 +1,49 @@ + + + + + + + + + + + +2007: +▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ +▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ +373.84 +2008: +▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ + +236.23 +2009: + +▇▇▇▇▇ +69.53 +2010: +▇▇▇▇ + +57.21 +2011: +▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ + +519.42 +2012: +▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ + +232.25 +2014: +▇▇ + +50.00 + + + + diff --git a/docs/assets/cal-heatmap.svg b/docs/assets/cal-heatmap.svg new file mode 100644 index 0000000..a967242 --- /dev/null +++ b/docs/assets/cal-heatmap.svg @@ -0,0 +1,73 @@ + + + + + + + + + + + +Jun +Jul +Aug +Sep +Oct +Nov +Dec +Jan +Feb +Mar +Apr +May +Jun +Mon: + +▒▒▒ + + + +Tue: + + +Wed: + + +▒▒ + + + + +▒▒ +Thu: + +▒▒ +▒▒ +▒▓ +Fri: + + + + + +Sat: +▒▒░ + + + + +Sun: + + + + + + + diff --git a/docs/chart-classes.md b/docs/chart-classes.md new file mode 100644 index 0000000..e4f54a0 --- /dev/null +++ b/docs/chart-classes.md @@ -0,0 +1,470 @@ +# Chart Classes API Documentation + +Termgraph provides several chart classes for different visualization needs. All chart classes inherit from the base `Chart` class and work with `Data` and `Args` objects. + +## Base Classes + +### Chart (Abstract Base Class) + +The `Chart` class is the foundation for all chart types. + +```python +from termgraph import Chart, Data, Args + +# Chart is abstract - use specific chart implementations +# chart = Chart(data, args) # Don't do this +``` + +**Constructor Parameters:** +- **data** (Data): Data object containing the values and labels +- **args** (Args): Configuration arguments for the chart + +**Methods:** +- `draw()`: Abstract method implemented by subclasses to render the chart + +### Colors Class + +Provides predefined color constants for chart styling. + +```python +from termgraph import Colors + +# Available colors +Colors.Black # Black color code +Colors.Red # Red color code +Colors.Green # Green color code +Colors.Yellow # Yellow color code +Colors.Blue # Blue color code +Colors.Magenta # Magenta color code +Colors.Cyan # Cyan color code +``` + +## Chart Types + +### BarChart + +Creates horizontal bar charts. Supports both single and multi-series data. + +```python +from termgraph import Data, BarChart, Args + +# Single series bar chart +data = Data([23, 45, 56, 78, 32], ["A", "B", "C", "D", "E"]) +chart = BarChart(data) +chart.draw() +``` + +**Features:** +- Horizontal bars with customizable width +- Multi-series support with categories +- Different scaling options +- Color support +- Value formatting + +#### Single Series Example + +```python +from termgraph import Data, BarChart, Args, Colors + +# Simple bar chart +data = Data( + data=[150, 230, 180, 290, 210], + labels=["Jan", "Feb", "Mar", "Apr", "May"] +) + +args = Args( + width=50, + title="Monthly Sales", + colors=[Colors.Green], + suffix=" units" +) + +chart = BarChart(data, args) +chart.draw() +``` + +Output: +``` +# Monthly Sales + +Jan : ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 150.00 units +Feb : ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 230.00 units +Mar : ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 180.00 units +Apr : ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 290.00 units +May : ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 210.00 units +``` + +#### Multi-Series Example + +```python +from termgraph import Data, BarChart, Args, Colors + +# Multi-series bar chart +data = Data( + data=[ + [120, 80], # Q1: Product A, Product B + [150, 95], # Q2: Product A, Product B + [180, 110], # Q3: Product A, Product B + [200, 125] # Q4: Product A, Product B + ], + labels=["Q1", "Q2", "Q3", "Q4"], + categories=["Product A", "Product B"] +) + +args = Args( + width=40, + title="Quarterly Sales by Product", + colors=[Colors.Blue, Colors.Red], + space_between=True +) + +chart = BarChart(data, args) +chart.draw() +``` + +Output: +``` +# Quarterly Sales by Product + +▇ Product A ▇ Product B + +Q1: ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 120.00 + ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 80.00 + +Q2: ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 150.00 + ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 95.00 + +Q3: ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 180.00 + ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 110.00 + +Q4: ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 200.00 + ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 125.00 +``` + +#### Different Scale Example + +```python +from termgraph import Data, BarChart, Args, Colors + +# Different scales for each series +data = Data( + data=[ + [1200, 45], # Revenue (thousands), Satisfaction (%) + [1500, 52], + [1800, 48], + [2000, 58] + ], + labels=["Q1", "Q2", "Q3", "Q4"], + categories=["Revenue ($K)", "Satisfaction (%)"] +) + +args = Args( + width=30, + different_scale=True, # Each series uses its own scale + colors=[Colors.Green, Colors.Yellow], + title="Revenue vs Customer Satisfaction" +) + +chart = BarChart(data, args) +chart.draw() +``` + +### StackedChart + +Creates stacked bar charts where multiple values are stacked on top of each other. + +```python +from termgraph import Data, StackedChart, Args, Colors + +# Stacked bar chart +data = Data( + data=[ + [30, 20, 10], # Desktop, Mobile, Tablet + [25, 30, 15], + [20, 35, 20], + [15, 40, 25] + ], + labels=["Q1", "Q2", "Q3", "Q4"], + categories=["Desktop", "Mobile", "Tablet"] +) + +args = Args( + width=50, + title="Traffic Sources by Quarter", + colors=[Colors.Blue, Colors.Green, Colors.Yellow] +) + +chart = StackedChart(data, args) +chart.draw() +``` + +Output: +``` +# Traffic Sources by Quarter + +▇ Desktop ▇ Mobile ▇ Tablet + +Q1: ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 60.00 +Q2: ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 70.00 +Q3: ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 75.00 +Q4: ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 80.00 +``` + +### VerticalChart + +Creates vertical bar charts (column charts). + +```python +from termgraph import Data, VerticalChart, Args, Colors + +# Vertical bar chart +data = Data( + data=[23, 45, 56, 78, 32], + labels=["A", "B", "C", "D", "E"] +) + +args = Args( + width=40, + title="Vertical Chart Example", + colors=[Colors.Cyan] +) + +chart = VerticalChart(data, args) +chart.draw() +``` + +Output: +``` +# Vertical Chart Example + + ▇ + ▇ + ▇ ▇ + ▇ ▇ +▇ ▇ ▇ ▇ +▇ ▇ ▇ ▇ +▇ ▇ ▇ ▇ ▇ +▇ ▇ ▇ ▇ ▇ +▇ ▇ ▇ ▇ ▇ +---------------- +23.00 45.00 56.00 78.00 32.00 +---------------- +A B C D E +``` + +### HistogramChart + +Creates histogram charts that bin continuous data into ranges. + +```python +from termgraph import Data, HistogramChart, Args, Colors + +# Histogram chart +# Note: For histograms, data should be the raw values you want to bin +data = Data( + data=[[12.5, 15.3, 18.7, 22.1, 25.6, 28.9, 32.4, 35.8, 38.2, 41.7]], + labels=["Temperature Readings"] +) + +args = Args( + width=40, + bins=5, # Number of bins + title="Temperature Distribution", + colors=[Colors.Red] +) + +chart = HistogramChart(data, args) +chart.draw() +``` + +Output: +``` +# Temperature Distribution + +12.0 – 18.0: ▇▇▇▇▇▇▇▇▇▇▇▇ 3.00 +18.0 – 24.0: ▇▇▇▇▇▇▇▇ 2.00 +24.0 – 30.0: ▇▇▇▇▇▇▇▇ 2.00 +30.0 – 36.0: ▇▇▇▇▇▇▇▇ 2.00 +36.0 – 42.0: ▇▇▇▇▇▇▇▇▇▇▇▇ 3.00 +``` + +## Configuration Options + +All chart classes accept an `Args` object for configuration. See [Args Class Documentation](args-class.md) for complete details. + +### Common Options + +```python +from termgraph import Args, Colors + +args = Args( + width=50, # Chart width in characters + title="My Chart", # Chart title + colors=[Colors.Blue], # Colors for series + suffix=" units", # Value suffix + format="{:<6.1f}", # Value formatting + no_labels=False, # Hide labels + no_values=False, # Hide values + space_between=True # Add space between bars +) +``` + +### Chart-Specific Options + +```python +# Bar chart specific +args = Args( + different_scale=True, # Use different scales for multi-series + label_before=True # Show labels before bars +) + +# Histogram specific +args = Args( + bins=10 # Number of histogram bins +) + +# Vertical chart specific +args = Args( + vertical=True # Enable vertical mode +) +``` + +## Advanced Examples + +### Complex Multi-Series Chart + +```python +from termgraph import Data, BarChart, Args, Colors + +# Sales data across multiple regions and quarters +data = Data( + data=[ + [150, 120, 90], # Q1: North, South, West + [180, 140, 110], # Q2: North, South, West + [200, 160, 130], # Q3: North, South, West + [220, 180, 150] # Q4: North, South, West + ], + labels=["Q1 2023", "Q2 2023", "Q3 2023", "Q4 2023"], + categories=["North Region", "South Region", "West Region"] +) + +args = Args( + width=60, + title="Regional Sales Performance", + colors=[Colors.Blue, Colors.Green, Colors.Yellow], + space_between=True, + suffix="K", + format="{:<5.0f}" +) + +chart = BarChart(data, args) +chart.draw() +``` + +### Percentage Data with Custom Formatting + +```python +from termgraph import Data, BarChart, Args, Colors + +# Percentage completion data +data = Data( + data=[0.65, 0.80, 0.45, 0.92, 0.73], + labels=["Project A", "Project B", "Project C", "Project D", "Project E"] +) + +args = Args( + width=40, + title="Project Completion Status", + colors=[Colors.Green], + percentage=True, # Format as percentages + format="{:<4.0f}", + suffix="%" +) + +chart = BarChart(data, args) +chart.draw() +``` + +### Negative Values Handling + +```python +from termgraph import Data, BarChart, Args, Colors + +# Profit/Loss data with negative values +data = Data( + data=[-50, 30, -20, 80, 45, -15], + labels=["Jan", "Feb", "Mar", "Apr", "May", "Jun"] +) + +args = Args( + width=50, + title="Monthly P&L", + colors=[Colors.Red], # Single color for all bars + suffix="K", + format="{:<+6.0f}" # Show + for positive values +) + +chart = BarChart(data, args) +chart.draw() +``` + +## Error Handling + +Charts will handle common data issues gracefully: + +```python +from termgraph import Data, BarChart + +try: + # Empty data + data = Data([], []) + chart = BarChart(data) + chart.draw() +except Exception as e: + print(f"Error: {e}") + +try: + # Mismatched dimensions + data = Data([10, 20], ["A"]) + chart = BarChart(data) + chart.draw() +except Exception as e: + print(f"Error: {e}") +``` + +## Integration Example + +Complete example showing how to use multiple chart types with the same data: + +```python +from termgraph import Data, BarChart, StackedChart, VerticalChart, Args, Colors + +# Sample data +data = Data( + data=[[30, 45], [25, 50], [40, 35], [35, 40]], + labels=["Q1", "Q2", "Q3", "Q4"], + categories=["Revenue", "Expenses"] +) + +args = Args( + width=40, + title="Financial Data", + colors=[Colors.Green, Colors.Red] +) + +# Different chart types with same data +print("=== Bar Chart ===") +bar_chart = BarChart(data, args) +bar_chart.draw() + +print("\n=== Stacked Chart ===") +stacked_chart = StackedChart(data, args) +stacked_chart.draw() + +print("\n=== Vertical Chart ===") +vertical_chart = VerticalChart(data, args) +vertical_chart.draw() +``` + +For more information about data preparation, see [Data Class Documentation](data-class.md). +For configuration options, see [Args Class Documentation](args-class.md). \ No newline at end of file diff --git a/docs/data-class.md b/docs/data-class.md new file mode 100644 index 0000000..97e6399 --- /dev/null +++ b/docs/data-class.md @@ -0,0 +1,258 @@ +# Data Class API Documentation + +The `Data` class is the core data container for termgraph charts. It handles data validation, normalization, and provides methods for working with both flat and nested data structures. + +## Constructor + +```python +from termgraph import Data + +# Basic usage +data = Data(data=[10, 20, 30, 40], labels=["Q1", "Q2", "Q3", "Q4"]) + +# With categories for multi-series data +data = Data( + data=[[10, 15], [20, 25], [30, 35], [40, 45]], + labels=["Q1", "Q2", "Q3", "Q4"], + categories=["Product A", "Product B"] +) +``` + +### Parameters + +- **data** (list): Required. The numeric data to be graphed. Can be: + - Flat list: `[10, 20, 30, 40]` + - Nested list: `[[10, 15], [20, 25], [30, 35]]` +- **labels** (list[str]): Required. Labels for each data point/row +- **categories** (list[str], optional): Category names for multi-series data + +### Validation + +The constructor validates that: +- Both `data` and `labels` are provided +- Data and labels have the same length +- For nested data, all inner lists have consistent dimensions + +## Methods + +### Data Statistics + +#### `find_min() -> Union[int, float]` +Returns the minimum value in the dataset. + +```python +# Flat data +data = Data([10, 5, 30, 15], ["A", "B", "C", "D"]) +print(data.find_min()) # Output: 5 + +# Nested data +data = Data([[10, 5], [30, 15], [20, 25]], ["X", "Y", "Z"]) +print(data.find_min()) # Output: 5 +``` + +#### `find_max() -> Union[int, float]` +Returns the maximum value in the dataset. + +```python +# Flat data +data = Data([10, 5, 30, 15], ["A", "B", "C", "D"]) +print(data.find_max()) # Output: 30 + +# Nested data +data = Data([[10, 5], [30, 15], [20, 25]], ["X", "Y", "Z"]) +print(data.find_max()) # Output: 30 +``` + +### Label Methods + +#### `find_min_label_length() -> int` +Returns the length of the shortest label. + +```python +data = Data([10, 20, 30], ["A", "BB", "CCC"]) +print(data.find_min_label_length()) # Output: 1 +``` + +#### `find_max_label_length() -> int` +Returns the length of the longest label. + +```python +data = Data([10, 20, 30], ["A", "BB", "CCC"]) +print(data.find_max_label_length()) # Output: 3 +``` + +### Data Normalization + +#### `normalize(width: int) -> list` +Normalizes data values to fit within the specified width for chart rendering. + +```python +# Flat data normalization +data = Data([10, 20, 30, 40], ["Q1", "Q2", "Q3", "Q4"]) +normalized = data.normalize(50) # Normalize to 50 character width +print(normalized) # [12.5, 25.0, 37.5, 50.0] + +# Nested data normalization +data = Data([[10, 15], [20, 25], [30, 35]], ["X", "Y", "Z"]) +normalized = data.normalize(40) +print(normalized) # [[11.43, 17.14], [22.86, 28.57], [34.29, 40.0]] +``` + +The normalize method: +- Handles negative values by offsetting all data +- Scales values proportionally to the target width +- Preserves relative relationships between data points +- Works with both flat and nested data structures + +## Properties + +### `data` +The raw data provided during initialization. + +### `labels` +The labels for each data row/point. + +### `categories` +Category names for multi-series data (empty list if not provided). + +### `dims` +Tuple representing the dimensions of the data structure. + +```python +# Flat data +data = Data([10, 20, 30], ["A", "B", "C"]) +print(data.dims) # (3,) + +# Nested data +data = Data([[10, 15], [20, 25]], ["X", "Y"]) +print(data.dims) # (2, 2) +``` + +## Examples + +### Basic Flat Data + +```python +from termgraph import Data + +# Simple sales data +sales_data = Data( + data=[150, 230, 180, 290, 210], + labels=["Jan", "Feb", "Mar", "Apr", "May"] +) + +print(f"Min sales: {sales_data.find_min()}") # Min sales: 150 +print(f"Max sales: {sales_data.find_max()}") # Max sales: 290 +print(f"Data: {sales_data}") +``` + +### Multi-Series Data with Categories + +```python +from termgraph import Data + +# Quarterly sales for two products +quarterly_data = Data( + data=[ + [120, 80], # Q1: Product A, Product B + [150, 95], # Q2: Product A, Product B + [180, 110], # Q3: Product A, Product B + [200, 125] # Q4: Product A, Product B + ], + labels=["Q1", "Q2", "Q3", "Q4"], + categories=["Product A", "Product B"] +) + +print(f"Dimensions: {quarterly_data.dims}") # (4, 2) +print(f"Categories: {quarterly_data.categories}") +``` + +### Working with Negative Values + +```python +from termgraph import Data + +# Profit/Loss data with negative values +pnl_data = Data( + data=[-50, 30, -20, 80, 45], + labels=["Jan", "Feb", "Mar", "Apr", "May"] +) + +# Normalization handles negative values automatically +normalized = pnl_data.normalize(40) +print(f"Normalized data: {normalized}") +``` + +### Data Validation Examples + +```python +from termgraph import Data + +# These will raise exceptions: +try: + # Missing labels + Data([10, 20, 30]) +except Exception as e: + print(f"Error: {e}") + +try: + # Mismatched dimensions + Data([10, 20], ["A", "B", "C"]) +except Exception as e: + print(f"Error: {e}") + +try: + # Inconsistent nested structure + Data([[10, 20], [30]], ["A", "B"]) +except Exception as e: + print(f"Error: {e}") +``` + +## String Representation + +The `Data` class provides a tabular string representation: + +```python +data = Data( + data=[[100, 150], [200, 250], [300, 350]], + labels=["Product 1", "Product 2", "Product 3"], + categories=["Sales", "Revenue"] +) + +print(data) +``` + +Output: +``` + Labels | Data +-----------|--------------- + Product 1 | (Sales) 100 + | (Revenue) 150 + | + Product 2 | (Sales) 200 + | (Revenue) 250 + | + Product 3 | (Sales) 300 + | (Revenue) 350 + | +``` + +## Integration with Charts + +The `Data` class is designed to work seamlessly with all chart types: + +```python +from termgraph import Data, BarChart, Args + +# Create data +data = Data([45, 32, 78, 56, 23], ["A", "B", "C", "D", "E"]) + +# Create chart with custom arguments +args = Args(width=60, colors=["red"]) +chart = BarChart(data, args) + +# Draw the chart +chart.draw() +``` + +For more examples of using Data with different chart types, see the [Chart Classes Documentation](chart-classes.md). \ No newline at end of file diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..d94a7ef --- /dev/null +++ b/docs/index.md @@ -0,0 +1,112 @@ +# Termgraph API Documentation + +Welcome to the Termgraph Python API documentation. Termgraph is a command-line tool for creating terminal-based charts and graphs, and can also be used as a Python library for programmatic chart generation. + +## Documentation Structure + +### 📖 **[Getting Started](README.md)** +Complete overview with examples and best practices for using termgraph as a Python module. + +### 📊 **[Data Class](data-class.md)** +Learn how to prepare and structure your data for charts: +- Data validation and normalization +- Working with flat and nested data +- Handling categories and labels +- Examples with real-world data + +### 📈 **[Chart Classes](chart-classes.md)** +Comprehensive guide to all available chart types: +- **BarChart** - Horizontal bar charts (single and multi-series) +- **StackedChart** - Stacked bar charts for part-to-whole relationships +- **VerticalChart** - Column charts for time series data +- **HistogramChart** - Distribution charts for continuous data +- **Colors** - Color constants for styling + +### ⚙️ **[Args Class](args-class.md)** +Configuration options for customizing chart appearance and behavior: +- Chart dimensions and layout +- Value formatting and display +- Color schemes and styling +- Chart-specific options + +## Quick Navigation + +### Common Tasks + +| Task | Documentation | +|------|---------------| +| Create a simple bar chart | [README.md - Quick Start](README.md#quick-start) | +| Work with multi-series data | [Data Class - Multi-Series](data-class.md#multi-series-data-with-categories) | +| Customize chart colors | [Args Class - Colors](args-class.md#colors) | +| Format numeric values | [Args Class - Value Formatting](args-class.md#value-formatting) | +| Create stacked charts | [Chart Classes - StackedChart](chart-classes.md#stackedchart) | +| Handle negative values | [Data Class - Negative Values](data-class.md#working-with-negative-values) | + +### Chart Type Selection + +| Data Type | Recommended Chart | Documentation | +|-----------|------------------|---------------| +| Simple categories | BarChart | [Chart Classes - BarChart](chart-classes.md#barchart) | +| Time series | VerticalChart | [Chart Classes - VerticalChart](chart-classes.md#verticalchart) | +| Parts of a whole | StackedChart | [Chart Classes - StackedChart](chart-classes.md#stackedchart) | +| Data distribution | HistogramChart | [Chart Classes - HistogramChart](chart-classes.md#histogramchart) | +| Multi-series comparison | BarChart (multi-series) | [Chart Classes - Multi-Series](chart-classes.md#multi-series-example) | + +### Example Scenarios + +| Scenario | Example Location | +|----------|------------------| +| Financial dashboard | [README.md - Financial Dashboard](README.md#financial-dashboard) | +| Performance metrics | [README.md - Performance Metrics](README.md#performance-metrics-dashboard) | +| Project tracking | [README.md - Project Status](README.md#project-status-tracking) | +| Sales analysis | [README.md - Sales Trend](README.md#sales-trend-analysis) | +| Data validation | [Data Class - Validation](data-class.md#data-validation-examples) | + +## Code Examples by Complexity + +### Beginner +- [Simple bar chart](README.md#simple-bar-chart) +- [Basic styling](README.md#styled-chart) +- [Single series data](data-class.md#basic-flat-data) + +### Intermediate +- [Multi-series charts](chart-classes.md#multi-series-example) +- [Custom formatting](args-class.md#advanced-formatting) +- [Different chart types](README.md#chart-types) + +### Advanced +- [Financial dashboard](README.md#financial-dashboard) +- [Different scales](chart-classes.md#different-scale-example) +- [Complex data structures](data-class.md#multi-series-data-with-categories) + +## API Classes Overview + +```python +from termgraph import Data, BarChart, StackedChart, VerticalChart, HistogramChart, Args, Colors + +# Core workflow +data = Data([10, 20, 30], ["A", "B", "C"]) # Data preparation +args = Args(width=50, colors=[Colors.Blue]) # Configuration +chart = BarChart(data, args) # Chart creation +chart.draw() # Visualization +``` + +## Installation and Setup + +```bash +# Install termgraph +pip install termgraph + +# Basic usage in Python +python -c "from termgraph import Data, BarChart; chart = BarChart(Data([1,2,3], ['A','B','C'])); chart.draw()" +``` + +## Support and Contributing + +- **Issues**: Report bugs and feature requests on [GitHub Issues](https://github.com/mkaz/termgraph/issues) +- **Documentation**: This documentation is maintained alongside the codebase +- **Examples**: Additional examples can be found in the repository's test files + +--- + +**Note**: This documentation covers using termgraph as a Python library. For command-line usage, see the main README in the repository root. \ No newline at end of file diff --git a/termgraph/chart.py b/termgraph/chart.py index 2766842..c5c0317 100644 --- a/termgraph/chart.py +++ b/termgraph/chart.py @@ -223,6 +223,15 @@ def draw(self) -> None: values = self.data.data[i] num_blocks = self.normal_data[i] + # Handle both flat data (numbers) and nested data (lists) + if not isinstance(values, list): + # Flat data: convert single value to list + values = [values] + if isinstance(num_blocks, list): + num_blocks = num_blocks + else: + num_blocks = [num_blocks] + if self.args.get_arg("space_between") and i != 0: print() diff --git a/termgraph/data.py b/termgraph/data.py index 6ee79ea..7fa98cd 100644 --- a/termgraph/data.py +++ b/termgraph/data.py @@ -1,7 +1,7 @@ """Data class for termgraph - handles all data-related operations.""" from __future__ import annotations -from typing import Union +from typing import Union, Optional class Data: @@ -9,17 +9,25 @@ class Data: def __init__( self, - data: list, - labels: list[str], + data: Optional[list] = None, + labels: Optional[list[str]] = None, categories: Union[list[str], None] = None, ): """Initialize data - :labels: The labels of the data :data: The data to graph on the chart + :labels: The labels of the data :categories: The categories of the data + + Can be called with positional or keyword arguments: + - Data([10, 20, 40, 26], ["Q1", "Q2", "Q3", "Q4"]) + - Data(data=[10, 20, 40, 26], labels=["Q1", "Q2", "Q3", "Q4"]) + - Data(labels=["Q1", "Q2", "Q3", "Q4"], data=[10, 20, 40, 26]) """ + if data is None or labels is None: + raise Exception("Both 'data' and 'labels' parameters are required") + if len(data) != len(labels): raise Exception("The dimensions of the data and labels must be the same") @@ -53,11 +61,23 @@ def _find_dims(self, data, labels, dims=None) -> Union[tuple[int], None]: def find_min(self) -> Union[int, float]: """Return the minimum value in sublist of list.""" - return min(value for sublist in self.data for value in sublist) + # Check if data is flat (list of numbers) or nested (list of lists) + is_flat = all(not isinstance(item, list) for item in self.data) + + if is_flat: + return min(self.data) + else: + return min(value for sublist in self.data for value in sublist) def find_max(self) -> Union[int, float]: """Return the maximum value in sublist of list.""" - return max(value for sublist in self.data for value in sublist) + # Check if data is flat (list of numbers) or nested (list of lists) + is_flat = all(not isinstance(item, list) for item in self.data) + + if is_flat: + return max(self.data) + else: + return max(value for sublist in self.data for value in sublist) def find_min_label_length(self) -> int: """Return the minimum length for the labels.""" @@ -121,33 +141,52 @@ def __str__(self): def normalize(self, width: int) -> list: """Normalize the data and return it.""" - # We offset by the minimum if there's a negative. - data_offset = [] - min_datum = min(value for sublist in self.data for value in sublist) - if min_datum < 0: - min_datum = abs(min_datum) - for datum in self.data: - data_offset.append([d + min_datum for d in datum]) + # Check if data is flat (list of numbers) or nested (list of lists) + is_flat = all(not isinstance(item, list) for item in self.data) + + if is_flat: + # Handle flat list data + min_datum = min(self.data) + if min_datum < 0: + min_datum = abs(min_datum) + data_offset = [d + min_datum for d in self.data] + else: + data_offset = self.data + + min_datum = min(data_offset) + max_datum = max(data_offset) + + if min_datum == max_datum: + return data_offset + + norm_factor = width / float(max_datum) + return [v * norm_factor for v in data_offset] else: - data_offset = self.data - min_datum = min(value for sublist in data_offset for value in sublist) - max_datum = max(value for sublist in data_offset for value in sublist) + # Handle nested list data (original logic) + data_offset = [] + min_datum = min(value for sublist in self.data for value in sublist) + if min_datum < 0: + min_datum = abs(min_datum) + for datum in self.data: + data_offset.append([d + min_datum for d in datum]) + else: + data_offset = self.data + min_datum = min(value for sublist in data_offset for value in sublist) + max_datum = max(value for sublist in data_offset for value in sublist) - if min_datum == max_datum: - return data_offset + if min_datum == max_datum: + return data_offset - # max_dat / width is the value for a single tick. norm_factor is the - # inverse of this value - # If you divide a number to the value of single tick, you will find how - # many ticks it does contain basically. - norm_factor = width / float(max_datum) - normal_data = [] - for datum in data_offset: - normal_data.append([v * norm_factor for v in datum]) + # max_dat / width is the value for a single tick. norm_factor is the + # inverse of this value + # If you divide a number to the value of single tick, you will find how + # many ticks it does contain basically. + norm_factor = width / float(max_datum) + normal_data = [] + for datum in data_offset: + normal_data.append([v * norm_factor for v in datum]) - return normal_data + return normal_data def __repr__(self): return f"Data(data={self.data if len(str(self.data)) < 25 else str(self.data)[:25] + '...'}, labels={self.labels}, categories={self.categories})" - - diff --git a/termgraph/termgraph.py b/termgraph/termgraph.py index f67213f..9af1f3e 100644 --- a/termgraph/termgraph.py +++ b/termgraph/termgraph.py @@ -17,8 +17,12 @@ init() +# DEPRECATED: Use Data.normalize() directly instead def normalize(data: list, width: int) -> list: - """Normalize the data and return it.""" + """Normalize the data and return it. + + DEPRECATED: This function is deprecated. Use Data(data, labels).normalize(width) directly. + """ # Create a temporary Data object and use its normalize method temp_data = Data(data, [f"label_{i}" for i in range(len(data))]) return temp_data.normalize(width) @@ -133,16 +137,16 @@ def chart(colors: list, data: list, args: dict, labels: list) -> None: chart_args_dict = dict(args) if "color" in chart_args_dict: chart_args_dict["colors"] = chart_args_dict.pop("color") - + # Remove CLI-specific args that don't belong in chart Args cli_only_args = ["filename", "delim", "verbose", "version"] for cli_arg in cli_only_args: chart_args_dict.pop(cli_arg, None) - + chart_args = Args(**chart_args_dict) if colors: chart_args.update_args(colors=colors) - + # Create Data object data_obj = Data(data, labels) @@ -156,7 +160,7 @@ def chart(colors: list, data: list, args: dict, labels: list) -> None: chart_obj = VerticalChart(data_obj, chart_args) else: chart_obj = BarChart(data_obj, chart_args) - + chart_obj.draw() @@ -246,7 +250,7 @@ def read_data(args: dict) -> tuple[list, list, list, list]: Data are inserted to a list of lists due to the categories. i.e. - labels = ['2001', '2002', '2003', ...] + labels = ['2001', '2002', '2003', ...] categories = ['boys', 'girls'] data = [ [20.4, 40.5], [30.7, 100.0], ...]""" @@ -331,7 +335,7 @@ def _label_row(row: list[str], delim: str) -> _LabeledRow: data.append(datum) labelling = False else: - raise ValueError("Multiple labels not allowed: {labels}, {text}") + raise ValueError(f"Multiple labels not allowed: {labels}, {text}") if labels: label = delim.join(labels) @@ -427,4 +431,4 @@ def calendar_heatmap(data: dict, labels: list, args: dict) -> None: if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/tests/module-test3.py b/tests/module-test3.py new file mode 100644 index 0000000..0951dde --- /dev/null +++ b/tests/module-test3.py @@ -0,0 +1,19 @@ +from termgraph import Data, Args, BarChart + +# Create data +data = Data( + labels=["Q1", "Q2", "Q3", "Q4"], + data=[10, 20, 40, 26], +) + +# Configure chart options +args = Args( + title="Quarterly Sales", + width=50, + format="{:.0f}", + suffix="K" +) + +# Create and display chart +chart = BarChart(data, args) +chart.draw()