{ "cells": [ { "cell_type": "markdown", "id": "9dba0829-9f58-4a30-ac7b-3068db32a9c7", "metadata": {}, "source": [ "# Other Visualizations\n", "\n", "`xyzpy` has two functions for visualizing arrays that are focused on quickly getting an overview of the values, and unlike plots don't require or assume any particular relationship between the dimensions. Think of these as visual pretty print functions. They are:\n", "\n", "- [`visualize_matrix`](xyzpy.visualize_matrix): for 2D or 1D arrays or sequences of arrays\n", "- [`visualize_tensor`](xyzpy.visualize_tensor): for ND arrays or sequences of arrays" ] }, { "cell_type": "code", "execution_count": 1, "id": "56d0f1c5-10b9-4f51-ada3-af8eccdc3740", "metadata": {}, "outputs": [], "source": [ "%config InlineBackend.figure_formats = ['svg']\n", "import numpy as np\n", "\n", "import xyzpy as xyz\n", "\n", "rng = np.random.default_rng(42)" ] }, { "cell_type": "markdown", "id": "83c4b346-e23e-4e67-8605-ccff64644e93", "metadata": {}, "source": [ "## Visualizing matrices\n", "\n", "[`visualize_matrix`](xyzpy.visualize_matrix) is for 2D or 1D arrays or sequences of arrays. It is a wrapper around [`matplotlib.pyplot.imshow`](matplotlib.pyplot.imshow)\n", " that adds some convenient and consistent defaults and a few extra features.\n", "\n", "For real arrays it shows positive numbers as blue and negative numbers as orange, the maximum magnitude is displayed below the legend (colorbar). By default the alpha channel is also mapped to magnitude, so that the more transparent a pixel is, the closer to zero the value is. This can be disabled with the ``alpha_map`` argument." ] }, { "cell_type": "code", "execution_count": 2, "id": "8020b5c6-b9ea-4b14-a828-616749068e5f", "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "x = rng.normal(size=(50, 100))\n", "xyz.visualize_matrix(x);" ] }, { "cell_type": "markdown", "id": "87ea7348-b2fb-4826-a384-8c00a04fe269", "metadata": {}, "source": [ "For complex matrices a color wheel is used, with positive imaginary values shown as green and negative imaginary values shown as pink. The magnitude is still shown below the legend, and the alpha channel is mapped to magnitude as with real matrices." ] }, { "cell_type": "code", "execution_count": 3, "id": "e03391f5-a3f6-4774-9b23-e8857c82f6fb", "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "x = rng.normal(size=(50, 30)) + 1j * rng.normal(size=(50, 30))\n", "xyz.visualize_matrix(x);" ] }, { "cell_type": "markdown", "id": "06291abe-3c55-4ad9-8940-fe9f462b692a", "metadata": {}, "source": [ "If you supply a sequence of arrays, they will shown as a row of images, sharing the same magnitude scale and legend (colorbar). 1D arrays shown as diagonal matrices." ] }, { "cell_type": "code", "execution_count": 4, "id": "ca85e8eb-52c9-4b61-8ac6-5204d47b5108", "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "fig, axs = xyz.visualize_matrix(\n", " [\n", " rng.normal(size=(10, 7)),\n", " rng.normal(size=(7)),\n", " rng.normal(size=(7, 11)),\n", " ]\n", ")" ] }, { "cell_type": "markdown", "id": "ddfde211-886f-40bc-b269-c2dfa93c09e4", "metadata": {}, "source": [ "More examples can be found here: {ref}`ex_viz_linalg`." ] }, { "cell_type": "markdown", "id": "df7eaf72-0c36-490a-809f-ba67a31e0dd5", "metadata": {}, "source": [ "## Visualizing tensors\n", "\n", "[`visualize_tensor`](xyzpy.visualize_tensor) is for ND arrays or sequences of arrays. It works by assigning an angle and associated unit vector to each dimension. The values are then drawn as circles on a grid which connects all neighboring points. The color of each circle, as with [`visualize_matrix`](xyzpy.visualize_matrix), is mapped to the magnitude and sign or phase of the number." ] }, { "cell_type": "code", "execution_count": 5, "id": "42667145-5228-4b05-aa3e-5b39371f7bcd", "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "x = rng.normal(size=(20, 40, 3)) + 1j * rng.normal(size=(20, 40, 3))\n", "\n", "# the compass kwarg shows which angles correspond to which dimensions\n", "xyz.visualize_tensor(x, compass=True);" ] }, { "cell_type": "code", "execution_count": 6, "id": "b98af78c-1572-490f-aae8-a7a7a661345f", "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "import matplotlib.pyplot as plt\n", "\n", "fig, axs = plt.subplots(1, 4, figsize=(10, 2.5))\n", "fig.patch.set_alpha(0)\n", "\n", "for i, ax in enumerate(axs, 1):\n", " x = rng.uniform(size=[3] * i)\n", " xyz.visualize_tensor(x, ax=ax, legend=False, compass=True)" ] }, { "cell_type": "markdown", "id": "703281a1-31d3-4858-8d08-173c1c518e17", "metadata": {}, "source": [ "While the plots can get very busy for higher dimensions, they can still be useful for getting a quick overview of the values in a tensor:" ] }, { "cell_type": "code", "execution_count": 7, "id": "f620b96b-5013-412f-94d5-70f58809b4ce", "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "shape = (2,) * 8\n", "\n", "x = np.ones(shape)\n", "x *= np.indices(shape).sum(0)\n", "x[(np.indices(shape).sum(0) % 2) == 1] *= -1\n", "\n", "xyz.visualize_tensor(x, compass=True);" ] }, { "cell_type": "markdown", "id": "f63123cc-89a5-417f-a2e8-fbbf6f52e199", "metadata": {}, "source": [ "To avoid aliasing / overlapping points, a slight skew is applied to the angles and scales mapped to each dimension, these can be controlled with the ``skew_angle_factor`` and ``skew_scale_factor`` arguments, for example turning the effect off:" ] }, { "cell_type": "code", "execution_count": 8, "id": "147420a8-b05d-4d9e-bcd7-2ba7ea0a0106", "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "xyz.visualize_tensor(x, skew_angle_factor=0, skew_scale_factor=0);" ] }, { "cell_type": "markdown", "id": "33682f90-19c3-4339-8f4a-ff94d3f1c37d", "metadata": {}, "source": [ "As an alternative to assigning a seperate angle to each dimension, you can also specify a maximum number of angles, or 'projections', and then multiple dimensions will be assigned to the same angle but with different scales (like fusing those axes)." ] }, { "cell_type": "code", "execution_count": 9, "id": "b8dff13c-60a7-4cd8-a1a8-03157c431606", "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "# max_projections=2 results in a matrix like visualization\n", "xyz.visualize_tensor(x, compass=True, max_projections=2);" ] }, { "cell_type": "markdown", "id": "42fedd1a-4901-4208-afaf-849e86a75e29", "metadata": {}, "source": [ "This matches up with the data layout if the axes were actually fused (reshaped):" ] }, { "cell_type": "code", "execution_count": 10, "id": "b40d8648-aa49-4686-87ab-e86f469ea7ee", "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "xyz.visualize_tensor(x.reshape(16, 16), compass=True);" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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" } }, "nbformat": 4, "nbformat_minor": 2 }