Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save taldcroft/0d5c733378d26816a1b8bb0c687d72a2 to your computer and use it in GitHub Desktop.
Save taldcroft/0d5c733378d26816a1b8bb0c687d72a2 to your computer and use it in GitHub Desktop.
Custom table class for custom multidimensional array formatting
Display the source blob
Display the rendered blob
Raw
{
"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