{ "cells": [ { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# Lesson 3b: Manipulating data\n", "\n", "> During the course of doing data analysis and modeling, a significant amount of time is spent on data preparation: loading, cleaning, transforming, and rearranging. Such tasks are often reported to take up 80% or more of an analyst's time.\n", ">\n", "> \\- Wes McKinney, the creator of Pandas, in his book *Python for Data Analysis*\n", "\n", "We've learned how to subset our DataFrames, in this lesson we'll focus on how to manipulate, create, drop, even identify missing value patterns across our DataFrame's columns." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Learning objectives\n", "\n", "By the end of this lesson you will be able to:\n", "\n", "- Rename columns\n", "- Perform calculations and operations with one or more columns\n", "- Add, drop, and overwrite columns in your DataFrame\n", "- Identify missing values and replace these (and even non-missing) values." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Renaming columns\n", "\n", "Often, one of the first things we want to do with a new data set is clean up the column names. We can do this a few different ways and to illustrate, let's look at the Ames housing data:" ] }, { "cell_type": "code", "execution_count": 1, "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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
OrderPIDMS SubClassMS ZoningLot FrontageLot AreaStreetAlleyLot ShapeLand Contour...Pool AreaPool QCFenceMisc FeatureMisc ValMo SoldYr SoldSale TypeSale ConditionSalePrice
0152630110020RL141.031770PaveNaNIR1Lvl...0NaNNaNNaN052010WDNormal215000
1252635004020RH80.011622PaveNaNRegLvl...0NaNMnPrvNaN062010WDNormal105000
2352635101020RL81.014267PaveNaNIR1Lvl...0NaNNaNGar21250062010WDNormal172000
3452635303020RL93.011160PaveNaNRegLvl...0NaNNaNNaN042010WDNormal244000
4552710501060RL74.013830PaveNaNIR1Lvl...0NaNMnPrvNaN032010WDNormal189900
\n", "

5 rows × 82 columns

\n", "
" ], "text/plain": [ " Order PID MS SubClass MS Zoning Lot Frontage Lot Area Street \\\n", "0 1 526301100 20 RL 141.0 31770 Pave \n", "1 2 526350040 20 RH 80.0 11622 Pave \n", "2 3 526351010 20 RL 81.0 14267 Pave \n", "3 4 526353030 20 RL 93.0 11160 Pave \n", "4 5 527105010 60 RL 74.0 13830 Pave \n", "\n", " Alley Lot Shape Land Contour ... Pool Area Pool QC Fence Misc Feature \\\n", "0 NaN IR1 Lvl ... 0 NaN NaN NaN \n", "1 NaN Reg Lvl ... 0 NaN MnPrv NaN \n", "2 NaN IR1 Lvl ... 0 NaN NaN Gar2 \n", "3 NaN Reg Lvl ... 0 NaN NaN NaN \n", "4 NaN IR1 Lvl ... 0 NaN MnPrv NaN \n", "\n", " Misc Val Mo Sold Yr Sold Sale Type Sale Condition SalePrice \n", "0 0 5 2010 WD Normal 215000 \n", "1 0 6 2010 WD Normal 105000 \n", "2 12500 6 2010 WD Normal 172000 \n", "3 0 4 2010 WD Normal 244000 \n", "4 0 3 2010 WD Normal 189900 \n", "\n", "[5 rows x 82 columns]" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import pandas as pd\n", "\n", "ames = pd.read_csv('../data/ames_raw.csv')\n", "ames.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Say we want to rename the \"MS SubClass\" and \"MS Zoning\" columns. We can do so with the `rename` method and passing a dictionary that maps old names to new names: `df.rename(columns={'old_name1': 'new_name1', 'old_name2': 'new_name2'})`." ] }, { "cell_type": "code", "execution_count": 2, "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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
OrderPIDMS SubClassMS ZoningLot FrontageLot AreaStreetAlleyLot ShapeLand Contour...Pool AreaPool QCFenceMisc FeatureMisc ValMo SoldYr SoldSale TypeSale ConditionSalePrice
0152630110020RL141.031770PaveNaNIR1Lvl...0NaNNaNNaN052010WDNormal215000
1252635004020RH80.011622PaveNaNRegLvl...0NaNMnPrvNaN062010WDNormal105000
2352635101020RL81.014267PaveNaNIR1Lvl...0NaNNaNGar21250062010WDNormal172000
3452635303020RL93.011160PaveNaNRegLvl...0NaNNaNNaN042010WDNormal244000
4552710501060RL74.013830PaveNaNIR1Lvl...0NaNMnPrvNaN032010WDNormal189900
\n", "

5 rows × 82 columns

\n", "
" ], "text/plain": [ " Order PID MS SubClass MS Zoning Lot Frontage Lot Area Street \\\n", "0 1 526301100 20 RL 141.0 31770 Pave \n", "1 2 526350040 20 RH 80.0 11622 Pave \n", "2 3 526351010 20 RL 81.0 14267 Pave \n", "3 4 526353030 20 RL 93.0 11160 Pave \n", "4 5 527105010 60 RL 74.0 13830 Pave \n", "\n", " Alley Lot Shape Land Contour ... Pool Area Pool QC Fence Misc Feature \\\n", "0 NaN IR1 Lvl ... 0 NaN NaN NaN \n", "1 NaN Reg Lvl ... 0 NaN MnPrv NaN \n", "2 NaN IR1 Lvl ... 0 NaN NaN Gar2 \n", "3 NaN Reg Lvl ... 0 NaN NaN NaN \n", "4 NaN IR1 Lvl ... 0 NaN MnPrv NaN \n", "\n", " Misc Val Mo Sold Yr Sold Sale Type Sale Condition SalePrice \n", "0 0 5 2010 WD Normal 215000 \n", "1 0 6 2010 WD Normal 105000 \n", "2 12500 6 2010 WD Normal 172000 \n", "3 0 4 2010 WD Normal 244000 \n", "4 0 3 2010 WD Normal 189900 \n", "\n", "[5 rows x 82 columns]" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ames.rename(columns={'MS SubClass': 'ms_subclass', 'MS Zoning': 'ms_zoning'})\n", "ames.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Wait? What happened? Nothing changed? In the code above we did actually rename columns of our DataFrame but we didn’t modify the DataFrame inplace, we made a copy of it. There are generally two options for making permanent DataFrame changes:\n", "\n", "1. Use the argument `inplace=True`, e.g., `df.rename(..., inplace=True)`, available in most Pandas functions/methods\n", "2. Re-assign, e.g., `df = df.rename(...)` The Pandas team recommends **Method 2 (re-assign)**, for a [few reasons](https://www.youtube.com/watch?v=hK6o_TDXXN8&t=700) (mostly to do with how memory is allocated under the hood).\n", "\n", "```{warning}\n", "Be sure to include the `columns=` when providing the argument dictionary. `rename` can be used to rename index values as well, which is actually the default behavior. So if you don't specify `columns=` it'll behave differently then expected and no error/warning messages will be provided.\n", "```" ] }, { "cell_type": "code", "execution_count": 3, "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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
OrderPIDms_subclassms_zoningLot FrontageLot AreaStreetAlleyLot ShapeLand Contour...Pool AreaPool QCFenceMisc FeatureMisc ValMo SoldYr SoldSale TypeSale ConditionSalePrice
0152630110020RL141.031770PaveNaNIR1Lvl...0NaNNaNNaN052010WDNormal215000
1252635004020RH80.011622PaveNaNRegLvl...0NaNMnPrvNaN062010WDNormal105000
2352635101020RL81.014267PaveNaNIR1Lvl...0NaNNaNGar21250062010WDNormal172000
3452635303020RL93.011160PaveNaNRegLvl...0NaNNaNNaN042010WDNormal244000
4552710501060RL74.013830PaveNaNIR1Lvl...0NaNMnPrvNaN032010WDNormal189900
\n", "

5 rows × 82 columns

\n", "
" ], "text/plain": [ " Order PID ms_subclass ms_zoning Lot Frontage Lot Area Street \\\n", "0 1 526301100 20 RL 141.0 31770 Pave \n", "1 2 526350040 20 RH 80.0 11622 Pave \n", "2 3 526351010 20 RL 81.0 14267 Pave \n", "3 4 526353030 20 RL 93.0 11160 Pave \n", "4 5 527105010 60 RL 74.0 13830 Pave \n", "\n", " Alley Lot Shape Land Contour ... Pool Area Pool QC Fence Misc Feature \\\n", "0 NaN IR1 Lvl ... 0 NaN NaN NaN \n", "1 NaN Reg Lvl ... 0 NaN MnPrv NaN \n", "2 NaN IR1 Lvl ... 0 NaN NaN Gar2 \n", "3 NaN Reg Lvl ... 0 NaN NaN NaN \n", "4 NaN IR1 Lvl ... 0 NaN MnPrv NaN \n", "\n", " Misc Val Mo Sold Yr Sold Sale Type Sale Condition SalePrice \n", "0 0 5 2010 WD Normal 215000 \n", "1 0 6 2010 WD Normal 105000 \n", "2 12500 6 2010 WD Normal 172000 \n", "3 0 4 2010 WD Normal 244000 \n", "4 0 3 2010 WD Normal 189900 \n", "\n", "[5 rows x 82 columns]" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ames = ames.rename(columns={'MS SubClass': 'ms_subclass', 'MS Zoning': 'ms_zoning'})\n", "ames.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using `rename` is great for renaming a single or even a handful of columns but can be tedious for renaming _many_ columns. For this we can use the `.columns` attribute, which just returns all the column names. " ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Index(['Order', 'PID', 'ms_subclass', 'ms_zoning', 'Lot Frontage', 'Lot Area',\n", " 'Street', 'Alley', 'Lot Shape', 'Land Contour', 'Utilities',\n", " 'Lot Config', 'Land Slope', 'Neighborhood', 'Condition 1',\n", " 'Condition 2', 'Bldg Type', 'House Style', 'Overall Qual',\n", " 'Overall Cond', 'Year Built', 'Year Remod/Add', 'Roof Style',\n", " 'Roof Matl', 'Exterior 1st', 'Exterior 2nd', 'Mas Vnr Type',\n", " 'Mas Vnr Area', 'Exter Qual', 'Exter Cond', 'Foundation', 'Bsmt Qual',\n", " 'Bsmt Cond', 'Bsmt Exposure', 'BsmtFin Type 1', 'BsmtFin SF 1',\n", " 'BsmtFin Type 2', 'BsmtFin SF 2', 'Bsmt Unf SF', 'Total Bsmt SF',\n", " 'Heating', 'Heating QC', 'Central Air', 'Electrical', '1st Flr SF',\n", " '2nd Flr SF', 'Low Qual Fin SF', 'Gr Liv Area', 'Bsmt Full Bath',\n", " 'Bsmt Half Bath', 'Full Bath', 'Half Bath', 'Bedroom AbvGr',\n", " 'Kitchen AbvGr', 'Kitchen Qual', 'TotRms AbvGrd', 'Functional',\n", " 'Fireplaces', 'Fireplace Qu', 'Garage Type', 'Garage Yr Blt',\n", " 'Garage Finish', 'Garage Cars', 'Garage Area', 'Garage Qual',\n", " 'Garage Cond', 'Paved Drive', 'Wood Deck SF', 'Open Porch SF',\n", " 'Enclosed Porch', '3Ssn Porch', 'Screen Porch', 'Pool Area', 'Pool QC',\n", " 'Fence', 'Misc Feature', 'Misc Val', 'Mo Sold', 'Yr Sold', 'Sale Type',\n", " 'Sale Condition', 'SalePrice'],\n", " dtype='object')" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ames.columns" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Pandas offers a lot of string methods that we can apply to string objects.\n", "\n", "```{tip}\n", "Check out some of the more common string methods [here](https://pandas.pydata.org/docs/user_guide/text.html#string-methods).\n", "```\n", "\n", "We can manipulate these column name values by using string methods that you can access via `.str.xxxx()`. For example, we can coerce all the column names to lower case with:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Index(['order', 'pid', 'ms_subclass', 'ms_zoning', 'lot frontage', 'lot area',\n", " 'street', 'alley', 'lot shape', 'land contour', 'utilities',\n", " 'lot config', 'land slope', 'neighborhood', 'condition 1',\n", " 'condition 2', 'bldg type', 'house style', 'overall qual',\n", " 'overall cond', 'year built', 'year remod/add', 'roof style',\n", " 'roof matl', 'exterior 1st', 'exterior 2nd', 'mas vnr type',\n", " 'mas vnr area', 'exter qual', 'exter cond', 'foundation', 'bsmt qual',\n", " 'bsmt cond', 'bsmt exposure', 'bsmtfin type 1', 'bsmtfin sf 1',\n", " 'bsmtfin type 2', 'bsmtfin sf 2', 'bsmt unf sf', 'total bsmt sf',\n", " 'heating', 'heating qc', 'central air', 'electrical', '1st flr sf',\n", " '2nd flr sf', 'low qual fin sf', 'gr liv area', 'bsmt full bath',\n", " 'bsmt half bath', 'full bath', 'half bath', 'bedroom abvgr',\n", " 'kitchen abvgr', 'kitchen qual', 'totrms abvgrd', 'functional',\n", " 'fireplaces', 'fireplace qu', 'garage type', 'garage yr blt',\n", " 'garage finish', 'garage cars', 'garage area', 'garage qual',\n", " 'garage cond', 'paved drive', 'wood deck sf', 'open porch sf',\n", " 'enclosed porch', '3ssn porch', 'screen porch', 'pool area', 'pool qc',\n", " 'fence', 'misc feature', 'misc val', 'mo sold', 'yr sold', 'sale type',\n", " 'sale condition', 'saleprice'],\n", " dtype='object')" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ames.columns.str.lower()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can even chain multiple string methods together. For example the following coerces the column names to lower case, replaces all white space in the column names with an underscore, and then assigns these converted values back to the `.columns` attribute." ] }, { "cell_type": "code", "execution_count": 6, "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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
orderpidms_subclassms_zoninglot_frontagelot_areastreetalleylot_shapeland_contour...pool_areapool_qcfencemisc_featuremisc_valmo_soldyr_soldsale_typesale_conditionsaleprice
0152630110020RL141.031770PaveNaNIR1Lvl...0NaNNaNNaN052010WDNormal215000
1252635004020RH80.011622PaveNaNRegLvl...0NaNMnPrvNaN062010WDNormal105000
2352635101020RL81.014267PaveNaNIR1Lvl...0NaNNaNGar21250062010WDNormal172000
3452635303020RL93.011160PaveNaNRegLvl...0NaNNaNNaN042010WDNormal244000
4552710501060RL74.013830PaveNaNIR1Lvl...0NaNMnPrvNaN032010WDNormal189900
\n", "

5 rows × 82 columns

\n", "
" ], "text/plain": [ " order pid ms_subclass ms_zoning lot_frontage lot_area street \\\n", "0 1 526301100 20 RL 141.0 31770 Pave \n", "1 2 526350040 20 RH 80.0 11622 Pave \n", "2 3 526351010 20 RL 81.0 14267 Pave \n", "3 4 526353030 20 RL 93.0 11160 Pave \n", "4 5 527105010 60 RL 74.0 13830 Pave \n", "\n", " alley lot_shape land_contour ... pool_area pool_qc fence misc_feature \\\n", "0 NaN IR1 Lvl ... 0 NaN NaN NaN \n", "1 NaN Reg Lvl ... 0 NaN MnPrv NaN \n", "2 NaN IR1 Lvl ... 0 NaN NaN Gar2 \n", "3 NaN Reg Lvl ... 0 NaN NaN NaN \n", "4 NaN IR1 Lvl ... 0 NaN MnPrv NaN \n", "\n", " misc_val mo_sold yr_sold sale_type sale_condition saleprice \n", "0 0 5 2010 WD Normal 215000 \n", "1 0 6 2010 WD Normal 105000 \n", "2 12500 6 2010 WD Normal 172000 \n", "3 0 4 2010 WD Normal 244000 \n", "4 0 3 2010 WD Normal 189900 \n", "\n", "[5 rows x 82 columns]" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ames.columns = ames.columns.str.lower().str.replace(\" \", \"_\")\n", "ames.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```{admonition} Video 🎥:\n", "\n", "```" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Calculations using columns" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "It's common to want to modify a column of a DataFrame, or sometimes even to create a new column. For example, let's look at the `saleprice` column in our data." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "0 215000\n", "1 105000\n", "2 172000\n", "3 244000\n", "4 189900\n", " ... \n", "2925 142500\n", "2926 131000\n", "2927 132000\n", "2928 170000\n", "2929 188000\n", "Name: saleprice, Length: 2930, dtype: int64" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sale_price = ames['saleprice']\n", "sale_price" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Say we wanted to convert the sales price of our homes to be represented as thousands; so rather than \"215000\" we want to represent it as \"215\"? To do this we can simply divide by 1,000." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "0 215.0\n", "1 105.0\n", "2 172.0\n", "3 244.0\n", "4 189.9\n", " ... \n", "2925 142.5\n", "2926 131.0\n", "2927 132.0\n", "2928 170.0\n", "2929 188.0\n", "Name: saleprice, Length: 2930, dtype: float64" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sale_price_k = sale_price / 1000\n", "sale_price_k" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Adding & removing columns\n", "\n", "So we've create a new series, `sale_price_k`. Right now it's totally separate from our original `ames` DataFrame, but we can make it a column of `ames` using the assignment syntax with the column reference syntax.\n", "```python\n", "df['new_column_name'] = new_column_series\n", "```" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "slideshow": { "slide_type": "slide" } }, "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", "
orderpidms_subclassms_zoninglot_frontagelot_areastreetalleylot_shapeland_contour...pool_qcfencemisc_featuremisc_valmo_soldyr_soldsale_typesale_conditionsalepricesale_price_k
0152630110020RL141.031770PaveNaNIR1Lvl...NaNNaNNaN052010WDNormal215000215.0
1252635004020RH80.011622PaveNaNRegLvl...NaNMnPrvNaN062010WDNormal105000105.0
2352635101020RL81.014267PaveNaNIR1Lvl...NaNNaNGar21250062010WDNormal172000172.0
3452635303020RL93.011160PaveNaNRegLvl...NaNNaNNaN042010WDNormal244000244.0
4552710501060RL74.013830PaveNaNIR1Lvl...NaNMnPrvNaN032010WDNormal189900189.9
\n", "

5 rows × 83 columns

\n", "
" ], "text/plain": [ " order pid ms_subclass ms_zoning lot_frontage lot_area street \\\n", "0 1 526301100 20 RL 141.0 31770 Pave \n", "1 2 526350040 20 RH 80.0 11622 Pave \n", "2 3 526351010 20 RL 81.0 14267 Pave \n", "3 4 526353030 20 RL 93.0 11160 Pave \n", "4 5 527105010 60 RL 74.0 13830 Pave \n", "\n", " alley lot_shape land_contour ... pool_qc fence misc_feature misc_val \\\n", "0 NaN IR1 Lvl ... NaN NaN NaN 0 \n", "1 NaN Reg Lvl ... NaN MnPrv NaN 0 \n", "2 NaN IR1 Lvl ... NaN NaN Gar2 12500 \n", "3 NaN Reg Lvl ... NaN NaN NaN 0 \n", "4 NaN IR1 Lvl ... NaN MnPrv NaN 0 \n", "\n", " mo_sold yr_sold sale_type sale_condition saleprice sale_price_k \n", "0 5 2010 WD Normal 215000 215.0 \n", "1 6 2010 WD Normal 105000 105.0 \n", "2 6 2010 WD Normal 172000 172.0 \n", "3 4 2010 WD Normal 244000 244.0 \n", "4 3 2010 WD Normal 189900 189.9 \n", "\n", "[5 rows x 83 columns]" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ames['sale_price_k'] = sale_price_k\n", "ames.head()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Note that `ames` now has a \"sale_price_k\" column at the end.\n", "\n", "```{note}\n", "Also note that in the code above, the column name goes in quotes within the bracket syntax, while the values that will become the column -- the Series we're using -- are on the right side of the statement, without any brackets or quotes.\n", "```\n", "\n", "This sequence of operations can be expressed as a single line:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "ames['sale_price_k'] = ames['saleprice'] / 1000" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "From a mathematical perspective, what we're doing here is adding a *scalar* -- a single value -- to a *vector* -- a series of values (aka a `Series`).\n", "Other vector-scalar math is supported as well." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "0 214988\n", "1 104988\n", "2 171988\n", "3 243988\n", "4 189888\n", "Name: saleprice, dtype: int64" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Subtraction\n", "(ames['saleprice']- 12).head()" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "0 2150000\n", "1 1050000\n", "2 1720000\n", "3 2440000\n", "4 1899000\n", "Name: saleprice, dtype: int64" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Multiplication\n", "(ames['saleprice'] * 10).head()" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "0 46225000000\n", "1 11025000000\n", "2 29584000000\n", "3 59536000000\n", "4 36062010000\n", "Name: saleprice, dtype: int64" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Exponentiation\n", "(ames['saleprice'] ** 2).head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We may want to drop columns as well. For this we can use the `.drop()` method:" ] }, { "cell_type": "code", "execution_count": 14, "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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
pidms_subclassms_zoninglot_frontagelot_areastreetalleylot_shapeland_contourutilities...pool_areapool_qcfencemisc_featuremisc_valmo_soldyr_soldsale_typesale_conditionsaleprice
052630110020RL141.031770PaveNaNIR1LvlAllPub...0NaNNaNNaN052010WDNormal215000
152635004020RH80.011622PaveNaNRegLvlAllPub...0NaNMnPrvNaN062010WDNormal105000
252635101020RL81.014267PaveNaNIR1LvlAllPub...0NaNNaNGar21250062010WDNormal172000
352635303020RL93.011160PaveNaNRegLvlAllPub...0NaNNaNNaN042010WDNormal244000
452710501060RL74.013830PaveNaNIR1LvlAllPub...0NaNMnPrvNaN032010WDNormal189900
\n", "

5 rows × 81 columns

\n", "
" ], "text/plain": [ " pid ms_subclass ms_zoning lot_frontage lot_area street alley \\\n", "0 526301100 20 RL 141.0 31770 Pave NaN \n", "1 526350040 20 RH 80.0 11622 Pave NaN \n", "2 526351010 20 RL 81.0 14267 Pave NaN \n", "3 526353030 20 RL 93.0 11160 Pave NaN \n", "4 527105010 60 RL 74.0 13830 Pave NaN \n", "\n", " lot_shape land_contour utilities ... pool_area pool_qc fence misc_feature \\\n", "0 IR1 Lvl AllPub ... 0 NaN NaN NaN \n", "1 Reg Lvl AllPub ... 0 NaN MnPrv NaN \n", "2 IR1 Lvl AllPub ... 0 NaN NaN Gar2 \n", "3 Reg Lvl AllPub ... 0 NaN NaN NaN \n", "4 IR1 Lvl AllPub ... 0 NaN MnPrv NaN \n", "\n", " misc_val mo_sold yr_sold sale_type sale_condition saleprice \n", "0 0 5 2010 WD Normal 215000 \n", "1 0 6 2010 WD Normal 105000 \n", "2 12500 6 2010 WD Normal 172000 \n", "3 0 4 2010 WD Normal 244000 \n", "4 0 3 2010 WD Normal 189900 \n", "\n", "[5 rows x 81 columns]" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ames = ames.drop(columns=['order', 'sale_price_k'])\n", "ames.head()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Knowledge check\n", "\n", "```{admonition} Question:\n", ":class: attention\n", "1. Create a new column `utility_space` that is 1/5 of the above ground living space (`gr_liv_area`). \n", "2. You will get fractional output with step #1. See if you can figure out how to round this output to the nearest integer.\n", "3. Now remove this column from your DataFrame\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```{admonition} Video 🎥:\n", "\n", "```" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Overwriting columns\n" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "What if we discovered a systematic error in our data? Perhaps we find out that the \"lot_area\" column is not entirely accurate because the recording process includes an extra 50 square feet for every property. We could create a new column, \"real_lot_area\" but we're not going to need the original \"lot_area\" column, and leaving it could cause confusion for others looking at our data.\n", "\n", "A better solution would be to replace the original column with the new, recalculated, values. We can do so using the same syntax as for creating a new column." ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "slideshow": { "slide_type": "slide" } }, "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", "
pidms_subclassms_zoninglot_frontagelot_areastreetalleylot_shapeland_contourutilities...pool_areapool_qcfencemisc_featuremisc_valmo_soldyr_soldsale_typesale_conditionsaleprice
052630110020RL141.031770PaveNaNIR1LvlAllPub...0NaNNaNNaN052010WDNormal215000
152635004020RH80.011622PaveNaNRegLvlAllPub...0NaNMnPrvNaN062010WDNormal105000
252635101020RL81.014267PaveNaNIR1LvlAllPub...0NaNNaNGar21250062010WDNormal172000
352635303020RL93.011160PaveNaNRegLvlAllPub...0NaNNaNNaN042010WDNormal244000
452710501060RL74.013830PaveNaNIR1LvlAllPub...0NaNMnPrvNaN032010WDNormal189900
\n", "

5 rows × 81 columns

\n", "
" ], "text/plain": [ " pid ms_subclass ms_zoning lot_frontage lot_area street alley \\\n", "0 526301100 20 RL 141.0 31770 Pave NaN \n", "1 526350040 20 RH 80.0 11622 Pave NaN \n", "2 526351010 20 RL 81.0 14267 Pave NaN \n", "3 526353030 20 RL 93.0 11160 Pave NaN \n", "4 527105010 60 RL 74.0 13830 Pave NaN \n", "\n", " lot_shape land_contour utilities ... pool_area pool_qc fence misc_feature \\\n", "0 IR1 Lvl AllPub ... 0 NaN NaN NaN \n", "1 Reg Lvl AllPub ... 0 NaN MnPrv NaN \n", "2 IR1 Lvl AllPub ... 0 NaN NaN Gar2 \n", "3 Reg Lvl AllPub ... 0 NaN NaN NaN \n", "4 IR1 Lvl AllPub ... 0 NaN MnPrv NaN \n", "\n", " misc_val mo_sold yr_sold sale_type sale_condition saleprice \n", "0 0 5 2010 WD Normal 215000 \n", "1 0 6 2010 WD Normal 105000 \n", "2 12500 6 2010 WD Normal 172000 \n", "3 0 4 2010 WD Normal 244000 \n", "4 0 3 2010 WD Normal 189900 \n", "\n", "[5 rows x 81 columns]" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ames.head()" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [ "# Subtract 50 from lot area, and then overwrite the original data.\n", "ames['lot_area'] = ames['lot_area'] - 50" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "slideshow": { "slide_type": "fragment" } }, "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", "
pidms_subclassms_zoninglot_frontagelot_areastreetalleylot_shapeland_contourutilities...pool_areapool_qcfencemisc_featuremisc_valmo_soldyr_soldsale_typesale_conditionsaleprice
052630110020RL141.031720PaveNaNIR1LvlAllPub...0NaNNaNNaN052010WDNormal215000
152635004020RH80.011572PaveNaNRegLvlAllPub...0NaNMnPrvNaN062010WDNormal105000
252635101020RL81.014217PaveNaNIR1LvlAllPub...0NaNNaNGar21250062010WDNormal172000
352635303020RL93.011110PaveNaNRegLvlAllPub...0NaNNaNNaN042010WDNormal244000
452710501060RL74.013780PaveNaNIR1LvlAllPub...0NaNMnPrvNaN032010WDNormal189900
\n", "

5 rows × 81 columns

\n", "
" ], "text/plain": [ " pid ms_subclass ms_zoning lot_frontage lot_area street alley \\\n", "0 526301100 20 RL 141.0 31720 Pave NaN \n", "1 526350040 20 RH 80.0 11572 Pave NaN \n", "2 526351010 20 RL 81.0 14217 Pave NaN \n", "3 526353030 20 RL 93.0 11110 Pave NaN \n", "4 527105010 60 RL 74.0 13780 Pave NaN \n", "\n", " lot_shape land_contour utilities ... pool_area pool_qc fence misc_feature \\\n", "0 IR1 Lvl AllPub ... 0 NaN NaN NaN \n", "1 Reg Lvl AllPub ... 0 NaN MnPrv NaN \n", "2 IR1 Lvl AllPub ... 0 NaN NaN Gar2 \n", "3 Reg Lvl AllPub ... 0 NaN NaN NaN \n", "4 IR1 Lvl AllPub ... 0 NaN MnPrv NaN \n", "\n", " misc_val mo_sold yr_sold sale_type sale_condition saleprice \n", "0 0 5 2010 WD Normal 215000 \n", "1 0 6 2010 WD Normal 105000 \n", "2 12500 6 2010 WD Normal 172000 \n", "3 0 4 2010 WD Normal 244000 \n", "4 0 3 2010 WD Normal 189900 \n", "\n", "[5 rows x 81 columns]" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ames.head()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Calculating based on multiple columns" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "So far we've only seen vector-scalar math. But vector-vector math is supported as well. Let's look at a toy example of creating a column that contains the price per square foot." ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "0 129.830918\n", "1 117.187500\n", "2 129.420617\n", "3 115.639810\n", "4 116.574586\n", "dtype: float64" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "price_per_sqft = ames['saleprice'] / ames['gr_liv_area']\n", "price_per_sqft.head()" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "slideshow": { "slide_type": "fragment" } }, "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", "
pidms_subclassms_zoninglot_frontagelot_areastreetalleylot_shapeland_contourutilities...pool_qcfencemisc_featuremisc_valmo_soldyr_soldsale_typesale_conditionsalepriceprice_per_sqft
052630110020RL141.031720PaveNaNIR1LvlAllPub...NaNNaNNaN052010WDNormal215000129.830918
152635004020RH80.011572PaveNaNRegLvlAllPub...NaNMnPrvNaN062010WDNormal105000117.187500
252635101020RL81.014217PaveNaNIR1LvlAllPub...NaNNaNGar21250062010WDNormal172000129.420617
352635303020RL93.011110PaveNaNRegLvlAllPub...NaNNaNNaN042010WDNormal244000115.639810
452710501060RL74.013780PaveNaNIR1LvlAllPub...NaNMnPrvNaN032010WDNormal189900116.574586
\n", "

5 rows × 82 columns

\n", "
" ], "text/plain": [ " pid ms_subclass ms_zoning lot_frontage lot_area street alley \\\n", "0 526301100 20 RL 141.0 31720 Pave NaN \n", "1 526350040 20 RH 80.0 11572 Pave NaN \n", "2 526351010 20 RL 81.0 14217 Pave NaN \n", "3 526353030 20 RL 93.0 11110 Pave NaN \n", "4 527105010 60 RL 74.0 13780 Pave NaN \n", "\n", " lot_shape land_contour utilities ... pool_qc fence misc_feature misc_val \\\n", "0 IR1 Lvl AllPub ... NaN NaN NaN 0 \n", "1 Reg Lvl AllPub ... NaN MnPrv NaN 0 \n", "2 IR1 Lvl AllPub ... NaN NaN Gar2 12500 \n", "3 Reg Lvl AllPub ... NaN NaN NaN 0 \n", "4 IR1 Lvl AllPub ... NaN MnPrv NaN 0 \n", "\n", " mo_sold yr_sold sale_type sale_condition saleprice price_per_sqft \n", "0 5 2010 WD Normal 215000 129.830918 \n", "1 6 2010 WD Normal 105000 117.187500 \n", "2 6 2010 WD Normal 172000 129.420617 \n", "3 4 2010 WD Normal 244000 115.639810 \n", "4 3 2010 WD Normal 189900 116.574586 \n", "\n", "[5 rows x 82 columns]" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ames['price_per_sqft'] = price_per_sqft\n", "ames.head()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "You can combine vector-vector and vector-scalar calculations in arbitrarily complex ways." ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "slideshow": { "slide_type": "fragment" } }, "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", "
pidms_subclassms_zoninglot_frontagelot_areastreetalleylot_shapeland_contourutilities...fencemisc_featuremisc_valmo_soldyr_soldsale_typesale_conditionsalepriceprice_per_sqftnonsense
052630110020RL141.031720PaveNaNIR1LvlAllPub...NaNNaN052010WDNormal215000129.8309183380102
152635004020RH80.011572PaveNaNRegLvlAllPub...MnPrvNaN062010WDNormal105000117.1875001823234
252635101020RL81.014217PaveNaNIR1LvlAllPub...NaNGar21250062010WDNormal172000129.4206172701405
352635303020RL93.011110PaveNaNRegLvlAllPub...NaNNaN042010WDNormal244000115.6398104277480
452710501060RL74.013780PaveNaNIR1LvlAllPub...MnPrvNaN032010WDNormal189900116.5745863307568
\n", "

5 rows × 83 columns

\n", "
" ], "text/plain": [ " pid ms_subclass ms_zoning lot_frontage lot_area street alley \\\n", "0 526301100 20 RL 141.0 31720 Pave NaN \n", "1 526350040 20 RH 80.0 11572 Pave NaN \n", "2 526351010 20 RL 81.0 14217 Pave NaN \n", "3 526353030 20 RL 93.0 11110 Pave NaN \n", "4 527105010 60 RL 74.0 13780 Pave NaN \n", "\n", " lot_shape land_contour utilities ... fence misc_feature misc_val mo_sold \\\n", "0 IR1 Lvl AllPub ... NaN NaN 0 5 \n", "1 Reg Lvl AllPub ... MnPrv NaN 0 6 \n", "2 IR1 Lvl AllPub ... NaN Gar2 12500 6 \n", "3 Reg Lvl AllPub ... NaN NaN 0 4 \n", "4 IR1 Lvl AllPub ... MnPrv NaN 0 3 \n", "\n", " yr_sold sale_type sale_condition saleprice price_per_sqft nonsense \n", "0 2010 WD Normal 215000 129.830918 3380102 \n", "1 2010 WD Normal 105000 117.187500 1823234 \n", "2 2010 WD Normal 172000 129.420617 2701405 \n", "3 2010 WD Normal 244000 115.639810 4277480 \n", "4 2010 WD Normal 189900 116.574586 3307568 \n", "\n", "[5 rows x 83 columns]" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ames['nonsense'] = (ames['yr_sold'] + 12) * ames['gr_liv_area'] + ames['lot_area'] - 50\n", "ames.head()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Knowledge check\n", "\n", "```{admonition} Question:\n", ":class: attention\n", "Create a new column `price_per_total_sqft` that is `saleprice` divided by the sum of `gr_liv_area`, `total_bsmt_sf`, `wood_deck_sf`, `open_porch_sf`.\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```{admonition} Video 🎥:\n", "\n", "```" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Non-numeric column operations" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "For simplicity, we started with mathematical operations. However, pandas supports string operations as well. We can use `+` to concatenate strings, with both vectors and scalars." ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "0 Home in NAmes neighborhood sold under Normal c...\n", "1 Home in NAmes neighborhood sold under Normal c...\n", "2 Home in NAmes neighborhood sold under Normal c...\n", "3 Home in NAmes neighborhood sold under Normal c...\n", "4 Home in Gilbert neighborhood sold under Normal...\n", " ... \n", "2925 Home in Mitchel neighborhood sold under Normal...\n", "2926 Home in Mitchel neighborhood sold under Normal...\n", "2927 Home in Mitchel neighborhood sold under Normal...\n", "2928 Home in Mitchel neighborhood sold under Normal...\n", "2929 Home in Mitchel neighborhood sold under Normal...\n", "Length: 2930, dtype: object" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'Home in ' + ames['neighborhood'] + ' neighborhood sold under ' + ames['sale_condition'] + ' condition'" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "More complex string operations are possible using methods available through the `.str` *accessor*.\n", "\n", "\n", "```{tip}\n", "We won't cover them in detail, so refer to the [documentation](https://pandas.pydata.org/pandas-docs/stable/user_guide/text.html) if you're interested. But realize that we can do _many_ different manipulations with string columns and its worth taking time to familiarize yourself with Pandas string capabilities.\n", "```" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "0 5\n", "1 5\n", "2 5\n", "3 5\n", "4 7\n", " ..\n", "2925 7\n", "2926 7\n", "2927 7\n", "2928 7\n", "2929 7\n", "Name: neighborhood, Length: 2930, dtype: int64" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# number of characters in string\n", "ames['neighborhood'].str.len()" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0 attached\n", "1 attached\n", "2 attached\n", "3 attached\n", "4 attached\n", " ... \n", "2925 detached\n", "2926 attached\n", "2927 NaN\n", "2928 attached\n", "2929 attached\n", "Name: garage_type, Length: 2930, dtype: object" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ames['garage_type'].str.lower().str.replace('tchd', 'tached')" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## More Complex Column Manipulation" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Replacing Values\n", "\n", "One fairly common situation in data wrangling is needing to convert one set of values to another, where there is a one-to-one correspondence between the values currently in the column and the new values that should replace them.\n", "This operation can be described as \"mapping one set of values to another\".\n", "\n", "Let's look at an example of this. In our Ames data the month sold is represented numerically:" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "0 5\n", "1 6\n", "2 6\n", "3 4\n", "4 3\n", "Name: mo_sold, dtype: int64" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ames['mo_sold'].head()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "Suppose we want to change this so that values are represented by the month name:\n", "\n", "- 1 = 'Jan'\n", "- 2 = 'Feb'\n", "- ...\n", "- 12 = 'Dec'\n", "\n", "We can express this *mapping* of old values to new values using a Python dictionary." ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "# Only specify the values we want to replace; don't include the ones that should stay the same.\n", "value_mapping = {\n", " 1: 'Jan',\n", " 2: 'Feb',\n", " 3: 'Mar',\n", " 4: 'Apr',\n", " 5: 'May',\n", " 6: 'Jun',\n", " 7: 'Jul',\n", " 8: 'Aug',\n", " 9: 'Sep',\n", " 10: 'Oct',\n", " 11: 'Nov',\n", " 12: 'Dec'\n", " }" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "Pandas provides a handy method on Series, `.replace`, that accepts this value mapping and updates the Series accordingly.\n", "We can use it to recode our values." ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "0 May\n", "1 Jun\n", "2 Jun\n", "3 Apr\n", "4 Mar\n", "Name: mo_sold, dtype: object" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ames['mo_sold'].replace(value_mapping).head()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "If you are a SQL user, this workflow may look familiar to you;\n", "it's quite similar to a `CASE WHEN` statement in SQL." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Missing values\n", "\n", "Missing values are typically denoted with NaN. We can use `df.isnull()` to find missing values in a dataframe. It returns a boolean for each element in the dataframe:" ] }, { "cell_type": "code", "execution_count": 28, "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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
pidms_subclassms_zoninglot_frontagelot_areastreetalleylot_shapeland_contourutilities...fencemisc_featuremisc_valmo_soldyr_soldsale_typesale_conditionsalepriceprice_per_sqftnonsense
0FalseFalseFalseFalseFalseFalseTrueFalseFalseFalse...TrueTrueFalseFalseFalseFalseFalseFalseFalseFalse
1FalseFalseFalseFalseFalseFalseTrueFalseFalseFalse...FalseTrueFalseFalseFalseFalseFalseFalseFalseFalse
2FalseFalseFalseFalseFalseFalseTrueFalseFalseFalse...TrueFalseFalseFalseFalseFalseFalseFalseFalseFalse
3FalseFalseFalseFalseFalseFalseTrueFalseFalseFalse...TrueTrueFalseFalseFalseFalseFalseFalseFalseFalse
4FalseFalseFalseFalseFalseFalseTrueFalseFalseFalse...FalseTrueFalseFalseFalseFalseFalseFalseFalseFalse
..................................................................
2925FalseFalseFalseFalseFalseFalseTrueFalseFalseFalse...FalseTrueFalseFalseFalseFalseFalseFalseFalseFalse
2926FalseFalseFalseTrueFalseFalseTrueFalseFalseFalse...FalseTrueFalseFalseFalseFalseFalseFalseFalseFalse
2927FalseFalseFalseFalseFalseFalseTrueFalseFalseFalse...FalseFalseFalseFalseFalseFalseFalseFalseFalseFalse
2928FalseFalseFalseFalseFalseFalseTrueFalseFalseFalse...TrueTrueFalseFalseFalseFalseFalseFalseFalseFalse
2929FalseFalseFalseFalseFalseFalseTrueFalseFalseFalse...TrueTrueFalseFalseFalseFalseFalseFalseFalseFalse
\n", "

2930 rows × 83 columns

\n", "
" ], "text/plain": [ " pid ms_subclass ms_zoning lot_frontage lot_area street alley \\\n", "0 False False False False False False True \n", "1 False False False False False False True \n", "2 False False False False False False True \n", "3 False False False False False False True \n", "4 False False False False False False True \n", "... ... ... ... ... ... ... ... \n", "2925 False False False False False False True \n", "2926 False False False True False False True \n", "2927 False False False False False False True \n", "2928 False False False False False False True \n", "2929 False False False False False False True \n", "\n", " lot_shape land_contour utilities ... fence misc_feature misc_val \\\n", "0 False False False ... True True False \n", "1 False False False ... False True False \n", "2 False False False ... True False False \n", "3 False False False ... True True False \n", "4 False False False ... False True False \n", "... ... ... ... ... ... ... ... \n", "2925 False False False ... False True False \n", "2926 False False False ... False True False \n", "2927 False False False ... False False False \n", "2928 False False False ... True True False \n", "2929 False False False ... True True False \n", "\n", " mo_sold yr_sold sale_type sale_condition saleprice price_per_sqft \\\n", "0 False False False False False False \n", "1 False False False False False False \n", "2 False False False False False False \n", "3 False False False False False False \n", "4 False False False False False False \n", "... ... ... ... ... ... ... \n", "2925 False False False False False False \n", "2926 False False False False False False \n", "2927 False False False False False False \n", "2928 False False False False False False \n", "2929 False False False False False False \n", "\n", " nonsense \n", "0 False \n", "1 False \n", "2 False \n", "3 False \n", "4 False \n", "... ... \n", "2925 False \n", "2926 False \n", "2927 False \n", "2928 False \n", "2929 False \n", "\n", "[2930 rows x 83 columns]" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ames.isnull()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can use this to easily compute the total number of missing values in each column:" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "pid 0\n", "ms_subclass 0\n", "ms_zoning 0\n", "lot_frontage 490\n", "lot_area 0\n", " ... \n", "sale_type 0\n", "sale_condition 0\n", "saleprice 0\n", "price_per_sqft 0\n", "nonsense 0\n", "Length: 83, dtype: int64" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ames.isnull().sum()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Recall we also get this information with `.info()`. Actually, we get the inverse as `.info()` tells us how many non-null values exist in each column." ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "RangeIndex: 2930 entries, 0 to 2929\n", "Data columns (total 83 columns):\n", " # Column Non-Null Count Dtype \n", "--- ------ -------------- ----- \n", " 0 pid 2930 non-null int64 \n", " 1 ms_subclass 2930 non-null int64 \n", " 2 ms_zoning 2930 non-null object \n", " 3 lot_frontage 2440 non-null float64\n", " 4 lot_area 2930 non-null int64 \n", " 5 street 2930 non-null object \n", " 6 alley 198 non-null object \n", " 7 lot_shape 2930 non-null object \n", " 8 land_contour 2930 non-null object \n", " 9 utilities 2930 non-null object \n", " 10 lot_config 2930 non-null object \n", " 11 land_slope 2930 non-null object \n", " 12 neighborhood 2930 non-null object \n", " 13 condition_1 2930 non-null object \n", " 14 condition_2 2930 non-null object \n", " 15 bldg_type 2930 non-null object \n", " 16 house_style 2930 non-null object \n", " 17 overall_qual 2930 non-null int64 \n", " 18 overall_cond 2930 non-null int64 \n", " 19 year_built 2930 non-null int64 \n", " 20 year_remod/add 2930 non-null int64 \n", " 21 roof_style 2930 non-null object \n", " 22 roof_matl 2930 non-null object \n", " 23 exterior_1st 2930 non-null object \n", " 24 exterior_2nd 2930 non-null object \n", " 25 mas_vnr_type 2907 non-null object \n", " 26 mas_vnr_area 2907 non-null float64\n", " 27 exter_qual 2930 non-null object \n", " 28 exter_cond 2930 non-null object \n", " 29 foundation 2930 non-null object \n", " 30 bsmt_qual 2850 non-null object \n", " 31 bsmt_cond 2850 non-null object \n", " 32 bsmt_exposure 2847 non-null object \n", " 33 bsmtfin_type_1 2850 non-null object \n", " 34 bsmtfin_sf_1 2929 non-null float64\n", " 35 bsmtfin_type_2 2849 non-null object \n", " 36 bsmtfin_sf_2 2929 non-null float64\n", " 37 bsmt_unf_sf 2929 non-null float64\n", " 38 total_bsmt_sf 2929 non-null float64\n", " 39 heating 2930 non-null object \n", " 40 heating_qc 2930 non-null object \n", " 41 central_air 2930 non-null object \n", " 42 electrical 2929 non-null object \n", " 43 1st_flr_sf 2930 non-null int64 \n", " 44 2nd_flr_sf 2930 non-null int64 \n", " 45 low_qual_fin_sf 2930 non-null int64 \n", " 46 gr_liv_area 2930 non-null int64 \n", " 47 bsmt_full_bath 2928 non-null float64\n", " 48 bsmt_half_bath 2928 non-null float64\n", " 49 full_bath 2930 non-null int64 \n", " 50 half_bath 2930 non-null int64 \n", " 51 bedroom_abvgr 2930 non-null int64 \n", " 52 kitchen_abvgr 2930 non-null int64 \n", " 53 kitchen_qual 2930 non-null object \n", " 54 totrms_abvgrd 2930 non-null int64 \n", " 55 functional 2930 non-null object \n", " 56 fireplaces 2930 non-null int64 \n", " 57 fireplace_qu 1508 non-null object \n", " 58 garage_type 2773 non-null object \n", " 59 garage_yr_blt 2771 non-null float64\n", " 60 garage_finish 2771 non-null object \n", " 61 garage_cars 2929 non-null float64\n", " 62 garage_area 2929 non-null float64\n", " 63 garage_qual 2771 non-null object \n", " 64 garage_cond 2771 non-null object \n", " 65 paved_drive 2930 non-null object \n", " 66 wood_deck_sf 2930 non-null int64 \n", " 67 open_porch_sf 2930 non-null int64 \n", " 68 enclosed_porch 2930 non-null int64 \n", " 69 3ssn_porch 2930 non-null int64 \n", " 70 screen_porch 2930 non-null int64 \n", " 71 pool_area 2930 non-null int64 \n", " 72 pool_qc 13 non-null object \n", " 73 fence 572 non-null object \n", " 74 misc_feature 106 non-null object \n", " 75 misc_val 2930 non-null int64 \n", " 76 mo_sold 2930 non-null int64 \n", " 77 yr_sold 2930 non-null int64 \n", " 78 sale_type 2930 non-null object \n", " 79 sale_condition 2930 non-null object \n", " 80 saleprice 2930 non-null int64 \n", " 81 price_per_sqft 2930 non-null float64\n", " 82 nonsense 2930 non-null int64 \n", "dtypes: float64(12), int64(28), object(43)\n", "memory usage: 1.9+ MB\n" ] } ], "source": [ "ames.info()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can use `any()` to identify which columns have missing values. We can use this information for various reasons such as subsetting for just those columns that have missing values." ] }, { "cell_type": "code", "execution_count": 31, "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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
lot_frontagealleymas_vnr_typemas_vnr_areabsmt_qualbsmt_condbsmt_exposurebsmtfin_type_1bsmtfin_sf_1bsmtfin_type_2...garage_typegarage_yr_bltgarage_finishgarage_carsgarage_areagarage_qualgarage_condpool_qcfencemisc_feature
0141.0NaNStone112.0TAGdGdBLQ639.0Unf...Attchd1960.0Fin2.0528.0TATANaNNaNNaN
180.0NaNNone0.0TATANoRec468.0LwQ...Attchd1961.0Unf1.0730.0TATANaNMnPrvNaN
281.0NaNBrkFace108.0TATANoALQ923.0Unf...Attchd1958.0Unf1.0312.0TATANaNNaNGar2
393.0NaNNone0.0TATANoALQ1065.0Unf...Attchd1968.0Fin2.0522.0TATANaNNaNNaN
474.0NaNNone0.0GdTANoGLQ791.0Unf...Attchd1997.0Fin2.0482.0TATANaNMnPrvNaN
..................................................................
292537.0NaNNone0.0TATAAvGLQ819.0Unf...Detchd1984.0Unf2.0588.0TATANaNGdPrvNaN
2926NaNNaNNone0.0GdTAAvBLQ301.0ALQ...Attchd1983.0Unf2.0484.0TATANaNMnPrvNaN
292762.0NaNNone0.0GdTAAvGLQ337.0Unf...NaNNaNNaN0.00.0NaNNaNNaNMnPrvShed
292877.0NaNNone0.0GdTAAvALQ1071.0LwQ...Attchd1975.0RFn2.0418.0TATANaNNaNNaN
292974.0NaNBrkFace94.0GdTAAvLwQ758.0Unf...Attchd1993.0Fin3.0650.0TATANaNNaNNaN
\n", "

2930 rows × 27 columns

\n", "
" ], "text/plain": [ " lot_frontage alley mas_vnr_type mas_vnr_area bsmt_qual bsmt_cond \\\n", "0 141.0 NaN Stone 112.0 TA Gd \n", "1 80.0 NaN None 0.0 TA TA \n", "2 81.0 NaN BrkFace 108.0 TA TA \n", "3 93.0 NaN None 0.0 TA TA \n", "4 74.0 NaN None 0.0 Gd TA \n", "... ... ... ... ... ... ... \n", "2925 37.0 NaN None 0.0 TA TA \n", "2926 NaN NaN None 0.0 Gd TA \n", "2927 62.0 NaN None 0.0 Gd TA \n", "2928 77.0 NaN None 0.0 Gd TA \n", "2929 74.0 NaN BrkFace 94.0 Gd TA \n", "\n", " bsmt_exposure bsmtfin_type_1 bsmtfin_sf_1 bsmtfin_type_2 ... \\\n", "0 Gd BLQ 639.0 Unf ... \n", "1 No Rec 468.0 LwQ ... \n", "2 No ALQ 923.0 Unf ... \n", "3 No ALQ 1065.0 Unf ... \n", "4 No GLQ 791.0 Unf ... \n", "... ... ... ... ... ... \n", "2925 Av GLQ 819.0 Unf ... \n", "2926 Av BLQ 301.0 ALQ ... \n", "2927 Av GLQ 337.0 Unf ... \n", "2928 Av ALQ 1071.0 LwQ ... \n", "2929 Av LwQ 758.0 Unf ... \n", "\n", " garage_type garage_yr_blt garage_finish garage_cars garage_area \\\n", "0 Attchd 1960.0 Fin 2.0 528.0 \n", "1 Attchd 1961.0 Unf 1.0 730.0 \n", "2 Attchd 1958.0 Unf 1.0 312.0 \n", "3 Attchd 1968.0 Fin 2.0 522.0 \n", "4 Attchd 1997.0 Fin 2.0 482.0 \n", "... ... ... ... ... ... \n", "2925 Detchd 1984.0 Unf 2.0 588.0 \n", "2926 Attchd 1983.0 Unf 2.0 484.0 \n", "2927 NaN NaN NaN 0.0 0.0 \n", "2928 Attchd 1975.0 RFn 2.0 418.0 \n", "2929 Attchd 1993.0 Fin 3.0 650.0 \n", "\n", " garage_qual garage_cond pool_qc fence misc_feature \n", "0 TA TA NaN NaN NaN \n", "1 TA TA NaN MnPrv NaN \n", "2 TA TA NaN NaN Gar2 \n", "3 TA TA NaN NaN NaN \n", "4 TA TA NaN MnPrv NaN \n", "... ... ... ... ... ... \n", "2925 TA TA NaN GdPrv NaN \n", "2926 TA TA NaN MnPrv NaN \n", "2927 NaN NaN NaN MnPrv Shed \n", "2928 TA TA NaN NaN NaN \n", "2929 TA TA NaN NaN NaN \n", "\n", "[2930 rows x 27 columns]" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "missing = ames.isnull().any() # identify if missing values exist in each column\n", "ames[missing[missing].index] # subset for just those columns that have missing values" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When you have missing values, we usually either drop them or impute them.You can drop missing values with `.dropna()`:" ] }, { "cell_type": "code", "execution_count": 32, "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", "
pidms_subclassms_zoninglot_frontagelot_areastreetalleylot_shapeland_contourutilities...fencemisc_featuremisc_valmo_soldyr_soldsale_typesale_conditionsalepriceprice_per_sqftnonsense
\n", "

0 rows × 83 columns

\n", "
" ], "text/plain": [ "Empty DataFrame\n", "Columns: [pid, ms_subclass, ms_zoning, lot_frontage, lot_area, street, alley, lot_shape, land_contour, utilities, lot_config, land_slope, neighborhood, condition_1, condition_2, bldg_type, house_style, overall_qual, overall_cond, year_built, year_remod/add, roof_style, roof_matl, exterior_1st, exterior_2nd, mas_vnr_type, mas_vnr_area, exter_qual, exter_cond, foundation, bsmt_qual, bsmt_cond, bsmt_exposure, bsmtfin_type_1, bsmtfin_sf_1, bsmtfin_type_2, bsmtfin_sf_2, bsmt_unf_sf, total_bsmt_sf, heating, heating_qc, central_air, electrical, 1st_flr_sf, 2nd_flr_sf, low_qual_fin_sf, gr_liv_area, bsmt_full_bath, bsmt_half_bath, full_bath, half_bath, bedroom_abvgr, kitchen_abvgr, kitchen_qual, totrms_abvgrd, functional, fireplaces, fireplace_qu, garage_type, garage_yr_blt, garage_finish, garage_cars, garage_area, garage_qual, garage_cond, paved_drive, wood_deck_sf, open_porch_sf, enclosed_porch, 3ssn_porch, screen_porch, pool_area, pool_qc, fence, misc_feature, misc_val, mo_sold, yr_sold, sale_type, sale_condition, saleprice, price_per_sqft, nonsense]\n", "Index: []\n", "\n", "[0 rows x 83 columns]" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ames.dropna()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Whoa! What just happened? Well, this data set actually has a missing value in every single row. `.dropna()` drops every row that contains a missing value so we end up dropping _all_ observations. Consequently, we probably want to figure out what's going on with these missing values and isolate the column causing the problem and imputing the values if possible.\n", "\n", "```{tip}\n", "Another \"drop\" method is `.drop_duplcates()` which will drop duplicated rows in your DataFrame.\n", "```\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Sometimes visualizations help identify patterns in missing values. One thing I often do is print a heatmap of my dataframe to get a feel for where my missing values are. We'll get into data visualization in future lessons but for now here is an example using the **searborn** library. We can see that several variables have a lot of missing values (`alley`, `fireplace_qu`, `pool_qc`, `fence`, `misc_feature`)." ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [], "source": [ "import seaborn as sns\n", "sns.set(rc={'figure.figsize':(12, 8)})" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "data": { "image/png": "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fzQnTkTEMeh4LnJnjj7/2CxKuS0iIiIidufoCesUOIuIiEPRaLBIzsnpwNbesrw4UERERETkUWJT4Dx9+nQaN26Mt7c38+fPB2DXrl00bdqUJk2aMGzYMJKSkgCIiIigY8eONGzYkL59+xIXF2d760VEREREskmWp2rs3buXn376iTVr1pCSkkLjxo3x8vJi5MiRhIaGUqZMGQYOHEh4eDitW7dm3LhxdOjQAW9vb2bPns0nn3zC0KFDM12fow/9i4iIObQ4UETsJcuB8z/+8Q++/PJLnJ2diYyMJDU1lQIFCpCamkpsbCypqakkJiaSN29ekpOT+fnnn5k9ezYALVq0oFOnTg8UODv6ZHMRETGHgloRsRebFge6uLgwY8YMQkNDadiwIUWKFGHs2LF07tyZggULUrJkSRo2bMiVK1coWLAgzs43q3N3dycyMvKB6lJgKyIiIiI5yebFgQMHDuTHH3/kwoULzJ49m5CQENatW8euXbuoXLkyQUFB3GmraIvFYmvVIiIiIiLZJssjzidOnCApKYny5cuTP39+6tevz5IlSyhbtizPPPMMAG3atGHw4MGMGDHCOn3DycmJS5cu4eHh8UD1aaqGiIiIiOSkLAfO586dY8aMGXz11VcAbNu2jaZNm/LVV18RHR3N008/zbZt26hYsSIuLi5Uq1aNDRs24OPjQ1hYGLVr136g+hTYiohIZmhxoIjYS5YDZy8vLw4ePEizZs1wcnKifv369O7dGw8PD7p06YKTkxOlS5cmMDAQgDFjxuDv78+cOXMoVqwYH3/88QPVpxFnEREREclJFuNOE5AfQvXytLZr+RqhEBEREUemQcLMyVP097s/lo3tEBERERHJtRQ4i4iIiIhkgk37OKebNGkSV65cITg4mFmzZrFy5Uoef/xx4ObOGh07duTkyZOMGTOGq1ev4u7uzscff8wTTzxhRvUiIiIiInZn84jzjz/+yOrVq623Dx06xMcff0x4eDjh4eF07NgRwzDo27cvvXr1Ys2aNZQvX565c+faWrWIiIiISLaxacT5r7/+YurUqfTp04ejR48CNwPnzz//nLNnz/Laa6/h5+fH8ePHKVCggHULuj59+nDt2jXbWy8iIiIikk1sGnEePXo0Q4YMsU7LiIuLo3z58vj5+bF69WquXbvGJ598wpkzZ3j66afx8/PDx8eHMWPG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o6M5e/YsxYsXZ+LEiezZs4fixYszatQou3RIREQebZqqISL2YnrK7ffff5++ffuSL18+GjZsyK+//kpKSgpHjhxhwIABjBw5khUrVuDv78/ChQtN64iIiAgoqBUR+zE15XZiYiKVKlUiLCwMgG+++YZSpUrh7u6Om5sbderUAaBJkyaMHz/enB6IiIjcQiPOIjlH29HdQXrK7Ro1alCgQAGWLVtG8+bNuXHjBm+//Tbr16/H1dWVhQsX0qZNG5555hmKFCnCzp078fLy4ttvv6VChQoPVGdO/6FERCR3UFAr4riy4/u9Je3uj5machtubkvXtm1bUlJSaNKkCT4+PgDMmjWLMWPGMGXKFAoWLEhwcHBWqhYRERGRh5SjD3TmmpTbaRfL5nQTbKZREBEREckpjh7UmuVeKbcztR2diIiIiMijLtek3Hb0yeYiIiIi8nDLcsrtAwcOEBQURFxcHOXKlSM4OJgTJ07g7+9vfU1MTAxPPPEE69atY//+/UycOJGUlBSefPJJJk6cSIkSJezSKRERERERs2VqqsbBgwdp3769NeV2bGwsAwYMIDAwkPXr1wPw9ddfU758ecLDwwkPD2fp0qU88cQTjB07FoChQ4cyYcIEwsPD8fHx0XZ0IiIiIpKrZCnl9g8//ECVKlXw9PQEICAggHr16mV4zWeffcZrr71GtWrVSEpKYtCgQdbnlytXjgsXLpjZDxERERERu8pSyu0///yTAgUK0K9fP86cOUO1atUyTNG4du0ay5cvZ+3atQC4urri6+sLQFpaGrNmzeKtt956oIZqDrKIiGSGEqCIiL1kaVeN1NRUdu3ahb+/P2FhYcTHxzN37lzr42vXruWtt96icOHCGV6XlJTEBx98QEpKCr1797at5SIiIiIi2ShLu2o8/fTTVK5cmVKlSgHQqFEjFi1aZH1869attwXGcXFx9O3blyeffJI5c+bg4uLyQHVqVw0RERERyUlZGnGuVasWhw8fts5TvjWFtmEYHD58mKpVq2Z4zdChQyldujTTp0/H1dXVxmaLiIiIiGSvLI04FytWjMDAQPr06UNiYiLly5fHz88PuLkFnYuLC3nz5rU+/8iRI2zbto0XXniBZs2aAeDh4cHnn39uew9ERERuofnHImIvuSbldr08re1avhaTiIiIiCPTtNTMUcptEREREREbKXAWERER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9u7du9m1axerV68mLCyMw4cPM3fuXObNm8fSpUtZs2YNaWlpLFmyBIAJEyYwcOBA1qxZw3PPPce8efMAmDZtGtWqVWPjxo20bt2aCRMm2LdnIiIiIiImum/g7O7ujr+/P66urri4uFCmTBmSkpIYO3YsBQsWxGKxULZsWSIiIgBIS0sjLi4OgPj4ePLlywfAjh078PHxAaBJkyZ89913JCcn26tfIiIiIiKmum/g/OKLL1KlShUATp8+zYYNG2jSpAmvv/46ADExMSxevJg333wTAH9/f0aOHEmtWrXYvXs37dq1AyAqKso6NcPZ2ZmCBQsSExNjjz6JiIiIiJgu04sDjx8/Tu/evfHz8+PZZ58FIDIykp49e9KyZUuqV69OQkICI0eO5IsvvqBSpUrMnz8fPz8/5s6de8cy8+S5b9xupQntIiIiIpKTMhU479+/n4EDBzJixAi8vb0BOHHiBL169aJTp050794dgN9//528efNSqVIlANq2bcv06dMB8PDwIDo6mqJFi5KSkkJsbCxPPvlkphuqfZxFRCQzdDx/uDjCYk19pjLP0eO1+w75XrhwgX79+hESEmINmmNjY+nRoweDBg2yBs0ApUuX5uLFi5w8eRKAbdu2UbFiReDmbhxhYWEAbNiwgWrVquHi4mJ2f0RERERE7OK+I87z5s0jMTGR4OBg632NGzcmOjqa0NBQQkNDAahbty6DBg0iKCiIwYMHYxgGhQsXZuLEiQAMGjQIf39/vL29eeyxxwgJCbFTl0REREREzGcxDMPI6UZkRr08re1avlK0ioiIiCPL6WkOuUWeor/f/bFsbIeIiIiISK6Va1Ju6yxJRERE5OHm6IsDs5xye8mSJSxevBjDMKz3WSwWZs2axcqVK3n88ccBaNOmDR07duTkyZOMGTOGq1ev4u7uzscff8wTTzyR6YY6+hshIiIikts5ejx138D51pTbFouFnj17smDBApYsWUJYWBh58+alY8eO/PDDD9SqVYtDhw7x8ccfU7VqVWsZhmHQt29fRo4cSe3atQkJCWHu3LkMHTrUrp0TERERETHLfQPnW1NuA5QpUwaLxcL69etxcXHhypUrxMbGWkeYDx06xOeff87Zs2d57bXX8PPz4/jx4xQoUIDatWsD0KdPH65du/ZADXX0MxgRETGHFnuLiL3cN3B+8cUXrf9PT7m9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7o9lYztERERERHKtTO+qISIikhtoNFhE7CVTI86zZs3C29sbb29vJk+eDMCuXbto2rQpTZo0YdiwYSQlJQFw7tw5OnbsiK+vL507d+b8+fMAJCUl8f777+Pj44Ovry+7d++2U5dERERERMx338D51pTbYWFhHD58mC1btjBy5EimTp3KunXrSEhIsC70mz59Ot7e3oSHh1O/fn2mTp0K3FwImJaWxtq1a5k8eTL+/v727ZmIiIiIiIlMT7mdlpZGbGwsAPHx8eTLl896f3x8PKmpqRnuFxERMZMWB4rkHHt/N3J6geMD7apx+vRp2rVrx9KlS/njjz947733rCm3Fy1ahKurK2fOnKFdu3Y4OTmRnJzMsmXLKF26NElJSXTp0oUzZ85w7do1Pv74Y+rXr5/phmpXDRERyYyc/mGVjBzht0+fqUfLvXbVMD3ltp+fH4GBgbz11lts3ryZ/v37s2bNGmbNmkWVKlX46quvOH36NF27dqVChQqUKFHClE6KiIhkF0cIBiXzHOX91gmA7UxNuR0TE8PJkyd56623AGjQoAFjxozhypUrbNu2jalTp2KxWHjuueeoXLkyv/zyiwJnERExlaMEOSLy8DE15fZTTz1F3rx52bdvH3Az4HZzc6NQoUJ4enqydetW4GYmwUOHDlG+fHl79UtERERExFSmpty2WCzMmjWLDz/8kISEBNzc3Jg5cyYAw4cPZ9SoUXh7e5MnTx7ee+89nn32Wbt1TERERETETEq5/T9aHCgi4hg0j/Ph4gi/ffpMPVpMWRwoIiIiNzlCMCiZ5yjvt04AbKfAWUREHIqjBDki8vDJNVM1RERERERy0n131RAREREREQXOIiIiIiKZosBZRERERCQTFDiLiIiIiGSCAmcRERERkUxQ4CwiIiIikgkKnEVEREREMkGBs4iIiIhIJihwFhERERHJBAXOIiLZ4MaNGzndBBERsZEC5wfw73//m0uXLuV0M0TEZBs2bAAgJibGbnV07twZgLFjx9qtDnn0rF69+rb7Fi9enAMtyX0SEhJyugkPrSNHjgBw/fp1fvzxxxxuzcPFOacbYKbz588TEBDA+fPnWbRoER988AETJ06kZMmSppSfkJBAp06dKF26NM2bN+ett97CxcXFlLLTJSUlMW/ePE6dOsXo0aNZsGAB77zzDq6urqbWs2/fPubNm8eNGzcwDIO0tDQiIiLYvn27TeX+/PPP93z8tddes6n87K7n6tWrTJkyhTNnzjB9+nQmT56Mv78/TzzxhCnlO4LffvuNCxcu8Nprr/HYY49Z7//222+pU6eOKXUcPXoUPz8/Ll68yFtvvcXw4cMpWLAgAM2bN79j8PAgZsyYQf369enRo4fNZd3NjRs3+OCDD/j+++9JTEy87fGgoCCbyvf09MRisQBgGEaGxywWC7/99ptN5f9dcnIyu3fv5sqVKxnub9asmWl1LF++nKlTp/LXX38BN/tlVl9mzZp1x/v79+9vc9np7Hn8WLBgAbGxsSxdupTz589b709JSWHdunV07NjR5jrSXb16lfXr13PlypUMny2z/lZnzpzhv//9Lz4+PowePZojR44wfPhwqlWrZkr5AJs3b2bWrFnEx8dbf/Pi4+P56aefbC57+PDh93zc1u92dtWRLiQkhCNHjhAaGkp8fDyffPIJ+/btY8CAAabVsX//fn7//XdatmzJwYMHTfvNvpW94imHCpxHjx5Njx49+Oijj3B3d6dJkyb4+fmZdvbdv39/+vfvz759+1i3bh0zZ86kRo0atG7dmvLly5tSR2BgIIUKFeLIkSM4OTlx5swZRo4cyZQpU0wpP11AQAC9evVi9erVdO7cme+++46XXnrJ5nJnzJhx18csFgtffvmlzXXcWs9ff/3FmTNneOWVV8iTJw8HDhygbNmyLF261JR6Ro0axRtvvMEvv/yCm5sbHh4eDB06lLlz55pSvr3Z+2D7xRdfsHz5ckqVKsWoUaMICQmhZs2awM33yKzAeezYsQwfPpxy5coxffp0unTpwsKFC3Fzc7stSMyKqlWrUrFiRQzDyPBdNjNQCw0NZc+ePezfv59//OMfNpf3d0ePHjW9zHsZNGgQly5dokyZMtaAHcwNnD/99FO+/PJLXnzxRdPKvJPk5GS+//57KleubGq59jx+lC5dmsOHD992f968eQkODra5/Fv169ePQoUK8eKLL2Z4r80yfPhwOnXqxLZt2zh9+jTDhw9n8uTJLF++3LQ6pkyZwvjx45k/fz59+vRh165dt530ZZU9vs93q+Pbb78lLi6Opk2b4uzszIYNGzIMWJhhx44dhIeHA+Dh4cH8+fNp3ry5aYHzF198wdatW4mKiqJhw4aMHj2aVq1a0aNHD1PKT2e3eMpwIM2bNzcMwzB8fX2t9zVt2tTUOm7cuGGsXr3a6N69u+Ht7W1MmzbN6NixoxESEmJK+c2aNTMM4//7kJaWZnh7e5tS9q3Sy58+fbqxe/duIy0tzVp3btKzZ0/j9OnT1tvnzp0zunfvblr5d/pM+fj42FzuG2+8YXh6et72r1y5coanp6fN5adbsWKFUbVqVeOrr74yVq1adds/WzVp0sS4ceOGYRiGsX//fuONN94wfv75Z8MwMv7NbPX3soKDg43OnTsbSUlJptbTp08f08q6m99++836/+vXrxu///67qeVHR0cb8+fPN2bNmmXMnDnTmD59ujF06FBT6zAMw2jQoIHpZf5dq1at7F5HusTERKNjx46mlmmv48et/vjjD1PLu5MmTZrYtfyWLVsahmEYI0aMMJYtW2YYxv//7cySXt7s2bONnTt32qUOwzCMK1euGBEREcb58+eNM2fOGLt37za1/FatWhmpqanW26mpqda/n1kaNGhgxMbGWm/fuHHD1M+Ar6+vkZiYaP1exMbGGo0aNTKt/HT2iqccasQ5X758XLx40XpGvG/fPlOnOLz//vv89NNPeHl50bdvX+tlpKSkJGrVqsX7779vcx0Wi4WkpCRrH65cuWKXM/y8efPy119/8dxzz3Hw4EFq1qxp6uIle00F+buIiAhKly5tvV28eHEiIiJMK9/JyYnr169b34PTp0+TJ4/tSwNWrlxJly5dmD17Ni+88ILN5d1Nq1atOH36NOfOneODDz6wSx358+cH4JVXXuHjjz9m8ODBhIaGmvq5dXNzY+fOndSuXRuLxYKfnx/vv/8+AwYMID4+3rR6pk+fztGjR/H09GTt2rUcOXKE7t274+7ublodv/76K1988QVDhw6lWbNmuLm5Ub9+fYYMGWJK+f379+eZZ57hv//9L2+99RY//PADnp6eppR9q2eeeYaIiAiKFy9uetlhYWHAze9z3759efPNN3F2/v+fKzNHtdPFxcWZeuwA+x0/AOrWrXvP79i2bdtMqQegbNmyHDp0iJdfftm0Mm/l5OTE5s2b2bFjB4MGDWLr1q2m/Z3S5cuXj1OnTlGmTBn27t1LjRo1uH79uql1fPzxxyxevJiUlBSeeuopIiMjefnll1mxYoVpdVy/fp2//vqLQoUKARAdHW36wuN27drRokUL6tatC8B3331n6tSfPHnyZIjN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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "ames_missing = ames[missing[missing].index]\n", "sns.heatmap(ames_missing.isnull(), cmap='viridis', cbar=False);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since we can't drop all missing values in this data set (since it leaves us with no rows), we need to impute (\"fill\") them in. There are several approaches we can use to do this; one of which uses the `.fillna()` method. This method has various options for filling, you can use a fixed value, the mean of the column, the previous non-nan value, etc:" ] }, { "cell_type": "code", "execution_count": 35, "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", "
ABCD
0NaN2.0NaN0
13.04.0NaN1
2NaNNaNNaN5
3NaN3.0NaN4
\n", "
" ], "text/plain": [ " A B C D\n", "0 NaN 2.0 NaN 0\n", "1 3.0 4.0 NaN 1\n", "2 NaN NaN NaN 5\n", "3 NaN 3.0 NaN 4" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import numpy as np\n", "\n", "# example DataFrame with missing values\n", "df = pd.DataFrame([[np.nan, 2, np.nan, 0],\n", " [3, 4, np.nan, 1],\n", " [np.nan, np.nan, np.nan, 5],\n", " [np.nan, 3, np.nan, 4]],\n", " columns=list('ABCD'))\n", "df" ] }, { "cell_type": "code", "execution_count": 36, "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", "
ABCD
00.02.00.00
13.04.00.01
20.00.00.05
30.03.00.04
\n", "
" ], "text/plain": [ " A B C D\n", "0 0.0 2.0 0.0 0\n", "1 3.0 4.0 0.0 1\n", "2 0.0 0.0 0.0 5\n", "3 0.0 3.0 0.0 4" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.fillna(0) # fill with 0" ] }, { "cell_type": "code", "execution_count": 37, "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", "
ABCD
03.02.0NaN0
13.04.0NaN1
23.03.0NaN5
33.03.0NaN4
\n", "
" ], "text/plain": [ " A B C D\n", "0 3.0 2.0 NaN 0\n", "1 3.0 4.0 NaN 1\n", "2 3.0 3.0 NaN 5\n", "3 3.0 3.0 NaN 4" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.fillna(df.mean()) # fill with the mean" ] }, { "cell_type": "code", "execution_count": 38, "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", "
ABCD
03.02.0NaN0
13.04.0NaN1
2NaN3.0NaN5
3NaN3.0NaN4
\n", "
" ], "text/plain": [ " A B C D\n", "0 3.0 2.0 NaN 0\n", "1 3.0 4.0 NaN 1\n", "2 NaN 3.0 NaN 5\n", "3 NaN 3.0 NaN 4" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.bfill() # backward (upwards) fill from non-nan values" ] }, { "cell_type": "code", "execution_count": 39, "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", "
ABCD
0NaN2.0NaN0
13.04.0NaN1
23.04.0NaN5
33.03.0NaN4
\n", "
" ], "text/plain": [ " A B C D\n", "0 NaN 2.0 NaN 0\n", "1 3.0 4.0 NaN 1\n", "2 3.0 4.0 NaN 5\n", "3 3.0 3.0 NaN 4" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.ffill() # forward (downward) fill from non-nan values" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Applying custom functions\n", "\n", "There will be times when you want to apply a function that is not built-in to Pandas. For this, we have methods:\n", "\n", "* `df.apply()`, applies a function column-wise or row-wise across a dataframe (the function must be able to accept/return an array)\n", "* `df.applymap()`, applies a function element-wise (for functions that accept/return single values at a time)\n", "* `series.apply()`/`series.map()`, same as above but for Pandas series\n", "\n", "For example, say you had the following custom function that defines if a home is considered a luxery home simply based on the price sold.\n", "\n", "```{note}\n", "Don't worry, you'll learn more about writing your own functions in future lessons!\n", "```" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0 Non-luxery\n", "1 Non-luxery\n", "2 Non-luxery\n", "3 Non-luxery\n", "4 Non-luxery\n", " ... \n", "2925 Non-luxery\n", "2926 Non-luxery\n", "2927 Non-luxery\n", "2928 Non-luxery\n", "2929 Non-luxery\n", "Name: saleprice, Length: 2930, dtype: object" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def is_luxery_home(x):\n", " if x > 500000:\n", " return 'Luxery'\n", " else:\n", " return 'Non-luxery'\n", "\n", "ames['saleprice'].apply(is_luxery_home)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This may have been better as a lambda function, which is just a shorter approach to writing functions. This may be a bit confusing but we'll talk more about lambda functions in the writing functions lesson. For now, just think of it as being able to write a function for single use application on the fly." ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0 Non-luxery\n", "1 Non-luxery\n", "2 Non-luxery\n", "3 Non-luxery\n", "4 Non-luxery\n", " ... \n", "2925 Non-luxery\n", "2926 Non-luxery\n", "2927 Non-luxery\n", "2928 Non-luxery\n", "2929 Non-luxery\n", "Name: saleprice, Length: 2930, dtype: object" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ames['saleprice'].apply(lambda x: 'Luxery' if x > 500000 else 'Non-luxery')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can even use functions that require additional arguments. Just specify the arguments in `.apply()`:" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0 Luxery\n", "1 Non-luxery\n", "2 Non-luxery\n", "3 Luxery\n", "4 Non-luxery\n", " ... \n", "2925 Non-luxery\n", "2926 Non-luxery\n", "2927 Non-luxery\n", "2928 Non-luxery\n", "2929 Non-luxery\n", "Name: saleprice, Length: 2930, dtype: object" ] }, "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def is_luxery_home(x, price):\n", " if x > price:\n", " return 'Luxery'\n", " else:\n", " return 'Non-luxery'\n", "\n", "ames['saleprice'].apply(is_luxery_home, price=200000)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Sometimes we may have a function that we want to apply to every element across multiple columns. For example, say we wanted to convert several of the square footage variables to be represented as square meters. For this we can use the [`.applymap()`](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.applymap.html) method." ] }, { "cell_type": "code", "execution_count": 43, "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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
gr_liv_areagarage_arealot_area
0153.84736849.0527842946.883160
183.24108867.8191901075.073516
2123.46808728.9857361320.801951
3196.02533048.4953661032.152330
4151.33898744.7792461280.203340
............
292593.18170954.626964732.725961
292683.79850644.965052820.798005
292790.1159100.000000965.355073
2928129.04226738.833454925.313880
2929185.80600060.386950889.732031
\n", "

2930 rows × 3 columns

\n", "
" ], "text/plain": [ " gr_liv_area garage_area lot_area\n", "0 153.847368 49.052784 2946.883160\n", "1 83.241088 67.819190 1075.073516\n", "2 123.468087 28.985736 1320.801951\n", "3 196.025330 48.495366 1032.152330\n", "4 151.338987 44.779246 1280.203340\n", "... ... ... ...\n", "2925 93.181709 54.626964 732.725961\n", "2926 83.798506 44.965052 820.798005\n", "2927 90.115910 0.000000 965.355073\n", "2928 129.042267 38.833454 925.313880\n", "2929 185.806000 60.386950 889.732031\n", "\n", "[2930 rows x 3 columns]" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def convert_to_sq_meters(x):\n", " return x*0.092903\n", "\n", "ames[['gr_liv_area', 'garage_area', 'lot_area']].map(convert_to_sq_meters)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Exercises\n", "\n", "```{admonition} Questions:\n", ":class: attention\n", "1. Import the heart.csv dataset.\n", "2. Are there any missing values in this data? If so, which columns? For these columns, fill the missing values with the value that appears most often (aka \"mode\"). This is a multi-step process and it would be worth reviewing the [`.fillna()` docs](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.fillna.html).\n", "3. Create a new column called `risk` that is equal to $ \\frac{age}{\\text{rest_bp} + chol + \\text{max_hr}} $\n", "4. Replace the values in the `rest_ecg` column so that:\n", " - normal = normal\n", " - left ventricular hypertrophy = lvh\n", " - ST-T wave abnormality = stt_wav_abn\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Computing environment" ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "tags": [ "hide-input" ] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Python implementation: CPython\n", "Python version : 3.9.4\n", "IPython version : 7.26.0\n", "\n", "jupyterlab: 3.1.4\n", "pandas : 1.2.4\n", "numpy : 1.19.5\n", "seaborn : 0.11.2\n", "\n" ] } ], "source": [ "%load_ext watermark\n", "%watermark -v -p jupyterlab,pandas,numpy,seaborn" ] } ], "metadata": { "interpreter": { "hash": "fd8d25807323b6a73e4e6e484dd361ea1af80a43f200f9cf712ca91de587529f" }, "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.9.12" }, "rise": { "autolaunch": true, "transition": "none" } }, "nbformat": 4, "nbformat_minor": 4 }