{ "cells": [ { "cell_type": "markdown", "id": "8bcec56d-cdf3-436a-b51f-3e24967a7e77", "metadata": {}, "source": [ "# `pandas`\n", "\n", "Now that we've discussed Scientific Computing generally and introduced `NumPy`, we're ready to learn the basics of `pandas`, the most popular python package for working with data. Similar to what we saw with `NumPy`, we'll first introduce the core object at the heart of `pandas` -- the DataFrame. From there, we'll introduce some of the attributes and most common methods for working with `DataFrame`s. And, we'll finish discussing additional functions within `pandas` that make working with data more straightforward.\n", "\n", "As with `NumPy`, you'll need to first `import` `pandas`. Convention is to import: `import padas as pd`, so we'll encourage that here. Using this import statement, any time you want to reference `pandas` functionality, you can do so with just the two letters `pd`.\n", "\n", "
\n", "Be sure to import pandas using import pandas as pd before attempting to run any of the included code in this section.\n", "
" ] }, { "cell_type": "code", "execution_count": 1, "id": "c8ab7a7b-eaf3-4726-a9ce-4e8a5c40744a", "metadata": {}, "outputs": [], "source": [ "import pandas as pd" ] }, { "cell_type": "markdown", "id": "f913c649-f992-4180-8314-eb64028cdbb3", "metadata": {}, "source": [ "## Heterogeneous Data\n", "\n", "While `NumPy` arrays make working with homogenous data (data that are all of one type, typically numbers), `pandas` adds functionality for working with heterogeneous data. What if you have information about individual's heights (floats) but also the color of their eyes (strings)? This would be an example of heterogeneous data and is precisely what `pandas` is designed to handle.\n", "\n", "More specifically, a `DataFrame` can store multiple types of data; however, each individual column will store a single type of data. " ] }, { "cell_type": "markdown", "id": "1688be98-6b47-4cc6-b0de-e8ca4a882c9d", "metadata": {}, "source": [ "## `pandas DataFrame`\n", "\n", "Enter, the `DataFrame` - the central object within `pandas`. `DataFrame`s are a 2-dimensional data structure that store data in a table/array, but that enable continous (numeric) and categorical (discrete and text-based) data to be stored in a single object. `DataFrame`s enable information to be stored in rows and columns (like an array); however, columns can be of mixed type and both rows and columns can have labels. If you're envisioning data stored in a spreadsheet-like object, you've got the right idea. For example, a `DataFrame` could be used to store the following information: \n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Participant IDHeight (cm)Eye Color
01170Blue
02165Green
03180Brown
04175Hazel
05160Brown
\n", "\n", "In fact, we can store that very information in a pandas DataFrame using `pd.DataFrame` and passing in the data:" ] }, { "cell_type": "code", "execution_count": 2, "id": "c27e6e12-866d-410f-9be0-d384463447a5", "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Participant IDHeight (cm)Eye Color
001170Blue
102165Green
203180Brown
304175Hazel
405160Brown
\n", "
" ], "text/plain": [ " Participant ID Height (cm) Eye Color\n", "0 01 170 Blue\n", "1 02 165 Green\n", "2 03 180 Brown\n", "3 04 175 Hazel\n", "4 05 160 Brown" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df = pd.DataFrame({'Participant ID': ['01', '02', '03', '04', '05'],\n", " 'Height (cm)': [170, 165, 180, 175, 160],\n", " 'Eye Color': ['Blue', 'Green', 'Brown', 'Hazel', 'Brown']\n", " })\n", "df" ] }, { "cell_type": "markdown", "id": "48b8f7af-7897-43ff-b9f1-0e77b7eb428f", "metadata": {}, "source": [ "We'll note a few things about this `df` object:\n", "1. We've called the object `df`. This is a convention you'll often see when people are working with `DataFrame`s with `df` being short for `df`. We'll use this convention frequently, but you could give your `DataFrame`s more informative names (i.e. `df_participants`).\n", "2. In `df` you'll notice that there are three column names at top.\n", "3. You'll also notice there are indices off to the left for each row. By default, `pandas` will assign each row an index starting with zero. " ] }, { "cell_type": "markdown", "id": "95d2190b-688e-418d-8649-a2542e449bd3", "metadata": {}, "source": [ "## Attributes\n", "\n", "As with arrays, when working with `DataFrame` objects, it's often helpful to be able to access information about the `DataFrame` by accessing attributes. \n", "\n", "### `shape`\n", "To get the dimensions of the `DataFrame`, the attribute is the same as in `Numpy`: `shape`" ] }, { "cell_type": "code", "execution_count": 3, "id": "7fe2904b-b4c6-4488-b7c8-630c8358d183", "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "(5, 3)" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.shape" ] }, { "cell_type": "markdown", "id": "e335dff4-4106-4ed8-b21d-761c974ea73d", "metadata": {}, "source": [ "Again, similar to arrays, the output specifies the number of rows first (5) and the number of columns second (3)." ] }, { "cell_type": "markdown", "id": "01121b34-bf36-425c-9960-accbbc09f404", "metadata": {}, "source": [ "### `columns` & `index`\n", "\n", "If you ever need to access the names of the columns in a `DataFrame` it can be helpful to know that the column names can be extracted from the `columns` attribute:" ] }, { "cell_type": "code", "execution_count": 4, "id": "b4be383e-aaa3-49b9-9b33-02d83f5ad3a5", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Index(['Participant ID', 'Height (cm)', 'Eye Color'], dtype='object')" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.columns" ] }, { "cell_type": "markdown", "id": "db1590bb-5b01-4e20-a8d3-f25f0e0cdb03", "metadata": {}, "source": [ "The same goes for extracting the indices (row names). This can be accessed through `index`:" ] }, { "cell_type": "code", "execution_count": 5, "id": "8b1aaa04-33c5-4b48-bcf3-65c2e56965ed", "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "RangeIndex(start=0, stop=5, step=1)" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.index" ] }, { "cell_type": "markdown", "id": "9f41bbbf-6e93-4cf0-957a-b34b36063cde", "metadata": {}, "source": [ "### `dtypes`\n", "\n", "`dtypes` can also be helpful to determine *how* the `DataFrame` is storing your data. This is particularly important in `pandas` due to the fact that DataFrames can store heterogenous data...and `pandas` makes its best guess as to the information stored in the `DataFrame`. This process is not perfect. If you're working with a very large dataset and you expect all of the values in a given column to be numbers, you may *assume* that the column stores numbers. However, what if one of the values in your large dataset was actually the word \"nine feet\"? In that case, `pandas` would convert everything to strings in that column as it doesn't know how to handle \"nine feet\" as a number (even if we humans may!). This is why it's good practice to just double check that each of your columns is of the expected type using the `dtypes` attribute:" ] }, { "cell_type": "code", "execution_count": 6, "id": "d3c7e7b8-fbe8-4b25-9b83-d6cf9e42e347", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Participant ID object\n", "Height (cm) int64\n", "Eye Color object\n", "dtype: object" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.dtypes" ] }, { "cell_type": "markdown", "id": "c35e9750-76be-4088-b1f8-d49ffe92cc04", "metadata": {}, "source": [ "In the above, you may have assumed that Participant ID would be an integer; however, despite appearing as numbers these IDs were entered as strings, making them `object` type variables.\n", "\n", "### head\n", "\n", "Also note that `head` returns the first five rows of a `DataFrame`. This can be helpful when working with larger `DataFrame`s just to get a sene of the data. \n", "\n", "Note that there is both a `head` *attribute*:" ] }, { "cell_type": "code", "execution_count": 7, "id": "8455e5ac-8bb6-4302-97e1-ea1e9eb763bb", "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.head" ] }, { "cell_type": "markdown", "id": "256289f3-58f3-4f80-a8f4-d625c23f42f5", "metadata": {}, "source": [ "...and a `head()` *method*:" ] }, { "cell_type": "code", "execution_count": 8, "id": "0b365189-d320-4ee2-96a2-df6423b9d63b", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Participant IDHeight (cm)Eye Color
001170Blue
102165Green
203180Brown
304175Hazel
405160Brown
\n", "
" ], "text/plain": [ " Participant ID Height (cm) Eye Color\n", "0 01 170 Blue\n", "1 02 165 Green\n", "2 03 180 Brown\n", "3 04 175 Hazel\n", "4 05 160 Brown" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.head()" ] }, { "cell_type": "markdown", "id": "e78673e4-4dab-4723-95f2-96da9f30c568", "metadata": {}, "source": [ "The difference is in how the data are displayed, with the method displaying the data in a slightly more reader-friendly fasion." ] }, { "cell_type": "markdown", "id": "d4084db1-8fbf-4415-aac1-d89f6de4f572", "metadata": {}, "source": [ "## Indexing & Slicing\n", "\n", "So far we've introduced the `DataFrame` as a way to store heterogenous data in rows and columns. But, I haven't mentioned exactly how `pandas` accomplishes that. While the `DataFrame` is the core object, it's also important to be aware that `DataFrame`s are comprised of **`Series`**, which are a 1-dimensional labeled array storing data. Each column in `pandas` is a `Series` object and can be accessed using the column name and indexing. For example, if we wanted to extract the `'Height'` column from our `df` we could do so using: " ] }, { "cell_type": "code", "execution_count": 9, "id": "97c6d823-136a-42a1-8789-e86203ca6bda", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0 170\n", "1 165\n", "2 180\n", "3 175\n", "4 160\n", "Name: Height (cm), dtype: int64" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df['Height (cm)']" ] }, { "cell_type": "markdown", "id": "9dc36fca-8e2e-4be6-af70-ab23abafcc5b", "metadata": {}, "source": [ "In the above, we can see that indexing into a `DataFrame` by specifying the column name, `pandas` will return all values in that column, along with their corresponding indices. It also reports the type of the variable and the name of the column.\n", "\n", "Specifically, the way `pandas` stores each column is as a `pd.Series` type object. We can see this explicitly by checking the `type` of the above output:" ] }, { "cell_type": "code", "execution_count": 10, "id": "181563ba-de06-43ed-9e0d-704c33b68f8e", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "pandas.core.series.Series" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(df['Height (cm)'])" ] }, { "cell_type": "markdown", "id": "15c9a41e-71f4-49b0-84e7-733c67c0b01d", "metadata": {}, "source": [ "Thus, conceptually, every `DataFrame` is comprised of `Series` of the same length stacked next to one another.\n", "\n", "We're also able to access information stored in rows; however, the syntax has to differ a little bit. `df[0]` will not work here, as `pandas` would be looking for a *column* with the label `0`. Rather, when we want to access data in a row, we have to return to our familiar slicing syntax, first specifing the row(s) we want to return using `start:stop` notation.\n", "\n", "For example, `df[:1]` will return from the beginning of the `DataFrame` up to but not including the row with the index `1` (using our familiar slicing approach):" ] }, { "cell_type": "code", "execution_count": 11, "id": "6a5eb984-dba9-45dc-ac29-51616927e841", "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Participant IDHeight (cm)Eye Color
001170Blue
\n", "
" ], "text/plain": [ " Participant ID Height (cm) Eye Color\n", "0 01 170 Blue" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df[:1]" ] }, { "cell_type": "markdown", "id": "d9aed219-e2b3-421f-89b0-065748b103f1", "metadata": {}, "source": [ "However, we will highlight here that what is being returned here is a `DataFrame` (a slice of the original) and not a `Series`:" ] }, { "cell_type": "code", "execution_count": 12, "id": "522b1f6c-7e97-4dcc-a899-1bb3bf58ce57", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "pandas.core.frame.DataFrame" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(df[:1])" ] }, { "cell_type": "markdown", "id": "bd876dfb-1156-4e4e-b4d9-9f01267ce399", "metadata": {}, "source": [ "`pd.Series` objects are specifically for data for a single variable in a *column*...and not for returning subsets of a larger `DataFrame` across columns. If multiple columns are involved, then it's a `DataFrame` object.\n", "\n", "If you wanted to extract the information as a series, you would need to use `loc`..." ] }, { "cell_type": "markdown", "id": "d5fa8df8-bb51-4394-b0a2-e226403718fa", "metadata": {}, "source": [ "### `.loc`\n", "\n", "But, what if you want to combine these two ideas? What if you want to specify a few rows and a few columns? This is where `loc` comes in. `loc` (standing for loccation) allows users to control which part of the `DataFrame` they want to return.\n", "\n", "For example, if we wanted the second and third rows and only the `Eye Color` column from `df` we could use the following:" ] }, { "cell_type": "code", "execution_count": 13, "id": "3dea026e-355a-4c58-9a71-ba33ca100573", "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "1 Green\n", "2 Brown\n", "Name: Eye Color, dtype: object" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.loc[1:2, 'Eye Color']" ] }, { "cell_type": "markdown", "id": "52655387-50a3-4f08-899f-e0b89c127b86", "metadata": {}, "source": [ "In the above syntax, notice that the convention of `[row, column]` is still followed. However, the `start:stop` for `loc` *is* inclusive of the `stop` value (unlike everywhere else in `Python`). Thus, to return the second and third rows, you specify that you want to start at index `1` and stop at (*and include*) index `2`. " ] }, { "cell_type": "markdown", "id": "e0f88ff9-99bb-47fe-88f1-61b50fba6f5d", "metadata": {}, "source": [ "Returning to our goal of extracting the data in the first row from above, if we wanted to extract just the values in a given rows as a `Series`, we could use `loc` to do so, first specifiying the index and then to extract all columns:" ] }, { "cell_type": "code", "execution_count": 14, "id": "968b6acd-2141-4c55-bc9d-552e4817a547", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Participant ID 01\n", "Height (cm) 170\n", "Eye Color Blue\n", "Name: 0, dtype: object" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.loc[0,:]" ] }, { "cell_type": "markdown", "id": "71d1a14a-39b1-4c1b-8b82-e36dca58aa62", "metadata": {}, "source": [ "Finally, putting all of this together, `loc` can also be used to extract singular values, by specifying a single location within the `DataFrame`:" ] }, { "cell_type": "code", "execution_count": 15, "id": "ce8ba469-0025-485e-bac9-9c2a0edc68f8", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Blue'" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.loc[0, 'Eye Color']" ] }, { "cell_type": "markdown", "id": "27f92a56-e6b1-4a1f-a6a5-3e82f9c80fcf", "metadata": {}, "source": [ "Note that the above returns a value of the type of data directly, and not a `DataFrame` or `Series`. Thus, 'Blue' here is a string." ] }, { "cell_type": "markdown", "id": "78bb6263-d6a9-4933-aaef-bbb2903bde42", "metadata": {}, "source": [ "## Methods\n", "\n", "There are [*many, many* methods](https://pandas.pydata.org/docs/reference/frame.html) available for `DataFrame` objects. This section will only cover a few of the most commonly used, but will give you a sense of the *types* of things one can do with a `DataFrame`.\n", "\n", "### `describe()`\n", "\n", "One powerful method is the `describe()` method, which will calculate and provide a number of descriptive statistics for all numeric variables in your dataset. In `df`, we have a single numeric varaible (`Height (cm)`), which is summarized below:" ] }, { "cell_type": "code", "execution_count": 16, "id": "58703860-272e-4e65-baa9-2f2996304885", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Height (cm)
count5.000000
mean170.000000
std7.905694
min160.000000
25%165.000000
50%170.000000
75%175.000000
max180.000000
\n", "
" ], "text/plain": [ " Height (cm)\n", "count 5.000000\n", "mean 170.000000\n", "std 7.905694\n", "min 160.000000\n", "25% 165.000000\n", "50% 170.000000\n", "75% 175.000000\n", "max 180.000000" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.describe()" ] }, { "cell_type": "markdown", "id": "a2c1218b-75be-4dfe-9583-9369e9ce030d", "metadata": {}, "source": [ "### `replace()`\n", "\n", "Of course, `DataFrame`s are mutable, with the ability to change or update values after creation, using `replace`:\n", "\n", "For example, what if I realized that the participant IDs should be zero padded with *two* zeroes instead of one. To accomplish this, we can use the `replace` method to change each `'0'` into `'00'`:" ] }, { "cell_type": "code", "execution_count": 17, "id": "980883e3-a3e7-467e-a7ec-4e3c3bda8cd9", "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Participant IDHeight (cm)Eye Color
0001170Blue
1002165Green
2003180Brown
3004175Hazel
4005160Brown
\n", "
" ], "text/plain": [ " Participant ID Height (cm) Eye Color\n", "0 001 170 Blue\n", "1 002 165 Green\n", "2 003 180 Brown\n", "3 004 175 Hazel\n", "4 005 160 Brown" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df['Participant ID'] = df['Participant ID'].replace('0', '00', regex=True)\n", "\n", "df" ] }, { "cell_type": "markdown", "id": "519db300-c7ba-4c11-b86a-6cbc39c89a6a", "metadata": {}, "source": [ "Notice in the syntax above a few things: \n", "1. The `replace` method is operating on the `Series` `'Partcipant ID'`. Thus, when we assign the new values, they are assigned back to that specific series. The rest of `df` remains unchanged.\n", "2. In `replace` the first argument is what is to be replaced, the second is what to replace it with. `regex=True` specifies to use regular expression to evaluate the change (rather than string literals). Without `regex=True`, `replace` would only change a cell with the exact value '0' to '00'...instead of changing every case where it finds a zero.\n", "\n", "We'll also note that it *is* possible to directly assign a new value to a given location within a `DataFrame` directly using indexing/slicing and assignment:" ] }, { "cell_type": "code", "execution_count": 18, "id": "e72abfb8-7679-4431-bdc1-e8886f351667", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Participant IDHeight (cm)Eye Color
0001173Blue
1002165Green
2003180Brown
3004175Hazel
4005160Brown
\n", "
" ], "text/plain": [ " Participant ID Height (cm) Eye Color\n", "0 001 173 Blue\n", "1 002 165 Green\n", "2 003 180 Brown\n", "3 004 175 Hazel\n", "4 005 160 Brown" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.loc[0, 'Height (cm)'] = 173\n", "\n", "df" ] }, { "cell_type": "markdown", "id": "fc211d14-ff24-4f10-b524-b98fe2e9d425", "metadata": {}, "source": [ "### `astype()`\n", "\n", "We mentioned `dtypes` is helpful for knowing what your value type is. But, what if you *want* to change it to another type. `astype()` allows you to typecast (meaning specify the type) for a variable. If we wanted `'Height (cm)'` to be a float instead of an integer, for example, we could accomplish that using `astype`:" ] }, { "cell_type": "code", "execution_count": 19, "id": "0bcc919c-b7ba-44ae-bf84-eb7f9b309e8f", "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Participant IDHeight (cm)Eye Color
0001173.0Blue
1002165.0Green
2003180.0Brown
3004175.0Hazel
4005160.0Brown
\n", "
" ], "text/plain": [ " Participant ID Height (cm) Eye Color\n", "0 001 173.0 Blue\n", "1 002 165.0 Green\n", "2 003 180.0 Brown\n", "3 004 175.0 Hazel\n", "4 005 160.0 Brown" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df['Height (cm)'] = df['Height (cm)'].astype(float)\n", "\n", "df" ] }, { "cell_type": "markdown", "id": "8b7ae659-4b05-425f-8dc9-37600484397e", "metadata": {}, "source": [ "Now instead of integers, the heights are floats.\n", "\n", "We'll note here that there are additional methods that help with typecasting, and we'll mention two here: \n", "1. `convert_dtypes` | will convert columns to the best possible `dtypes`\n", "2. `to_numpy` | will convert a `DataFrame` to a `NumPy` array\n", "\n", "### `value_counts()`\n", "\n", "Often when working with data (particularly categorical data), we're interest in how many different values there are. For example, how many different eye colors are in our dataset? And how many individuals are there with each eye color? While we can easily determine this for our limited dataset here, as datasets grow in size, it's less easy to determine at a glance. Instead, we can use `value_counts` to answer this question:" ] }, { "cell_type": "code", "execution_count": 20, "id": "33c10bc3-5bad-4f56-96f4-fcea4d3c30dd", "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "Eye Color\n", "Brown 2\n", "Blue 1\n", "Green 1\n", "Hazel 1\n", "Name: count, dtype: int64" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df['Eye Color'].value_counts()" ] }, { "cell_type": "markdown", "id": "2d6c792d-8861-48dc-b1de-59dddda68bb0", "metadata": {}, "source": [ "In the above output, each unique eye color is at left and the corresponding number of how many times each shows up is at right. Eye colors are sorted by frequency of appearance, with most frequent first. For categories with ties, labels are sorted alphanumerically. \n", "\n", "### `unique` & `nunique`\n", "\n", "Similarly, if you don't want *all* of the information above, but want pieces of information about uniqueness, there are additional helpful methods. \n", "\n", "For example `unique` returns all the unique values (as an array):" ] }, { "cell_type": "code", "execution_count": 21, "id": "e8279a0c-6e2b-4ee3-bb7c-76e36ac45e8e", "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "array(['Blue', 'Green', 'Brown', 'Hazel'], dtype=object)" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df['Eye Color'].unique()" ] }, { "cell_type": "markdown", "id": "72c47678-793e-415c-abab-2176393f5135", "metadata": {}, "source": [ "...and `nunique()` returns how many unique values there are (in our case, 4):" ] }, { "cell_type": "code", "execution_count": 22, "id": "2a31fcf7-6a04-471e-801a-38cedb551751", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df['Eye Color'].nunique()" ] }, { "cell_type": "markdown", "id": "c7b4f72a-dce9-482c-89cc-54dbc7685eeb", "metadata": {}, "source": [ "### Method Chaining\n", "\n", "Building on the above, one key important features within `pandas` is the ability to chain methods together using the general syntax `df.method1().method2()`. In this, `method1` would be applied to `df` *and then* `method2()` would be appled to the output of `method1()`.\n", "\n", "For example, what if you didn't want the unique eye colors as an array...but rather as a list. You can do that with method chaining:" ] }, { "cell_type": "code", "execution_count": 23, "id": "c56bddfb-0a3f-4759-9888-d1efa1ed64e9", "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "['Blue', 'Green', 'Brown', 'Hazel']" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df['Eye Color'].unique().tolist()" ] }, { "cell_type": "markdown", "id": "1fb8ac3e-d27b-46c8-af30-3373f5c699c0", "metadata": {}, "source": [ "...and what if you didn't want *all* of the `value_counts` output...but just the category that shows up the most? You can use `value_counts()` chained with `max()`, the output of which lets you know of all the eye colors, the one that shows up the most shows up twice:" ] }, { "cell_type": "code", "execution_count": 24, "id": "facd28fb-61af-4456-8737-c74c164cab18", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df['Eye Color'].value_counts().max()" ] }, { "cell_type": "markdown", "id": "3946471c-a863-4cda-b312-300e9aeaf1f2", "metadata": {}, "source": [ "...but what if you didn't want to know how many times the most common eye color showed up, but just what that eye color that showed up the most was. There's `idxmax()` for that, which can be chained with `value_counts()`:" ] }, { "cell_type": "code", "execution_count": 25, "id": "0065f35e-3de1-42ec-930f-47c285b152eb", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Brown'" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df['Eye Color'].value_counts().idxmax()" ] }, { "cell_type": "markdown", "id": "a5172261-f166-47db-bd1f-05226a50096a", "metadata": {}, "source": [ "Note that method chaining is *not* limited to two methods. Many methods can be chained together the output of each subsequent method becoming the input for the next. The reason this is possible is because most `pandas` methods return a `DataFrame`...enabling the next method to operate on that returned DataFrame. " ] }, { "cell_type": "markdown", "id": "00814daa-f385-4e83-be8d-ff543f6703f5", "metadata": {}, "source": [ "While we won't walk through examples of all of the existing methods in `pandas`, we'll summarize a few additional common ones here:\n", "\n", "| Function | Purpose |\n", "|-------------------|------------------------------------------------------------|\n", "| `fillna()` | Fill in missing data |\n", "| `groupby()` | Group data by a variable prior to carrying out some operation within group |\n", "| `concat ()` | Combine (concatenate) `Series`/`DataFrame`s together |\n", "| `merge()` | Enable SQL-like joins across `DataFrame`s |\n", "| `assign()` | Create new columns |\n", "| `query()` | Filter rows using Boolean logic |\n", "| `isin()` | Returns rows with match |\n", "\n", "Additional methods are summarized in the [`pandas` Cheat Sheet](https://pandas.pydata.org/Pandas_Cheat_Sheet.pdf)." ] }, { "cell_type": "markdown", "id": "37b0bce9-93f5-4ad2-9032-0ed5b51da704", "metadata": {}, "source": [ "## Functions\n", "\n", "While the `DataFrame` and `Series` objects are at the center of the `pandas` package, there are additional functions within `pandas` that support working with data in Python.\n", "\n", "### `read_*`\n", "\n", "In this chapter so far, we've worked with data that we generated directly and stored in a `DataFrame`. However, typically data live elsewhere and individuals want to read it into python for data analysis. This is where the suite of `read_*` functions come into play. There are a number of them, the most common of which is likely `read_csv()`, which reads data from CSV (comma-separated value files) into python. Files read in can be either filepaths or URLs.\n", "\n", "For example, the following reads in data from a CSV stored on GitHub (passing in the URL of the file as a string:" ] }, { "cell_type": "code", "execution_count": 26, "id": "ddb189cd-cc2c-489d-81bc-87d8f781a4dc", "metadata": {}, "outputs": [], "source": [ "df_msleep = pd.read_csv('https://raw.githubusercontent.com/ShanEllis/datasets/master/msleep.csv')" ] }, { "cell_type": "code", "execution_count": 27, "id": "595c94ed-67b2-4174-a4c6-ad98e1177a92", "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
namegenusvoreorderconservationsleep_totalsleep_remsleep_cycleawakebrainwtbodywt
0CheetahAcinonyxcarniCarnivoralc12.1NaNNaN11.9NaN50.000
1Owl monkeyAotusomniPrimatesNaN17.01.8NaN7.00.015500.480
2Mountain beaverAplodontiaherbiRodentiant14.42.4NaN9.6NaN1.350
3Greater short-tailed shrewBlarinaomniSoricomorphalc14.92.30.1333339.10.000290.019
4CowBosherbiArtiodactyladomesticated4.00.70.66666720.00.42300600.000
....................................
78Tree shrewTupaiaomniScandentiaNaN8.92.60.23333315.10.002500.104
79Bottle-nosed dolphinTursiopscarniCetaceaNaN5.2NaNNaN18.8NaN173.330
80GenetGenettacarniCarnivoraNaN6.31.3NaN17.70.017502.000
81Arctic foxVulpescarniCarnivoraNaN12.5NaNNaN11.50.044503.380
82Red foxVulpescarniCarnivoraNaN9.82.40.35000014.20.050404.230
\n", "

83 rows × 11 columns

\n", "
" ], "text/plain": [ " name genus vore order conservation \\\n", "0 Cheetah Acinonyx carni Carnivora lc \n", "1 Owl monkey Aotus omni Primates NaN \n", "2 Mountain beaver Aplodontia herbi Rodentia nt \n", "3 Greater short-tailed shrew Blarina omni Soricomorpha lc \n", "4 Cow Bos herbi Artiodactyla domesticated \n", ".. ... ... ... ... ... \n", "78 Tree shrew Tupaia omni Scandentia NaN \n", "79 Bottle-nosed dolphin Tursiops carni Cetacea NaN \n", "80 Genet Genetta carni Carnivora NaN \n", "81 Arctic fox Vulpes carni Carnivora NaN \n", "82 Red fox Vulpes carni Carnivora NaN \n", "\n", " sleep_total sleep_rem sleep_cycle awake brainwt bodywt \n", "0 12.1 NaN NaN 11.9 NaN 50.000 \n", "1 17.0 1.8 NaN 7.0 0.01550 0.480 \n", "2 14.4 2.4 NaN 9.6 NaN 1.350 \n", "3 14.9 2.3 0.133333 9.1 0.00029 0.019 \n", "4 4.0 0.7 0.666667 20.0 0.42300 600.000 \n", ".. ... ... ... ... ... ... \n", "78 8.9 2.6 0.233333 15.1 0.00250 0.104 \n", "79 5.2 NaN NaN 18.8 NaN 173.330 \n", "80 6.3 1.3 NaN 17.7 0.01750 2.000 \n", "81 12.5 NaN NaN 11.5 0.04450 3.380 \n", "82 9.8 2.4 0.350000 14.2 0.05040 4.230 \n", "\n", "[83 rows x 11 columns]" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df_msleep" ] }, { "cell_type": "markdown", "id": "7411cc23-e645-49de-aca5-b0af69580415", "metadata": {}, "source": [ "In addition to reading in CSV files, there are `pd.read_*` functions for reading in JSON, XML, HTML, and excel datasets (among others).\n", "\n", "Similarly there are corresponding `to_*` methods that allow for `DataFrames` to be written out to files of various types (i.e. CSV, JSON, etc.)" ] }, { "cell_type": "markdown", "id": "785df8e3-2277-4acc-b2dd-4c79f3c0b43e", "metadata": {}, "source": [ "### Plotting\n", "\n", "While there are many ways to generate plots in python, `pandas` does have the ability to generate plots via the `plotting` module in `pandas`:" ] }, { "cell_type": "code", "execution_count": 28, "id": "75ebe95b-0727-4af1-bc8e-96425caa1be6", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "pd.plotting.boxplot(df_msleep, column='sleep_total', by='vore')" ] }, { "cell_type": "markdown", "id": "09c96540-9039-4acc-a069-6c1ee3f2442d", "metadata": {}, "source": [ "While we won't be discussing the full capabilities here (partially because it's beyond the scope and partially because many people use other approaches for plotting in python), we did at least want to introduce this as a general capability within `pandas`." ] }, { "cell_type": "markdown", "id": "420276ae-fd1f-4979-a95f-4646ca7026e9", "metadata": {}, "source": [ "## Exercises\n", "\n", "Q1. **Comparing them to standard library Python types, which is the best mapping for `NumPy` arrays and `pandas` `DataFrames`?**\n", "\n", "A) DataFrames are like lists, arrays are like tuples \n", "B) DataFrames and arrays are like lists \n", "C) DataFrames are like tuples, arrays are like lists \n", "D) DataFrames and arrays are like dictionaries \n", "E) Dataframes are like dictionaries, arrays are like lists \n", "\n", "Q2. **If a `DataFrame` contained only numeric values, how would it differ from a `NumPy` array?**\n", "\n", "A) The `DataFrame` would additionally have indices and column names \n", "B) They would be exactly the same \n", "C) `DataFrame`s cannot store homogenous data \n", "D) The `DataFrame` would convert all of the numeric values to strings \n", "E) `NumPy` arrays cannot store numeric values \n", "\n", "Q3. **Read in the `df_msleep` DataFrame provided above. Use `pandas` attributes to determine the dimensions and types of information stored in `df_msleep`**\n", "\n", "Q4. **Again using `df_msleep`, use a `pandas` method to determine the average amount of total sleep the animals in this dataset get.**\n", "\n", "Q5. **Again using `df_msleep`, use a `pandas` method to determine which `'vore'` is most common in this dataset, as well as how many animals there are of that `'vore'`.**" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.11.8" } }, "nbformat": 4, "nbformat_minor": 5 }