mirror of
https://github.com/wassname/greater_tables_project.git
synced 2026-09-12 12:22:43 +08:00
table float format; tabs; markdown input
This commit is contained in:
@@ -7,6 +7,8 @@ __pycache__/
|
||||
*.so
|
||||
.idea/*
|
||||
tests/tables_files/*
|
||||
tests/quarto_doc/*
|
||||
tests/*.quarto_ipynb
|
||||
tests/.*
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
.. image:: https://img.shields.io/readthedocs/greater_tables_project
|
||||
:alt: Read the Docs
|
||||
|
||||
Release Notes
|
||||
===============
|
||||
|
||||
1.0.0
|
||||
------
|
||||
|
||||
* Allow input via list of lists, or markdown table
|
||||
* Specify overall float format for whole table
|
||||
* Specify column alingment with 'llrc' style string
|
||||
* ``show_index`` option
|
||||
* Added more tests
|
||||
* Docs updated
|
||||
* Set tabs for width; use of width in HTML format.
|
||||
|
||||
|
||||
0.6.0
|
||||
------
|
||||
|
||||
* Initial release
|
||||
|
||||
Early development
|
||||
-------------------
|
||||
|
||||
* 0.1.0 - 0.5.0: Early development
|
||||
* tikz code from great.pres_manager
|
||||
|
||||
TODO
|
||||
=====
|
||||
|
||||
* Index aligners
|
||||
|
||||
|
||||
https://shields.io/badges/read-the-docs
|
||||
@@ -1,4 +1,4 @@
|
||||
__version__ = '0.6.0'
|
||||
__version__ = '1.0.0'
|
||||
__project__ = 'greater_tables'
|
||||
__author__ = 'Stephen J Mildenhall'
|
||||
|
||||
|
||||
+321
-134
@@ -49,10 +49,12 @@ class GT(object):
|
||||
aligners=None,
|
||||
ratio_cols=None,
|
||||
year_cols=None,
|
||||
show_index=True,
|
||||
default_integer_str='{x:,d}',
|
||||
default_float_str='{x:,.3f}',
|
||||
default_date_str='%Y-%m-%d',
|
||||
default_ratio_str='{x:.1%}',
|
||||
table_float_format=None,
|
||||
table_hrule_width=1,
|
||||
table_vrule_width=1,
|
||||
hrule_widths=None,
|
||||
@@ -69,6 +71,9 @@ class GT(object):
|
||||
pef_lower=-3,
|
||||
pef_upper=6,
|
||||
cast_to_floats=True,
|
||||
header_row=True,
|
||||
tabs=None,
|
||||
equal=False,
|
||||
debug=False):
|
||||
"""
|
||||
Create a greater_tables formatting object.
|
||||
@@ -79,15 +84,17 @@ class GT(object):
|
||||
Recommended usage is to derive from GT and set defaults suitable to your particular application.
|
||||
In that way you can maintain a "house-style"
|
||||
|
||||
:param df: target DataFrame
|
||||
:param df: target DataFrame or list of lists or markdown table string
|
||||
:param caption: table caption, optional
|
||||
:param aligners: None or dict (type or colname) -> left | center | right
|
||||
:param ratio_cols: None, or "all" or list of column names treated as ratios. Set defaults in derived class suitable to application.
|
||||
:param year_cols: None, or "all" or list of column names treated as years (no commas, no decimals). Set defaults in derived class suitable to application.
|
||||
:param show_index: if True, show the index columns, default True
|
||||
:param default_integer_str: format f-string for integers, default '{x:,d}'
|
||||
:param default_float_str: format f-string for floats, default '{x:,.3f}'
|
||||
:param default_date_str: format f-string for dates, default '%Y-%m-%d'. NOTE: no braces or x!
|
||||
:param default_ratio_str: format f-string for ratios, default '{x:.1%}'
|
||||
:param table_float_format: None or format string for floats in the table format function, applied to entire table, default None
|
||||
:param cast_to_floats: if True, try to cast all non-integer, non-date columns to floats
|
||||
:param table_hrule_width: width of the table top, botton and header hrule, default 1
|
||||
:param table_vrule_width: width of the table vrule, separating the index from the body, default 1
|
||||
@@ -104,8 +111,32 @@ class GT(object):
|
||||
:param pef_precision: precision (digits after period) for pandas engineering format, default 3.
|
||||
:param pef_lower: apply engineering format to floats with absolute value < 10**pef_lower; default -3.
|
||||
:param pef_upper: apply engineering format to floats with absolute value > 10**pef_upper; default 6.
|
||||
:param header_row: True: use first row as headers; False no headings. Default True
|
||||
:param tabs: None or list of column widths in characters or a common int or float width. (It is converted into em; one character is about 0.5em on average; digits are exactly 0.5em.) If None, will be calculated. Default None.
|
||||
:param equal: if True, set all column widths equal. Default False.
|
||||
:param debug: if True, add id to caption and use colored lines in table, default False.
|
||||
"""
|
||||
# deal with alternative input modes
|
||||
if isinstance(df, pd.DataFrame):
|
||||
# usual use case
|
||||
pass
|
||||
elif isinstance(df, pd.Series):
|
||||
df = df.to_frame()
|
||||
elif isinstance(df, list):
|
||||
df = pd.DataFrame(df)
|
||||
# override this selection come what may
|
||||
show_index = False
|
||||
if header_row:
|
||||
# Set first row as column names
|
||||
df.columns = df.iloc[0]
|
||||
# Drop first row and reset index
|
||||
df = df[1:].reset_index(drop=True)
|
||||
elif isinstance(df, str):
|
||||
df, aligners = GT.md_to_df(df)
|
||||
show_index = False
|
||||
else:
|
||||
raise ValueError('df must be a DataFrame, a list of lists, or a markdown table string')
|
||||
|
||||
if not df.columns.is_unique:
|
||||
raise ValueError('df column names are not unique')
|
||||
self.df = df.copy(deep=True) # the object being formatted
|
||||
@@ -118,14 +149,16 @@ class GT(object):
|
||||
self.caption = caption + (' (id: ' + self.df_id + ')' if self.debug else '')
|
||||
|
||||
# before messing
|
||||
self.nindex = self.df.index.nlevels
|
||||
self.show_index = show_index
|
||||
self.nindex = self.df.index.nlevels if self.show_index else 0
|
||||
self.ncolumns = self.df.columns.nlevels
|
||||
self.ncols = self.df.shape[1]
|
||||
self.dt = self.df.dtypes
|
||||
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore", category=pd.errors.PerformanceWarning)
|
||||
self.df = self.df.reset_index(drop=False, col_level=self.df.columns.nlevels - 1)
|
||||
if self.show_index:
|
||||
warnings.simplefilter("ignore", category=pd.errors.PerformanceWarning)
|
||||
self.df = self.df.reset_index(drop=False, col_level=self.df.columns.nlevels - 1)
|
||||
# want the new index to be ints - that is not default if old was multiindex
|
||||
self.df.index = np.arange(self.df.shape[0], dtype=int)
|
||||
self.index_change_level = GT.changed_column(self.df.iloc[:, :self.nindex])
|
||||
@@ -203,16 +236,20 @@ class GT(object):
|
||||
if aligners is None:
|
||||
# not using
|
||||
aligners = []
|
||||
elif isinstance(aligners, str):
|
||||
# lrc for each column
|
||||
aligners = {c: a for c, a in zip(self.df.columns, aligners)}
|
||||
self.df_aligners = []
|
||||
|
||||
lrc = {'l': 'grt-left', 'r': 'grt-right', 'c': 'grt-center'}
|
||||
# FIX INDEX ALIGNERS HERE
|
||||
# TODO: index aligners
|
||||
for i, c in enumerate(self.df.columns):
|
||||
if i < self.nindex:
|
||||
# test aligners BEFORE index!
|
||||
if c in aligners:
|
||||
self.df_aligners.append(lrc.get(aligners[c], 'grt-center'))
|
||||
elif i < self.nindex:
|
||||
# index -> left
|
||||
self.df_aligners.append('grt-left')
|
||||
elif c in aligners:
|
||||
self.df_aligners.append(lrc.get(aligners[c], 'grt-center'))
|
||||
elif c in self.ratio_cols or i in self.float_col_indices or i in self.integer_col_indices:
|
||||
# number -> right
|
||||
self.df_aligners.append('grt-right')
|
||||
@@ -236,6 +273,8 @@ class GT(object):
|
||||
self.pef_lower = pef_lower
|
||||
self.pef_upper = pef_upper
|
||||
self._pef = None
|
||||
self.table_float_format = table_float_format
|
||||
self.default_float_formatter = None
|
||||
self.hrule_widths = hrule_widths or (0, 0, 0)
|
||||
self.vrule_widths = vrule_widths or (0, 0, 0)
|
||||
self.table_hrule_width = table_hrule_width
|
||||
@@ -245,6 +284,15 @@ class GT(object):
|
||||
self.font_caption = font_caption
|
||||
self.font_bold_index = font_bold_index
|
||||
self.sparsify_columns = sparsify_columns
|
||||
if tabs is None:
|
||||
self.tabs = None
|
||||
elif isinstance(tabs, (int, float)):
|
||||
self.tabs = (tabs,)
|
||||
elif isinstance(tabs, (np.ndarray, list, tuple)):
|
||||
self.tabs = tabs # Already iterable, self.tabs = as is
|
||||
else:
|
||||
self.tabs = [tabs] # Fallback for anything else
|
||||
self.equal = equal
|
||||
|
||||
if padding_trbl is None:
|
||||
if spacing == 'tight':
|
||||
@@ -313,12 +361,17 @@ class GT(object):
|
||||
def default_formatter(self, x):
|
||||
"""Universal formatter for other types."""
|
||||
try:
|
||||
i = int(x)
|
||||
f = float(x)
|
||||
if self.default_float_formatter:
|
||||
return self.default_float_formatter(f)
|
||||
try:
|
||||
i = int(x)
|
||||
except ValueError:
|
||||
i = int(f)
|
||||
if i == f:
|
||||
return self.default_integer_str.format(x=i)
|
||||
else:
|
||||
# TODo BEEF UP?
|
||||
# TODO BEEF UP?
|
||||
return self.default_float_str.format(x=f)
|
||||
except (TypeError, ValueError):
|
||||
return str(x)
|
||||
@@ -373,10 +426,10 @@ class GT(object):
|
||||
return fmt.format(x=x)
|
||||
# well and good but results in ugly differences
|
||||
# by entries in a column
|
||||
if x == int(x) and np.abs(x) < pu:
|
||||
return f'{x:,.0f}.'
|
||||
else:
|
||||
return fmt.format(x=x)
|
||||
# if x == int(x) and np.abs(x) < pu:
|
||||
# return f'{x:,.0f}.'
|
||||
# else:
|
||||
# return fmt.format(x=x)
|
||||
except (ValueError, TypeError):
|
||||
return str(x)
|
||||
return ff
|
||||
@@ -391,6 +444,32 @@ class GT(object):
|
||||
each column.
|
||||
"""
|
||||
# because of non-unique indexes, index by position not name
|
||||
if self.table_float_format is not None:
|
||||
if callable(self.table_float_format):
|
||||
# wrap in error protections
|
||||
def ff(x):
|
||||
try:
|
||||
return self.table_float_format(x=x)
|
||||
except ValueError:
|
||||
return str(x)
|
||||
except Exception as e:
|
||||
logger.error(f'Custom float function raised {e=}')
|
||||
self.default_float_formatter = ff
|
||||
else:
|
||||
if type(self.table_float_format) != str:
|
||||
raise ValueError('table_float_format must be a string or a function')
|
||||
fmt = self.table_float_format
|
||||
def ff(x):
|
||||
try:
|
||||
return fmt.format(x=x)
|
||||
except ValueError:
|
||||
return str(x)
|
||||
except Exception as e:
|
||||
logger.error(f'Custom float format string raised {e=}')
|
||||
self.default_float_formatter = ff
|
||||
else:
|
||||
self.default_float_formatter = False
|
||||
|
||||
if self._df_formatters is None:
|
||||
self._df_formatters = []
|
||||
for i, c in enumerate(self.df.columns):
|
||||
@@ -408,7 +487,7 @@ class GT(object):
|
||||
self._df_formatters.append(self.default_integer_formatter)
|
||||
elif i in self.float_col_indices:
|
||||
# trickier approach...
|
||||
self._df_formatters.append(self.make_float_formatter(self.df.iloc[:, i]))
|
||||
self._df_formatters.append(self.default_float_formatter or self.make_float_formatter(self.df.iloc[:, i]))
|
||||
else:
|
||||
# print(f'{i} default')
|
||||
self._df_formatters.append(self.default_formatter)
|
||||
@@ -429,7 +508,8 @@ class GT(object):
|
||||
"""
|
||||
return self.html
|
||||
|
||||
def make_style(self):
|
||||
def make_style(self, tabs):
|
||||
"""Write out custom CSS for the table."""
|
||||
if self.debug:
|
||||
head_tb = '#0ff'
|
||||
body_b = '#f0f'
|
||||
@@ -457,13 +537,16 @@ class GT(object):
|
||||
# for local use
|
||||
padt, padr, padb, padl = self.padt, self.padr, self.padb, self.padl
|
||||
|
||||
style = f'''
|
||||
style = [f'''
|
||||
<style>
|
||||
#{self.df_id} {{
|
||||
border-collapse: collapse;
|
||||
font-family: "Roboto", "Open Sans Condensed", "Arial", 'Segoe UI', sans-serif;
|
||||
font-size: {self.font_body}em;
|
||||
width: auto;
|
||||
/* tb and lr
|
||||
width: fit-content; */
|
||||
margin: 10px auto;
|
||||
border: none;
|
||||
overflow: auto;
|
||||
margin-left: auto;
|
||||
@@ -543,12 +626,15 @@ class GT(object):
|
||||
#{self.df_id} .grt-bold {{
|
||||
font-weight: bold;
|
||||
}}
|
||||
</style>
|
||||
'''
|
||||
return style
|
||||
''']
|
||||
for i, w in enumerate(tabs):
|
||||
style.append(f' #{self.df_id} .grt-c-{i} {{ width: {w}em; }}')
|
||||
style.append('</style>')
|
||||
logger.info('CREATED CSS')
|
||||
return '\n'.join(style)
|
||||
|
||||
def make_html(self):
|
||||
"""Convert a pandas DataFrame to an HTML table with sparsification."""
|
||||
"""Convert a pandas DataFrame to an HTML table."""
|
||||
index_name_to_level = dict(zip(self.raw_df.index.names, range(self.nindex)))
|
||||
index_change_level = self.index_change_level.map(index_name_to_level)
|
||||
# this is easier and computed in the init
|
||||
@@ -564,6 +650,36 @@ class GT(object):
|
||||
idx_header = bit.iloc[:self.nindex, :self.ncolumns]
|
||||
columns = bit.iloc[self.nindex:, :self.ncolumns]
|
||||
|
||||
colw, tabs = GT.estimate_column_widths(self.df, nc_index=self.nindex, scale=1, equal=self.equal)
|
||||
if self.debug:
|
||||
print(f'Input {self.tabs=}\nComputed {tabs=}')
|
||||
if self.tabs is not None:
|
||||
if len(tabs) == len(self.tabs):
|
||||
tabs = self.tabs
|
||||
elif len(self.tabs) == 1:
|
||||
tabs = self.tabs * len(tabs)
|
||||
else:
|
||||
logger.error(f'{self.tabs=} must be None, a single number, or a list of numbers of the correct length. Ignoring.')
|
||||
# print('HTML ' + ', '.join([f'{c:,.2f}' for c in tabs]))
|
||||
|
||||
# set column widths; tabs returns lengths of strings in each column
|
||||
# for proportional fonts, average char is 0.4 to 0.5 em but numbers with
|
||||
# tabular-nums are fixed 0.5, so use that
|
||||
# scale: want tables about 150-200 char wide, 1 char = 0.5 px size of font
|
||||
# so what 75-100 em wide in total
|
||||
# add the padding
|
||||
# TODO FONT SIZE
|
||||
# /4 works well for the tests (handles dates) but seems a bit illogical...
|
||||
tabs = np.array(tabs) + (self.padl + self.padr) / 12 # guessing font size...
|
||||
# em_per_char = 0.5; true exactly for tabular-nums
|
||||
em_per_char = 0.6
|
||||
tabs = tabs * em_per_char
|
||||
# this gets stripped out by quarto, so make part of style
|
||||
html.append('<colgroup>')
|
||||
for w in tabs:
|
||||
html.append(f'<col style="width: {w * em_per_char}em;">')
|
||||
html.append('</colgroup>')
|
||||
|
||||
# TODO Add header aligners
|
||||
# this is TRANSPOSED!!
|
||||
if self.sparsify_columns:
|
||||
@@ -571,10 +687,10 @@ class GT(object):
|
||||
for i in range(self.ncolumns):
|
||||
# one per row of columns m index, usually only 1
|
||||
html.append("<tr>")
|
||||
for j, r in enumerate(idx_header.iloc[:, i]):
|
||||
# columns one per level of index
|
||||
html.append(f'<th class="grt-left">{r}</th>')
|
||||
cum_col = 0 # keep track of where we are up to
|
||||
if self.show_index:
|
||||
for j, r in enumerate(idx_header.iloc[:, i]):
|
||||
# columns one per level of index
|
||||
html.append(f'<th class="grt-left">{r}</th>')
|
||||
# if not for col span issue you could just to this:
|
||||
# for j in range(self.ncols):
|
||||
# hrule = f'grt-bhrule-{i}' if i < self.ncolumns - 1 else ''
|
||||
@@ -589,6 +705,7 @@ class GT(object):
|
||||
# here, the groupby needs to consider all levels at and above i
|
||||
# this concats all the levels
|
||||
# need :i+1 to get down to the ith level
|
||||
cum_col = 0 # keep track of where we are up to
|
||||
for j, (nm, g) in enumerate(groupby(columns.iloc[:, :i+1].
|
||||
apply(lambda x: ':::'.join(str(i) for i in x), axis=1))):
|
||||
# ::: needs to be something that does not appear in the col names
|
||||
@@ -599,10 +716,16 @@ class GT(object):
|
||||
colspan = sum(1 for _ in g)
|
||||
if 0 < j:
|
||||
vrule = f'grt-vrule-{column_change_level[cum_col]}'
|
||||
elif j == 0:
|
||||
# start with the first column come what may
|
||||
elif j == 0 and self.show_index:
|
||||
# start with the first column if showing index
|
||||
vrule = f'grt-vrule-index'
|
||||
html.append(f'<th colspan="{colspan}" class="grt-center {hrule} {vrule}">{nm}</th>')
|
||||
else:
|
||||
vrule = ''
|
||||
if j == 0 and not self.show_index:
|
||||
# first column, no index, left align label
|
||||
html.append(f'<th colspan="{colspan}" class="grt-left {hrule} {vrule}">{nm}</th>')
|
||||
else:
|
||||
html.append(f'<th colspan="{colspan}" class="grt-center {hrule} {vrule}">{nm}</th>')
|
||||
cum_col += colspan
|
||||
html.append("</tr>")
|
||||
html.append("</thead>")
|
||||
@@ -611,9 +734,10 @@ class GT(object):
|
||||
for i in range(self.ncolumns):
|
||||
# one per row of columns m index, usually only 1
|
||||
html.append("<tr>")
|
||||
for j, r in enumerate(idx_header.iloc[:, i]):
|
||||
# columns one per level of index
|
||||
html.append(f'<th class="grt-left">{r}</th>')
|
||||
if self.show_index:
|
||||
for j, r in enumerate(idx_header.iloc[:, i]):
|
||||
# columns one per level of index
|
||||
html.append(f'<th class="grt-left">{r}</th>')
|
||||
for j, r in enumerate(columns.iloc[:, i]):
|
||||
# one per column of dataframe
|
||||
# figure how high up mindex the vrules go
|
||||
@@ -621,7 +745,7 @@ class GT(object):
|
||||
hrule = f'grt-bhrule-{i}' if i < self.ncolumns - 1 else ''
|
||||
if 0 < j < self.ncols and i >= column_change_level[j]:
|
||||
vrule = f'grt-vrule-{column_change_level[j]}'
|
||||
elif j == 0:
|
||||
elif j == 0 and self.show_index:
|
||||
# start with the first column come what may
|
||||
vrule = f'grt-vrule-index'
|
||||
else:
|
||||
@@ -636,29 +760,36 @@ class GT(object):
|
||||
# one per row of dataframe
|
||||
html.append("<tr>")
|
||||
hrule = ''
|
||||
for j, c in enumerate(r.iloc[:self.nindex]):
|
||||
# dx = data in index
|
||||
# if this is the level that changes for this row
|
||||
# will use a top rule hence omit i = 0 which already has an hrule
|
||||
if i > 0 and hrule == '' and j == index_change_level[i]:
|
||||
hrule = f'grt-hrule-{j}'
|
||||
# html.append(f'<td class="grt-dx-r-{i} grt-dx-c-{j} {self.df_aligners[j]} {hrule}">{c}</td>')
|
||||
html.append(f'<td class="{bold_idx} {self.df_aligners[j]} {hrule}">{c}</td>')
|
||||
if self.show_index:
|
||||
for j, c in enumerate(r.iloc[:self.nindex]):
|
||||
# dx = data in index
|
||||
# if this is the level that changes for this row
|
||||
# will use a top rule hence omit i = 0 which already has an hrule
|
||||
if i > 0 and hrule == '' and j == index_change_level[i]:
|
||||
hrule = f'grt-hrule-{j}'
|
||||
# html.append(f'<td class="grt-dx-r-{i} grt-dx-c-{j} {self.df_aligners[j]} {hrule}">{c}</td>')
|
||||
col_id = f'grt-c-{j}'
|
||||
html.append(f'<td class="{col_id} {bold_idx} {self.df_aligners[j]} {hrule}">{c}</td>')
|
||||
for j, c in enumerate(r.iloc[self.nindex:]):
|
||||
# first col left handled by index/body divider
|
||||
if 0 < j < self.ncols:
|
||||
vrule = f'grt-vrule-{column_change_level[j]}'
|
||||
elif j == 0:
|
||||
elif j == 0 and self.show_index:
|
||||
# start with the first column come what may
|
||||
vrule = f'grt-vrule-index'
|
||||
else:
|
||||
vrule = ''
|
||||
# html.append(f'<td class="grt-data-r-{i} grt-data-c-{j} {self.df_aligners[j+self.nindex]} {hrule} {vrule}">{c}</td>')
|
||||
html.append(f'<td class="{self.df_aligners[j+self.nindex]} {hrule} {vrule}">{c}</td>')
|
||||
col_id = f'grt-c-{j+self.nindex}'
|
||||
html.append(f'<td class="{col_id} {self.df_aligners[j+self.nindex]} {hrule} {vrule}">{c}</td>')
|
||||
html.append("</tr>")
|
||||
html.append("</tbody>")
|
||||
text = '\n'.join(html)
|
||||
text = GT.clean_html_tex(text)
|
||||
self.df_html = GT.clean_html_tex(text)
|
||||
logger.info('CREATED HTML')
|
||||
return text
|
||||
self.df_style = self.make_style(tabs)
|
||||
|
||||
return self.df_html
|
||||
|
||||
def clean_style(self, soup):
|
||||
"""Minify CSS inside <style> blocks and remove /* ... */ comments."""
|
||||
@@ -668,20 +799,23 @@ class GT(object):
|
||||
# Remove CSS comments
|
||||
cleaned_css = re.sub(r'/\*.*?\*/', '', style_tag.string, flags=re.DOTALL)
|
||||
# Minify whitespace
|
||||
cleaned_css = re.sub(r'\s+', ' ', cleaned_css).strip()
|
||||
# cleaned_css = re.sub(r'\s+', ' ', cleaned_css).strip()
|
||||
style_tag.string.replace_with(cleaned_css)
|
||||
return soup
|
||||
|
||||
@property
|
||||
def html(self):
|
||||
if self._clean_html == '':
|
||||
if self.df_html == '':
|
||||
# makes style and html (need tabs)
|
||||
self.df_html = self.make_html()
|
||||
code = ["<div class='greater-table'>",
|
||||
self.make_style() if self.df_style == '' else self.df_style,
|
||||
self.make_html() if self.df_html == '' else self.df_html,
|
||||
self.df_style,
|
||||
self.df_html,
|
||||
"</div>"]
|
||||
soup = BeautifulSoup('\n'.join(code), 'html.parser')
|
||||
soup = self.clean_style(soup)
|
||||
self._clean_html = str(soup) # .prettify()
|
||||
self._clean_html = str(soup) # .prettify() -> too many newlines
|
||||
return self._clean_html
|
||||
|
||||
def _repr_latex_(self):
|
||||
@@ -743,7 +877,9 @@ class GT(object):
|
||||
# create the df and the index separately
|
||||
data_formatters = self.df_formatters[self.nindex:]
|
||||
new_body = GT.apply_formatters_work(df, data_formatters)
|
||||
# now create the index
|
||||
if not self.show_index:
|
||||
return new_body
|
||||
# else have to handle the index
|
||||
index_formatters = self.df_formatters[:self.nindex]
|
||||
df_index = df.reset_index(drop=False, col_level=self.df.columns.nlevels - 1).iloc[:, :self.nindex]
|
||||
new_index = GT.apply_formatters_work(df_index, index_formatters)
|
||||
@@ -756,15 +892,12 @@ class GT(object):
|
||||
raise ValueError(f'unknown mode {mode}')
|
||||
|
||||
def make_tikz(self,
|
||||
float_format=None,
|
||||
tabs=None,
|
||||
scale=0.635,
|
||||
column_sep=3 / 8,
|
||||
row_sep=1 / 8,
|
||||
figure='figure',
|
||||
extra_defs='',
|
||||
hrule=None,
|
||||
equal=False,
|
||||
vrule=None,
|
||||
post_process='',
|
||||
label='',
|
||||
@@ -821,7 +954,6 @@ class GT(object):
|
||||
:param df:
|
||||
:param fn_out:
|
||||
:param float_format:
|
||||
:param tabs:
|
||||
:param show_index:
|
||||
:param scale:
|
||||
:param column_sep:
|
||||
@@ -870,24 +1002,27 @@ class GT(object):
|
||||
# always a good idea to do this...need to deal with underscores, %
|
||||
# and it handles index types that are not strings
|
||||
df = GT.clean_index(df)
|
||||
if not np.all([i=='object' for i in df.dtypes]):
|
||||
print('cols of df not all objects: ', df.dtypes, sep='\n')
|
||||
# make sure percents are escaped, but not if already escaped
|
||||
df = df.replace(r"(?<!\\)%", r"\%", regex=True)
|
||||
|
||||
# we are always showing the index...may regret that???
|
||||
# put condition here if needed
|
||||
nc_index = df.index.nlevels
|
||||
# col_level puts the label at the bottom of the column m index.
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore", category=pd.errors.PerformanceWarning)
|
||||
df = df.reset_index(drop=False, col_level=df.columns.nlevels - 1)
|
||||
if sparsify:
|
||||
if hrule is None:
|
||||
hrule = set()
|
||||
for i in range(sparsify):
|
||||
df.iloc[:, i], rules = GT.sparsify(df.iloc[:, i])
|
||||
# don't want lines everywhere
|
||||
if len(rules) < len(df) - 1:
|
||||
hrule = set(hrule).union(rules)
|
||||
# pres_maker.df_to_tikz code line 1931
|
||||
if self.show_index:
|
||||
nc_index = df.index.nlevels
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore", category=pd.errors.PerformanceWarning)
|
||||
df = df.reset_index(drop=False, col_level=df.columns.nlevels - 1)
|
||||
if sparsify:
|
||||
if hrule is None:
|
||||
hrule = set()
|
||||
for i in range(sparsify):
|
||||
df.iloc[:, i], rules = GT.sparsify(df.iloc[:, i])
|
||||
# don't want lines everywhere
|
||||
if len(rules) < len(df) - 1:
|
||||
hrule = set(hrule).union(rules)
|
||||
else:
|
||||
nc_index = 0
|
||||
|
||||
if vrule is None:
|
||||
vrule = set()
|
||||
@@ -902,12 +1037,22 @@ class GT(object):
|
||||
# internal TeX code (same as HTML code)
|
||||
matrix_name = self.df_id
|
||||
|
||||
# note this happens AFTER you have reset the index...need to pass number of index columns
|
||||
# note this happens AFTER you have reset the index...need to pass
|
||||
# number of index columns
|
||||
# have also converted everything to formatted strings
|
||||
# colw, mxmn, tabs = GT.guess_column_widths(df, nc_index=nc_index, float_format=wfloat_format, tabs=tabs,
|
||||
colw, mxmn, tabs = GT.guess_column_widths(df, nc_index=nc_index, float_format=lambda x: x, tabs=tabs,
|
||||
scale=scale, equal=equal)
|
||||
# print(colw, tabs)
|
||||
# estimate... originally called guess_column_widths, with more parameters
|
||||
colw, tabs = GT.estimate_column_widths(df, nc_index=nc_index, scale=scale, equal=self.equal)
|
||||
if self.debug:
|
||||
print(f'Input {self.tabs=}\nComputed {tabs=}')
|
||||
if self.tabs is not None:
|
||||
if len(tabs) == len(self.tabs):
|
||||
tabs = self.tabs
|
||||
elif len(self.tabs) == 1:
|
||||
tabs = self.tabs * len(tabs)
|
||||
else:
|
||||
logger.error(f'{self.tabs=} must be None, a single number, or a list of numbers of the correct length. Ignoring.')
|
||||
# print('TIKZ ' + ', '.join([f'{c:,.2f}' for c in tabs]))
|
||||
# print(f'TIKZ {colw=}, {tabs=}')
|
||||
logger.info(f'tabs: {tabs}')
|
||||
logger.info(f'colw: {colw}')
|
||||
|
||||
@@ -1059,7 +1204,7 @@ class GT(object):
|
||||
f'([yshift={-yshift}em]{matrix_name}-{ln}-{nc+1}.south east);\n')
|
||||
|
||||
written = set(range(1, nc_index + 1))
|
||||
if vrule:
|
||||
if vrule and self.show_index:
|
||||
# to left of col, 1 based, includes index
|
||||
# write these first
|
||||
# TODO fix madness vrule is to the left, mi_vrules are to the right...
|
||||
@@ -1112,25 +1257,28 @@ class GT(object):
|
||||
return self.tex
|
||||
|
||||
@staticmethod
|
||||
def guess_column_widths(df, nc_index, float_format, tabs=None, scale=1, equal=False):
|
||||
def estimate_column_widths(df, nc_index, scale=1, equal=False):
|
||||
"""
|
||||
estimate sensible column widths for the dataframe [in what units?]
|
||||
Estimate sensible column widths for the dataframe [in what units?]
|
||||
|
||||
Internal variables:
|
||||
mxmn affects alignment: are all columns the same width?
|
||||
|
||||
:param df:
|
||||
:param nc_index: number of columns in the index...these are not counted as "data columns"
|
||||
:param float_format:
|
||||
:param tabs:
|
||||
:param equal: if True, try to make all data columns the same width (hint can be rejected)
|
||||
:return:
|
||||
colw affects how the table is printed in the md file (actual width of data elements)
|
||||
mxmn affects alignment: are all columns the same width?
|
||||
colw affects how the tex is printed to ensure it "looks neat" (actual width of data elements)
|
||||
tabs affects the actual output
|
||||
equal if True, all try to make all data columns the same width (can be rejected)
|
||||
"""
|
||||
# this
|
||||
# tabs from _tabs, an estimate column widths, determines the size of the table columns as displayed
|
||||
# print(f'{nc_index=}, {scale=}, {equal=}')
|
||||
colw = dict.fromkeys(df.columns, 0)
|
||||
headw = dict.fromkeys(df.columns, 0)
|
||||
_tabs = []
|
||||
tabs = []
|
||||
mxmn = {}
|
||||
nl = nc_index
|
||||
for i, c in enumerate(df.columns):
|
||||
@@ -1144,12 +1292,19 @@ class GT(object):
|
||||
cw = max(map(len, c.split(' ')))
|
||||
# logger.info(f'leng col = {len(c)}, longest word = {cw}')
|
||||
else:
|
||||
# could be float etc.
|
||||
# column name could be float etc. or if multi index a tuple
|
||||
try:
|
||||
cw = max(map(lambda x: len(float_format(x)), c))
|
||||
if isinstance(c, tuple):
|
||||
# multiindex: join and split into words and take length of each word
|
||||
words = ' '.join(c).split(' ')
|
||||
cw = max(map(lambda x: len(str(x)), words))
|
||||
else:
|
||||
cw = max(map(lambda x: len(str(x)), c))
|
||||
# print(f'{c}: {cw=} no error')
|
||||
except TypeError:
|
||||
# not a MI, float or something
|
||||
cw = len(str(c))
|
||||
# print(f'{c}: {cw=} WITH error')
|
||||
headw[c] = cw
|
||||
# now figure the width of the elements in the column
|
||||
# mxmn is used to determine whether to center the column (if all the same size)
|
||||
@@ -1157,61 +1312,58 @@ class GT(object):
|
||||
# wierdness here were some objects actually contain floats, str evaluates to NaN
|
||||
# and picks up width zero
|
||||
try:
|
||||
# _ = list(map(lambda x: len(float_format(x)), df.iloc[:, i]))
|
||||
_ = df.iloc[:, i].map(lambda x: len(float_format(x)))
|
||||
colw[c] = _.max()
|
||||
mxmn[c] = (_.max(), _.min())
|
||||
except:
|
||||
e = sys.exc_info()[0]
|
||||
lens = df.iloc[:, i].map(lambda x: len(str(x)))
|
||||
colw[c] = lens.max()
|
||||
mxmn[c] = (lens.max(), lens.min())
|
||||
except Exception as e:
|
||||
logger.error(f'{c} error {e} DO SOMETHING ABOUT THIS...if it never occurs dont need the if')
|
||||
colw[c] = df[c].str.len().max()
|
||||
mxmn[c] = (df[c].str.len().max(), df[c].str.len().min())
|
||||
else:
|
||||
# _ = list(map(lambda x: len(float_format(x)), df[c]))
|
||||
_ = df.iloc[:, i].map(lambda x: len(float_format(x)))
|
||||
colw[c] = _.max()
|
||||
mxmn[c] = (_.max(), _.min())
|
||||
# debugging grief
|
||||
# if c == 'p':
|
||||
# print(c, df[c], colw[c], mxmn[c], list(map(len, list(map(float_format, df[c])))))
|
||||
if tabs is None:
|
||||
# now know all column widths...decide what to do
|
||||
# are all the columns about the same width?
|
||||
data_cols = np.array([colw[k] for k in df.columns[nl:]])
|
||||
same_size = (data_cols.std() <= 0.1 * data_cols.mean())
|
||||
common_size = 0
|
||||
if same_size:
|
||||
common_size = int(data_cols.mean() + data_cols.std())
|
||||
logger.info(f'data cols appear same size = {common_size}')
|
||||
for i, c in enumerate(df.columns):
|
||||
if i < nl or not same_size:
|
||||
# index columns
|
||||
_tabs.append(int(max(colw[c], headw[c])))
|
||||
else:
|
||||
# data all seems about the same width
|
||||
_tabs.append(common_size)
|
||||
logger.info(f'Determined tab spacing: {_tabs}')
|
||||
if equal:
|
||||
# see if equal widths makes sense
|
||||
dt = _tabs[nl:]
|
||||
if max(dt) / sum(dt) < 4 / 3:
|
||||
_tabs = _tabs[:nl] + [max(dt)] * (len(_tabs) - nl)
|
||||
logger.info(f'Taking equal width hint: {_tabs}')
|
||||
else:
|
||||
logger.info(f'Rejecting equal width hint')
|
||||
# look to rescale, shoot for width of 150 on 100 scale basis
|
||||
data_width = sum(_tabs[nl:])
|
||||
index_width = sum(_tabs[:nl])
|
||||
target_width = 150 * scale - index_width
|
||||
if data_width / target_width < 0.9:
|
||||
# don't rescale above 1:1 - don't want too large
|
||||
rescale = min(1 / scale, target_width / data_width)
|
||||
_tabs = [w if i < nl else w * rescale for i, w in enumerate(_tabs)]
|
||||
logger.info(f'Rescale {rescale} applied; tabs = {_tabs}')
|
||||
|
||||
tabs = _tabs
|
||||
|
||||
return colw, mxmn, tabs
|
||||
lens = df.iloc[:, i].map(lambda x: len(str(x)))
|
||||
colw[c] = lens.max()
|
||||
mxmn[c] = (lens.max(), lens.min())
|
||||
# print(f'{headw[c]=}, {colw[c]=}, {mxmn[c]=}, {c=}')
|
||||
# now know all column widths...decide what to do
|
||||
# are all the data columns about the same width?
|
||||
data_cols = np.array([colw[k] for k in df.columns[nl:]])
|
||||
same_size = (data_cols.std() <= 0.1 * data_cols.mean())
|
||||
# print(f'same size test requires {data_cols.std()} <= {0.1 * data_cols.mean()}')
|
||||
common_size = 0
|
||||
if same_size:
|
||||
common_size = int(data_cols.mean() + data_cols.std())
|
||||
logger.info(f'data cols appear same size = {common_size}')
|
||||
# print(f'data cols appear same size = {common_size}')
|
||||
for i, c in enumerate(df.columns):
|
||||
if i < nl or not same_size:
|
||||
# index columns
|
||||
tabs.append(int(max(colw[c], headw[c])))
|
||||
else:
|
||||
# data all seems about the same width
|
||||
tabs.append(common_size)
|
||||
logger.info(f'Determined tab spacing: {tabs}')
|
||||
if equal:
|
||||
# see if equal widths makes sense
|
||||
dt = tabs[nl:]
|
||||
if max(dt) / sum(dt) < 4 / 3:
|
||||
tabs = tabs[:nl] + [max(dt)] * (len(tabs) - nl)
|
||||
logger.info(f'Taking equal width hint: {tabs}')
|
||||
# print(f'Taking equal width hint: {tabs}')
|
||||
else:
|
||||
logger.info(f'Rejecting equal width hint')
|
||||
# print(f'Rejecting equal width hint')
|
||||
# look to rescale, shoot for width of 150 on 100 scale basis
|
||||
data_width = sum(tabs[nl:])
|
||||
index_width = sum(tabs[:nl])
|
||||
target_width = 150 * scale - index_width
|
||||
if data_width / target_width < 0.9:
|
||||
# don't rescale above 1:1 - don't want too large
|
||||
rescale = min(1 / scale, target_width / data_width)
|
||||
tabs = [w if i < nl else w * rescale for i, w in enumerate(tabs)]
|
||||
logger.info(f'Rescale {rescale} applied; tabs = {tabs}')
|
||||
# print(f'Rescale {rescale} applied; tabs = {tabs}')
|
||||
# print(f'{colw.values()=}\n{tabs=}')
|
||||
return colw, tabs
|
||||
|
||||
@staticmethod
|
||||
def sparsify(col):
|
||||
@@ -1356,6 +1508,35 @@ class GT(object):
|
||||
p.write_text(soup.prettify(), encodign='utf-8')
|
||||
logger.info(f'Saved to {p}')
|
||||
|
||||
@staticmethod
|
||||
def md_to_df(txt):
|
||||
"""Convert markdown text string table to DataFrame."""
|
||||
# remove starting and ending | in each line (optional anyway)
|
||||
txt = re.sub(r'^\||\|$', '', txt, flags=re.MULTILINE)
|
||||
txt = txt.strip().replace('*', '').split('\n')
|
||||
# remove the alignment row
|
||||
alignment_row = txt.pop(1)
|
||||
aligners = []
|
||||
for t in alignment_row.split('|'):
|
||||
if t[0] == ':' and t[-1] == ':':
|
||||
aligners.append('c')
|
||||
elif t[0] == ':':
|
||||
aligners.append('l')
|
||||
elif t[-1] == ':':
|
||||
aligners.append('r')
|
||||
else:
|
||||
# no alignment info
|
||||
pass
|
||||
if len(aligners) == 0:
|
||||
aligners = None
|
||||
else:
|
||||
aligners = ''.join(aligners)
|
||||
txt = [[j.strip() for j in i.split('|')] for i in txt]
|
||||
df = pd.DataFrame(txt).T
|
||||
df = df.set_index(0)
|
||||
df = df.T
|
||||
return df, aligners
|
||||
|
||||
|
||||
class sGT(GT):
|
||||
"""
|
||||
@@ -1365,7 +1546,13 @@ class sGT(GT):
|
||||
in this way.
|
||||
"""
|
||||
def __init__(self, df, caption="", guess_years=True, ratio_regex='lr|roe|coc', **kwargs):
|
||||
"""Create Steve House-Style Formatter."""
|
||||
"""Create Steve House-Style Formatter. Does not handle list of lists input."""
|
||||
if isinstance(df, str):
|
||||
df, aligners_ = GT.md_to_df(df)
|
||||
if 'aligners' not in kwargs:
|
||||
kwargs['aligners'] = aligners_
|
||||
kwargs['show_index'] = False
|
||||
|
||||
nindex = df.index.nlevels
|
||||
ncolumns = df.columns.nlevels
|
||||
if 'ratio_cols' in kwargs:
|
||||
|
||||
@@ -0,0 +1,356 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"><head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
|
||||
<meta name="author" content="Stephen J. Mildenhall">
|
||||
<meta name="dcterms.date" content="2025-03-14">
|
||||
<title>SINGLE Table</title>
|
||||
<style>
|
||||
code{white-space: pre-wrap;}
|
||||
span.smallcaps{font-variant: small-caps;}
|
||||
div.columns{display: flex; gap: min(4vw, 1.5em);}
|
||||
div.column{flex: auto; overflow-x: auto;}
|
||||
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||
ul.task-list{list-style: none;}
|
||||
ul.task-list li input[type="checkbox"] {
|
||||
width: 0.8em;
|
||||
margin: 0 0.8em 0.2em -1em; /* quarto-specific, see https://github.com/quarto-dev/quarto-cli/issues/4556 */
|
||||
vertical-align: middle;
|
||||
}
|
||||
/* CSS for syntax highlighting */
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
.sourceCode { overflow: visible; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { display: inline-block; text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js" integrity="sha512-bLT0Qm9VnAYZDflyKcBaQ2gg0hSYNQrJ8RilYldYQ1FxQYoCLtUjuuRuZo+fjqhx/qtq/1itJ0C2ejDxltZVFg==" crossorigin="anonymous"></script><script src="tables_files/libs/clipboard/clipboard.min.js"></script>
|
||||
<script src="tables_files/libs/quarto-html/quarto.js"></script>
|
||||
<script src="tables_files/libs/quarto-html/popper.min.js"></script>
|
||||
<script src="tables_files/libs/quarto-html/tippy.umd.min.js"></script>
|
||||
<script src="tables_files/libs/quarto-html/anchor.min.js"></script>
|
||||
<link href="tables_files/libs/quarto-html/tippy.css" rel="stylesheet">
|
||||
<link href="tables_files/libs/quarto-html/quarto-syntax-highlighting-01c78b5cd655e4cd89133cf59d535862.css" rel="stylesheet" id="quarto-text-highlighting-styles">
|
||||
<script src="tables_files/libs/bootstrap/bootstrap.min.js"></script>
|
||||
<link href="tables_files/libs/bootstrap/bootstrap-icons.css" rel="stylesheet">
|
||||
<link href="tables_files/libs/bootstrap/bootstrap-fcfb2e27d9f44eaf269ffcda1f840c64.min.css" rel="stylesheet" append-hash="true" id="quarto-bootstrap" data-mode="light">
|
||||
<style>html{ scroll-behavior: smooth; }</style>
|
||||
<link rel="icon" href="img/favicon.ico" type="image/x-icon">
|
||||
|
||||
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js" integrity="sha512-c3Nl8+7g4LMSTdrm621y7kf9v3SDPnhxLNhcjFJbKECVnmZHTdo+IRO05sNLTH/D3vA6u1X32ehoLC7WFVdheg==" crossorigin="anonymous"></script>
|
||||
|
||||
<script type="application/javascript">define('jquery', [],function() {return window.jQuery;})</script>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/polyfill/v3/polyfill.min.js?features=es6"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml-full.js" type="text/javascript"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
const typesetMath = (el) => {
|
||||
if (window.MathJax) {
|
||||
// MathJax Typeset
|
||||
window.MathJax.typeset([el]);
|
||||
} else if (window.katex) {
|
||||
// KaTeX Render
|
||||
var mathElements = el.getElementsByClassName("math");
|
||||
var macros = [];
|
||||
for (var i = 0; i < mathElements.length; i++) {
|
||||
var texText = mathElements[i].firstChild;
|
||||
if (mathElements[i].tagName == "SPAN") {
|
||||
window.katex.render(texText.data, mathElements[i], {
|
||||
displayMode: mathElements[i].classList.contains('display'),
|
||||
throwOnError: false,
|
||||
macros: macros,
|
||||
fleqn: false
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
window.Quarto = {
|
||||
typesetMath
|
||||
};
|
||||
</script>
|
||||
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<h1>Table 1</h1>
|
||||
|
||||
<div class="greater-table">
|
||||
<style>
|
||||
|
||||
#T25N3COST23ZV {
|
||||
border-collapse: collapse;
|
||||
font-family: "Roboto", "Open Sans Condensed", "Arial", 'Segoe UI', sans-serif;
|
||||
font-size: 0.8em;
|
||||
width: 50em;
|
||||
margin: 10px auto;
|
||||
border: none;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#T25N3COST23ZV caption { padding: 8px 10px 4px 10px; font-size: 0.88em; text-align: center; font-weight: normal; caption-side: top; }
|
||||
|
||||
#T25N3COST23ZV thead { border-top: 1px solid #000; border-bottom: 1px solid #000; font-size: 0.88em; }
|
||||
|
||||
#T25N3COST23ZV tbody { border-bottom: 1px solid #000; }
|
||||
|
||||
#T25N3COST23ZV th { vertical-align: bottom; padding: 8px 10px 8px 10px; }
|
||||
|
||||
#T25N3COST23ZV td { padding: 4px 10px 4px 10px; vertical-align: top; }
|
||||
|
||||
#T25N3COST23ZV .grt-hrule-0 { border-top: 0px solid #000; }
|
||||
|
||||
#T25N3COST23ZV .grt-hrule-1 { border-top: 0px solid #000; }
|
||||
|
||||
#T25N3COST23ZV .grt-hrule-2 { border-top: 0px solid #000; }
|
||||
|
||||
#T25N3COST23ZV .grt-bhrule-0 { border-bottom: 0px solid #000; }
|
||||
|
||||
#T25N3COST23ZV .grt-bhrule-1 { border-bottom: 0px solid #000; }
|
||||
|
||||
#T25N3COST23ZV .grt-vrule-index { border-left: 1px solid #000; }
|
||||
|
||||
#T25N3COST23ZV .grt-vrule-0 { border-left: 0px solid #000; }
|
||||
|
||||
#T25N3COST23ZV .grt-vrule-1 { border-left: 0px solid #000; }
|
||||
|
||||
#T25N3COST23ZV .grt-vrule-2 { border-left: 0px solid #000; }
|
||||
|
||||
#T25N3COST23ZV .grt-left { text-align: left; }
|
||||
|
||||
#T25N3COST23ZV .grt-center { text-align: center; }
|
||||
|
||||
#T25N3COST23ZV .grt-right { text-align: right; font-variant-numeric: tabular-nums; }
|
||||
|
||||
#T25N3COST23ZV .grt-col-1 {width: 3em;}
|
||||
#T25N3COST23ZV .grt-col-2 {width: 3em;}
|
||||
#T25N3COST23ZV .grt-col-3 {width: 15em;}
|
||||
#T25N3COST23ZV .grt-col-4 {width: 4em;}
|
||||
#T25N3COST23ZV .grt-col-5 {width: 4em;}
|
||||
#T25N3COST23ZV .grt-col-6 {width: 4em;}
|
||||
|
||||
#T25N3COST23ZV .grt-head { font-family: "Times New Roman", 'Courier New'; font-size: 0.88em; }
|
||||
|
||||
#T25N3COST23ZV .grt-bold { font-weight: bold; }</style>
|
||||
<table id="T25N3COST23ZV">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="grt-left">index</th>
|
||||
<th class="grt-center grt-vrule-index" colspan="1">level_0</th>
|
||||
<th class="grt-center grt-vrule-0" colspan="1">level_1</th>
|
||||
<th class="grt-center grt-vrule-0" colspan="1">2025</th>
|
||||
<th class="grt-center grt-vrule-0" colspan="1">2026</th>
|
||||
<th class="grt-center grt-vrule-0" colspan="1">2027</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="grt-col-1 grt-left">0</td>
|
||||
<td class="grt-col-2 grt-left grt-vrule-index">GAAP</td>
|
||||
<td class="grt-col-3 grt-left grt-vrule-0">Underwriting Result</td>
|
||||
<td class="grt-col-4 grt-right grt-vrule-0"></td>
|
||||
<td class="grt-col-5 grt-right grt-vrule-0">-394.81</td>
|
||||
<td class="grt-col-6 grt-right grt-vrule-0"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="grt-left">1</td>
|
||||
<td class="grt-left grt-vrule-index">GAAP</td>
|
||||
<td class="grt-left grt-vrule-0">Net Investment Income</td>
|
||||
<td class="grt-right grt-vrule-0"></td>
|
||||
<td class="grt-right grt-vrule-0">60.52</td>
|
||||
<td class="grt-right grt-vrule-0">66.57</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="grt-left">2</td>
|
||||
<td class="grt-left grt-vrule-index">GAAP</td>
|
||||
<td class="grt-left grt-vrule-0">Operating Result</td>
|
||||
<td class="grt-right grt-vrule-0"></td>
|
||||
<td class="grt-right grt-vrule-0">-334.29</td>
|
||||
<td class="grt-right grt-vrule-0">66.57</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="grt-left">3</td>
|
||||
<td class="grt-left grt-vrule-index">GAAP</td>
|
||||
<td class="grt-left grt-vrule-0">Dividends</td>
|
||||
<td class="grt-right grt-vrule-0"></td>
|
||||
<td class="grt-right grt-vrule-0"></td>
|
||||
<td class="grt-right grt-vrule-0"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></div>
|
||||
|
||||
<h1>Table 2</h1>
|
||||
|
||||
|
||||
<p>Some text above the table.</p>
|
||||
|
||||
<div class="greater-table">
|
||||
<style>
|
||||
|
||||
#TEJECQF5AYPNM {
|
||||
border-collapse: collapse; font-family: "Roboto", "Open Sans Condensed", "Arial", 'Segoe UI', sans-serif;
|
||||
font-size: 0.8em;
|
||||
width: fit-content;
|
||||
/* tb and lr */
|
||||
margin: 10px auto;
|
||||
}
|
||||
|
||||
#TEJECQF5AYPNM caption { padding: 2px 10px 1px 10px; font-size: 0.88em; text-align: center; font-weight: normal; caption-side: top; }
|
||||
|
||||
#TEJECQF5AYPNM thead { border-top: 1px solid #000; border-bottom: 1px solid #000; font-size: 0.88em; }
|
||||
|
||||
#TEJECQF5AYPNM tbody { border-bottom: 1px solid #000; }
|
||||
|
||||
#TEJECQF5AYPNM th { vertical-align: bottom; padding: 2px 10px 2px 10px; }
|
||||
|
||||
#TEJECQF5AYPNM td { padding: 1px 10px 1px 10px; vertical-align: top; }
|
||||
|
||||
#TEJECQF5AYPNM .grt-hrule-0 { border-top: 0px solid #000; }
|
||||
|
||||
#TEJECQF5AYPNM .grt-hrule-1 { border-top: 0px solid #000; }
|
||||
|
||||
#TEJECQF5AYPNM .grt-hrule-2 { border-top: 0px solid #000; }
|
||||
|
||||
#TEJECQF5AYPNM .grt-bhrule-0 { border-bottom: 1.5px solid #000; }
|
||||
|
||||
#TEJECQF5AYPNM .grt-bhrule-1 { border-bottom: 1px solid #000; }
|
||||
|
||||
#TEJECQF5AYPNM .grt-vrule-index { border-left: 1.5px solid #000; }
|
||||
|
||||
#TEJECQF5AYPNM .grt-vrule-0 { border-left: 1.5px solid #000; }
|
||||
|
||||
#TEJECQF5AYPNM .grt-vrule-1 { border-left: 1px solid #000; }
|
||||
|
||||
#TEJECQF5AYPNM .grt-vrule-2 { border-left: 0.5px solid #000; }
|
||||
|
||||
#TEJECQF5AYPNM .grt-left { text-align: left; }
|
||||
|
||||
#TEJECQF5AYPNM .grt-center { text-align: center; }
|
||||
|
||||
#TEJECQF5AYPNM .grt-right { text-align: right; font-variant-numeric: tabular-nums; }
|
||||
|
||||
#TEJECQF5AYPNM .grt-head { font-family: "Times New Roman", 'Courier New'; font-size: 0.88em; }
|
||||
|
||||
#TEJECQF5AYPNM .grt-bold { font-weight: bold; }</style>
|
||||
<table id="TEJECQF5AYPNM" style="float:center">
|
||||
<caption>Table 1. A table with varied column widths.</caption>
|
||||
<colgroup>
|
||||
<col width="250px">
|
||||
<col style="width: 50px">
|
||||
<col style="width: 50px">
|
||||
<col style="width: 50px">
|
||||
<col style="width: 50px">
|
||||
<col style="width: 75px">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="grt-left"></th>
|
||||
<th class="grt-center grt-bhrule-0 grt-vrule-index" colspan="2">A</th>
|
||||
<th class="grt-center grt-bhrule-0 grt-vrule-0" colspan="2">B</th>
|
||||
<th class="grt-center grt-bhrule-0 grt-vrule-0" colspan="1">C</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="grt-left">years!</th>
|
||||
<th class="grt-center grt-vrule-index" colspan="1">Int</th>
|
||||
<th class="grt-center grt-vrule-1" colspan="1">Float</th>
|
||||
<th class="grt-center grt-vrule-0" colspan="1">Float</th>
|
||||
<th class="grt-center grt-vrule-1" colspan="1">3</th>
|
||||
<th class="grt-center grt-vrule-0" colspan="1">Longer Text</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="grt-left">2000</td>
|
||||
<td class="grt-right grt-vrule-index">-100,000</td>
|
||||
<td class="grt-right grt-vrule-1"> 2.389p</td>
|
||||
<td class="grt-right grt-vrule-0">-1,601.00</td>
|
||||
<td class="grt-center grt-vrule-1">2025-03-14</td>
|
||||
<td class="grt-left grt-vrule-0">once upon a time, once upon a time, once upon a time, once upon a time</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="grt-left grt-hrule-0">2001</td>
|
||||
<td class="grt-right grt-hrule-0 grt-vrule-index">-91,667</td>
|
||||
<td class="grt-right grt-hrule-0 grt-vrule-1"> 22.217p</td>
|
||||
<td class="grt-right grt-hrule-0 grt-vrule-0">-1,367.62</td>
|
||||
<td class="grt-center grt-hrule-0 grt-vrule-1">2025-03-26</td>
|
||||
<td class="grt-left grt-hrule-0 grt-vrule-0"> risk is hard to define</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="grt-left grt-hrule-0">2002</td>
|
||||
<td class="grt-right grt-hrule-0 grt-vrule-index">-83,333</td>
|
||||
<td class="grt-right grt-hrule-0 grt-vrule-1"> 206.619p</td>
|
||||
<td class="grt-right grt-hrule-0 grt-vrule-0">-1,134.25</td>
|
||||
<td class="grt-center grt-hrule-0 grt-vrule-1">2025-04-07</td>
|
||||
<td class="grt-left grt-hrule-0 grt-vrule-0"> not in Kansas anymore</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="grt-left grt-hrule-0">2003</td>
|
||||
<td class="grt-right grt-hrule-0 grt-vrule-index">-75,000</td>
|
||||
<td class="grt-right grt-hrule-0 grt-vrule-1"> 1.922n</td>
|
||||
<td class="grt-right grt-hrule-0 grt-vrule-0">-900.88</td>
|
||||
<td class="grt-center grt-hrule-0 grt-vrule-1">2025-04-19</td>
|
||||
<td class="grt-left grt-hrule-0 grt-vrule-0"> neutrinos are hard to detect</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="grt-left grt-hrule-0">2004</td>
|
||||
<td class="grt-right grt-hrule-0 grt-vrule-index">-66,667</td>
|
||||
<td class="grt-right grt-hrule-0 grt-vrule-1"> 17.870n</td>
|
||||
<td class="grt-right grt-hrule-0 grt-vrule-0">-667.50</td>
|
||||
<td class="grt-center grt-hrule-0 grt-vrule-1">2025-05-01</td>
|
||||
<td class="grt-left grt-hrule-0 grt-vrule-0"> Adam Smith is the father of economics</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="grt-left grt-hrule-0">2005</td>
|
||||
<td class="grt-right grt-hrule-0 grt-vrule-index">-58,333</td>
|
||||
<td class="grt-right grt-hrule-0 grt-vrule-1"> 166.196n</td>
|
||||
<td class="grt-right grt-hrule-0 grt-vrule-0">-434.12</td>
|
||||
<td class="grt-center grt-hrule-0 grt-vrule-1">2025-05-13</td>
|
||||
<td class="grt-left grt-hrule-0 grt-vrule-0">once upon a time</td>
|
||||
</tr>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan=3>Footer 1 stuff. This is very long. This is very long. This is very long. This is very long. </td>
|
||||
<td>Footer 2 stuff.</td>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<p>Some text below the table.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
+1570
-1110
File diff suppressed because it is too large
Load Diff
+5244
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+177
-75
@@ -13,70 +13,61 @@ tbl-align: left
|
||||
number-sections: true
|
||||
number-offset: 0
|
||||
number-depth: 3
|
||||
|
||||
code-line-numbers: false
|
||||
code-copy: true
|
||||
code-overflow: wrap
|
||||
code-fold: true # code there but in folded up mode
|
||||
|
||||
code-fold: true
|
||||
fig-format: svg
|
||||
fig-align: left
|
||||
|
||||
format:
|
||||
html:
|
||||
html-table-processing: none
|
||||
theme: litera # cosmo
|
||||
theme: litera
|
||||
fontsize: 0.9em
|
||||
css: styles.css
|
||||
include-in-header: pmir-header.html
|
||||
smooth-scroll: true
|
||||
toc-title: 'In this chapter:'
|
||||
citations-hover: true
|
||||
# code-tools: true
|
||||
crossrefs-hover: false
|
||||
fig-responsive: true
|
||||
footnotes-hover: true
|
||||
lightbox: true
|
||||
link-external-icon: true # arrow in a box for external links
|
||||
link-external-newwindow: true # open in separate window
|
||||
page-layout: article # optimized body region; full expands contents if nothing in margins
|
||||
link-external-icon: true
|
||||
link-external-newwindow: true
|
||||
page-layout: article
|
||||
page-navigation: true
|
||||
reference-section-title: ' ' # title for references section, passed to pandoc
|
||||
reference-section-title: ' '
|
||||
page-footer:
|
||||
left: "Stephen J. Mildenhall. License: [CC BY-SA 2.0](https://creativecommons.org/licenses/by-sa/2.0/)."
|
||||
left: 'Stephen J. Mildenhall. License: [CC BY-SA 2.0](https://creativecommons.org/licenses/by-sa/2.0/).'
|
||||
twitter-card: true
|
||||
open-graph: true
|
||||
toc: true
|
||||
toc-depth: 3
|
||||
math: mathjax
|
||||
# math:
|
||||
# engine: mathjax
|
||||
# url: https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js
|
||||
|
||||
pdf:
|
||||
include-in-header: prefobnicate.tex
|
||||
documentclass: scrartcl # report scrreprt artcile scrreprt
|
||||
papersize: a4
|
||||
fontsize: 12pt
|
||||
keep-tex: true # keep the tex file
|
||||
geometry: margin=0.8in
|
||||
pdf-engine: lualatex
|
||||
pdf-engine-opts:
|
||||
- '-interaction=nonstopmode'
|
||||
toc: false
|
||||
include-in-header: prefobnicate.tex
|
||||
documentclass: scrartcl
|
||||
papersize: a4
|
||||
fontsize: 11pt
|
||||
keep-tex: true
|
||||
geometry: margin=0.8in
|
||||
pdf-engine: lualatex
|
||||
pdf-engine-opts:
|
||||
- '-interaction=nonstopmode'
|
||||
toc: false
|
||||
execute:
|
||||
eval: true
|
||||
echo: true
|
||||
cache: true # this will reexecute all cells if any cell changes
|
||||
cache-type: jupyter # project is notebook/python heavy
|
||||
freeze: false # freezer locks the whole file, md and code; rarely what you want
|
||||
cache: true
|
||||
cache-type: jupyter
|
||||
freeze: false
|
||||
kernel: python3
|
||||
engine: jupyter # use IPython kernel
|
||||
engine: jupyter
|
||||
daemon: 1200
|
||||
|
||||
jupyter:
|
||||
jupytext:
|
||||
formats: ipynb,qmd
|
||||
formats: ipynb,qmd:quarto
|
||||
text_representation:
|
||||
extension: .qmd
|
||||
format_name: quarto
|
||||
@@ -88,10 +79,8 @@ jupyter:
|
||||
name: python3
|
||||
---
|
||||
|
||||
# Python set-up
|
||||
|
||||
```{python}
|
||||
#| echo: true
|
||||
#| echo: true
|
||||
#| label: setup
|
||||
from IPython.display import HTML, display
|
||||
import matplotlib as mpl
|
||||
@@ -112,10 +101,12 @@ gter.logger.setLevel(gter.logging.WARNING)
|
||||
|
||||
Second level index has mixed types. Range of magnitudes. Picking out years.
|
||||
|
||||
\footnotesize
|
||||
|
||||
|
||||
```{python}
|
||||
#| label: tbl-hard-rules
|
||||
#| tbl-cap: Quarto generated caption
|
||||
#| tbl-cap: Default display output (Quarto generated caption)
|
||||
level_1 = ["A", "A", "B", "B", 'C']
|
||||
level_2 = ['Int', 'Float', 'Float', 3, 'Longer Text']
|
||||
|
||||
@@ -139,43 +130,56 @@ hard.columns = multi_index
|
||||
hard
|
||||
```
|
||||
|
||||
`sGT` format
|
||||
\normalsize
|
||||
|
||||
@tbl-hard-rules shows the default output and @tbl-hard-rules-2 the `sGT` format output.
|
||||
|
||||
```{python}
|
||||
#| label: tbl-hard-rules-2
|
||||
#| tbl-cap: Quarto generated caption
|
||||
#| tbl-cap: Greater Tables output (Quarto generated caption)
|
||||
sGT(hard, 'A table with varied columns.')
|
||||
```
|
||||
|
||||
Illustrate some alternatives.
|
||||
Here are some alternatives:
|
||||
|
||||
* @tbl-hard-rules-3a hrules no vrules
|
||||
* @tbl-hard-rules-3b change date and integer formats and
|
||||
* @tbl-hard-rules-3c change padding and debug mode.
|
||||
|
||||
```{python}
|
||||
#| echo: fenced
|
||||
#| label: tbl-hard-rules-3
|
||||
#| tbl-cap: Quarto generated caption
|
||||
|
||||
display(sGT(hard.sample(5).sort_index(), 'No v rules, but h rules',
|
||||
#| label: tbl-hard-rules-3a
|
||||
#| tbl-cap: No V rules but hrules (Quarto generated caption)
|
||||
display(sGT(hard.sample(5).sort_index(),
|
||||
caption='GT caption No v rules, but h rules',
|
||||
vrule_widths=(0,0,0),
|
||||
hrule_widths=(1,0,0)))
|
||||
|
||||
display(sGT(hard.sample(5).sort_index(),
|
||||
'Change default date and integer formats',
|
||||
default_date_str='%m-%d', default_integer_str='[{x:d}]'))
|
||||
|
||||
display(sGT(hard.sample(5).sort_index(),
|
||||
'Change padding, debug mode lines',
|
||||
default_date_str='%m-%d', default_integer_str='[{x:d}]',
|
||||
padding_trbl=(10, 10, 20, 20), debug=True))
|
||||
|
||||
```
|
||||
|
||||
Here is the raw output.
|
||||
```{python}
|
||||
#| echo: fenced
|
||||
#| label: tbl-hard-rules-3b
|
||||
#| tbl-cap: Change date and integer formats (Quarto generated caption)
|
||||
display(sGT(hard.sample(5).sort_index(),
|
||||
caption='Change default date and integer formats',
|
||||
default_date_str='%m-%d', default_integer_str='[{x:d}]'))
|
||||
```
|
||||
|
||||
```{python}
|
||||
#| echo: fenced
|
||||
#| label: tbl-hard-rules-3c
|
||||
#| tbl-cap: Change padding and debug mode, boxes (Quarto generated caption)
|
||||
display(sGT(hard.sample(5).sort_index(),
|
||||
caption='Change padding, debug mode lines',
|
||||
padding_trbl=(10, 10, 20, 20), debug=True))
|
||||
```
|
||||
|
||||
Here is the raw HTML and LaTeX output.
|
||||
|
||||
\footnotesize
|
||||
|
||||
```{python}
|
||||
#| label: raw-output
|
||||
|
||||
f = sGT(hard.head(4), debug=True)
|
||||
print('HTML output\n')
|
||||
print(f._repr_html_())
|
||||
@@ -187,11 +191,11 @@ print(f._repr_latex_())
|
||||
\normalsize
|
||||
|
||||
|
||||
# A Table with TeX
|
||||
# A Table with TeX Content
|
||||
|
||||
```{python}
|
||||
#| label: tbl-tex
|
||||
#| tbl-cap: "Quarto generated caption: table displayed by default routine."
|
||||
#| tbl-cap: '(Quarto generated caption): table displayed by default routine.'
|
||||
index = pd.Index(["A", "B", "$C_1$", "C_2 not tex", '$\\cos(A)$'])
|
||||
tex = pd.DataFrame(
|
||||
{'x': np.arange(2020, 2025, dtype=int),
|
||||
@@ -208,7 +212,7 @@ tex
|
||||
|
||||
```{python}
|
||||
#| label: tbl-tex-2
|
||||
#| tbl-cap: Quarto generated caption
|
||||
#| tbl-cap: GT output (Quarto generated caption)
|
||||
sGT(tex, 'GT Caption')
|
||||
```
|
||||
|
||||
@@ -221,23 +225,21 @@ tex.columns = ["A (%)", "B", "$C_1$", "C_2 not tex", '$\\cos(A)$']
|
||||
sGT(tex, 'Ratio columns in A', ratio_cols='A (%)')
|
||||
```
|
||||
|
||||
|
||||
# Greater_tables Test Suite
|
||||
|
||||
```{python}
|
||||
#| echo: true
|
||||
#| echo: true
|
||||
#| label: greater-tables-test
|
||||
test_gen = gtu.TestDFGenerator(0, 0)
|
||||
ans = test_gen.test_suite()
|
||||
```
|
||||
|
||||
|
||||
## Test Table: basic
|
||||
|
||||
|
||||
```{python}
|
||||
#| echo: fold
|
||||
#| echo: true
|
||||
#| label: tbl-greater-tables-test-0
|
||||
#| tbl-cap: Output for test table basic
|
||||
#| tbl-cap: GT output for test table basic
|
||||
hrw = (0, 0, 0)
|
||||
sGT(ans['basic'], "Basic", ratio_cols='z', aligners={'w': 'l'},
|
||||
hrule_widths=hrw)
|
||||
@@ -248,11 +250,11 @@ Comments go here.
|
||||
|
||||
|
||||
## Test Table: timeseries
|
||||
|
||||
|
||||
```{python}
|
||||
#| echo: fold
|
||||
#| echo: true
|
||||
#| label: tbl-greater-tables-test-1
|
||||
#| tbl-cap: Output for test table timeseries
|
||||
#| tbl-cap: GT output for test table timeseries
|
||||
hrw = (0, 0, 0)
|
||||
sGT(ans['timeseries'], "Timeseries", ratio_cols='z', aligners={'w': 'l'},
|
||||
hrule_widths=hrw)
|
||||
@@ -264,11 +266,11 @@ Comments go here.
|
||||
|
||||
|
||||
## Test Table: multiindex
|
||||
|
||||
|
||||
```{python}
|
||||
#| echo: fold
|
||||
#| echo: true
|
||||
#| label: tbl-greater-tables-test-2
|
||||
#| tbl-cap: Output for test table multiindex
|
||||
#| tbl-cap: GT output for test table multiindex
|
||||
hrw = (1.5, 1.0, 0.5)
|
||||
sGT(ans['multiindex'], "Multiindex", ratio_cols='z', aligners={'w': 'l'},
|
||||
hrule_widths=hrw)
|
||||
@@ -280,11 +282,11 @@ Comments go here.
|
||||
|
||||
|
||||
## Test Table: multicolumns
|
||||
|
||||
|
||||
```{python}
|
||||
#| echo: fold
|
||||
#| echo: true
|
||||
#| label: tbl-greater-tables-test-3
|
||||
#| tbl-cap: Output for test table multicolumns
|
||||
#| tbl-cap: GT output for test table multicolumns
|
||||
hrw = (0, 0, 0)
|
||||
sGT(ans['multicolumns'], "Multicolumns", ratio_cols='z', aligners={'w': 'l'},
|
||||
hrule_widths=hrw)
|
||||
@@ -296,11 +298,11 @@ Comments go here.
|
||||
|
||||
|
||||
## Test Table: complex
|
||||
|
||||
|
||||
```{python}
|
||||
#| echo: fold
|
||||
#| echo: true
|
||||
#| label: tbl-greater-tables-test-4
|
||||
#| tbl-cap: Output for test table complex
|
||||
#| tbl-cap: GT output for test table complex
|
||||
hrw = (1.5, 1.0, 0.5)
|
||||
sGT(ans['complex'], "Complex", ratio_cols='z', aligners={'w': 'l'},
|
||||
hrule_widths=hrw)
|
||||
@@ -308,3 +310,103 @@ sGT(ans['complex'], "Complex", ratio_cols='z', aligners={'w': 'l'},
|
||||
|
||||
Comments go here.
|
||||
|
||||
# Other input formats
|
||||
|
||||
## Markown
|
||||
|
||||
| **Insured group or insurance product** | **Sat** | **RP** | **RF** |
|
||||
|:------------------------------------------------------------|:-------:|:------:|:------:|
|
||||
| Non-standard auto | x | | |
|
||||
| General liability for judgment proof corporation | x | | |
|
||||
| Term life insurance | | x | |
|
||||
| Catastrophe Reinsurance, outside rating agency bounds | | x | |
|
||||
| High limit property per risk reinsurance | | x | |
|
||||
| Personal lines for affluent individuals | x | x | |
|
||||
| Small commercial lines | x | x | |
|
||||
| Catastrophe reinsurance, within rating agency bounds | x | x | |
|
||||
| Large account captive reinsurance | | | x |
|
||||
| Structured quota share, requiring a risk transfer test | x | | x |
|
||||
| Working layer casualty excess of loss | | x | x |
|
||||
| Surplus relief quota share on cat exposed line | x | x | x |
|
||||
| Middle market commercial lines work comp or commercial auto | x | x | x |
|
||||
|
||||
```{python}
|
||||
#| echo: true
|
||||
#| label: tbl-greater-tables-test-5
|
||||
#| tbl-cap: GT from markdown table input
|
||||
txt = '''
|
||||
|
||||
| **Insured group or insurance product** | **Sat** | **RP** | **RF** |
|
||||
|:------------------------------------------------------------|:-------:|:------:|:------:|
|
||||
| Non-standard auto | x | | |
|
||||
| General liability for judgment proof corporation | x | | |
|
||||
| Term life insurance | | x | |
|
||||
| Catastrophe Reinsurance, outside rating agency bounds | | x | |
|
||||
| High limit property per risk reinsurance | | x | |
|
||||
| Personal lines for affluent individuals | x | x | |
|
||||
| Small commercial lines | x | x | |
|
||||
| Catastrophe reinsurance, within rating agency bounds | x | x | |
|
||||
| Large account captive reinsurance | | | x |
|
||||
| Structured quota share, requiring a risk transfer test | x | | x |
|
||||
| Working layer casualty excess of loss | | x | x |
|
||||
| Surplus relief quota share on cat exposed line | x | x | x |
|
||||
| Middle market commercial lines work comp or commercial auto | x | x | x |
|
||||
|
||||
|
||||
'''
|
||||
|
||||
GT(txt)
|
||||
```
|
||||
|
||||
## List of lists
|
||||
|
||||
```{python}
|
||||
x = None
|
||||
if x:
|
||||
print(123)
|
||||
```
|
||||
|
||||
```{python}
|
||||
#| echo: true
|
||||
#| label: tbl-greater-tables-test-6
|
||||
#| tbl-cap: GT output for list of lists input
|
||||
lol = [['a', 'b', 'c', 'd'], ['west', 10, 20, 30], ['east', 10, 200, 30], ['north', 10, 20, 300], ['south', 100, 20, 30]]
|
||||
GT(lol)
|
||||
```
|
||||
|
||||
```{python}
|
||||
f = GT(lol)
|
||||
f
|
||||
```
|
||||
|
||||
```{python}
|
||||
tbl = '''
|
||||
|
||||
Var | Amount
|
||||
:---|------:
|
||||
A | 100.0
|
||||
B | 0.123
|
||||
C | A string
|
||||
|
||||
'''
|
||||
|
||||
def ff(x):
|
||||
if abs(x) < 1:
|
||||
return f'{x:.1%}'
|
||||
else:
|
||||
return f'{x:,.2f}'
|
||||
|
||||
sGT(tbl, table_float_format=ff)
|
||||
```
|
||||
|
||||
```{python}
|
||||
P = 1000 * 1.075**-10 + 120
|
||||
L = 1000
|
||||
ry = .1
|
||||
v = 1/(1+ry)
|
||||
T = 10
|
||||
pv = P - v**T * L
|
||||
fv = pv / v**T
|
||||
pv, fv
|
||||
```
|
||||
|
||||
|
||||
+615
-406
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user