{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Intro to Matrices"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n",
"Matrices are a natural extension of the vectors that we have been working with in the last couple reading; where a vector is a collection of data of the same type ordered along a *single* dimension, a matrix is a collection of data of the same type ordered along *two* dimensions.\n",
"\n",
"If you've taken a linear algebra course before, the idea of a matrix will be very familiar, but if you haven't, don't fret! You can also think of a matrix as either (a) a collection of vectors lined up side-by-side, or, if it feels more familiar, (b) an Excel spreadsheet with data arranged in rows and columns. For example a 3x3 matrix might look something like:\n",
"\n",
"$$\n",
"\\begin{bmatrix} \n",
"1 & 2 & 3 \\\\\n",
"4 & 5 & 6 \\\\\n",
"7 & 8 & 9 \\\\\n",
"\\end{bmatrix}\n",
"\\quad\n",
"$$\n",
"\n",
"Just as vectors are commonly used in data science because we usually don't just have a single observation of data, but instead lots of observations (different customers) that we might want to put into vector, so too do we often have information not just on one type of measurement (amount of last purchase), but lots of different measurements about each observation (weeks since first purchase, weeks since last purchase, total spent over customer lifetime). Matrices are commonly used to represent this type of data by using each row for an observation (a customer), and each column for a different thing were measuring.\n",
"\n",
"For example, suppose we were conducting an opinion survey, and we surveyed four people. Further, suppose our first respondent was twenty years old, had an income of 22,000 dollars, and twelve years of education, the second respondent was thirty-five years old, had an income of 65,000 dollars, and sixteen years of education, and the third and fourth respondents were fifty-five and forty-five years old, had incomes of 19,000 and 35,000 dollars, and had eleven and twelve years of education, respectively. We could represent that information in a matrix that looks like:\n",
"\n",
"$$\n",
"\\begin{bmatrix} \n",
"20 & 22,000 & 12 \\\\\n",
"35 & 65,000 & 16 \\\\\n",
"55 & 19,000 & 11 \\\\\n",
"45 & 35,000 & 12 \\\\\n",
"\\end{bmatrix}\n",
"\\quad\n",
"$$\n",
"\n",
"And while it may not be immediately obvious why, this way of representing our data will turn out to not only be a useful organizational scheme, but also be incredibly valuable for statistical analyses.\n",
"\n",
"## Why Learn About Matrices?\n",
"\n",
"There are (at least) four reasons to learn about matrices as a data scientist. The first is that a matrix is one of the standard ways that we organize tabular data -- data where each row is a different *observation* or *individual unit* we are studying, and each column is a different property about which we have data for those observations. So we'll use matrices directly a lot. \n",
"\n",
"The second is that matrices are a natural steppingstone from vectors to arrays of arbitrary dimension (N-D arrays), as well as another data structure we'll work with a lot: pandas `DataFrames`. So everything we learn here will be immediately applicable in our following lessons.\n",
"\n",
"The third is that matrices are also the standard way we represent image data, which is also commonly used by data scientists interested in computer vision and image processing. A picture is just a grid of pixels, each containing information about the color containing in that pixel. Matrices are the natural way to represent this type of gridded data, so as we'll see below we can easily represent images as matrices. \n",
"\n",
"And the fourth reason is that matrices (and linear algebra, the mathematics of matrices) underlie nearly all the statistical models that we use in data science (like linear regression, logistic regression models, SVMs, etc.). So when we begin to work with statistics and machine learning packages, matrices will be inescapable."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Constructing Matrices\n",
"\n",
"As with vectors, there are several ways of constructing matrices. \n",
"\n",
"The first is simply passing lists of lists to `np.array`. For example, here we can create the matrix of survey responses we talked about above by putting each row into a list, then putting those lists into a bigger list and passing it to `np.array`. This will give use our matrix where each row represents a different person, and the columns represent respondent age, income, and years of education. \n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[ 20, 22000, 12],\n",
" [ 35, 65000, 16],\n",
" [ 55, 19000, 11],\n",
" [ 45, 35000, 12]])"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import numpy as np\n",
"\n",
"survey = np.array(\n",
" [[20, 22_000, 12], [35, 65_000, 16], [55, 19_000, 11], [45, 35_000, 12]]\n",
")\n",
"\n",
"survey"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Great! We can already see from how this has been printed out that this is a matrix with four rows and three columns, but we can also verify this directly by checking the `.shape` attribute of our matrix:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(4, 3)"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"survey.shape"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As we can see, `.shape` reports that our matrix has four rows (the first entry) and three columns (the second entry). Moreover, the fact we see two entries is what tells us that this is a 2-dimensional array."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In addition, we can also build matrices with the tools like `ones` and `zeros` we saw before by specifying the size of the result we want with a tuple containing the number of rows and the number of columns we want in the result:"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[1., 1., 1., 1., 1.],\n",
" [1., 1., 1., 1., 1.]])"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"np.ones((2, 5))"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[0., 0.],\n",
" [0., 0.],\n",
" [0., 0.],\n",
" [0., 0.]])"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"np.zeros((4, 2))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"(As with vectors, the `.` appearing after the `1`s and `0`s above is numpy's way of telling us that these are arrays of floats, not integers, even though these numbers could be stored as integers.)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Typing\n",
"\n",
"As with vectors, all the entries in a matrix must be of the same type—in other words, matrices are a *homogenously typed* data structure. And as with vectors, we can check the types of our matrices using `.dtype`:\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"dtype('int64')"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"survey.dtype"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"dtype('float64')"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"np.ones((2, 5)).dtype"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Math with Matrices\n",
"\n",
"Just like vectors, we can use matrices to do math in especially efficient ways.\n",
"\n",
"Suppose, for example, we had a dataset of with the salaries of three people where the first column is each person's salary before they got a technical certification, and the second column is each person's salary after receiving the technical certification: "
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[30000, 37000],\n",
" [42000, 45000],\n",
" [22000, 29000]])"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"salaries = np.array([[30_000, 37_000], [42_000, 45_000], [22_000, 29_000]])\n",
"salaries"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now suppose I wanted to convert these salaries in dollars to salaries in thousands of dollars to make the table easier to fit on a graph. I can just do:"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[30., 37.],\n",
" [42., 45.],\n",
" [22., 29.]])"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"salaries_in_thousands = salaries / 1000\n",
"salaries_in_thousands"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Similarly, matrices can be added if they have the same size. If we also had a matrix of tax refunds, and we wanted to calculate everyone's total after-tax incomes, we could just add the matrices:"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[5000, 3000],\n",
" [4000, 4000],\n",
" [8000, 7000]])"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"refunds = np.array([[5_000, 3_000], [4_000, 4_000], [8_000, 7_000]])\n",
"refunds"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[35000, 40000],\n",
" [46000, 49000],\n",
" [30000, 36000]])"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"total_income = salaries + refunds\n",
"total_income"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Of course, we won't always want to do full-matrix manipulations -- for example, we might want to adjust one income column for inflation to ensure the two incomes are comparable. Manipulating *subsets* of matrices (such as individual rows, columns, or entries) is something we'll discuss in detail in our next reading. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Exercises \n",
"\n",
"1. Using `np.array`, create a new matrix with two rows and three columns. In the first row, place information about yourself, and in the second row place information about a good friend. In the first column enter your ages, in the second column enter an estimate of your current income rounded to the nearest thousand, and in the third column add a `1` if you identify as a woman and a `0` otherwise.\n",
"2. Confirm the shape of your matrix with `.shape`.\n",
"3. What data type is your matrix?\n",
"4. Without writing any code, what data type matrix do you think you would get if, instead of rounding your income to the nearest thousand, you entered your income to the nearest cent (or your local currency's decimal equivalent—Indian paise, British pence, etc.)?\n",
"5. Check your answer to number 4 above in Python."
]
}
],
"metadata": {
"interpreter": {
"hash": "3e0a5228cb9726a24d36227c69ed0d3aac98cecda769d1c9adb080711d57f90d"
},
"kernelspec": {
"display_name": "R",
"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.9.7"
},
"orig_nbformat": 4
},
"nbformat": 4,
"nbformat_minor": 2
}