diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8fb20e7..d63326e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,11 +1,11 @@ repos: - repo: https://github.com/PyCQA/isort - rev: 5.9.3 + rev: 5.10.1 hooks: - id: isort - repo: https://github.com/psf/black - rev: 21.9b0 + rev: 21.12b0 hooks: - id: black language_version: python3 diff --git a/README.md b/README.md index 5b7294c..1d7c052 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,14 @@ -# termplotlib +
Plotting on the command line.
+ [](https://pypi.org/project/termplotlib) [](https://pypi.org/pypi/termplotlib/) [](https://github.com/nschloe/termplotlib) -[](https://pypistats.org/packages/termplotlib) +[](https://pepy.tech/project/termplotlib) + + [](https://github.com/nschloe/termplotlib/actions?query=workflow%3Aci) [](https://codecov.io/gh/nschloe/termplotlib) diff --git a/setup.cfg b/setup.cfg index d1c3c8c..72b94d8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = termplotlib -version = 0.3.9 +version = 0.3.10 author = Nico Schlömer author_email = nico.schloemer@gmail.com description = Python plotting for the command line @@ -37,9 +37,11 @@ keywords = package_dir = =src packages = find: -install_requires = - numpy >= 1.20 python_requires = >=3.7 [options.packages.find] where=src + +[options.extras_require] +all = + numpy >= 1.20 diff --git a/src/termplotlib/barh.py b/src/termplotlib/barh.py index 576aa4f..38d1ee2 100644 --- a/src/termplotlib/barh.py +++ b/src/termplotlib/barh.py @@ -1,23 +1,20 @@ -import decimal -from typing import List, Optional +from __future__ import annotations -import numpy as np -from numpy.typing import ArrayLike +import decimal +import numbers from .helpers import is_unicode_standard_output def barh( - vals: List[int], - labels: Optional[List[str]] = None, + vals: list[int], + labels: list[str] | None = None, max_width: int = 40, - bar_width: int = 1, show_vals: bool = True, - val_format: Optional[str] = None, + val_format: str | None = None, force_ascii: bool = False, ): partition = _get_partition(vals, max_width) - partition = np.repeat(partition, bar_width, axis=1) if is_unicode_standard_output() and not force_ascii: chars = [" ", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█"] @@ -33,16 +30,16 @@ def barh( if show_vals: if val_format is not None: cfmt = val_format - elif np.issubdtype(np.asarray(vals).dtype, float): + elif all(isinstance(val, numbers.Integral) for val in vals): + max_len = max(len(str(val)) for val in vals) + cfmt = f"{{:{max_len}d}}" + elif all(isinstance(val, numbers.Real) for val in vals): # find max decimal length # https://stackoverflow.com/a/6190291/353337 num_digits = max( -decimal.Decimal(str(val)).as_tuple().exponent for val in vals ) cfmt = f"{{:.{num_digits}f}}" - elif np.issubdtype(np.asarray(vals).dtype, np.integer): - max_len = max(len(str(val)) for val in vals) - cfmt = f"{{:{max_len}d}}" else: cfmt = "{}" fmt.append("[" + cfmt + "]") @@ -51,9 +48,7 @@ def barh( fmt = " ".join(fmt) out = [] - for k, (val, num_full, remainder) in enumerate( - zip(vals, partition[0], partition[1]) - ): + for k, (val, num_full, remainder) in enumerate(zip(vals, *partition)): data = [] if labels is not None: data.append(str(labels[k])) @@ -67,12 +62,11 @@ def barh( return out -def _get_partition(values: ArrayLike, max_size: int): - values = np.asarray(values) - assert np.all(values >= 0) - maxval = np.max(values) +def _get_partition(values: list[int], max_size: int) -> tuple[list[int], list[int]]: + assert all(val >= 0 for val in values) + maxval = max(values) if maxval == 0: maxval = 1 - eighths = np.around(values / maxval * max_size * 8).astype(int) - return np.array([eighths // 8, eighths % 8]) + eighths = [round(val / maxval * max_size * 8) for val in values] + return [val // 8 for val in eighths], [val % 8 for val in eighths] diff --git a/src/termplotlib/figure.py b/src/termplotlib/figure.py index 731d578..40d3649 100644 --- a/src/termplotlib/figure.py +++ b/src/termplotlib/figure.py @@ -1,4 +1,4 @@ -from typing import Optional +from __future__ import annotations from .barh import barh from .helpers import create_padding_tuple @@ -11,13 +11,13 @@ def figure(*args, **kwargs): class Figure: - def __init__(self, width: Optional[int] = None, padding: int = 0): + def __init__(self, width: int | None = None, padding: int = 0): self._content = [] self._width = width self._subfigures = None self._padding = create_padding_tuple(padding) - def __rich_console__(self, *args): + def __rich_console__(self, *_): yield self.get_string() def aprint(self, string): diff --git a/src/termplotlib/helpers.py b/src/termplotlib/helpers.py index 4b67fd0..7ab884e 100644 --- a/src/termplotlib/helpers.py +++ b/src/termplotlib/helpers.py @@ -1,10 +1,11 @@ +from __future__ import annotations + import re import subprocess import sys -from typing import List, Tuple, Union -def create_padding_tuple(padding: Union[int, List[int], Tuple[int, int]]): +def create_padding_tuple(padding: int | list[int] | tuple[int, int]): # self._padding is a 4-tuple: top, right, bottom, left (just like CSS) if isinstance(padding, int): out = (padding, padding, padding, padding) diff --git a/src/termplotlib/hist.py b/src/termplotlib/hist.py index 6089019..d0c9641 100644 --- a/src/termplotlib/hist.py +++ b/src/termplotlib/hist.py @@ -1,14 +1,12 @@ -from typing import List, Optional - -import numpy as np +from __future__ import annotations from .barh import _get_partition, barh from .helpers import is_unicode_standard_output def hist( - counts: List[int], - bin_edges: List[float], + counts: list[int], + bin_edges: list[float], orientation: str = "vertical", max_width: int = 40, grid=None, @@ -30,16 +28,14 @@ def hist( counts, bin_edges, max_width=max_width, - bar_width=bar_width, force_ascii=force_ascii, ) def hist_horizontal( - counts: List[int], - bin_edges: List[float], + counts: list[int], + bin_edges: list[float], max_width: int = 40, - bar_width: int = 1, show_bin_edges: bool = True, show_counts: bool = True, force_ascii: bool = False, @@ -56,24 +52,26 @@ def hist_horizontal( counts, labels=labels, max_width=max_width, - bar_width=bar_width, show_vals=show_counts, force_ascii=force_ascii, ) def hist_vertical( - counts: List[int], + counts: list[int], max_height: int = 10, bar_width: int = 2, strip: bool = False, - xgrid: Optional[List[int]] = None, + xgrid: list[int] | None = None, force_ascii: bool = False, ): + import numpy as np + if xgrid is None: xgrid = [] partition = _get_partition(counts, max_height) + partition = np.asarray(partition) if strip: # Cut off leading and trailing rows of 0 @@ -112,9 +110,7 @@ def hist_vertical( return out -def _get_matrix_of_eighths( - nums_full_blocks, remainders, max_size, bar_width: int -) -> np.ndarray: +def _get_matrix_of_eighths(nums_full_blocks, remainders, max_size: int, bar_width: int): """ Returns a matrix of integers between 0-8 encoding bar lengths in histogram. @@ -122,6 +118,8 @@ def _get_matrix_of_eighths( that the first 3 segments should be graphed with full blocks, the 4th block should be 3/8ths full, and that the rest of the bar should be empty. """ + import numpy as np + matrix = np.zeros((len(nums_full_blocks), max_size), dtype=int) for row, num_full_blocks, remainder in zip(matrix, nums_full_blocks, remainders): diff --git a/src/termplotlib/plot.py b/src/termplotlib/plot.py index 0f46b87..e6bbb57 100644 --- a/src/termplotlib/plot.py +++ b/src/termplotlib/plot.py @@ -1,21 +1,22 @@ +from __future__ import annotations + import subprocess -from typing import List, Optional, Tuple def plot( - x: List[float], - y: List[float], + x: list[float], + y: list[float], width: int = 80, height: int = 25, - label: Optional[str] = None, - xlim: Optional[Tuple[float, float]] = None, - ylim: Optional[Tuple[float, float]] = None, - xlabel: Optional[str] = None, - title: Optional[str] = None, - extra_gnuplot_arguments: Optional[List[str]] = None, + label: str | None = None, + xlim: tuple[float, float] | None = None, + ylim: tuple[float, float] | None = None, + xlabel: str | None = None, + title: str | None = None, + extra_gnuplot_arguments: list[str] | None = None, plot_command: str = "plot '-' w lines", ticks_scale: int = 0, -): +) -> list[str]: p = subprocess.Popen( ["gnuplot"], stdout=subprocess.PIPE, @@ -62,5 +63,5 @@ def plot( return _remove_empty_lines(out.decode()) -def _remove_empty_lines(string: str): +def _remove_empty_lines(string: str) -> list[str]: return string.split("\n")[1:-2] diff --git a/src/termplotlib/subplot.py b/src/termplotlib/subplot.py index 812d78d..ee4a865 100644 --- a/src/termplotlib/subplot.py +++ b/src/termplotlib/subplot.py @@ -1,4 +1,4 @@ -from typing import List, Optional, Tuple, Union +from __future__ import annotations from .figure import Figure @@ -11,10 +11,10 @@ class SubplotGrid: def __init__( self, layout, - width: Optional[int] = None, - column_widths: Optional[List[int]] = None, + width: int | None = None, + column_widths: list[int] | None = None, border_style: str = "thin", - padding: Union[int, List[int], Tuple[int, int]] = (1, 2), + padding: int | list[int] | tuple[int, int] = (1, 2), ): assert ( len(layout) == 2 diff --git a/tox.ini b/tox.ini index d3aef74..42fdd9b 100644 --- a/tox.ini +++ b/tox.ini @@ -4,9 +4,9 @@ isolated_build = True [testenv] deps = - numpy pytest - pytest-cov pytest-codeblocks + pytest-cov +extras = all commands = pytest {posargs} --codeblocks