added tikz and docs

This commit is contained in:
Stephen Mildenhall
2025-03-09 08:19:25 +00:00
parent 09cdf82398
commit d399e56e61
6 changed files with 203 additions and 61 deletions
+8
View File
@@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
+20
View File
@@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+55
View File
@@ -0,0 +1,55 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- Project information -----------------------------------------------------
project = 'greater_tables'
copyright = '2025, Stephen J Mildenhall'
author = 'Stephen J Mildenhall'
# The full version, including alpha/beta/rc tags
release = '1.0.0'
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
+20
View File
@@ -0,0 +1,20 @@
.. greater_tables documentation master file, created by
sphinx-quickstart on Sun Mar 9 08:18:13 2025.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to greater_tables's documentation!
==========================================
.. toctree::
:maxdepth: 2
:caption: Contents:
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
+35
View File
@@ -0,0 +1,35 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.https://www.sphinx-doc.org/
exit /b 1
)
if "%1" == "" goto help
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd
+62 -58
View File
@@ -20,11 +20,11 @@ if logger.hasHandlers():
# Clear existing handlers
logger.handlers.clear()
# SET DEGBUUGER LEVEL
LEVEL = logging.DEBUG # DEBUG or INFO, WARNING, ERROR, CRITICAL
LEVEL = logging.ERROR # DEBUG or INFO, WARNING, ERROR, CRITICAL
logger.setLevel(LEVEL)
handler = logging.StreamHandler(sys.stderr)
handler.setLevel(LEVEL)
formatter = logging.Formatter('%(asctime)s | %(levelname)s | %(funcName)-15s | %(message)s', datefmt='%H:%M:%S.%f')
formatter = logging.Formatter('%(asctime)s | %(levelname)s | %(funcName)-15s | %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.info('Logger Setup; module recompiled.')
@@ -39,7 +39,7 @@ class GT(object):
date_default_str='{x:%Y-%m-%d}',
ratio_cols=None, ratio_default_str='{x:.1%}',
cast_to_floats=True, hrule_widths=None,
index_names=True,
index_names=False,
pef_precision=3, pef_lower=-3, pef_upper=6, font_size=0.9):
"""
Create a greater_tables formatting object, setting defaults.
@@ -56,7 +56,7 @@ class GT(object):
:param formatters: None or dict type -> format function to override defaults.
"""
self.caption = caption
self.df = df.copy() # the object being formatted
self.df = df.copy(deep=True) # the object being formatted
self.df_id = f'T{id(df):x}'[::2].upper()
# set defaults
@@ -80,12 +80,12 @@ class GT(object):
self.ratio_cols = [ratio_cols]
if cast_to_floats:
for i, c in enumerate(df.columns):
old_type = df.dtypes[c]
if not np.any((is_integer_dtype(df.iloc[:, i]),
is_datetime64_any_dtype(df.iloc[:, i]))):
for i, c in enumerate(self.df.columns):
old_type = self.df.dtypes[c]
if not np.any((is_integer_dtype(self.df.iloc[:, i]),
is_datetime64_any_dtype(self.df.iloc[:, i]))):
try:
df.iloc[:, i] = df.iloc[:, i].astype(float)
self.df.iloc[:, i] = self.df.iloc[:, i].astype(float)
logger.debug(f'coerce {i}={c} from {old_type} to float')
except ValueError:
logger.debug(f'coercing {i}={c} from {old_type} to float FAILED')
@@ -96,20 +96,20 @@ class GT(object):
self.date_col_indices = []
self.object_col_indices = []
# manage non-unique col names here
logger.info('FIGURING TYPES')
logger.debug('FIGURING TYPES')
for i in range(self.df.shape[1]):
ser = df.iloc[:, i]
ser = self.df.iloc[:, i]
if is_datetime64_any_dtype(ser):
logger.info(f'col {i} = {self.df.columns[i]} is DATE')
logger.debug(f'col {i} = {self.df.columns[i]} is DATE')
self.date_col_indices.append(i)
elif is_integer_dtype(ser):
logger.info(f'col {i} = {self.df.columns[i]} is INTEGER')
logger.debug(f'col {i} = {self.df.columns[i]} is INTEGER')
self.integer_col_indices.append(i)
elif is_float_dtype(ser):
logger.info(f'col {i} = {self.df.columns[i]} is FLOAT')
logger.debug(f'col {i} = {self.df.columns[i]} is FLOAT')
self.float_col_indices.append(i)
else:
logger.info(f'col {i} = {self.df.columns[i]} is OBJECT')
logger.debug(f'col {i} = {self.df.columns[i]} is OBJECT')
self.object_col_indices.append(i)
self.integer_default_str = integer_default_str
@@ -168,7 +168,7 @@ class GT(object):
else:
# TODo BEEF UP?
return self.float_default_str.format(x=f)
except ValueError:
except (TypeError, ValueError):
return str(x)
def pef(self, x):
@@ -214,7 +214,7 @@ class GT(object):
return f'{x:,.0f}.'
else:
return fmt.format(x=x)
except ValueError:
except (ValueError, TypeError):
return str(x)
return ff
@@ -246,13 +246,14 @@ class GT(object):
self._df_formatters.append(self.make_float_formatter(self.df.iloc[:, i]))
else:
# print(f'{i} default')
self._df_formatters.append(self.default)
self._df_formatters.append(self.default_formatter)
# self._df_formatters is now a list of length equal to cols in df
if len(self._df_formatters) != self.df.shape[1]:
raise ValueError(f'Something wrong: {len(self._df_formatters)=} != {self.df.shape=}')
return self._df_formatters
def __repr__(self):
"""Basic representation."""
return f"GreaterTable wrapping df {len(self.df)} rows, id {self.df_id}"
def _repr_html_(self):
@@ -267,6 +268,7 @@ class GT(object):
ncols = self.df.shape[1]
dt = self.df.dtypes
# call pandas built-in html converter
html = self.df.to_html(table_id=self.df_id, formatters=self.df_formatters, index_names=self.index_names)
# TODO USE SELF.ALIGNERS
@@ -284,99 +286,98 @@ class GT(object):
else:
hrule_widths = (0, 0, 0)
# start to build style
df_id = self.df_id # shorter
style = []
style.append('<style>')
style.append(f'''
# {df_id} {{
#{self.df_id} {{
border-collapse: collapse;
font-family: "Roboto", "Open Sans Condensed", "Arial", 'Segoe UI', sans-serif;
font-size: {self.font_size}em;
width: auto;
overflow: auto; }}
# {df_id} thead {{
#{self.df_id} thead {{
border-top: 2px solid #000;
border-bottom: 2px solid #000;
}}
# {df_id} tbody {{
#{self.df_id} tbody {{
border-bottom: 2px solid #000;
}}
# {df_id} thead tr:nth-of-type({ncolumns+1}) th:nth-of-type(-n + {nindex - 1}) {{
#{self.df_id} thead tr:nth-of-type({ncolumns+1}) th:nth-of-type(-n + {nindex - 1}) {{
border-right: 0.5px solid #000;
}}
# {df_id} thead th:nth-of-type({nindex+1}) {{
#{self.df_id} thead th:nth-of-type({nindex+1}) {{
border-left: 1.5px solid #000;
border-bottom: 0.5px solid #000;
}}
# {df_id} thead th:nth-of-type(n+{nindex+2}) {{
#{self.df_id} thead th:nth-of-type(n+{nindex+2}) {{
border-left: 0.5px solid #000;
border-bottom: 0.5px solid #000;
}}
# {df_id} thead th:nth-of-type({nindex}) {{
#{self.df_id} thead th:nth-of-type({nindex}) {{
border-bottom: 0.5px solid #000;
}}
# {df_id} tbody tr th:nth-of-type(-n + {nindex - 1}) {{
#{self.df_id} tbody tr th:nth-of-type(-n + {nindex - 1}) {{
border-right: 0.5px solid #000;
}}
/*
# {df_id} tbody tr th {{
#{self.df_id} tbody tr th {{
border-bottom: 0.5px solid #00f;
}}
# {df_id} tbody tr td {{
#{self.df_id} tbody tr td {{
border-bottom: 0.5px solid #f00;
}}
# {df_id} tbody td {{
#{self.df_id} tbody td {{
padding-left: 10px;
padding-right: 10px;
}}
# {df_id} tbody tr td:nth-of-type(1) {{
#{self.df_id} tbody tr td:nth-of-type(1) {{
border-left: 1.5px solid #000;
}}
*/
# T6F2FE tbody td:nth-of-type({nindex + 1}) {{
#{self.df_id} tbody td:nth-of-type({nindex + 1}) {{
border-left: 1.5px solid #000;
}}
# {df_id} tbody tr td:nth-of-type(n + 2) {{
#{self.df_id} tbody tr td:nth-of-type(n + 2) {{
border-left: 0.5px solid #000;
}}
# {df_id} tbody th {{
#{self.df_id} tbody th {{
vertical-align: top;
}}
# {df_id} caption {{
#{self.df_id} caption {{
padding-top: 10px;
padding-bottom: 4px;
font-size: 1.1em;
text-align: left;
/* font-weight: bold; */
/* color: #f0f; */
font-weight: bold;
caption-side: top;
}}
# {df_id} .gt_body_column {{
#{self.df_id} .gt_body_column {{
border-left: 1px solid #000;
}}
# {df_id} .gt_ruled_row_0 {{
#{self.df_id} .gt_ruled_row_0 {{
border-top: {hrule_widths[0]}px solid #000;
}}
# {df_id} .gt_ruled_row_1 {{
#{self.df_id} .gt_ruled_row_1 {{
border-top: {hrule_widths[0]}px solid #444;
}}
# {df_id} .gt_ruled_row_2 {{
#{self.df_id} .gt_ruled_row_2 {{
border-top: {hrule_widths[0]}px solid #888;
}}
# {df_id} .gt_left {{
#{self.df_id} .gt_left {{
text-align: left;
}}
# {df_id} .gt_center {{
#{self.df_id} .gt_center {{
text-align: center;
}}
# {df_id} .gt_right {{
#{self.df_id} .gt_right {{
text-align: right;
font-variant-numeric: tabular-nums;
}}
# {df_id} .gt_head {{
#{self.df_id} .gt_head {{
/* font-family: "Times New Roman", 'Courier New'; */
font-size: {self.font_size}em;
}}
# {df_id} td, th {{
#{self.df_id} td, th {{
/* top, right, bottom left */
padding: 2px 10px 2px 10px;
}}
@@ -384,6 +385,8 @@ class GT(object):
# ================================================
style.append('</style>\n')
# OK
if len(style) > 2:
style = '\n'.join(style)
else:
@@ -466,7 +469,8 @@ class GT(object):
html = soup.prettify()
self.df_html = html # after alteration
# return
logger.debug('CREATED HTML STYLE')
logger.info('CREATED HTML STYLE')
return style + html
@property
@@ -475,7 +479,7 @@ class GT(object):
def _repr_latex_(self):
"""Generate a LaTeX tabular representation."""
logger.debug('CREATED LATEX STYLE')
logger.info('CREATED LATEX STYLE')
# latex = self.df.to_latex(caption=self.caption, formatters=self._df_formatters)
latex = self.df_to_tikz()
return latex
@@ -486,7 +490,8 @@ class GT(object):
Very ingenious GTP code with some SM enhancements.
"""
idx = self.df.index
# otherwise you alter the actual index
idx = self.df.index.copy()
idx.names = [i for i in range(idx.nlevels)]
# Determine at which level the index changes
index_df = idx.to_frame(index=False) # Convert MultiIndex to a DataFrame
@@ -570,9 +575,10 @@ class GT(object):
# local variable
df = self.df
# \\begin{{{figure}}}{latex}
header = """
\\begin{{{figure}}}{latex}
\\centering
\\footnotesize
{extra_defs}
\\begin{{tikzpicture}}[
auto,
@@ -591,9 +597,9 @@ class GT(object):
{post_process}
\\end{{tikzpicture}}
{caption}
\\end{{{figure}}}
"""
# {caption}
# \\end{{{figure}}}
# make a safe float format
if float_format is None:
@@ -634,9 +640,6 @@ class GT(object):
else:
nc_index = 0
# this is handled by the caller
# df = df.astype(float, errors='ignore')
if nc_index:
if vrule is None:
vrule = set()
@@ -659,6 +662,7 @@ class GT(object):
scale=scale, equal=equal)
# print(colw, tabs)
logger.debug(f'tabs: {tabs}')
logger.debug(f'colw: {colw}')
# alignment dictionaries
ad = {'l': 'left', 'r': 'right', 'c': 'center'}
@@ -1093,13 +1097,13 @@ class GT(object):
ans = ef(x)
return ans
except ValueError as e:
logger.error(f'ValueError {e}')
logger.debug(f'ValueError {e}')
return str(x)
except TypeError as e:
logger.error(f'TypeError {e}')
logger.debug(f'TypeError {e}')
return str(x)
except AttributeError as e:
logger.error(f'AttributeError {e}')
logger.debug(f'AttributeError {e}')
return str(x)
# class GT_ORIGINAL(object):