{ "cells": [ { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "(c) 2016 - present. Enplus Advisors, Inc." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "ExecuteTime": { "end_time": "2019-04-28T22:05:17.460286Z", "start_time": "2019-04-28T22:05:16.630625Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "import numpy as np\n", "import pandas as pd\n", "\n", "from IPython.display import Image\n", "\n", "pd.set_option('display.precision', 2)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# Programming with Data:<br>Advanced Python and Pandas\n", "\n", "# Merge, Join, & Combine" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Types of Joins\n", "\n", "* Inner\n", "* Left\n", "* Right\n", "* Full" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Inner Join" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "ExecuteTime": { "end_time": "2019-04-28T22:05:17.471824Z", "start_time": "2019-04-28T22:05:17.462109Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "<IPython.core.display.Image object>" ] }, "execution_count": 2, "metadata": { "image/png": { "height": 190, "width": 329 } }, "output_type": "execute_result" } ], "source": [ "Image(filename='assets/inner-join.png', retina=True)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "notes" } }, "source": [ "**TODO:** Show with a markdown table" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Left/Right Join" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "ExecuteTime": { "end_time": "2019-04-28T22:05:17.478485Z", "start_time": "2019-04-28T22:05:17.474256Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "<IPython.core.display.Image object>" ] }, "execution_count": 3, "metadata": { "image/png": { "height": 190, "width": 329 } }, "output_type": "execute_result" } ], "source": [ "Image(filename='assets/left-join.png', retina=True)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Full Join" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "ExecuteTime": { "end_time": "2019-04-28T22:05:17.484777Z", "start_time": "2019-04-28T22:05:17.480589Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "<IPython.core.display.Image object>" ] }, "execution_count": 4, "metadata": { "image/png": { "height": 190, "width": 329 } }, "output_type": "execute_result" } ], "source": [ "Image(filename='assets/full-join.png', retina=True)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## The Data\n", "\n", "<img src=\"assets/stock-trading-1600x1200.jpg\" width=700></img>\n", "\n", "Photo by Chris Liverani on Unsplash" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "notes" } }, "source": [ "Going to use stock data because I used to work as a quant." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Somewhat simple data" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "ExecuteTime": { "end_time": "2019-04-28T22:05:17.506112Z", "start_time": "2019-04-28T22:05:17.486652Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>ticker</th>\n", " <th>open</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>AAPL</td>\n", " <td>426.23</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>MSFT</td>\n", " <td>42.30</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>IBM</td>\n", " <td>101.65</td>\n", " </tr>\n", " <tr>\n", " <th>3</th>\n", " <td>YHOO</td>\n", " <td>35.53</td>\n", " </tr>\n", " <tr>\n", " <th>4</th>\n", " <td>GOOG</td>\n", " <td>200.41</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " ticker open\n", "0 AAPL 426.23\n", "1 MSFT 42.30\n", "2 IBM 101.65\n", "3 YHOO 35.53\n", "4 GOOG 200.41" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df1 = pd.DataFrame({\n", " 'ticker': ['AAPL', 'MSFT', 'IBM', 'YHOO', 'GOOG'],\n", " 'open': [426.23, 42.30, 101.65, 35.53, 200.41]\n", "})\n", "df1" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "notes" } }, "source": [ "`df1` has ticker and `open` price (the price of the stock when the NYSE first opens at 0930)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### More somewhat simple data" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Tickers and close prices. Additional ticker for `NFLX`." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "ExecuteTime": { "end_time": "2019-04-28T22:05:17.520001Z", "start_time": "2019-04-28T22:05:17.507619Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>ticker</th>\n", " <th>close</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>AAPL</td>\n", " <td>427.53</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>GOOG</td>\n", " <td>210.96</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>NFLX</td>\n", " <td>91.86</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " ticker close\n", "0 AAPL 427.53\n", "1 GOOG 210.96\n", "2 NFLX 91.86" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df2 = pd.DataFrame({\n", " 'ticker': ['AAPL', 'GOOG', 'NFLX'],\n", " 'close': [427.53, 210.96, 91.86]\n", "}, columns=['ticker', 'close'])\n", "df2" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Coding an inner join" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "An inner join gives us the intersection of the keys." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "ExecuteTime": { "end_time": "2019-04-28T22:05:17.539425Z", "start_time": "2019-04-28T22:05:17.522215Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>ticker</th>\n", " <th>open</th>\n", " <th>close</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>AAPL</td>\n", " <td>426.23</td>\n", " <td>427.53</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>GOOG</td>\n", " <td>200.41</td>\n", " <td>210.96</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " ticker open close\n", "0 AAPL 426.23 427.53\n", "1 GOOG 200.41 210.96" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df1m2 = pd.merge(df1, df2, on='ticker')\n", "df1m2" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Verifying the inner join" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "We drop everything except tickers that are present **both** data frames." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "ExecuteTime": { "end_time": "2019-04-28T22:05:17.546596Z", "start_time": "2019-04-28T22:05:17.541871Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "{'AAPL', 'GOOG'}" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "common_tickers = set(df1.ticker) & set(df2.ticker)\n", "common_tickers" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "ExecuteTime": { "end_time": "2019-04-28T22:05:17.551480Z", "start_time": "2019-04-28T22:05:17.548943Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "assert set(df1m2.ticker) == common_tickers" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Aside: 99% of the time, use `pd.merge`" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Most flexible way to join two data frames" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "\n", "* `pd.concat` is more general - useful to join a collection (e.g. `list`) of data frames\n", "* `pd.DataFrame.join` works in more specific circumstances" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Left Join" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Include all keys from the `left` data frame." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "ExecuteTime": { "end_time": "2019-04-28T22:05:17.563296Z", "start_time": "2019-04-28T22:05:17.552988Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>ticker</th>\n", " <th>open</th>\n", " <th>close</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>AAPL</td>\n", " <td>426.23</td>\n", " <td>427.53</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>MSFT</td>\n", " <td>42.30</td>\n", " <td>NaN</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>IBM</td>\n", " <td>101.65</td>\n", " <td>NaN</td>\n", " </tr>\n", " <tr>\n", " <th>3</th>\n", " <td>YHOO</td>\n", " <td>35.53</td>\n", " <td>NaN</td>\n", " </tr>\n", " <tr>\n", " <th>4</th>\n", " <td>GOOG</td>\n", " <td>200.41</td>\n", " <td>210.96</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " ticker open close\n", "0 AAPL 426.23 427.53\n", "1 MSFT 42.30 NaN\n", "2 IBM 101.65 NaN\n", "3 YHOO 35.53 NaN\n", "4 GOOG 200.41 210.96" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df1m2_left = pd.merge(df1, df2, on='ticker', how='left')\n", "df1m2_left" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "ExecuteTime": { "end_time": "2019-04-28T22:05:17.567055Z", "start_time": "2019-04-28T22:05:17.564554Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "assert set(df1.ticker) == set(df1m2_left.ticker)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Filling missing levels" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "ExecuteTime": { "end_time": "2019-04-28T22:05:17.580407Z", "start_time": "2019-04-28T22:05:17.568356Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>ticker</th>\n", " <th>open</th>\n", " <th>close</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>AAPL</td>\n", " <td>426.23</td>\n", " <td>427.53</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>MSFT</td>\n", " <td>42.30</td>\n", " <td>NaN</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>IBM</td>\n", " <td>101.65</td>\n", " <td>NaN</td>\n", " </tr>\n", " <tr>\n", " <th>3</th>\n", " <td>YHOO</td>\n", " <td>35.53</td>\n", " <td>NaN</td>\n", " </tr>\n", " <tr>\n", " <th>4</th>\n", " <td>GOOG</td>\n", " <td>200.41</td>\n", " <td>210.96</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " ticker open close\n", "0 AAPL 426.23 427.53\n", "1 MSFT 42.30 NaN\n", "2 IBM 101.65 NaN\n", "3 YHOO 35.53 NaN\n", "4 GOOG 200.41 210.96" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df1m2_left" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Notice that `pandas` fills missing levels from `df2` with `NaN`. Comparable to `SQL`\n", "where values would be `NULL`." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Right Join" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Include all keys from the `right` data frame." ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "ExecuteTime": { "end_time": "2019-04-28T22:05:17.592876Z", "start_time": "2019-04-28T22:05:17.582057Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>ticker</th>\n", " <th>open</th>\n", " <th>close</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>AAPL</td>\n", " <td>426.23</td>\n", " <td>427.53</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>GOOG</td>\n", " <td>200.41</td>\n", " <td>210.96</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>NFLX</td>\n", " <td>NaN</td>\n", " <td>91.86</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " ticker open close\n", "0 AAPL 426.23 427.53\n", "1 GOOG 200.41 210.96\n", "2 NFLX NaN 91.86" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pd.merge(df1, df2, on='ticker', how='right')" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Same missingness handling as `left` join." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Outer/Full Join" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "ExecuteTime": { "end_time": "2019-04-28T22:05:17.606884Z", "start_time": "2019-04-28T22:05:17.594128Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>ticker</th>\n", " <th>open</th>\n", " <th>close</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>AAPL</td>\n", " <td>426.23</td>\n", " <td>427.53</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>MSFT</td>\n", " <td>42.30</td>\n", " <td>NaN</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>IBM</td>\n", " <td>101.65</td>\n", " <td>NaN</td>\n", " </tr>\n", " <tr>\n", " <th>3</th>\n", " <td>YHOO</td>\n", " <td>35.53</td>\n", " <td>NaN</td>\n", " </tr>\n", " <tr>\n", " <th>4</th>\n", " <td>GOOG</td>\n", " <td>200.41</td>\n", " <td>210.96</td>\n", " </tr>\n", " <tr>\n", " <th>5</th>\n", " <td>NFLX</td>\n", " <td>NaN</td>\n", " <td>91.86</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " ticker open close\n", "0 AAPL 426.23 427.53\n", "1 MSFT 42.30 NaN\n", "2 IBM 101.65 NaN\n", "3 YHOO 35.53 NaN\n", "4 GOOG 200.41 210.96\n", "5 NFLX NaN 91.86" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df1m2_full = pd.merge(df1, df2, on='ticker', how='outer')\n", "df1m2_full" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "ExecuteTime": { "end_time": "2019-04-28T22:05:17.613140Z", "start_time": "2019-04-28T22:05:17.609391Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "assert set(df1.ticker) | set(df2.ticker) == set(df1m2_full.ticker)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Concatenation/Binding\n", "\n", "* **Join** and bind across rows or columns\n", "* Pass 1 or more `Series` or `DataFrame`s\n", "* Add rows and columns (`pd.concat`)\n" ] }, { "cell_type": "markdown", "metadata": { "ExecuteTime": { "end_time": "2019-04-28T21:57:49.236364Z", "start_time": "2019-04-28T21:57:49.233873Z" }, "slideshow": { "slide_type": "slide" } }, "source": [ "### Data for Concatenation" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "notes" } }, "source": [ "We're going to make this data explicitly **daily** so we're going to \n", "add a `date` column. Take the first two records only so the data\n", "fits on the slide." ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "ExecuteTime": { "end_time": "2019-04-28T22:05:17.629651Z", "start_time": "2019-04-28T22:05:17.614830Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>ticker</th>\n", " <th>open</th>\n", " <th>date</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>AAPL</td>\n", " <td>426.23</td>\n", " <td>2018-01-04</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>MSFT</td>\n", " <td>42.30</td>\n", " <td>2018-01-04</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " ticker open date\n", "0 AAPL 426.23 2018-01-04\n", "1 MSFT 42.30 2018-01-04" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df3 = df1.assign(date=pd.Timestamp(\"2018-01-04\"))\\\n", " .iloc[:2, ] # first 2 rows only\n", "df3" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "ExecuteTime": { "end_time": "2019-04-28T22:05:17.646471Z", "start_time": "2019-04-28T22:05:17.631213Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>ticker</th>\n", " <th>open</th>\n", " <th>date</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>AAPL</td>\n", " <td>436.23</td>\n", " <td>2018-01-05</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>MSFT</td>\n", " <td>52.30</td>\n", " <td>2018-01-05</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " ticker open date\n", "0 AAPL 436.23 2018-01-05\n", "1 MSFT 52.30 2018-01-05" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df4 = df3.assign(\n", " date=pd.Timestamp(\"2018-01-05\"),\n", " open=lambda x: x.open + 10\n", ")\n", "df4" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Adding rows" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "ExecuteTime": { "end_time": "2019-04-28T22:05:17.656406Z", "start_time": "2019-04-28T22:05:17.647763Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>ticker</th>\n", " <th>open</th>\n", " <th>date</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>AAPL</td>\n", " <td>426.23</td>\n", " <td>2018-01-04</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>MSFT</td>\n", " <td>42.30</td>\n", " <td>2018-01-04</td>\n", " </tr>\n", " <tr>\n", " <th>0</th>\n", " <td>AAPL</td>\n", " <td>436.23</td>\n", " <td>2018-01-05</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>MSFT</td>\n", " <td>52.30</td>\n", " <td>2018-01-05</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " ticker open date\n", "0 AAPL 426.23 2018-01-04\n", "1 MSFT 42.30 2018-01-04\n", "0 AAPL 436.23 2018-01-05\n", "1 MSFT 52.30 2018-01-05" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pd.concat([df3, df4])" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Notice how the index is repeated and duplicated for the default `pd.RangeIndex`" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### No dups please" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "To check for duplicated index values:" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "ExecuteTime": { "end_time": "2019-04-28T22:05:17.662685Z", "start_time": "2019-04-28T22:05:17.657752Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Indexes have overlapping values: Int64Index([0, 1], dtype='int64')\n" ] } ], "source": [ "try:\n", " pd.concat([df3, df4], verify_integrity=True)\n", "except ValueError as e:\n", " print(e)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Ignore the index\n", "\n", "`ignore_index` discards the indexes from the bound data frames" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "ExecuteTime": { "end_time": "2019-04-28T22:05:17.673287Z", "start_time": "2019-04-28T22:05:17.664189Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>ticker</th>\n", " <th>open</th>\n", " <th>date</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>AAPL</td>\n", " <td>426.23</td>\n", " <td>2018-01-04</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>MSFT</td>\n", " <td>42.30</td>\n", " <td>2018-01-04</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>AAPL</td>\n", " <td>436.23</td>\n", " <td>2018-01-05</td>\n", " </tr>\n", " <tr>\n", " <th>3</th>\n", " <td>MSFT</td>\n", " <td>52.30</td>\n", " <td>2018-01-05</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " ticker open date\n", "0 AAPL 426.23 2018-01-04\n", "1 MSFT 42.30 2018-01-04\n", "2 AAPL 436.23 2018-01-05\n", "3 MSFT 52.30 2018-01-05" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pd.concat([df3, df4], ignore_index=True)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "We usually don't need to validate the index when we pass `ignore_index`\n", "because we're creating a new index!" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Rows and Columns with `concat`\n", "\n", "* `concat` does an outer join on both rows and columns" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "ExecuteTime": { "end_time": "2019-04-28T22:05:17.683924Z", "start_time": "2019-04-28T22:05:17.674714Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>ticker</th>\n", " <th>open</th>\n", " <th>date</th>\n", " <th>close</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>AAPL</td>\n", " <td>426.23</td>\n", " <td>2018-01-04</td>\n", " <td>435.23</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>MSFT</td>\n", " <td>42.30</td>\n", " <td>2018-01-04</td>\n", " <td>51.30</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " ticker open date close\n", "0 AAPL 426.23 2018-01-04 435.23\n", "1 MSFT 42.30 2018-01-04 51.30" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df3a = df3.assign(close=lambda x: (x.open + 9))\n", "df3a" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### A union of the columns" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "ExecuteTime": { "end_time": "2019-04-28T22:05:17.699635Z", "start_time": "2019-04-28T22:05:17.685577Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>ticker</th>\n", " <th>open</th>\n", " <th>date</th>\n", " <th>close</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>AAPL</td>\n", " <td>426.23</td>\n", " <td>2018-01-04</td>\n", " <td>435.23</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>MSFT</td>\n", " <td>42.30</td>\n", " <td>2018-01-04</td>\n", " <td>51.30</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>AAPL</td>\n", " <td>436.23</td>\n", " <td>2018-01-05</td>\n", " <td>NaN</td>\n", " </tr>\n", " <tr>\n", " <th>3</th>\n", " <td>MSFT</td>\n", " <td>52.30</td>\n", " <td>2018-01-05</td>\n", " <td>NaN</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " ticker open date close\n", "0 AAPL 426.23 2018-01-04 435.23\n", "1 MSFT 42.30 2018-01-04 51.30\n", "2 AAPL 436.23 2018-01-05 NaN\n", "3 MSFT 52.30 2018-01-05 NaN" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pd.concat([df3a, df4], ignore_index=True)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Bind across columns only" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "df5 = pd.DataFrame({'a': [1, 2]})\n", "df6 = pd.DataFrame({'b': [3, 4]})" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>a</th>\n", " <th>b</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>1</td>\n", " <td>3</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>2</td>\n", " <td>4</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " a b\n", "0 1 3\n", "1 2 4" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pd.concat([df5, df6], axis=1)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### `concat` binds rows and columns\n", "\n", "* Always performs an outer join on the concatenation axis" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>a</th>\n", " <th>b</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>1.0</td>\n", " <td>NaN</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>2.0</td>\n", " <td>NaN</td>\n", " </tr>\n", " <tr>\n", " <th>6</th>\n", " <td>NaN</td>\n", " <td>3.0</td>\n", " </tr>\n", " <tr>\n", " <th>7</th>\n", " <td>NaN</td>\n", " <td>4.0</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " a b\n", "0 1.0 NaN\n", "1 2.0 NaN\n", "6 NaN 3.0\n", "7 NaN 4.0" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df6a = df6.set_index(pd.Index([6, 7]))\n", "pd.concat([df5, df6a])" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Specify behavior of non-concatenation axis\n", "\n", "* The `join` parameter only applies to the non-concatenation axis\n", "* Set to `inner` to only get the common columns" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th>ticker</th>\n", " <th>open</th>\n", " <th>date</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th>0</th>\n", " <td>AAPL</td>\n", " <td>426.23</td>\n", " <td>2018-01-04</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>MSFT</td>\n", " <td>42.30</td>\n", " <td>2018-01-04</td>\n", " </tr>\n", " <tr>\n", " <th>2</th>\n", " <td>AAPL</td>\n", " <td>436.23</td>\n", " <td>2018-01-05</td>\n", " </tr>\n", " <tr>\n", " <th>3</th>\n", " <td>MSFT</td>\n", " <td>52.30</td>\n", " <td>2018-01-05</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " ticker open date\n", "0 AAPL 426.23 2018-01-04\n", "1 MSFT 42.30 2018-01-04\n", "2 AAPL 436.23 2018-01-05\n", "3 MSFT 52.30 2018-01-05" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pd.concat([df3a, df4], ignore_index=True, join='inner')" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Notice there is no `close` column because it's not present in both\n", "data frames" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Identify the source Series/DataFrame with `keys`" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th></th>\n", " <th>ticker</th>\n", " <th>open</th>\n", " <th>date</th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th rowspan=\"2\" valign=\"top\">df3</th>\n", " <th>0</th>\n", " <td>AAPL</td>\n", " <td>426.23</td>\n", " <td>2018-01-04</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>MSFT</td>\n", " <td>42.30</td>\n", " <td>2018-01-04</td>\n", " </tr>\n", " <tr>\n", " <th rowspan=\"2\" valign=\"top\">df4</th>\n", " <th>0</th>\n", " <td>AAPL</td>\n", " <td>436.23</td>\n", " <td>2018-01-05</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>MSFT</td>\n", " <td>52.30</td>\n", " <td>2018-01-05</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " ticker open date\n", "df3 0 AAPL 426.23 2018-01-04\n", " 1 MSFT 42.30 2018-01-04\n", "df4 0 AAPL 436.23 2018-01-05\n", " 1 MSFT 52.30 2018-01-05" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pd.concat([df3, df4], keys=['df3', 'df4'])" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Use `keys` and `names`" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/html": [ "<div>\n", "<style scoped>\n", " .dataframe tbody tr th:only-of-type {\n", " vertical-align: middle;\n", " }\n", "\n", " .dataframe tbody tr th {\n", " vertical-align: top;\n", " }\n", "\n", " .dataframe thead th {\n", " text-align: right;\n", " }\n", "</style>\n", "<table border=\"1\" class=\"dataframe\">\n", " <thead>\n", " <tr style=\"text-align: right;\">\n", " <th></th>\n", " <th></th>\n", " <th>ticker</th>\n", " <th>open</th>\n", " <th>date</th>\n", " </tr>\n", " <tr>\n", " <th>source</th>\n", " <th>row_num</th>\n", " <th></th>\n", " <th></th>\n", " <th></th>\n", " </tr>\n", " </thead>\n", " <tbody>\n", " <tr>\n", " <th rowspan=\"2\" valign=\"top\">df3</th>\n", " <th>0</th>\n", " <td>AAPL</td>\n", " <td>426.23</td>\n", " <td>2018-01-04</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>MSFT</td>\n", " <td>42.30</td>\n", " <td>2018-01-04</td>\n", " </tr>\n", " <tr>\n", " <th rowspan=\"2\" valign=\"top\">df4</th>\n", " <th>0</th>\n", " <td>AAPL</td>\n", " <td>436.23</td>\n", " <td>2018-01-05</td>\n", " </tr>\n", " <tr>\n", " <th>1</th>\n", " <td>MSFT</td>\n", " <td>52.30</td>\n", " <td>2018-01-05</td>\n", " </tr>\n", " </tbody>\n", "</table>\n", "</div>" ], "text/plain": [ " ticker open date\n", "source row_num \n", "df3 0 AAPL 426.23 2018-01-04\n", " 1 MSFT 42.30 2018-01-04\n", "df4 0 AAPL 436.23 2018-01-05\n", " 1 MSFT 52.30 2018-01-05" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pd.concat([df3, df4], keys=['df3', 'df4'], names=['source', 'row_num'])" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "notes" } }, "source": [ "TODO: \n", "\n", "* Add concatenation of Series\n", "* Add join\n", "* Show differences between concat, merge, and join" ] } ], "metadata": { "celltoolbar": "Slideshow", "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.10.8" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": false, "sideBar": true, "skip_h1_title": true, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": { "height": "calc(100% - 180px)", "left": "10px", "top": "150px", "width": "288px" }, "toc_section_display": true, "toc_window_display": true } }, "nbformat": 4, "nbformat_minor": 4 }