Created
May 27, 2022 19:38
-
-
Save taldcroft/0d5c733378d26816a1b8bb0c687d72a2 to your computer and use it in GitHub Desktop.
Custom table class for custom multidimensional array formatting
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "code", | |
"execution_count": 17, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"from astropy.table.pprint import (\n", | |
" TableFormatter, \n", | |
" dtype_info_name, \n", | |
" get_auto_format_func\n", | |
")\n", | |
"from astropy.table import Table\n", | |
"import numpy as np\n" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 24, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"def _possible_string_format_functions(format_):\n", | |
" \"\"\"Iterate through possible string-derived format functions.\n", | |
"\n", | |
" A string can either be a format specifier for the format built-in,\n", | |
" a new-style format string, or an old-style format string.\n", | |
" \"\"\"\n", | |
" yield lambda format_, val: format(val, format_)\n", | |
" yield lambda format_, val: format_.format(val)\n", | |
" yield lambda format_, val: format_ % val\n", | |
" yield lambda format_, val: format_.format(**{k: val[k] for k in val.dtype.names})\n", | |
"\n", | |
" # NEW to allow formatting an array of values\n", | |
" yield lambda format_, val: format_.format(*val)\n" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 25, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"class CustomTableFormatter(TableFormatter):\n", | |
" def _pformat_col_iter(self, col, max_lines, show_name, show_unit, outs,\n", | |
" show_dtype=False, show_length=None):\n", | |
" \"\"\"Iterator which yields formatted string representation of column values.\n", | |
"\n", | |
" Parameters\n", | |
" ----------\n", | |
" max_lines : int\n", | |
" Maximum lines of output (header + data rows)\n", | |
"\n", | |
" show_name : bool\n", | |
" Include column name. Default is True.\n", | |
"\n", | |
" show_unit : bool\n", | |
" Include a header row for unit. Default is to show a row\n", | |
" for units only if one or more columns has a defined value\n", | |
" for the unit.\n", | |
"\n", | |
" outs : dict\n", | |
" Must be a dict which is used to pass back additional values\n", | |
" defined within the iterator.\n", | |
"\n", | |
" show_dtype : bool\n", | |
" Include column dtype. Default is False.\n", | |
"\n", | |
" show_length : bool\n", | |
" Include column length at end. Default is to show this only\n", | |
" if the column is not shown completely.\n", | |
" \"\"\"\n", | |
" max_lines, _ = self._get_pprint_size(max_lines, -1)\n", | |
" dtype = getattr(col, 'dtype', None)\n", | |
" multidims = getattr(col, 'shape', [0])[1:]\n", | |
" # *** CUT from original ***\n", | |
" # if multidims:\n", | |
" # multidim0 = tuple(0 for n in multidims)\n", | |
" # multidim1 = tuple(n - 1 for n in multidims)\n", | |
" # trivial_multidims = np.prod(multidims) == 1\n", | |
"\n", | |
" i_dashes = None\n", | |
" i_centers = [] # Line indexes where content should be centered\n", | |
" n_header = 0\n", | |
" if show_name:\n", | |
" i_centers.append(n_header)\n", | |
" # Get column name (or 'None' if not set)\n", | |
" col_name = str(col.info.name)\n", | |
" n_header += 1\n", | |
" yield self._name_and_structure(col_name, dtype)\n", | |
" if show_unit:\n", | |
" i_centers.append(n_header)\n", | |
" n_header += 1\n", | |
" yield str(col.info.unit or '')\n", | |
" if show_dtype:\n", | |
" i_centers.append(n_header)\n", | |
" n_header += 1\n", | |
" if dtype is not None:\n", | |
" col_dtype = dtype_info_name((dtype, multidims))\n", | |
" else:\n", | |
" col_dtype = col.__class__.__qualname__ or 'object'\n", | |
" yield col_dtype\n", | |
" if show_unit or show_name or show_dtype:\n", | |
" i_dashes = n_header\n", | |
" n_header += 1\n", | |
" yield '---'\n", | |
"\n", | |
" max_lines -= n_header\n", | |
" n_print2 = max_lines // 2\n", | |
" n_rows = len(col)\n", | |
"\n", | |
" # This block of code is responsible for producing the function that\n", | |
" # will format values for this column. The ``format_func`` function\n", | |
" # takes two args (col_format, val) and returns the string-formatted\n", | |
" # version. Some points to understand:\n", | |
" #\n", | |
" # - col_format could itself be the formatting function, so it will\n", | |
" # actually end up being called with itself as the first arg. In\n", | |
" # this case the function is expected to ignore its first arg.\n", | |
" #\n", | |
" # - auto_format_func is a function that gets called on the first\n", | |
" # column value that is being formatted. It then determines an\n", | |
" # appropriate formatting function given the actual value to be\n", | |
" # formatted. This might be deterministic or it might involve\n", | |
" # try/except. The latter allows for different string formatting\n", | |
" # options like %f or {:5.3f}. When auto_format_func is called it:\n", | |
"\n", | |
" # 1. Caches the function in the _format_funcs dict so for subsequent\n", | |
" # values the right function is called right away.\n", | |
" # 2. Returns the formatted value.\n", | |
" #\n", | |
" # - possible_string_format_functions is a function that yields a\n", | |
" # succession of functions that might successfully format the\n", | |
" # value. There is a default, but Mixin methods can override this.\n", | |
" # See Quantity for an example.\n", | |
" #\n", | |
" # - get_auto_format_func() returns a wrapped version of auto_format_func\n", | |
" # with the column id and possible_string_format_functions as\n", | |
" # enclosed variables.\n", | |
" col_format = col.info.format or getattr(col.info, 'default_format',\n", | |
" None)\n", | |
" pssf = (getattr(col.info, 'possible_string_format_functions', None)\n", | |
" or _possible_string_format_functions)\n", | |
" auto_format_func = get_auto_format_func(col, pssf)\n", | |
" format_func = col.info._format_funcs.get(col_format, auto_format_func)\n", | |
"\n", | |
" if len(col) > max_lines:\n", | |
" if show_length is None:\n", | |
" show_length = True\n", | |
" i0 = n_print2 - (1 if show_length else 0)\n", | |
" i1 = n_rows - n_print2 - max_lines % 2\n", | |
" indices = np.concatenate([np.arange(0, i0 + 1),\n", | |
" np.arange(i1 + 1, len(col))])\n", | |
" else:\n", | |
" i0 = -1\n", | |
" indices = np.arange(len(col))\n", | |
"\n", | |
" def format_col_str(idx):\n", | |
" if multidims:\n", | |
" # *** CUT from original ***\n", | |
" # if trivial_multidims:\n", | |
" # return format_func(col_format, col[(idx,) + multidim0])\n", | |
" # else:\n", | |
" # left = format_func(col_format, col[(idx,) + multidim0])\n", | |
" # right = format_func(col_format, col[(idx,) + multidim1])\n", | |
" # return f'{left} .. {right}' \n", | |
" return format_func(col_format, col[idx].ravel())\n", | |
" else:\n", | |
" return format_func(col_format, col[idx])\n", | |
"\n", | |
" # Add formatted values if within bounds allowed by max_lines\n", | |
" for idx in indices:\n", | |
" if idx == i0:\n", | |
" yield '...'\n", | |
" else:\n", | |
" try:\n", | |
" yield format_col_str(idx)\n", | |
" except ValueError:\n", | |
" raise ValueError(\n", | |
" 'Unable to parse format string \"{}\" for entry \"{}\" '\n", | |
" 'in column \"{}\"'.format(col_format, col[idx],\n", | |
" col.info.name))\n", | |
"\n", | |
" outs['show_length'] = show_length\n", | |
" outs['n_header'] = n_header\n", | |
" outs['i_centers'] = i_centers\n", | |
" outs['i_dashes'] = i_dashes\n", | |
"\n", | |
" " | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 26, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"class CustomTable(Table):\n", | |
" TableFormatter = CustomTableFormatter" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 41, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"t = CustomTable()\n", | |
"t['a'] = [[1,2,3], [3,4,5]]\n", | |
"t['b'] = [[1,2], [3,4]]\n", | |
"t['c'] = [1, 2]" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 42, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"t['a'].format = 'val:{:.2f} min:{:.2f} max:{:.2f}'" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 43, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/html": [ | |
"<div><i>CustomTable length=2</i>\n", | |
"<table id=\"table140551740400064\" class=\"table-striped table-bordered table-condensed\">\n", | |
"<thead><tr><th>a</th><th>b</th><th>c</th></tr></thead>\n", | |
"<thead><tr><th>int64[3]</th><th>int64[2]</th><th>int64</th></tr></thead>\n", | |
"<tr><td>val:1.00 min:2.00 max:3.00</td><td>[1 2]</td><td>1</td></tr>\n", | |
"<tr><td>val:3.00 min:4.00 max:5.00</td><td>[3 4]</td><td>2</td></tr>\n", | |
"</table></div>" | |
], | |
"text/plain": [ | |
"<CustomTable length=2>\n", | |
" a b c \n", | |
" int64[3] int64[2] int64\n", | |
"-------------------------- -------- -----\n", | |
"val:1.00 min:2.00 max:3.00 [1 2] 1\n", | |
"val:3.00 min:4.00 max:5.00 [3 4] 2" | |
] | |
}, | |
"execution_count": 43, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"t" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [] | |
} | |
], | |
"metadata": { | |
"interpreter": { | |
"hash": "30d701cce601284c0078c07ed56c46ab9cf6c839e2bfdcf6e0951a8805cc9206" | |
}, | |
"kernelspec": { | |
"display_name": "Python 3.8.12 ('astropy')", | |
"language": "python", | |
"name": "python3" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython3", | |
"version": "3.8.12" | |
}, | |
"orig_nbformat": 4 | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 2 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment