#### Note

The ``@`` symbol indicates matrix multiplication on NumPy arrays, and was\n introduced in Python 3.5 / NumPy 1.10. The notation ``plot(*point)`` uses\n Python `argument expansion`_ to \"unpack\" the elements of ``point`` into\n separate positional arguments to the function. In other words,\n ``plot(*point)`` expands to ``plot(3, 2, 5)``.

\n\nNotice that we used matrix multiplication to compute the projection of our\npoint $(3, 2, 5)$onto the $x, y$ plane:\n\n\\begin{align}\\left[\n \\begin{matrix} 1 & 0 & 0 \\\\ 0 & 1 & 0 \\\\ 0 & 0 & 0 \\end{matrix}\n \\right]\n \\left[ \\begin{matrix} 3 \\\\ 2 \\\\ 5 \\end{matrix} \\right] =\n \\left[ \\begin{matrix} 3 \\\\ 2 \\\\ 0 \\end{matrix} \\right]\\end{align}\n\n...and that applying the projection again to the result just gives back the\nresult again:\n\n\\begin{align}\\left[\n \\begin{matrix} 1 & 0 & 0 \\\\ 0 & 1 & 0 \\\\ 0 & 0 & 0 \\end{matrix}\n \\right]\n \\left[ \\begin{matrix} 3 \\\\ 2 \\\\ 0 \\end{matrix} \\right] =\n \\left[ \\begin{matrix} 3 \\\\ 2 \\\\ 0 \\end{matrix} \\right]\\end{align}\n\nFrom an information perspective, this projection has taken the point\n$x, y, z$ and removed the information about how far in the $z$\ndirection our point was located; all we know now is its position in the\n$x, y$ plane. Moreover, applying our projection matrix to *any point*\nin $x, y, z$ space will reduce it to a corresponding point on the\n$x, y$ plane. The term for this is a *subspace*: the projection matrix\nprojects points in the original space into a *subspace* of lower dimension\nthan the original. The reason our subspace is the $x,y$ plane (instead\nof, say, the $y,z$ plane) is a direct result of the particular values\nin our projection matrix.\n\n\n### Example: projection as noise reduction\n\nAnother way to describe this \"loss of information\" or \"projection into a\nsubspace\" is to say that projection reduces the rank (or \"degrees of\nfreedom\") of the measurement \u2014 here, from 3 dimensions down to 2. On the\nother hand, if you know that measurement component in the $z$ direction\nis just noise due to your measurement method, and all you care about are the\n$x$ and $y$ components, then projecting your 3-dimensional\nmeasurement into the $x, y$ plane could be seen as a form of noise\nreduction.\n\nOf course, it would be very lucky indeed if all the measurement noise were\nconcentrated in the $z$ direction; you could just discard the $z$\ncomponent without bothering to construct a projection matrix or do the matrix\nmultiplication. Suppose instead that in order to take that measurement you\nhad to pull a trigger on a measurement device, and the act of pulling the\ntrigger causes the device to move a little. If you measure how\ntrigger-pulling affects measurement device position, you could then \"correct\"\nyour real measurements to \"project out\" the effect of the trigger pulling.\nHere we'll suppose that the average effect of the trigger is to move the\nmeasurement device by $(3, -1, 1)$:\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"trigger_effect = np.array([[3, -1, 1]]).T"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Knowing that, we can compute a plane that is orthogonal to the effect of the\ntrigger (using the fact that a plane through the origin has equation\n$Ax + By + Cz = 0$ given a normal vector $(A, B, C)$), and\nproject our real measurements onto that plane.\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# compute the plane orthogonal to trigger_effect\nx, y = np.meshgrid(np.linspace(-1, 5, 61), np.linspace(-1, 5, 61))\nA, B, C = trigger_effect\nz = (-A * x - B * y) / C\n# cut off the plane below z=0 (just to make the plot nicer)\nmask = np.where(z >= 0)\nx = x[mask]\ny = y[mask]\nz = z[mask]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Computing the projection matrix from the ``trigger_effect`` vector is done\nusing `singular value decomposition