<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>MetaSnake - misc</title><link href="https://metasnake.com/" rel="alternate"/><link href="https://metasnake.com/feeds/misc.atom.xml" rel="self"/><id>https://metasnake.com/</id><updated>2026-08-11T00:00:00-06:00</updated><entry><title>Python for Finance: Pandas Resample, Groupby, and Rolling</title><link href="https://metasnake.com/blog/python-for-finance-pandas-resample-groupby-rolling.html" rel="alternate"/><published>2023-05-31T00:00:00-06:00</published><updated>2026-08-11T00:00:00-06:00</updated><author><name>Matt Harrison</name></author><id>tag:metasnake.com,2023-05-31:/blog/python-for-finance-pandas-resample-groupby-rolling.html</id><summary type="html">&lt;p&gt;Use pandas resample, groupby, Grouper, and rolling operations to understand trends in time-series data through an analysis of FDIC bank failures.&lt;/p&gt;</summary><content type="html">&lt;div class="article-note"&gt;
&lt;strong&gt;Republication note:&lt;/strong&gt; I originally wrote this article for Ponder, where it was published on May 31, 2023. Ponder later removed the post following its acquisition, so I have preserved it here. The Ponder-specific code reflects the environment available when the article was written; the pandas concepts remain broadly applicable. &lt;a href="https://web.archive.org/web/20230613201128/https://ponder.io/python-for-finance-pandas-resample-groupby-and-rolling/"&gt;View the archived original.&lt;/a&gt;
&lt;/div&gt;

&lt;figure class="article-figure article-figure--hero"&gt;
&lt;img src="/static/img/blog/pandas-finance/hero.png" width="896" height="512" alt="Bank buildings and a financial time-series chart" decoding="async"&gt;
&lt;figcaption&gt;Exploring FDIC bank failures with pandas time-series tools.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Welcome back! Today we will explore how you can leverage pandas to understand trends in time-series data quickly, with a particular focus on time-series methods like &lt;code&gt;.resample&lt;/code&gt;, grouping by dates, and performing &lt;code&gt;.rolling&lt;/code&gt; operations to smooth out the results. Along the way, I might grumble a bit (I’d like to think that the grey strands in my beard are from wisdom and not fighting with a computer), but I intend to give you some superpowers.&lt;/p&gt;
&lt;p&gt;If you work with time series data, you will want to understand &lt;code&gt;.resample&lt;/code&gt;, grouping, and &lt;code&gt;.rolling&lt;/code&gt;, as they will make your life much easier.&lt;/p&gt;
&lt;p&gt;The code below preserves that original environment and demonstrates the pandas concepts as they appeared in the original post.&lt;/p&gt;
&lt;p&gt;Let’s start by picking a dataset that might be relevant considering recent news… bank failures.&lt;/p&gt;
&lt;h2&gt;Data Overview: FDIC BankFind Suite&lt;/h2&gt;
&lt;p&gt;In recent years, the banking sector in the United States has witnessed many financial institution failures, leading to substantial economic losses and systemic risks. The Federal Deposit Insurance Corporation (FDIC) bank failure data provides valuable insights into the underlying causes of these failures and helps develop effective risk management and regulatory compliance strategies. By exploring this data with the aid of the popular data analysis library, pandas, one can identify patterns and trends, prepare data for modeling, and visualize findings effectively.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://banks.data.fdic.gov/bankfind-suite/"&gt;BankFind Suite&lt;/a&gt; is a comprehensive database provided by the FDIC, which allows users to search for current and former FDIC-insured banking institutions based on name, FDIC certificate number, website, or location. This tool offers detailed information on an institution’s financial history and trends, enabling users to track the performance of individual institutions, groups of institutions, or the industry as a whole.&lt;/p&gt;
&lt;p&gt;Let’s use the Bank Failure data to explore. I will download the data from 1934 to 2023 containing over 4000 entries.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pandas&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;bankfail.csv&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;
&lt;span class="go"&gt;    CERT    CHCLASS1    CITYST          COST    FAILDATE    FIN ID  NAME                    QBFASSET    QBFDEP      RESTYPE RESTYPE1    SAVR&lt;/span&gt;
&lt;span class="go"&gt;0   15426.0 NM      ALMENA, KS      16806.0 10/23/2020  10538   4104    ALMENA STATE BANK           65733.0     64941.0     FAILURE PA      DIF&lt;/span&gt;
&lt;span class="go"&gt;1   16748.0 NM      FORT WALTON BEACH, FL   7247.0  10/16/2020  10537   4103    FIRST CITY BANK OF FLORIDA      136566.0    133936.0    FAILURE PA      DIF&lt;/span&gt;
&lt;span class="go"&gt;2   14361.0 NM      BARBOURSVILLE, WV   45913.0 4/3/2020    10536   4102    THE FIRST STATE BANK            151808.0    143102.0    FAILURE PA      DIF&lt;/span&gt;
&lt;span class="go"&gt;3   18265.0 NM      ERICSON, NE     25293.0 2/14/2020   10535   4101    ERICSON STATE BANK          100879.0    95159.0     FAILURE PA      DIF&lt;/span&gt;
&lt;span class="go"&gt;4   21111.0 N       NEWARK, NJ      1946.0  11/1/2019   10534   4100    CITY NATIONAL BANK OF NEW JERSEY    120574.0    111234.0    FAILURE PA      DIF&lt;/span&gt;
&lt;span class="go"&gt;... ... ...     ...         ... ...     ... ... ...                 ...     ...     ... ...     ...&lt;/span&gt;
&lt;span class="go"&gt;4099    NaN NM      LEWISPORT, KY       NaN 8/6/1934    0   137 BANK OF LEWISPORT           81.0        68.0        FAILURE PO      BIF&lt;/span&gt;
&lt;span class="go"&gt;4100    NaN N       LIMA, MT        NaN 7/18/1934   0   135 FIRST NATIONAL BANK OF LIMA     91.0        42.0        FAILURE PO      BIF&lt;/span&gt;
&lt;span class="go"&gt;4101    NaN NM      FLORENCE, IN        NaN 7/18/1934   0   136 FLORENCE DEPOSIT BANK           105.0       69.0        FAILURE PO      BIF&lt;/span&gt;
&lt;span class="go"&gt;4102    NaN NM      EAST PEORIA, IL     NaN 5/28/1934   0   133 FON DU LAC STATE BANK           374.0       238.0       FAILURE PO      BIF&lt;/span&gt;
&lt;span class="go"&gt;4103    NaN NM      PITTSBURGH, PA      NaN 4/19/1934   0   134 BANK OF AMERICA TRUST CO.       1435.0      1064.0      FAILURE PO      BIF&lt;/span&gt;
&lt;span class="go"&gt;4104 rows × 13 columns&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It looks like my CSV export loaded ok. However, the existing column names are a little annoying. I’m going to clean them up.&lt;/p&gt;
&lt;p&gt;Here’s some code to rename the columns, create a &lt;em&gt;city&lt;/em&gt; and &lt;em&gt;state&lt;/em&gt; column, and convert the &lt;em&gt;date&lt;/em&gt; column to a datetime type.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;colnames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;CERT&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;cert&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;  &lt;span class="s1"&gt;&amp;#39;CHCLASS1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;charter_class&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;   &lt;span class="s1"&gt;&amp;#39;CITYST&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;location&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;   &lt;span class="s1"&gt;&amp;#39;COST&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;estimated_loss&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;   &lt;span class="s1"&gt;&amp;#39;FAILDATE&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;effective_date&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;   &lt;span class="s1"&gt;&amp;#39;FIN&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;fin&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;   &lt;span class="s1"&gt;&amp;#39;ID&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;   &lt;span class="s1"&gt;&amp;#39;NAME&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;institution&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;   &lt;span class="s1"&gt;&amp;#39;QBFASSET&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;total_assets&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;   &lt;span class="s1"&gt;&amp;#39;QBFDEP&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;total_deposits&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;   &lt;span class="s1"&gt;&amp;#39;RESTYPE&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;resolution&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;   &lt;span class="s1"&gt;&amp;#39;RESTYPE1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;transaction_type&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;   &lt;span class="s1"&gt;&amp;#39;SAVR&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;insurance_fund&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;tweak_bank&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FAILDATE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FAILDATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;%m/&lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s1"&gt;/%Y&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;                  &lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CITYST&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;,&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;                  &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CITYST&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;,&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;                 &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;CITYST&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;colnames&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;         &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;bank&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tweak_bank&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;bank&lt;/span&gt;
&lt;span class="go"&gt;    cert    charter_class   estimated_loss  effective_date  fin id  institution             total_assets    total_deposits  resolution  transaction_type    insurance_fund  city            state&lt;/span&gt;
&lt;span class="go"&gt;0   15426.0 NM      16806.0     2020-10-23  10538   4104    ALMENA STATE BANK           65733.0     64941.0     FAILURE     PA          DIF     ALMENA          KS&lt;/span&gt;
&lt;span class="go"&gt;1   16748.0 NM      7247.0      2020-10-16  10537   4103    FIRST CITY BANK OF FLORIDA      136566.0    133936.0    FAILURE     PA          DIF     FORT WALTON BEACH   FL&lt;/span&gt;
&lt;span class="go"&gt;2   14361.0 NM      45913.0     2020-04-03  10536   4102    THE FIRST STATE BANK            151808.0    143102.0    FAILURE     PA          DIF     BARBOURSVILLE       WV&lt;/span&gt;
&lt;span class="go"&gt;3   18265.0 NM      25293.0     2020-02-14  10535   4101    ERICSON STATE BANK          100879.0    95159.0     FAILURE     PA          DIF     ERICSON         NE&lt;/span&gt;
&lt;span class="go"&gt;4   21111.0 N       1946.0      2019-11-01  10534   4100    CITY NATIONAL BANK OF NEW JERSEY    120574.0    111234.0    FAILURE     PA          DIF     NEWARK          NJ&lt;/span&gt;
&lt;span class="go"&gt;... ... ...     ...     ...     ... ... ...                 ...     ...     ...     ...         ...     ...         ...&lt;/span&gt;
&lt;span class="go"&gt;4099    NaN NM      NaN     1934-08-06  0   137 BANK OF LEWISPORT           81.0        68.0        FAILURE     PO          BIF     LEWISPORT       KY&lt;/span&gt;
&lt;span class="go"&gt;4100    NaN N       NaN     1934-07-18  0   135 FIRST NATIONAL BANK OF LIMA     91.0        42.0        FAILURE     PO          BIF     LIMA            MT&lt;/span&gt;
&lt;span class="go"&gt;4101    NaN NM      NaN     1934-07-18  0   136 FLORENCE DEPOSIT BANK           105.0       69.0        FAILURE     PO          BIF     FLORENCE        IN&lt;/span&gt;
&lt;span class="go"&gt;4102    NaN NM      NaN     1934-05-28  0   133 FON DU LAC STATE BANK           374.0       238.0       FAILURE     PO          BIF     EAST PEORIA     IL&lt;/span&gt;
&lt;span class="go"&gt;4103    NaN NM      NaN     1934-04-19  0   134 BANK OF AMERICA TRUST CO.       1435.0      1064.0      FAILURE     PO          BIF     PITTSBURGH      PA&lt;/span&gt;
&lt;span class="go"&gt;4104 rows × 14 columns&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Not bad. This is going to be great data for learning &lt;code&gt;.resample&lt;/code&gt;, &lt;code&gt;.groupby&lt;/code&gt;, and &lt;code&gt;.rolling&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Data Dictionary&lt;/h3&gt;
&lt;p&gt;Here is the description of the columns for those (like me) who aren’t subject matter experts. (All descriptions taken from &lt;a href="https://banks.data.fdic.gov/bankfind-suite/failures/help#DEFINITIONS"&gt;here&lt;/a&gt;.)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;cert – Certificate number assigned by FDIC to identify the institution&lt;/li&gt;
&lt;li&gt;charter_class – The FDIC assigns classification codes indicating an institution’s charter type (commercial bank, savings bank, or savings association), its chartering agent (state or federal government), its Federal Reserve membership status (member or nonmember), and its primary federal regulator (state-chartered institutions are subject to both federal and state supervision). These codes are:&lt;/li&gt;
&lt;li&gt;N National chartered commercial bank supervised by the Office of the Comptroller of the&lt;/li&gt;
&lt;li&gt;SM State charter Fed member commercial bank supervised by the Federal Reserve&lt;/li&gt;
&lt;li&gt;NM State charter Fed nonmember commercial bank supervised by the FDIC&lt;/li&gt;
&lt;li&gt;SA State or federal charter savings association supervised by the Office of Thrift Supervision or Office of the Comptroller of the Currency&lt;/li&gt;
&lt;li&gt;SB State charter savings bank supervised by the FDIC&lt;/li&gt;
&lt;li&gt;estimated_loss – the difference between the amount disbursed from the Deposit Insurance Fund (DIF) to cover obligations to insured depositors and the amount estimated to be ultimately recovered from the liquidation of the receivership estate. Estimated losses reflect unpaid principal amounts deemed unrecoverable and do not reflect interest that may be due on the DIF’s administrative or subrogated claims should its principal be repaid in full.&lt;/li&gt;
&lt;li&gt;effective_date – date of failure.&lt;/li&gt;
&lt;li&gt;fin – Financial institute number. Another unique identifier.&lt;/li&gt;
&lt;li&gt;id – Institution directory number&lt;/li&gt;
&lt;li&gt;name – Legal name of the institution.&lt;/li&gt;
&lt;li&gt;total_assets – The Total assets owned by the institution, including cash, loans, securities, bank premises, and other assets as of the last Call Report or Thrift Financial Report.&lt;/li&gt;
&lt;li&gt;total_deposits – Total including demand deposits, money market deposits, other savings deposits, time deposits and deposits in foreign offices as of the last Call Report.&lt;/li&gt;
&lt;li&gt;resolution – Failure stature. Failure or assistance by merging with other institution.&lt;/li&gt;
&lt;li&gt;transaction_type – Resolution type. Cat 1 – Institution’s charter survives. Cat 2 – Charter terminated. Cat 3 – Payout:&lt;/li&gt;
&lt;li&gt;A/A – Cat1. (Assistance Transactions). Assistance was provided to the acquirer, who purchased the entire institution.&lt;/li&gt;
&lt;li&gt;REP – Cat1. Re-privatization, management takeover with or without assistance at takeover, followed by a sale with or without additional assistance.&lt;/li&gt;
&lt;li&gt;P&amp;amp;A – Cat2. Purchase and Assumption, where some or all of the deposits, certain other liabilities, and a portion of the assets (sometimes all of the assets) were sold to an acquirer. It was not determined if all the deposits (PA) or only the insured deposits (PI) were assumed.&lt;/li&gt;
&lt;li&gt;PA – Cat2. Purchase and Assumption, where the insured and uninsured deposits, certain other liabilities, and a portion of the assets were sold to an acquirer.&lt;/li&gt;
&lt;li&gt;PI – Cat2. Purchase and Assumption of the insured deposits only, where the traditional P&amp;amp;A was modified so that the acquiring institution assumed only the insured deposits.&lt;/li&gt;
&lt;li&gt;IDT – Cat2. Insured deposit Transfer, where the acquiring institution served as a paying agent for the insurer, established accounts on their books for depositors and often acquired some assets as well.&lt;/li&gt;
&lt;li&gt;ABT – Cat2. Asset-backed transfer, an FSLIC transaction that is very similar to an IDT.&lt;/li&gt;
&lt;li&gt;MGR – Cat2.An institution where FSLIC took over management and generally provided financial assistance. FSLIC closed down before the institution was sold. transactions&lt;/li&gt;
&lt;li&gt;PO – Cat3 – Payout, where the insurer paid the depositors directly and placed the assets in a liquidating receivership.&lt;/li&gt;
&lt;li&gt;insurance_fund – Insurance fund.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Counts of failures by year&lt;/h2&gt;
&lt;p&gt;Let’s use pandas to find the count of failures by year.&lt;/p&gt;
&lt;p&gt;When I hear the words “by year,” I immediately think we need to group this data by year. This is like one of those word math problems that everyone hated in junior high until they understood how to convert the words to math. The same thing happens here. When your boss tells you they want “failures by year,” you should think, “I’m going to use the &lt;em&gt;year&lt;/em&gt; column in the pandas &lt;code&gt;.groupby&lt;/code&gt; method.”&lt;/p&gt;
&lt;p&gt;In this case, we don't have a &lt;code&gt;year&lt;/code&gt; column. But due to the magic of datetime columns, we can access the year through the &lt;code&gt;.dt&lt;/code&gt; accessor.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;bank&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;effective_date&lt;/span&gt;
&lt;span class="go"&gt;0      2020-10-23&lt;/span&gt;
&lt;span class="go"&gt;1      2020-10-16&lt;/span&gt;
&lt;span class="go"&gt;2      2020-04-03&lt;/span&gt;
&lt;span class="go"&gt;3      2020-02-14&lt;/span&gt;
&lt;span class="go"&gt;4      2019-11-01&lt;/span&gt;
&lt;span class="go"&gt;          ...    &lt;/span&gt;
&lt;span class="go"&gt;4099   1934-08-06&lt;/span&gt;
&lt;span class="go"&gt;4100   1934-07-18&lt;/span&gt;
&lt;span class="go"&gt;4101   1934-07-18&lt;/span&gt;
&lt;span class="go"&gt;4102   1934-05-28&lt;/span&gt;
&lt;span class="go"&gt;4103   1934-04-19&lt;/span&gt;
&lt;span class="go"&gt;Name: effective_date, Length: 4104, dtype: datetime64[ns]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bank&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bank&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;effective_date&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;effective_date&lt;/span&gt;
&lt;span class="go"&gt;1934     9&lt;/span&gt;
&lt;span class="go"&gt;1935    25&lt;/span&gt;
&lt;span class="go"&gt;1936    69&lt;/span&gt;
&lt;span class="go"&gt;1937    75&lt;/span&gt;
&lt;span class="go"&gt;1938    74&lt;/span&gt;
&lt;span class="go"&gt;        ..&lt;/span&gt;
&lt;span class="go"&gt;2015     8&lt;/span&gt;
&lt;span class="go"&gt;2016     5&lt;/span&gt;
&lt;span class="go"&gt;2017     8&lt;/span&gt;
&lt;span class="go"&gt;2019     4&lt;/span&gt;
&lt;span class="go"&gt;2020     4&lt;/span&gt;
&lt;span class="go"&gt;Length: 84, dtype: int64&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is ok. I would prefer a visualization to this table of data. Let’s try a bar chart.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bank&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bank&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;effective_date&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt="Bank failures by year shown as a bar chart" src="/static/img/blog/pandas-finance/failures-by-year-bar.png"&gt;&lt;/p&gt;
&lt;p&gt;This is a start, but the x-axis is not happy. Just be aware that if you intend on making bar plots with pandas, it converts the index to categories. In this case, the dates are converted to strings. This makes me sad, but not every library is perfect.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/pandas-dev/pandas/issues/17001"&gt;https://github.com/pandas-dev/pandas/issues/17001&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Let’s try and make a line plot instead.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bank&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bank&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;effective_date&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt="Bank failures by year shown as a line chart" src="/static/img/blog/pandas-finance/failures-by-year-line.png"&gt;&lt;/p&gt;
&lt;p&gt;This looks good. Line plots in pandas (unlike bar plots) respect dates in the index.&lt;/p&gt;
&lt;p&gt;Now let’s jump into grouping functionality that is time-series specific.&lt;/p&gt;
&lt;h2&gt;Using Offset Aliases with Pandas Resample&lt;/h2&gt;
&lt;p&gt;Hidden away in the pandas documentation is a mention of &lt;em&gt;offset aliases&lt;/em&gt;. (Sadly, these are not accessible from the docstrings inside of Jupyter, which is one lament I have with pandas otherwise excellent documentation).&lt;/p&gt;
&lt;p&gt;You can find them &lt;a href="https://pandas.pydata.org/docs/user_guide/timeseries.html#dateoffset-objects"&gt;here&lt;/a&gt;, or documented in my book, &lt;em&gt;Effective Pandas&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;In short, &lt;code&gt;Y&lt;/code&gt;, &lt;code&gt;Q&lt;/code&gt;, &lt;code&gt;M&lt;/code&gt;, &lt;code&gt;W&lt;/code&gt;, and &lt;code&gt;D&lt;/code&gt; mean year, quarter, month, week, and day respectively. If you memorize these single-letter shortcuts, the &lt;code&gt;.resample&lt;/code&gt; method, or the &lt;code&gt;pd.Grouper&lt;/code&gt; class, you can quickly aggregate date information by different time intervals.&lt;/p&gt;
&lt;p&gt;Let's try and view failures by month using the &lt;code&gt;.resample&lt;/code&gt; method. Pandas resampling is powerful because it lets you convert time-series data with one time interval into time-series data with different time intervals. You can upsample so you have more rows over shorter intervals, or downsample so you have fewer rows over longer intervals. The key to using this method is sticking a date column into the index and then calling &lt;code&gt;.resample&lt;/code&gt; instead of &lt;code&gt;.groupby&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bank&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;effective_date&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;M&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;effective_date&lt;/span&gt;
&lt;span class="go"&gt;1934-04-30    1&lt;/span&gt;
&lt;span class="go"&gt;1934-05-31    1&lt;/span&gt;
&lt;span class="go"&gt;1934-07-31    2&lt;/span&gt;
&lt;span class="go"&gt;1934-08-31    1&lt;/span&gt;
&lt;span class="go"&gt;1934-09-30    1&lt;/span&gt;
&lt;span class="go"&gt;             ..&lt;/span&gt;
&lt;span class="go"&gt;2019-10-31    2&lt;/span&gt;
&lt;span class="go"&gt;2019-11-30    1&lt;/span&gt;
&lt;span class="go"&gt;2020-02-29    1&lt;/span&gt;
&lt;span class="go"&gt;2020-04-30    1&lt;/span&gt;
&lt;span class="go"&gt;2020-10-31    2&lt;/span&gt;
&lt;span class="go"&gt;Length: 540, dtype: int64&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Notice the index in the resulting &lt;code&gt;Series&lt;/code&gt;. Each entry ends on the last day of the month. This is because we passed the &lt;code&gt;M&lt;/code&gt; (month) offset alias into the &lt;code&gt;.resample&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;I generally plot this by chaining on a call to &lt;code&gt;.plot&lt;/code&gt;. This will plot the date on the x-axis and draw the values on the y-axis.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bank&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;effective_date&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;M&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt="Monthly bank failures over time" src="/static/img/blog/pandas-finance/failures-by-month.png"&gt;&lt;/p&gt;
&lt;h2&gt;Smoothing By Rougher Aggregations Using Pandas Resample&lt;/h2&gt;
&lt;p&gt;Let’s try something a little fancier. We will make a function that plots the monthly failures (which we already saw was bumpy) and then plot the yearly aggregation on top.&lt;/p&gt;
&lt;p&gt;I'll use the &lt;code&gt;.pipe&lt;/code&gt; method to refactor the plotting logic into a single function.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;matplotlib.pyplot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;plt&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;fig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subplots&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;figsize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;plot_monthly_and_yearly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ax&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;M&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ax&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Monthly&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Y&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ax&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Annual&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="n"&gt;ax&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;legend&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;

&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bank&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;effective_date&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plot_monthly_and_yearly&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ax&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ax&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;        cert    charter_class   estimated_loss  fin id  institution             total_assets    total_deposits  resolution  transaction_type    insurance_fund  city            state&lt;/span&gt;
&lt;span class="go"&gt;effective_date                                                  &lt;/span&gt;
&lt;span class="go"&gt;2020-10-23  15426.0 NM      16806.0     10538   4104    ALMENA STATE BANK           65733.0     64941       FAILURE     PA          DIF     ALMENA          KS&lt;/span&gt;
&lt;span class="go"&gt;2020-10-16  16748.0 NM      7247.0      10537   4103    FIRST CITY BANK OF FLORIDA      136566.0    133936      FAILURE     PA          DIF     FORT WALTON BEACH   FL&lt;/span&gt;
&lt;span class="go"&gt;2020-04-03  14361.0 NM      45913.0     10536   4102    THE FIRST STATE BANK            151808.0    143102      FAILURE     PA          DIF     BARBOURSVILLE       WV&lt;/span&gt;
&lt;span class="go"&gt;2020-02-14  18265.0 NM      25293.0     10535   4101    ERICSON STATE BANK          100879.0    95159       FAILURE     PA          DIF     ERICSON         NE&lt;/span&gt;
&lt;span class="go"&gt;2019-11-01  21111.0 N       1946.0      10534   4100    CITY NATIONAL BANK OF NEW JERSEY    120574.0    111234      FAILURE     PA          DIF     NEWARK          NJ&lt;/span&gt;
&lt;span class="go"&gt;...     ... ...     ...     ... ... ...                 ...     ...     ...     ...         ...     ...         ...&lt;/span&gt;
&lt;span class="go"&gt;1934-08-06  NaN NM      NaN     0   137 BANK OF LEWISPORT           81.0        68      FAILURE     PO          BIF     LEWISPORT       KY&lt;/span&gt;
&lt;span class="go"&gt;1934-07-18  NaN N       NaN     0   135 FIRST NATIONAL BANK OF LIMA     91.0        42      FAILURE     PO          BIF     LIMA            MT&lt;/span&gt;
&lt;span class="go"&gt;1934-07-18  NaN NM      NaN     0   136 FLORENCE DEPOSIT BANK           105.0       69      FAILURE     PO          BIF     FLORENCE        IN&lt;/span&gt;
&lt;span class="go"&gt;1934-05-28  NaN NM      NaN     0   133 FON DU LAC STATE BANK           374.0       238     FAILURE     PO          BIF     EAST PEORIA     IL&lt;/span&gt;
&lt;span class="go"&gt;1934-04-19  NaN NM      NaN     0   134 BANK OF AMERICA TRUST CO.       1435.0      1064        FAILURE     PO          BIF     PITTSBURGH      PA&lt;/span&gt;
&lt;span class="go"&gt;4104 rows x 13 columns&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt="Monthly and annual bank failures plotted together" src="/static/img/blog/pandas-finance/monthly-and-annual-failures.png"&gt;&lt;/p&gt;
&lt;h2&gt;Resolution Type Over Time&lt;/h2&gt;
&lt;p&gt;I want to talk about one more feature of pandas: the &lt;code&gt;pd.Grouper&lt;/code&gt; class. I use this class to group by different frequencies of a date column without having to stick the date in the index and call &lt;code&gt;.resample&lt;/code&gt;. You might ask, why?&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;.resample&lt;/code&gt; method only allows us to have a single grouping. If you have played around with &lt;code&gt;.groupby&lt;/code&gt;, then you know that you can provide multiple columns or series to &lt;code&gt;.groupby&lt;/code&gt;. In this example, let's assume this word problem:&lt;/p&gt;
&lt;p&gt;What are the counts of the different resolution types by month?&lt;/p&gt;
&lt;p&gt;This is a little tricky. We have “by month,” so we want to group by month, but we also really have “size by resolution type by month.”&lt;/p&gt;
&lt;p&gt;A first stab might look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bank&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;bank&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;effective_date&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;month&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;resolution&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;effective_date  resolution&lt;/span&gt;
&lt;span class="go"&gt;1               ASSISTANCE     22&lt;/span&gt;
&lt;span class="go"&gt;                FAILURE       274&lt;/span&gt;
&lt;span class="go"&gt;2               ASSISTANCE     26&lt;/span&gt;
&lt;span class="go"&gt;                FAILURE       302&lt;/span&gt;
&lt;span class="go"&gt;3               ASSISTANCE     32&lt;/span&gt;
&lt;span class="go"&gt;                FAILURE       443&lt;/span&gt;
&lt;span class="go"&gt;4               ASSISTANCE     87&lt;/span&gt;
&lt;span class="go"&gt;                FAILURE       328&lt;/span&gt;
&lt;span class="go"&gt;5               ASSISTANCE     30&lt;/span&gt;
&lt;span class="go"&gt;                FAILURE       283&lt;/span&gt;
&lt;span class="go"&gt;6               ASSISTANCE     28&lt;/span&gt;
&lt;span class="go"&gt;                FAILURE       298&lt;/span&gt;
&lt;span class="go"&gt;7               ASSISTANCE     48&lt;/span&gt;
&lt;span class="go"&gt;                FAILURE       359&lt;/span&gt;
&lt;span class="go"&gt;8               ASSISTANCE     62&lt;/span&gt;
&lt;span class="go"&gt;                FAILURE       280&lt;/span&gt;
&lt;span class="go"&gt;9               ASSISTANCE     57&lt;/span&gt;
&lt;span class="go"&gt;                FAILURE       219&lt;/span&gt;
&lt;span class="go"&gt;10              ASSISTANCE     47&lt;/span&gt;
&lt;span class="go"&gt;                FAILURE       284&lt;/span&gt;
&lt;span class="go"&gt;11              ASSISTANCE     30&lt;/span&gt;
&lt;span class="go"&gt;                FAILURE       212&lt;/span&gt;
&lt;span class="go"&gt;12              ASSISTANCE    110&lt;/span&gt;
&lt;span class="go"&gt;                FAILURE       243&lt;/span&gt;
&lt;span class="go"&gt;dtype: int64&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This sort of works if we want to know how many banks failed in December. But we want monthly data over time. Because we also want to group by resolution, we can't use the &lt;code&gt;.resample&lt;/code&gt; method. This is where &lt;code&gt;pd.Grouper&lt;/code&gt; comes in.&lt;/p&gt;
&lt;p&gt;We provide the name of a datetime column and a frequency (this is that offset alias that we talked about above). The code looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bank&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Grouper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;effective_date&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;freq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;M&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;           &lt;span class="s1"&gt;&amp;#39;resolution&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;effective_date  resolution&lt;/span&gt;
&lt;span class="go"&gt;1934-04-30      FAILURE       1&lt;/span&gt;
&lt;span class="go"&gt;1934-05-31      FAILURE       1&lt;/span&gt;
&lt;span class="go"&gt;1934-07-31      FAILURE       2&lt;/span&gt;
&lt;span class="go"&gt;1934-08-31      FAILURE       1&lt;/span&gt;
&lt;span class="go"&gt;1934-09-30      FAILURE       1&lt;/span&gt;
&lt;span class="go"&gt;                             ..&lt;/span&gt;
&lt;span class="go"&gt;2019-10-31      FAILURE       2&lt;/span&gt;
&lt;span class="go"&gt;2019-11-30      FAILURE       1&lt;/span&gt;
&lt;span class="go"&gt;2020-02-29      FAILURE       1&lt;/span&gt;
&lt;span class="go"&gt;2020-04-30      FAILURE       1&lt;/span&gt;
&lt;span class="go"&gt;2020-10-31      FAILURE       2&lt;/span&gt;
&lt;span class="go"&gt;Length: 635, dtype: int64&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;By now, you know that I want to plot this. However, tacking on &lt;code&gt;.plot&lt;/code&gt; is unsatisfying as we have a hierarchical index and this sticks both date and resolution as a tuple on the x-axis:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bank&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Grouper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;effective_date&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;freq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;M&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
           &lt;span class="s1"&gt;&amp;#39;resolution&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt="Unsatisfactory plot using a hierarchical index" src="/static/img/blog/pandas-finance/resolution-hierarchical-index.png"&gt;&lt;/p&gt;
&lt;p&gt;What I want to do instead is unstack the innermost index, the resolution index. The &lt;code&gt;.unstack&lt;/code&gt; method will pull out the inner index by default and stick it up into the columns. Now we have monthly dates in the index and a column for each resolution type.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bank&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Grouper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;effective_date&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;freq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;M&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;           &lt;span class="s1"&gt;&amp;#39;resolution&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unstack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;... &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;        __reduced___ASSISTANCE  __reduced___FAILURE&lt;/span&gt;
&lt;span class="go"&gt;effective_date      &lt;/span&gt;
&lt;span class="go"&gt;1934-04-30  NaN         1&lt;/span&gt;
&lt;span class="go"&gt;1934-05-31  NaN         1&lt;/span&gt;
&lt;span class="go"&gt;1934-07-31  NaN         2&lt;/span&gt;
&lt;span class="go"&gt;1934-08-31  NaN         1&lt;/span&gt;
&lt;span class="go"&gt;1934-09-30  NaN         1&lt;/span&gt;
&lt;span class="go"&gt;...     ...         ...&lt;/span&gt;
&lt;span class="go"&gt;2019-10-31  NaN         2&lt;/span&gt;
&lt;span class="go"&gt;2019-11-30  NaN         1&lt;/span&gt;
&lt;span class="go"&gt;2020-02-29  NaN         1&lt;/span&gt;
&lt;span class="go"&gt;2020-04-30  NaN         1&lt;/span&gt;
&lt;span class="go"&gt;2020-10-31  NaN         2&lt;/span&gt;
&lt;span class="go"&gt;540 rows x 2 columns&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If we plot this, we will get a line for each resolution type over time. It looks like the &lt;em&gt;FAILURE&lt;/em&gt; resolution is much more common since 1989.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;fix_cols&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;__reduced___&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bank&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Grouper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;effective_date&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;freq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;M&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
           &lt;span class="s1"&gt;&amp;#39;resolution&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unstack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fillna&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fix_cols&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt="Bank assistance and failure resolution types over time" src="/static/img/blog/pandas-finance/resolution-types-over-time.png"&gt;&lt;/p&gt;
&lt;p&gt;Let’s do a 12-month rolling average to smooth this out a bit.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bank&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Grouper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;effective_date&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;freq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;M&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
           &lt;span class="s1"&gt;&amp;#39;resolution&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unstack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fix_cols&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fillna&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rolling&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt="Twelve-month rolling average of resolution types" src="/static/img/blog/pandas-finance/resolution-rolling-average.png"&gt;&lt;/p&gt;
&lt;p&gt;Below I plot the yearly resolution size. Again, you can see that the basic shape is the same, but the yearly rolling average tells a better story.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bank&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Grouper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;effective_date&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;freq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Y&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
           &lt;span class="s1"&gt;&amp;#39;resolution&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unstack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fix_cols&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fillna&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt="Annual bank resolutions by type" src="/static/img/blog/pandas-finance/resolution-yearly.png"&gt;&lt;/p&gt;
&lt;p&gt;And finally, let’s look at the 12-month rolling average by resolution type.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bank&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Grouper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;effective_date&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;freq&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;M&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
           &lt;span class="s1"&gt;&amp;#39;transaction_type&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unstack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fix_cols&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fillna&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rolling&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt="Twelve-month rolling average by transaction type" src="/static/img/blog/pandas-finance/transaction-type-rolling-average.png"&gt;&lt;/p&gt;
&lt;h2&gt;Now You Have Pandas Resample, Groupby, and Rolling Superpowers&lt;/h2&gt;
&lt;p&gt;I hope that this blog post exposed you to the powerful time-series functionality in pandas, particularly &lt;code&gt;.resample&lt;/code&gt;, &lt;code&gt;.groupby&lt;/code&gt;, and &lt;code&gt;.rolling&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Folks often ask me why I use pandas to manipulate my data. Could I do this in SQL? Yes… probably. But I’ve written enough SQL in my life to know that it wouldn’t be fun for me.&lt;/p&gt;</content><category term="misc"/><category term="pandas"/><category term="finance"/><category term="time-series"/></entry><entry><title>I Wrote A Book</title><link href="https://metasnake.com/blog/xgboostauthor.html" rel="alternate"/><published>2023-03-27T00:00:00-06:00</published><updated>2023-03-27T00:00:00-06:00</updated><author><name>Matt Harrison</name></author><id>tag:metasnake.com,2023-03-27:/blog/xgboostauthor.html</id><summary type="html"/><content type="html">&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt; - I made over $25,000 in the two-day launch of my new self-published book, &lt;a href="https://store.metasnake.com/xgboost"&gt;Effective XGBoost&lt;/a&gt;. I discuss how that happened and if writing a book is worth the time and effort.&lt;/p&gt;
&lt;figure class='border'&gt;
&lt;img src='/static/img/xgb.png' class='img-fluid' alt='bunch of books'/&gt;
&lt;figcaption&gt;My new Effective XGBoost book walks through end-to-end classification from model creation to tuning to interpretation, to deployment.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2&gt;Why Write a Book?&lt;/h2&gt;
&lt;p&gt;Every potential author has to answer this for themself. I have written and worked as a technical editor for many books (Illustrated Guide to Python 3, Intermediate Python, Effective Pandas, Machine Learning Pocket Reference, Learning the Pandas Library, Pandas 1.x Cookbook, Tiny Python Notebook (3.5-3.10), Effective Pycharm, Hands-on Machine Learning with Scikit-Learn, Keras &amp;amp; Tensorflow, Hands-on Unsupervised Learning Using Python, and maybe a few others), so I'm aware of what is involved in bringing a book from an idea to something you can hold in your hand. I also know and &lt;a href="https://store.metasnake.com/effective-authoring"&gt;have interviewed over a dozen technical authors&lt;/a&gt; and know many other Python and data science authors. The general advice that you would make more per hour consulting is true. &lt;/p&gt;
&lt;figure class='border'&gt;
&lt;img src='/static/img/books.jpg' class='img-fluid' alt='bunch of books'/&gt;
&lt;figcaption&gt;Some of the books I have written.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Many folks write because they have the bug. Others write for credibility. Others want a book with an animal on the cover. Since transitioning from development into mostly corporate training and consulting, authoring has been great marketing for me.&lt;/p&gt;
&lt;p&gt;With self-publishing being relatively easy these days, the sole gatekeeper to writing a book is yourself. Be aware, book creation is a marathon. Don't jump in thinking it is a sprint.&lt;/p&gt;
&lt;h2&gt;You Made More in Two Days Then I Make in...&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://store.metasnake.com/xgboost"&gt;Effective XGBoost&lt;/a&gt; has been my best launch to date. After two days I had over $25,000 in sales. Perhaps this feels like bragging or an overnight success. But I've been writing books since 2011 and finally feel like I'm starting to crack the nut (or at least listen to common advice like setting up a mailing list).&lt;/p&gt;
&lt;p&gt;At this point, I have a few thousand on my mailing list, 140k followers on &lt;a href="https://www.twitter.com/__mharrison__"&gt;Twitter&lt;/a&gt;, and around 20k connections on &lt;a href="https://www.linkedin.com/in/panela/"&gt;LinkedIn&lt;/a&gt;. I've fully succumbed to the reality that I must do the marketing myself, and I've worked pretty hard (particularly on the Twitter side) to grow my audience.&lt;/p&gt;
&lt;p&gt;Also, note that I've been working on this book since the summer of 2022. Cranking out a quality book is hard work and takes a lot of time. Creating video courses is much easier and less time-consuming for me. Due to the nature of my work, I was often able to spend entire days working on the book. That was not the case when I had fulltime employment and wrote my first books. Many days I woke up early to crank out content. My back-of-the-napkin calculation is that I spent 10 hours a week over 25 weeks writing this book. That comes out to about $100/hour. &lt;/p&gt;
&lt;p&gt;If you are now itching to write a book, note that my consulting fees are slightly higher than $100 per hour. Plus, I've authored multiple books, and this book performed much better than previous launches. For comparison, &lt;em&gt;Effective Pandas&lt;/em&gt;, only did around $8,000 on its release.&lt;/p&gt;
&lt;h2&gt;Audience&lt;/h2&gt;
&lt;p&gt;Since the dawn of self-publishing, authors have touted the importance of a mailing list. I didn't have an email list when I created my first books. Bad choice, Matt! &lt;/p&gt;
&lt;p&gt;I used the Gumroad platform initially, and while the features were great for sales, that was about the end of the features. Eventually, I moved on to Podia, which includes mailing list functionality. Honestly, my mailing list-fu is lacking, but I'm continually looking to improve. I might create a Substack, but as a one-man shop, I like to keep things simple and limit platforms. The draw of Podia is that it is all in one. &lt;/p&gt;
&lt;figure class='border'&gt;
&lt;img src='/static/img/xgbsales.png' class='img-fluid' alt='bunch of books'/&gt;
&lt;figcaption&gt;Launch sales.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I have a few thousand subscribers on my mailing list right now, and they accounted for 35% of my sales.&lt;/p&gt;
&lt;p&gt;My next largest source of sales was from Twitter. (If you are wondering how I tracked this, I used Podia coupons with platform-specific codes.) I doubled down on Twitter during Covid and now have 140K followers. They accounted for around 32% of my sales. With all of the hijinks Elon has been pulling recently, having all my "eggs" in the Twitter basket is scary.&lt;/p&gt;
&lt;p&gt;LinkedIn came in around 11%, but I only have 20K followers there. I'm actively working on growing that audience. Just another thing to put in the to-do list...&lt;/p&gt;
&lt;p&gt;Around 15% of my sales aren't attributable or are from folks who didn't use a coupon. Many of my followers pay full price to support creators like me. I'm grateful to those "super fans". Some of these folks might not have figured out how to enter the coupon code at checkout. Podia can be tricky like that sometimes.&lt;/p&gt;
&lt;p&gt;Around 5% of the sales during this time were upsells. So these weren't sales of Effective XGBoost but in-cart discounts to my &lt;em&gt;Effective Pandas&lt;/em&gt; material. Podia can be tricky like that sometimes.&lt;/p&gt;
&lt;p&gt;You'll note that I haven't mentioned the elephant in the book sales business ... Amazon.&lt;/p&gt;
&lt;h2&gt;Physical Books&lt;/h2&gt;
&lt;p&gt;My initial release was only for a digital book with an option for a course and consulting. I highly recommend creating an accompanying course for your book. Around 70% of my sales included the course. Here's the math. 100 books at $50 = $5,000. Contrast that with having a course, offering 70 courses at $100 and 30 books = $7,000 + $1,500 = $8,500.&lt;/p&gt;
&lt;p&gt;Oh yeah, Amazon. Amazon accounts for a large number of sales. However, they also take a large cut. And they don't give me access to my readers.&lt;/p&gt;
&lt;p&gt;The compromise I have settled on is letting Amazon (and other long-tail providers) handle the physical book. Many prefer to read from a physical book, and Amazon does a great job pushing your book for you, especially if you can get that fly-wheel moving. However, I feed my family with my book sales and would prefer not to have a middleman for my audience's digital sales.&lt;/p&gt;
&lt;p&gt;I created both a paperback and hardback book (&lt;a href="https://amzn.to/3TRBF00"&gt;Amazon now offers these&lt;/a&gt;) and announced to my mailing list that these books were available without disclosing them to the rest of the world. I also deeply discounted it (to the point where I'm making $1/sale) as a bonus for my mailing list and a way to say "thanks" to my audience. &lt;/p&gt;
&lt;figure class='border'&gt;
&lt;img src='/static/img/xgbamz.png' class='img-fluid' alt='bunch of books'/&gt;
&lt;figcaption&gt;Amazon ranking after launch to mailing list.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;This (hopefully) kicks in the Amazon machine, as my mailing list pushed the book to the top of the lists and a rank of around 3,000 overall on Amazon.&lt;/p&gt;
&lt;p&gt;As of this post, I've bumped the price back up to normal. Amazon can take the cut of sales they bring in, but I will push my digital version and let readers know about the Amazon option if they prefer physical books.&lt;/p&gt;
&lt;p&gt;Based on the performance of &lt;em&gt;Effective Pandas&lt;/em&gt; (which has done well on Amazon), the Amazon machine can get close to Podia in terms of ongoing revenue.&lt;/p&gt;
&lt;p&gt;Note that I'm doing a full-color book on Amazon. These are expensive to print. I could probably double my Amazon income if I keep the same price but sold only a grayscale interior. However, my book has dozens of charts and accompanying code, and the color print on this batch of &lt;em&gt;Effective XGBoost&lt;/em&gt; has been excellent. (I'm thinking that Amazon upgraded the POD printers, or I've been really lucky.)&lt;/p&gt;
&lt;h2&gt;Traditional Publishing?&lt;/h2&gt;
&lt;p&gt;What about traditional publishing? Are my readers getting an inferior product? My content is not prefect. I make tpyos, like the rest of the world. But editors are also not perfect and also miss typos as well. 😉&lt;/p&gt;
&lt;p&gt;I feel like my process is decent, and having been through the process of two other publishers, I feel confident about the final outcome. I can "execute" my books via doctests or notebooks, so I know the code works. Grammarly helps with most typos these days.&lt;/p&gt;
&lt;p&gt;I recruit dozens of volunteers from my mailing list review the drafts of the book. This turns out a quality product based on feedback and experience.&lt;/p&gt;
&lt;p&gt;What about the other stuff? Layout, cover design, marketing? Yep, that is work. Consider using a publisher if you can't figure out how to do it professionally.&lt;/p&gt;
&lt;p&gt;What about distribution? A publisher does provide more distribution than just Amazon. However, Amazon is the elephant here. And I've found that after any initial promotion, the onus is solely on me to promote my book. I use Ingram Spark to reach the long tail, but the squeeze is a lot of effort for the juice when I could be doing other things.&lt;/p&gt;
&lt;h2&gt;AI and the Death of Books&lt;/h2&gt;
&lt;p&gt;When I was about 90% done with the book, ChatGPT was released. As a technologist, I jumped on this boat and was really happy using it to augment where possible. As a one-man shop, Chat (as I like to call it) is a game-changer.&lt;/p&gt;
&lt;p&gt;I won't go deep into how I used Chat here (perhaps in a different post). I will say, Chat's hallucinations are something to be aware of. Did I use Chat content in the book? Yes, however, I edited everything that Chat created. &lt;/p&gt;
&lt;p&gt;Will I use Chat in future content creation? Yes.&lt;/p&gt;
&lt;p&gt;Do I think Chat means the end of books? No. I don't feel like Chat can completely automate book creation now. I also think there is some creative direction that Chat can't create on its own.&lt;/p&gt;
&lt;p&gt;Does that mean that books won't be written by Chat? No. They already are. This isn't particularly disturbing to me because I've seen folks creating garbage books since Amazon opened the floodgates of self-publishing. You can even pick out these scam books on the charts. Shady folks sell shady stuff to make a buck and then use bots to help game the system. Alas, this is the world we live in. I feel like my audience trusts me to provide a quality product, and &lt;a href="https://store.metasnake.com/effective-pandas-book"&gt;the reviews confirm that&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Will Writing A Book Help You?&lt;/h2&gt;
&lt;p&gt;Thanks for getting this far. &lt;/p&gt;
&lt;p&gt;Should you write a book? I get this question a lot. &lt;/p&gt;
&lt;p&gt;As a corporate trainer, getting a foot in the door is much easier if you have "written the book on the subject". I know a lot of trainers, and many of them have written books... I wonder why that is? 😉&lt;/p&gt;
&lt;p&gt;For more software or data-type folks, should you write a book? Again, if you want to be able to show expertise, this is one (albeit a long one) road to accomplish that task. Having a portfolio project and being able to discuss it in detail would probably accomplish the same for most folks but take much less time.&lt;/p&gt;
&lt;p&gt;If you have a large platform or means to promote the book, it can be profitable. Note that this is a long tail game, and the news that you read about is typically the winners with outlier results.&lt;/p&gt;
&lt;p&gt;Many just have a "book inside of them" that needs to get out. Just realize that getting it out is a marathon and like most things tech, will probably take two to three times as long as you think it will.&lt;/p&gt;
&lt;p&gt;Let me know if you have any questions. I'm at &lt;a href="https://www.twitter.com/__mharrison__"&gt;@__mharrison__.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;(Chat did not write this post.)&lt;/p&gt;</content><category term="misc"/><category term="bookauthor"/></entry><entry><title>Pandas Series Introduction</title><link href="https://metasnake.com/blog/pandas-series-intro.html" rel="alternate"/><published>2021-06-03T00:00:00-06:00</published><updated>2021-06-03T00:00:00-06:00</updated><author><name>Matt Harrison</name></author><id>tag:metasnake.com,2021-06-03:/blog/pandas-series-intro.html</id><summary type="html">&lt;p class="first last"&gt;Pandas Series Introduction&lt;/p&gt;
</summary><content type="html">&lt;p&gt;A &lt;tt class="docutils literal"&gt;Series&lt;/tt&gt; is used to model one-dimensional data, similar to a list
in Python. The &lt;tt class="docutils literal"&gt;Series&lt;/tt&gt; object also has a few more bits of data,
including an index and a name. A common idea through pandas is the
notion of an axis. Because a series is one dimensiona-, it has a single
&lt;em&gt;axis&lt;/em&gt;—the index.&lt;/p&gt;
&lt;p&gt;Below is a table of counts of songs artists composed. We will use this to
explore how the series type functions:&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="55%" /&gt;
&lt;col width="45%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;Artist&lt;/th&gt;
&lt;th class="head"&gt;Data&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;145&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;142&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;38&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;If you wanted to represent this data in pure Python, you could use a data structure
similar to the one that follows. It is a dictionary that has a list of
the data points stored under the &lt;tt class="docutils literal"&gt;'data'&lt;/tt&gt; key. In addition to an
entry in the dictionary for the actual data, there is an explicit
entry for the corresponding index values for the data (in the
&lt;tt class="docutils literal"&gt;'index'&lt;/tt&gt; key), as well as an entry for the name of the data (in the
&lt;tt class="docutils literal"&gt;'name'&lt;/tt&gt; key):&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; ser = {
...   'index':[0, 1, 2, 3],
...   'data':[145, 142, 38, 13],
...   'name':'songs'
...   }
&lt;/pre&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;get&lt;/tt&gt; function defined below can pull items out of this data
structure based on the index:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; def get(ser, idx):
...     value_idx = ser['index'].index(idx)
...     return ser['data'][value_idx]

&amp;gt;&amp;gt;&amp;gt; get(ser, 1)
142
&lt;/pre&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;The code samples in this book are shown as if they were
typed directly into an interpreter. Lines starting with &lt;tt class="docutils literal"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/tt&gt; and
&lt;tt class="docutils literal"&gt;...&lt;/tt&gt; are interpreter markers for the &lt;em&gt;input prompt&lt;/em&gt; and
&lt;em&gt;continuation prompt&lt;/em&gt; respectively. Lines that are not prefixed by one
of those sequences are the output from the interpreter after running
the code.&lt;/p&gt;
&lt;p class="last"&gt;The Python interpreter will print the return value of the last
invocation (even if the &lt;tt class="docutils literal"&gt;print&lt;/tt&gt; statement is missing)
automatically. If you desire to use the code samples found in this book, leave the
interpreter prompts out.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="the-index-abstraction"&gt;
&lt;h2&gt;The index abstraction&lt;/h2&gt;
&lt;p&gt;This double abstraction of the index seems unnecessary at first
glance—a list already has integer indexes. But there is a trick up pandas'
sleeves. By allowing non-integer values, the data structure
supports other index types such as strings, dates, as well as
arbitrarily ordered indices or even duplicate index values.&lt;/p&gt;
&lt;p&gt;Below is an example that has string values for the index:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; songs = {
...   'index':['Paul', 'John', 'George', 'Ringo'],
...   'data':[145, 142, 38, 13],
...   'name':'counts'
...   }

&amp;gt;&amp;gt;&amp;gt; get(songs, 'John')
142
&lt;/pre&gt;
&lt;p&gt;The index is a core feature of pandas’ data structures given the
library’s past in analysis of financial data or &lt;em&gt;time series
data&lt;/em&gt;. Many of the operations performed on a &lt;tt class="docutils literal"&gt;Series&lt;/tt&gt; operate directly on the index
or by index lookup.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="the-pandas-series"&gt;
&lt;h2&gt;The pandas &lt;tt class="docutils literal"&gt;Series&lt;/tt&gt;&lt;/h2&gt;
&lt;p&gt;With that background in mind, let’s look at how to create a &lt;tt class="docutils literal"&gt;Series&lt;/tt&gt;
in pandas.  It is easy to create a &lt;tt class="docutils literal"&gt;Series&lt;/tt&gt; object from a list:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; import pandas as pd
&amp;gt;&amp;gt;&amp;gt; songs2 = pd.Series([145, 142, 38, 13],
...      name='counts')

&amp;gt;&amp;gt;&amp;gt; songs2
0    145
1    142
2     38
3     13
Name: counts, dtype: int64
&lt;/pre&gt;
&lt;p&gt;When the interpreter prints our series, pandas makes a best effort to
format it for the current terminal size. The leftmost column is the
&lt;em&gt;index&lt;/em&gt; column which contains entries for the index. The generic name
for an index is an &lt;em&gt;axis&lt;/em&gt;, and the values of the index—0, 1, 2, 3—are called
&lt;em&gt;axis labels&lt;/em&gt;. The two-dimensional structure in
pandas—a &lt;tt class="docutils literal"&gt;DataFrame&lt;/tt&gt;—has two axes, one for the rows and another for
the columns.&lt;/p&gt;
&lt;img alt="foo" class="img-fluid" src="/static/img/pandas2/04-series.png" /&gt;
&lt;p class="figcaption"&gt;The parts of a Series.&lt;/p&gt;
&lt;p&gt;The rightmost column in the output contains the &lt;em&gt;values&lt;/em&gt; of the series—145, 142, 38, and 13. In this
case, they are integers (the console representation says &lt;tt class="docutils literal"&gt;dtype: int64&lt;/tt&gt;,
&lt;tt class="docutils literal"&gt;dtype&lt;/tt&gt; meaning data type, and &lt;tt class="docutils literal"&gt;int64&lt;/tt&gt; meaning 64-bit integer),
but in general values of a &lt;tt class="docutils literal"&gt;Series&lt;/tt&gt; can hold strings, floats, booleans, or
arbitrary Python objects. To get the best speed (and to leverage vectorized operations), the
values should be of the same type, though this is not required.&lt;/p&gt;
&lt;p&gt;It is easy to inspect the index of a series (or data frame), as it
is an attribute of the object:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; songs2.index
RangeIndex(start=0, stop=4, step=1)
&lt;/pre&gt;
&lt;p&gt;The default values for an index are monotonically increasing integers. &lt;tt class="docutils literal"&gt;songs2&lt;/tt&gt;
has an integer-based index.&lt;/p&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;The index can be string based as well, in which case pandas
indicates that the datatype for the index is &lt;tt class="docutils literal"&gt;object&lt;/tt&gt; (not
string):&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; songs3 = pd.Series([145, 142, 38, 13],
...      name='counts',
...      index=['Paul', 'John', 'George', 'Ringo'])
&lt;/pre&gt;
&lt;p&gt;Note that the &lt;tt class="docutils literal"&gt;dtype&lt;/tt&gt; that we see when we print a &lt;tt class="docutils literal"&gt;Series&lt;/tt&gt; is the type of the values,
not of the index:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; songs3
Paul      145
John      142
George     38
Ringo      13
Name: counts, dtype: int64
&lt;/pre&gt;
&lt;p&gt;When we inspect the index attribute, we see that the &lt;tt class="docutils literal"&gt;dtype&lt;/tt&gt; is &lt;tt class="docutils literal"&gt;object&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="last literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; songs3.index   # doctest: +NORMALIZE_WHITESPACE
Index(['Paul', 'John', 'George', 'Ringo'],
dtype='object')
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The actual data for a series does not have to be numeric or
homogeneous. We can insert Python objects into a series:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; class Foo:
...     pass

&amp;gt;&amp;gt;&amp;gt; ringo = pd.Series(
...      ['Richard', 'Starkey', 13, Foo()],
...      name='ringo')

&amp;gt;&amp;gt;&amp;gt; ringo  # doctest: +SKIP +NORMALIZE_WHITESPACE
0                             Richard
1                             Starkey
2                                  13
3    &amp;lt;__main__.Foo instance at 0x...&amp;gt;
Name: ringo, dtype: object
&lt;/pre&gt;
&lt;p&gt;In the above case, the &lt;tt class="docutils literal"&gt;dtype&lt;/tt&gt;-&lt;em&gt;datatype&lt;/em&gt;-of the &lt;tt class="docutils literal"&gt;Series&lt;/tt&gt; is
&lt;tt class="docutils literal"&gt;object&lt;/tt&gt; (meaning a Python object). This can be good or bad.&lt;/p&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;object&lt;/tt&gt; data type is used for strings. But, it is also used for
values that have heterogeneous types. If you have just numeric data in a series, you
wouldn't want it stored as a Python object, but rather as an &lt;tt class="docutils literal"&gt;int64&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;float64&lt;/tt&gt;,
which allow you to do vectorized numeric operations.&lt;/p&gt;
&lt;p&gt;If you have time data and it says it has the &lt;tt class="docutils literal"&gt;object&lt;/tt&gt; type, you probably have
strings for the dates. Using strings instead of date types is bad as you don't get the date operations that you
would get if the type were &lt;tt class="docutils literal"&gt;datetime64[ns]&lt;/tt&gt;. Strings, on the other hand, are
stored in pandas as &lt;tt class="docutils literal"&gt;object&lt;/tt&gt;. Don't worry; we will see how to convert types
later in the book.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="the-nan-value"&gt;
&lt;h2&gt;The &lt;tt class="docutils literal"&gt;NaN&lt;/tt&gt; value&lt;/h2&gt;
&lt;p&gt;A value that may be familiar to NumPy users, but not Python users in
general, is &lt;tt class="docutils literal"&gt;NaN&lt;/tt&gt;. When pandas determines that a series holds
numeric values, but it cannot find a number to represent an entry, it
will use &lt;tt class="docutils literal"&gt;NaN&lt;/tt&gt;. This value stands for &lt;em&gt;Not A Number&lt;/em&gt; and is usually
ignored in arithmetic operations. (Similar to NULL in SQL).&lt;/p&gt;
&lt;p&gt;Here is a series that has &lt;tt class="docutils literal"&gt;NaN&lt;/tt&gt; in it:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; import numpy as np
&amp;gt;&amp;gt;&amp;gt; nan_ser = pd.Series([2, np.nan],
...    index=['Ono', 'Clapton'])
&amp;gt;&amp;gt;&amp;gt; nan_ser
Ono        2.0
Clapton    NaN
dtype: float64
&lt;/pre&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;One thing to note is that the type of this series is &lt;tt class="docutils literal"&gt;float64&lt;/tt&gt;, not &lt;tt class="docutils literal"&gt;int64&lt;/tt&gt;!
The type is a float because &lt;tt class="docutils literal"&gt;float64&lt;/tt&gt; supports &lt;tt class="docutils literal"&gt;NaN&lt;/tt&gt;, which &lt;tt class="docutils literal"&gt;int64&lt;/tt&gt; does not. When pandas sees numeric
data (&lt;tt class="docutils literal"&gt;2&lt;/tt&gt;) as well as the &lt;tt class="docutils literal"&gt;None&lt;/tt&gt;, it coerced the &lt;tt class="docutils literal"&gt;2&lt;/tt&gt; to a float value.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Below is an example of how pandas ignores &lt;tt class="docutils literal"&gt;NaN&lt;/tt&gt;. The &lt;tt class="docutils literal"&gt;.count&lt;/tt&gt; method,
which counts the number of values in a series, disregards &lt;tt class="docutils literal"&gt;NaN&lt;/tt&gt;. In
this case, it indicates that the count of items in the &lt;tt class="docutils literal"&gt;Series&lt;/tt&gt; is one,
one for the value of &lt;tt class="docutils literal"&gt;2&lt;/tt&gt; at index location
&lt;tt class="docutils literal"&gt;Ono&lt;/tt&gt;, ignoring the &lt;tt class="docutils literal"&gt;NaN&lt;/tt&gt; value at index location &lt;tt class="docutils literal"&gt;Clapton&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; nan_ser.count()
1
&lt;/pre&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;If you load data from a CSV file, an empty value for an otherwise
numeric column will become &lt;tt class="docutils literal"&gt;NaN&lt;/tt&gt;. Later, methods such as
&lt;tt class="docutils literal"&gt;.fillna&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;.dropna&lt;/tt&gt; will explain how to deal with &lt;tt class="docutils literal"&gt;NaN&lt;/tt&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;None&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;NaN&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;nan&lt;/tt&gt;, and null are synonyms in this book when
referring to empty or missing data found in a pandas series or
data frame.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="optional-integer-support-for-nan"&gt;
&lt;h2&gt;Optional Integer Support for &lt;tt class="docutils literal"&gt;NaN&lt;/tt&gt;&lt;/h2&gt;
&lt;p&gt;As of pandas 0.24, there is optional support for the integer type to hold &lt;tt class="docutils literal"&gt;NaN&lt;/tt&gt; values.
The documentation calls this type the &lt;em&gt;nullable integer type&lt;/em&gt;.
When you create a series, you can pass in &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;dtype='Int64'&lt;/span&gt;&lt;/tt&gt; (note the capitalization):&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; nan_ser2 = pd.Series([2, None],
...    index=['Ono', 'Clapton'],
...    dtype='Int64')
&amp;gt;&amp;gt;&amp;gt; nan_ser2
Ono           2
Clapton    &amp;lt;NA&amp;gt;
dtype: Int64
&lt;/pre&gt;
&lt;p&gt;Operations on these series still ignore &lt;tt class="docutils literal"&gt;NaN&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; nan_ser2.count()
1
&lt;/pre&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;You can use the &lt;tt class="docutils literal"&gt;.astype&lt;/tt&gt; method to convert columns to the nullable integer type. Just use
the string &lt;tt class="docutils literal"&gt;'Int64'&lt;/tt&gt; as the type:&lt;/p&gt;
&lt;pre class="last literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; nan_ser.astype('Int64')
Ono           2
Clapton    &amp;lt;NA&amp;gt;
dtype: Int64
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="similar-to-numpy"&gt;
&lt;h2&gt;Similar to NumPy&lt;/h2&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;Series&lt;/tt&gt; object behaves similarly to a NumPy
array. As shown below, both types respond to index operations:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; import numpy as np
&amp;gt;&amp;gt;&amp;gt; numpy_ser = np.array([145, 142, 38, 13])
&amp;gt;&amp;gt;&amp;gt; songs3[1]
142
&amp;gt;&amp;gt;&amp;gt; numpy_ser[1]
142
&lt;/pre&gt;
&lt;p&gt;They both have methods in common:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; songs3.mean()
84.5
&amp;gt;&amp;gt;&amp;gt; numpy_ser.mean()
84.5
&lt;/pre&gt;
&lt;p&gt;They also both have a notion of a &lt;em&gt;boolean array&lt;/em&gt;. A boolean array is a series with the same index as the series you are working with that has boolean values. It can be used
as a mask to filter out items. Normal Python lists do not support such fancy
index operations:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; mask = songs3 &amp;gt; songs3.median()  # boolean array

&amp;gt;&amp;gt;&amp;gt; mask
Paul       True
John       True
George    False
Ringo     False
Name: counts, dtype: bool
&lt;/pre&gt;
&lt;p&gt;Once we have a mask, we can use that as a filter. We just need to pass the mask into
an index operation. If the mask has a &lt;tt class="docutils literal"&gt;True&lt;/tt&gt; value for a given index, the value is
kept. Otherwise, the value is dropped. The mask above represents the locations
that have a value higher than the median value of the series.&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; songs3[mask]
Paul    145
John    142
Name: counts, dtype: int64
&lt;/pre&gt;
&lt;img alt="foo" class="img-fluid" src="/static/img/pandas2/04-booleanarray.png" /&gt;
&lt;p class="figcaption"&gt;Filtering a series with a boolean array.&lt;/p&gt;
&lt;p&gt;NumPy also has filtering by boolean arrays, but lacks the &lt;tt class="docutils literal"&gt;.median&lt;/tt&gt;
method on an array. Instead, NumPy provides a &lt;tt class="docutils literal"&gt;median&lt;/tt&gt; function in
the NumPy namespace:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; numpy_ser[numpy_ser &amp;gt; np.median(numpy_ser)]
array([145, 142])
&lt;/pre&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;Both NumPy and pandas have adopted the convention of using import
statements in combination with an &lt;tt class="docutils literal"&gt;as&lt;/tt&gt; statement to rename their imports to two
letter acronyms:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; import pandas as pd
&amp;gt;&amp;gt;&amp;gt; import numpy as np
&lt;/pre&gt;
&lt;p&gt;Renaming imports provides a slight typing benefit (four fewer characters) while still allowing the user to be
explicit with their namespaces.&lt;/p&gt;
&lt;p&gt;Be careful, as you may see the following cast about in code samples, blogs, or documentation:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; from pandas import *
&lt;/pre&gt;
&lt;p class="last"&gt;Though you see &lt;em&gt;star imports&lt;/em&gt; frequently used in examples online, I would advise not
to use star imports. I never use them in examples or my work. They have the potential to clobber items in your
namespace and make tracing the source of a definition more difficult
(especially if you have multiple star imports). As the Zen of Python states,
“Explicit is better than implicit” &lt;a class="footnote-reference" href="#footnote-1" id="footnote-reference-1"&gt;[1]&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-1" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Type &lt;tt class="docutils literal"&gt;import this&lt;/tt&gt; into an interpreter to see the Zen of Python. Or search for &amp;quot;PEP 20&amp;quot;.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="section" id="categorical-data"&gt;
&lt;h2&gt;Categorical Data&lt;/h2&gt;
&lt;p&gt;When you load data, you can indicate that the data is categorical. If we know that
our data is limited to a few values; we might want to use categorical data. Categorical values
have a few benefits:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Use less memory than strings&lt;/li&gt;
&lt;li&gt;Can have an ordering&lt;/li&gt;
&lt;li&gt;Can perform operations on categories&lt;/li&gt;
&lt;li&gt;Enforce membership on values&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Categories are not limited to strings; we can also use numbers or datetime values.&lt;/p&gt;
&lt;p&gt;To create a category, we pass &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;dtype=&amp;quot;category&amp;quot;&lt;/span&gt;&lt;/tt&gt; into the &lt;tt class="docutils literal"&gt;Series&lt;/tt&gt;
constructor. Alternatively, we can call the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;.as_type(&amp;quot;category&amp;quot;)&lt;/span&gt;&lt;/tt&gt; method on a series:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; s = pd.Series(['m', 'l', 'xs', 's', 'xl'], dtype='category')
&amp;gt;&amp;gt;&amp;gt; s
0     m
1     l
2    xs
3     s
4    xl
dtype: category
Categories (5, object): ['l', 'm', 's', 'xl', 'xs']
&lt;/pre&gt;
&lt;p&gt;If this series represents size, there is a natural ordering as a small is less
than a medium. By default, categories don't have an ordering. We can verify this by inspecting the &lt;tt class="docutils literal"&gt;.cat&lt;/tt&gt; attribute
that has various properties:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; s.cat.ordered
False
&lt;/pre&gt;
&lt;p&gt;To convert a non-categorical series to an ordered category, we can create a type with the &lt;tt class="docutils literal"&gt;CategoricalDtype&lt;/tt&gt;
constructor and the appropriate parameters. Then we pass this type into the &lt;tt class="docutils literal"&gt;.astype&lt;/tt&gt; method:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; s2 = pd.Series(['m', 'l', 'xs', 's', 'xl'])
&amp;gt;&amp;gt;&amp;gt; size_type = pd.api.types.CategoricalDtype(
...     categories=['s','m','l'], ordered=True)
&amp;gt;&amp;gt;&amp;gt; s3 = s2.astype(size_type)
...
&amp;gt;&amp;gt;&amp;gt; s3
0      m
1      l
2    NaN
3      s
4    NaN
dtype: category
Categories (3, object): ['s' &amp;lt; 'm' &amp;lt; 'l']
&lt;/pre&gt;
&lt;p&gt;In this case, we limited the categories to just &lt;tt class="docutils literal"&gt;'s'&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;'m'&lt;/tt&gt;, and &lt;tt class="docutils literal"&gt;'l'&lt;/tt&gt;,
but the data had values that were not in those categories. These extra values were
replaced with &lt;tt class="docutils literal"&gt;NaN&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;If we have ordered categories, we can do comparisons on them:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; s3 &amp;gt; 's'
0     True
1     True
2    False
3    False
4    False
dtype: bool
&lt;/pre&gt;
&lt;p&gt;The prior example created a new &lt;tt class="docutils literal"&gt;Series&lt;/tt&gt; from existing data that was not categorical. We can also add
ordering information to categorical data. We just need to make sure that we
specify all of the members of the category or pandas will throw a
&lt;tt class="docutils literal"&gt;ValueError&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; s.cat.reorder_categories(['xs','s','m','l', 'xl'],
...                          ordered=True)
0     m
1     l
2    xs
3     s
4    xl
dtype: category
Categories (5, object): ['xs' &amp;lt; 's' &amp;lt; 'm' &amp;lt; 'l' &amp;lt; 'xl']
&lt;/pre&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;String and datetime series have a &lt;tt class="docutils literal"&gt;str&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;dt&lt;/tt&gt; attribute that allow us to perform common
operations specific to that type. If we convert these types to categorical types, we can still
use the &lt;tt class="docutils literal"&gt;str&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;dt&lt;/tt&gt; attributes on them:&lt;/p&gt;
&lt;pre class="last literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; s3.str.upper()
0      M
1      L
2    NaN
3      S
4    NaN
dtype: object
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="summary"&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;Series&lt;/tt&gt; object is a one-dimensional data structure. It can hold numerical data,
time data, strings, or arbitrary Python objects. If you are dealing with numeric
data, using pandas rather than a Python list will give you additional benefits. Pandas is
faster, consumes less memory, and comes with built-in methods that are very useful to manipulate the data.
Also, the index abstraction allows for accessing values by position or label.
A &lt;tt class="docutils literal"&gt;Series&lt;/tt&gt; can also have empty values and has some similarities to NumPy arrays.
It is the basic workhorse of pandas; mastering it will pay dividends.&lt;/p&gt;
&lt;/div&gt;
</content><category term="misc"/><category term="pandas"/></entry><entry><title>Pandas Data Structures</title><link href="https://metasnake.com/blog/pandas-data-structures.html" rel="alternate"/><published>2021-06-02T00:00:00-06:00</published><updated>2021-06-02T00:00:00-06:00</updated><author><name>Matt Harrison</name></author><id>tag:metasnake.com,2021-06-02:/blog/pandas-data-structures.html</id><summary type="html">&lt;p class="first last"&gt;Pandas Data Structures&lt;/p&gt;
</summary><content type="html">&lt;p&gt;One of the keys to understanding pandas is to understand the data
model. At the core of pandas are two data structures:&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;caption&gt;Different dimensions of pandas data structures&lt;/caption&gt;
&lt;colgroup&gt;
&lt;col width="25%" /&gt;
&lt;col width="32%" /&gt;
&lt;col width="42%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;Data Structure&lt;/th&gt;
&lt;th class="head"&gt;Dimensionality&lt;/th&gt;
&lt;th class="head"&gt;Spreadsheet Analog&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;&lt;tt class="docutils literal"&gt;Series&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;1D&lt;/td&gt;
&lt;td&gt;Column&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;tt class="docutils literal"&gt;DataFrame&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;2D&lt;/td&gt;
&lt;td&gt;Single Sheet&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The most widely used data structures are the &lt;tt class="docutils literal"&gt;Series&lt;/tt&gt; and the
&lt;tt class="docutils literal"&gt;DataFrame&lt;/tt&gt; that deal with array data and tabular data,
respectively. An analogy with the spreadsheet world illustrates the
basic differences between these types. A &lt;tt class="docutils literal"&gt;DataFrame&lt;/tt&gt; is similar to a
sheet with rows and columns, while a &lt;tt class="docutils literal"&gt;Series&lt;/tt&gt; is similar to a single
column of data.&lt;/p&gt;
&lt;img alt="foo" class="img-fluid" src="/static/img/pandas2/03-datastructures.png" /&gt;
&lt;p class="figcaption"&gt;Figure showing the relation between the main data structures in pandas. Namely,
that a data frame can have multiple series.&lt;/p&gt;
&lt;p&gt;Diving into these core data structures a little more is useful because a bit of
understanding goes a long way towards better use of the library. We will spend a
good portion of time discussing the &lt;tt class="docutils literal"&gt;Series&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;DataFrame&lt;/tt&gt;. Both the
&lt;tt class="docutils literal"&gt;Series&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;DataFrame&lt;/tt&gt; share features. For example, they both have an
index, which we will need to examine to understand how pandas works.&lt;/p&gt;
&lt;p&gt;Also, because the &lt;tt class="docutils literal"&gt;DataFrame&lt;/tt&gt; can be thought of as a collection of columns
that are really &lt;tt class="docutils literal"&gt;Series&lt;/tt&gt; objects, it is imperative that we have a comprehensive
study of the &lt;tt class="docutils literal"&gt;Series&lt;/tt&gt; first. Additionally (and perhaps odd to some), we will see this when we iterate over rows, and the rows are
represented as &lt;tt class="docutils literal"&gt;Series&lt;/tt&gt;.&lt;/p&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Some have compared the data structures to Python lists or dictionaries,
and I think this is a stretch that doesn't provide much benefit.
Mapping the list and dictionary methods on top of pandas' data structures
just leads to confusion.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="summary"&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;The pandas library includes three main data structures and associated functions for manipulating them.
This book will focus on the &lt;tt class="docutils literal"&gt;Series&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;DataFrame&lt;/tt&gt;. First, we will look at the &lt;tt class="docutils literal"&gt;Series&lt;/tt&gt; as
the &lt;tt class="docutils literal"&gt;DataFrame&lt;/tt&gt; can be thought of as a collection of &lt;tt class="docutils literal"&gt;Series&lt;/tt&gt;.&lt;/p&gt;
&lt;/div&gt;
</content><category term="misc"/><category term="pandas"/></entry><entry><title>The Future of Corporate Training</title><link href="https://metasnake.com/blog/postcovidetraining.html" rel="alternate"/><published>2021-04-15T00:00:00-06:00</published><updated>2021-04-15T00:00:00-06:00</updated><author><name>Matt Harrison</name></author><id>tag:metasnake.com,2021-04-15:/blog/postcovidetraining.html</id><summary type="html"/><content type="html">&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt; - I think in a post-Covid19 society, virtual training will be the default, but live training will return to office campuses and quarterly onsites.&lt;/p&gt;
&lt;h2&gt;Training Flipped&lt;/h2&gt;
&lt;p&gt;The last year threw corporate training for a loop. One week I was wrapping up a wonderful Machine Learning course for my biggest client and the next week they had dropped the dozen classes they had planned for the year.&lt;/p&gt;
&lt;p&gt;It is never fun to lose your biggest client, but rather than look back, I doubled down and invested in upping my virtual training skills. I read a lot of material, bought equipment, and the feedback has been wonderful:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I have had many programming teachers who were super knowledgeable but didn't
know much about pedagogy or vice versa and it was refreshing to be taught by
someone who had both sets of skills.&lt;/p&gt;
&lt;p&gt;Over all I think this was an excellent course and worthwhile. I like your
teaching style and appreciate your willingness to answer questions, even when
you had already explained it once before.&lt;/p&gt;
&lt;p&gt;I really enjoyed it and learned a lot, thanks!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Default to Work from Home?&lt;/h2&gt;
&lt;p&gt;The jury is mixed on what the future of work will look like. Tech titans like Netflix and Google are insisted on a return to the office, while Twitter is leading the way pushing for permanent work from home. Between these extremes are the many companies that can operate remotely, but may do so only part time.&lt;/p&gt;
&lt;p&gt;I asked my network on Twitter and LinkedIn and got very mixed results. The only take away is that a small percentage of my contacts will be going to the Netflix/Google model. The question is the degree from which they will work from home.&lt;/p&gt;
&lt;h2&gt;Where does Training Go?&lt;/h2&gt;
&lt;p&gt;My feeling is that much of training will remain virtual. However, I'm getting feeders from my network that in-person training is also desired. I suspect much of this may migrate to onsites. &lt;/p&gt;
&lt;p&gt;I feel that even with my investment into virtual training, in-person is just a bit better. You get to see people (many are Zoom-shy or just Zoom-exhausted), you can help them easier, and they can help each other easier. &lt;/p&gt;
&lt;p&gt;If you are looking for virtual or in-person (I'm vaccinated) training for Python or Data Science, reach out. Happy to help!&lt;/p&gt;</content><category term="misc"/><category term="training"/></entry><entry><title>Best Way to Learn Python in 2021</title><link href="https://metasnake.com/blog/learn-python-2021.html" rel="alternate"/><published>2021-01-12T00:00:00-07:00</published><updated>2021-01-12T00:00:00-07:00</updated><author><name>Matt Harrison</name></author><id>tag:metasnake.com,2021-01-12:/blog/learn-python-2021.html</id><summary type="html">&lt;p&gt;Lot's of Options&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;strong&gt;TLDR:&lt;/strong&gt; There is a great demand for Python skills. There are also lots of ways to learn.&lt;/p&gt;
&lt;p&gt;How does the saying go? The best time to learn Python was 20 years ago. The second best time is now. &lt;/p&gt;
&lt;p&gt;That's not quite true, Python was a niche language 20 years ago. Today Python is ubiquitous and one of the top languages used to develop applications, analyze data, create predictive models, manage servers, and more.&lt;/p&gt;
&lt;p&gt;When I learned Python in 2000, it was frowned upon as a less expressive Perl. It fit my brain, and I never looked back even though I have used Java, C#, Clojure, JavaScript, and other tools since.&lt;/p&gt;
&lt;p&gt;A common question I get these days is: what is the best way to learn Python? I'm going to answer that with a staple of data science, a decision tree. The answer really depends, but after teaching Python to thousands over many years, I have a few opinions on the topic.&lt;/p&gt;
&lt;figure&gt;
&lt;img src='/static/img/pythonflow.png' class='img-fluid' alt='Learning Python'/&gt;
&lt;figcaption&gt;&lt;strong&gt;TLDR:&lt;/strong&gt; Follow this for Python mastery&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;(&lt;em&gt;Full disclosure: I  make my living selling snake oil. I literally tell and enable people to do amazing things with Python that cure most ails.&lt;/em&gt;)&lt;/p&gt;
&lt;h2&gt;Are You a Corporation?&lt;/h2&gt;
&lt;p&gt;First things first. If you are a corporation, treat your employees well, and consider 
corporate training. There is something to be said about getting a team together and having
them work through training together. (I said work because training can be intense).
Your team will be on the same page at the end of the day. Training will fill in 
their knowledge gaps. They can address any issues or questions they have immediately with
a knowledgeable instructor. Good trainers can facilitate discussions that 
bring out customers issues and solutions to those quickly.&lt;/p&gt;
&lt;figure&gt;
&lt;img src='/static/img/custom-training.png' class='img-fluid' alt='Learning Python'/&gt;
&lt;figcaption&gt;Corporate Training is the quickest way to get your team on the same page.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Here is a quote from a manager at a large video company:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Nice breadth, very practical, easy to follow along. Everybody loved it. We already used some of the skills to debug two different production issues. Awesome!”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Seriously, invest in your employees. Here is a common complaint. I talk to many
companies that spend money on on-demand courses (full-disclosure: I have created
content for many of these courseware providers), and while the content may be
good, most employees are not motivated nor incentivized to go through it. The
employers are not getting the value out of these courses. This is a typical
criticism (in fact, most if not all of my clients have on-demand courses in
addition to live training because they want their employees to have certain skills and most
employees do not do on-demand.)&lt;/p&gt;
&lt;p&gt;One more thing to consider with live corporate training. Not all corporate
training is the same. Who would you rather have training you? Someone who
is a professional slide reader? Or someone who has worked with the technology
for 20+ years and also has had extensive experience tutoring, teaching, and 
writing?&lt;/p&gt;
&lt;p&gt;Going with a quality trainer who has in-the-trenches experience and teaching
skills can make a big difference. I generally only train on material that
I have used as a consultant or as an employee. &lt;/p&gt;
&lt;p&gt;If you are not interested in MetaSnake's offerings, reach out, I know a bunch
of other quality trainers that also have more experience than just reciting
Powerpoint presentations.&lt;/p&gt;
&lt;h2&gt;The Best for Individuals&lt;/h2&gt;
&lt;p&gt;How bad do you want to learn Python? I've come across countless people who
claim they want to learn Python, but are not willing to invest anything to get results.
The best results come from having a coach to identify weak spots and 
provide advice and practice to improve those faults.&lt;/p&gt;
&lt;figure&gt;
&lt;img src='/static/img/Brandon-Payne-profile.jpeg' class='img-fluid' alt='Brandon Payne'/&gt;
&lt;figcaption&gt;Coach you probably don't recognize.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;You have probably never heard of the coach above. His name is Brandon
Payne. Still at a loss? His client is 
likely the best shooter in basketball history. Steph Curry
has won NBA championships, MVPs, and made an amazing 100 three pointers in 
a row. Why would he need a coach? In Steph's own words:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I appreciate [Brandon's] perspective on things — everything from watching game film and cutting it up to advice and direction on &lt;strong&gt;reaching my full potential&lt;/strong&gt;... And we created a vision for where I wanted to be in terms of ballhandling, footwork, balance and, of course, &lt;strong&gt;accountability&lt;/strong&gt;.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A coach is a partner who can help with reaching full potential and perhaps more
importantly accountability.&lt;/p&gt;
&lt;p&gt;Those who are at the top of their game use coaches: CEOs, athletes,
entrepreneurs, and programmers. Why? Because they invest in their future. A
coach can help identify and cure weaknesses and further strengthen existing
skills. I could go on, but the point is that people who are serious about
something invest in it, and coaching is probably the best way to improve.&lt;/p&gt;
&lt;p&gt;A "free" option for coaching might be a more senior colleague who is willing
to let you tag along and pair program. If this includes mentoring along the
way, this can be invaluable. Some companies encourage this sort of behavior
while others view it as "off task" and a waste of precious developer time.&lt;/p&gt;
&lt;h2&gt;Workshops &amp;amp; Live Courses&lt;/h2&gt;
&lt;p&gt;Similar to corporate training, workshops and live courses are the equivalents
for an individual. Sometimes smaller companies or startups invest in one-off
trainings for individuals when they don't have a whole team to train.&lt;/p&gt;
&lt;p&gt;These courses lack many of the bonding and insight moments that come with team
training, but if you want to grok something quickly, these are the way to go.
Again, an experienced trainer can pinpoint weaknesses or gaps and develop them
much quicker than going solo.&lt;/p&gt;
&lt;p&gt;These may appear to cost more, but consider they can be like drinking from a firehose. You get comments like this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;I felt like I learned 3 months worth of material in 3 days!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;After three days, most students are off and running with their new skills.
Thrilled to be using thier new knowledge.&lt;/p&gt;
&lt;p&gt;I was once in a training, and after demoing some code, a participant did a face
palm. I asked what was the matter and they replied that the one line of code I
had just showed them had taken them almost a month to implement. 🤦‍&lt;/p&gt;
&lt;h2&gt;Projects&lt;/h2&gt;
&lt;p&gt;Invariably, some are not sold on learning fast and thoroughly. (They have not 
allocated budget to invest in the future.)&lt;/p&gt;
&lt;p&gt;At this point, there is a new requirement for learning Python (or 
any new skill), &lt;strong&gt;sustained motivation&lt;/strong&gt;. Sitting through a couple of days of training
is really easy compared to this, but I digress.&lt;/p&gt;
&lt;p&gt;A great way to pick up Python is to choose a project and implement it in Python.
In fact, this is how I learned Python. Was my code idiomatic Python when I was done?
Nope, I was pretty clueless, but I enjoyed the language features that I did use.
Did I have an expert helping me? No, it was the blind leading the blind. Was
it quick? Somewhat, but again I wasn't using the tools how I should be. However,
it did give me a good chance to play around with the language and compare it
to other tools that I was used to.&lt;/p&gt;
&lt;p&gt;If you have a hobby project that you are itching to do, or your boss is 
breathing down your neck about your next task, consider using this
as a forcing function to get familiar with Python. There can be some inherent 
motivation here to learn.&lt;/p&gt;
&lt;h2&gt;Videos and On-demand Training&lt;/h2&gt;
&lt;p&gt;For those with sufficient self-motivation, on-demand video courses can
be excellent. Again, the quality of the material can vary. You might 
want to consider if you are acquiring the courses from sources that
focus on the topics at hand or if they are trying to be everything to
everyone. &lt;/p&gt;
&lt;p&gt;Over the years, many have asked me for training and I ask them how big their
team is. When the response was "just me", I felt bad, but in the past year I
have been developing a library of training for such individuals in addition to
my corporate training. (Indeed, this can be complementary to corporate training
as review material.)&lt;/p&gt;
&lt;p&gt;I think my material is wonderful (but also realize that I'm heavily biased),
and am willing to share my thoughts about other platforms and their pros and cons.&lt;/p&gt;
&lt;p&gt;Again, the lack of budget might be a concern for some. I offer discounts to
individuals not from the world's 30 wealthiest countries (or those laid off due
to Covid19 and students). Still, for some, their willingness to invest in
learning consists of spending no money but lots of time. (I hope you can
see that I think this is the wrong tradeoff, especially for professionals).&lt;/p&gt;
&lt;p&gt;In that case, most courses are paid courses. However, there are thousands if 
not millions of hours of free content on Youtube. These vary in quality of 
content, audio, and video. However, you can strike gold with these. &lt;/p&gt;
&lt;p&gt;Unless you come across some University course (which might lean more theoretical
and lacking application or at least experience in the battlefield), you will
be picking up nuggets here and there without a coherent learning path. Again,
not an effective use of time, in my opinion, but if the only currency you are
willing to spend is time, this is an option.&lt;/p&gt;
&lt;h2&gt;Books&lt;/h2&gt;
&lt;p&gt;The final category of my learning options is the written word. You should
ask yourself (or your company) "what are you willing to invest?". Many companies
are willing to reimburse book purchases.&lt;/p&gt;
&lt;figure&gt;
&lt;img src='/static/img/books.jpg' class='img-fluid' alt='Lots of books'/&gt;
&lt;figcaption&gt;Some of the books that I have written&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Why are books a good investment? Because the author was forced to fill out a
proposal form that required that they think about taking the end-user, aka
the reader, aka you, down a learning path. During the writing process, there
are editors and reviewers whose purpose is to ensure that the learning 
happens.&lt;/p&gt;
&lt;p&gt;Having written a few books (and seeing the reader feedback) as well as reading
and reviewing many technical books, I'm a huge proponent of books. However, if
you just read books, science tells us that you will not retain most of what
you read. To retain information, you will need to apply it (or work on getting
it into long term storage). This is probably my biggest beef with many books,
they do not include exercises, or they are hard to apply (before they leave
short-term memory).&lt;/p&gt;
&lt;p&gt;Again, there are books of differing quality. I tend to read a lot of Python 
books, and am more than willing to proffer my opinion.&lt;/p&gt;
&lt;p&gt;We are now back at the "lack of budget" folks. One option is the library. However most
libraries do not have up-to-date material. Offering to be a technical reviewer might
be a way to get your hands on newer material, but generally these are 
jobs that are requested from you rather than the other way around.&lt;/p&gt;
&lt;p&gt;The cheap option, that sadly has suffered in the past few years with the rise
of Twitter and blogging platforms like Medium, is the blog. This is the Youtube of
written content. There is a lot out there. Some is gold, some is outdated. But,
these posts generally lack a learning path and are not great for taking you
along on a learning journey.&lt;/p&gt;
&lt;h2&gt;Get Started Learning&lt;/h2&gt;
&lt;p&gt;There you have it. My (obviously biased) thoughts around the most effective ways
to learn Python after having been involved in almost every aspect of this for 
the past twenty years. Feel free to reach out on Twitter or LinkedIn to discuss.&lt;/p&gt;</content><category term="misc"/><category term="python"/></entry><entry><title>Why Write a Book?</title><link href="https://metasnake.com/blog/authoring.html" rel="alternate"/><published>2020-11-13T00:00:00-07:00</published><updated>2020-11-13T00:00:00-07:00</updated><author><name>Matt Harrison</name></author><id>tag:metasnake.com,2020-11-13:/blog/authoring.html</id><summary type="html">&lt;p&gt;Why Write?&lt;/p&gt;</summary><content type="html">&lt;h2&gt;The New Business Card&lt;/h2&gt;
&lt;p&gt;I never considered myself an &lt;em&gt;author&lt;/em&gt;. But after answering many one-off calls asking for advice on book writing and realizing that I have published over a dozen books on Amazon, I guess I should admit it. I am an author!&lt;/p&gt;
&lt;figure class='border'&gt;
&lt;img src='/static/img/books.jpg' class='img-fluid' alt='bunch of books'/&gt;
&lt;figcaption&gt;Many of the books that I have written over the years.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Authoring has never been easier. Let's chat about some of the reasons for writing a book and see if they might resonate with you. &lt;/p&gt;
&lt;p&gt;Note: I'm specifically talking about writing a non-fiction technical book. I've yet to tackle fiction (other than publishing a book of short stories from a book writing class I taught at the local elementary school.)&lt;/p&gt;
&lt;h2&gt;Lend me your ears and I'll sing you a song&lt;/h2&gt;
&lt;p&gt;Is there a story inside of you? I just said that I was not referring to fiction, so what does this even mean?&lt;/p&gt;
&lt;p&gt;I started my writing journey after preparing and delivering a half-day Python tutorial. I was very excited about the tutorial's content and felt that it was "the tutorial that I wanted when I was learning Python." The tutorial feedback was extremely positive, and the tutorial had been accepted to be offered at another conference. &lt;/p&gt;
&lt;p&gt;Rather than just rewriting my slides in preparation for the upcoming conference, I figured why not take this awesome content and turn it into a book? It should not be too hard, plus I had three months before the conference, so this would allow me to double-down and learn the material inside out. &lt;/p&gt;
&lt;p&gt;Long story short, it took a bit more than three months to write the book, but this effort became the first version of what is now &lt;a href="https://www.amazon.com/Illustrated-Guide-Python-Walkthrough-Illustrations/dp/1977921752/ref=sxts_sxwds-bia-wc-p13n1_0?crid=BP7UUYGQWD9C&amp;amp;cv_ct_cx=illustrated+guide+to+python+3&amp;amp;dchild=1&amp;amp;keywords=illustrated+guide+to+python+3&amp;amp;pd_rd_i=1977921752&amp;amp;pd_rd_r=a4fbe37f-a03e-42ea-8e0d-1a67b3db4573&amp;amp;pd_rd_w=ctKMv&amp;amp;pd_rd_wg=eLACG&amp;amp;pf_rd_p=1835a2a9-7ed8-48dc-ad07-fcd7527bd2bc&amp;amp;pf_rd_r=H81VRR49YZ8SAYXMA9WD&amp;amp;psc=1&amp;amp;qid=1605381873&amp;amp;sprefix=illustrated+guide+to+p%2Caps%2C198&amp;amp;sr=1-1-80ba0e26-a1cd-4e7b-87a0-a2ffae3a273c"&gt;&lt;em&gt;Illustrated Guide to Python 3&lt;/em&gt;&lt;/a&gt;. And this kickstarted my path as an author.&lt;/p&gt;
&lt;p&gt;This seems to be a common occurrence. A (lead) engineer gets asked what resources they would recommend for ... and they come up empty. There are no good resources (or bikesheds that are their favorite color). So they set out to create their own. I have interviewed many authors who had this experience. They set out to create the resource they desired.&lt;/p&gt;
&lt;h2&gt;Recognition&lt;/h2&gt;
&lt;p&gt;After I published my book, I didn't want to make too much noise about it; because technical people realize that the value is in the product not the marketing or sales (how wrong I was). &lt;/p&gt;
&lt;p&gt;Then I had the coolest experience, I was walking down the hallway at work, and a colleague approached me and said, "Hey, aren't you the author of &lt;em&gt;Treading On Python&lt;/em&gt;?". I was dumbfounded. Without any mention on my part, the Amazon sales engine sold a book to someone who worked a few cubicles away from me. That felt awesome. But even better was that they had high praise for the book. Recognition is something that everyone craves.&lt;/p&gt;
&lt;p&gt;Many potential authors want this recognition. Perhaps it can be better described as the author wants a "book with an animal on the cover" (along with their name).&lt;/p&gt;
&lt;figure class='border'&gt;
&lt;img src='/static/img/animalcover.jpg' class='img-fluid' alt='framed OReilly book'/&gt;
&lt;figcaption&gt;The coveted animal cover.&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;I'm guilty of that. I have a framed copy of my book with a northern crested newt on the cover. (I wish it were a short-horned lizard, but I digress). This work has since been translated into seven languages and distributed around the world.&lt;/p&gt;
&lt;p&gt;That experience of serendipitous recognition has been repeated over the years. I don't have a paparazzi following me, but...&lt;/p&gt;
&lt;h2&gt;Hustle&lt;/h2&gt;
&lt;p&gt;Recognition opens doors.&lt;/p&gt;
&lt;p&gt;Many authors (or would-be authors) are interested in "the hustle." This could be looking for side gigs, augmenting income, looking for a better job, or starting consulting.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Book writing is an excellent business card&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;One of my author friends, Mark Beckner, put it this way in his book &lt;a href="https://www.amazon.com/Coders-Path-Wealth-Independence/dp/1484204220/ref=sr_1_1?dchild=1&amp;amp;keywords=the+coders+path+to+wealth+and+independence&amp;amp;qid=1605382078&amp;amp;s=books&amp;amp;sr=1-1"&gt;&lt;em&gt;The Coder's Path to Wealth and Independence&lt;/em&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you publish intelligently, your business will be transformed. 
If you write with the intention that &lt;em&gt;your publication will create unexpected opportunities&lt;/em&gt;, it will enable you to move to a new level in your business.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I have found this to be true. And I know and have spoken with many other authors who had this experience.&lt;/p&gt;
&lt;p&gt;It is well known that the process of writing helps you to understand a subject better. You have literally &lt;em&gt;written the book on the subject&lt;/em&gt;. With self-publishing (or even purchasing bulk copies from your publisher), you can use your book as a cost-effective business card booster. (In the current Covid-19 climate, this is a little harder. I have a bookshelf of books I meant to deliver to (potential) clients this year at conferences.)&lt;/p&gt;
&lt;p&gt;This opens the doors to opportunities that would not come your way even if you were an expert in said technology.&lt;/p&gt;
&lt;p&gt;Part of the hustle is money. While I know many authors who have sold six figures worth of books, these tend to be outliers. However, many authors I know have indicated that though they earned minimum wage from the writing, they have indirectly profited much more after the book from side gigs, job opportunities, and more.&lt;/p&gt;
&lt;h2&gt;Taking the Plunge&lt;/h2&gt;
&lt;p&gt;Having said this, writing a book is a marathon. Many have sprinted out of the gate only to fail. (I've been approached by a few publishers to "become a second author" on an in-progress book, which is code for "the author burned out").&lt;/p&gt;
&lt;p&gt;Have you considered writing a book? I've created a course that walks from ideation to holding a book in your hand. It discusses the tradeoffs of self-publishing and going with a publisher, how to outline, what tools to use, how to get distribution, ebook and physical book tips, audiobook creation, and more. I've come full-circle. This is the course I wanted 10 years ago when I set out to write my first book.&lt;/p&gt;</content><category term="misc"/><category term="authoring"/></entry><entry><title>Learn to use the assign method in Pandas (or how to deal with SettingWithCopyWarning issues)</title><link href="https://metasnake.com/blog/pydata-assign.html" rel="alternate"/><published>2020-04-03T00:00:00-06:00</published><updated>2020-04-03T00:00:00-06:00</updated><author><name>Matt Harrison</name></author><id>tag:metasnake.com,2020-04-03:/blog/pydata-assign.html</id><summary type="html">&lt;p&gt;Do not use index assignment&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;strong&gt;TLDR:&lt;/strong&gt; Favor the &lt;code&gt;.assign&lt;/code&gt; method, and you will not deal with &lt;code&gt;SettingWithCopyWarning&lt;/code&gt; issues.&lt;/p&gt;
&lt;figure class='border'&gt;
&lt;img src='/static/img/pandas-err2.png' class='img-fluid' alt=Python TLDR'/&gt;
&lt;figcaption&gt;Master the &lt;tt&gt;.assign&lt;/tt&gt; method and not index assignment!&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;!--
## Pandas King

I will proclaim myself the Pandas King. I have used pandas since it came out. I
have taught thousands pandas. I created one of the first video courses on
Pandas. I have written three books on Pandas and am working on another. Listen
to the Pandas King.
--&gt;

&lt;h2&gt;Bad Code&lt;/h2&gt;
&lt;p&gt;Today I'm going to show you the solution to the dreaded
&lt;code&gt;SettingWithCopyWarning&lt;/code&gt; error that you eventually run into, but no
one seems to explain it or give you a solution that you can understand.&lt;/p&gt;
&lt;p&gt;I just saw &lt;a href="https://www.kdnuggets.com/2020/04/stop-hurting-pandas.html"&gt;another article discussing&lt;/a&gt; it today and thought I would share
the best solution (that no one else talks about).&lt;/p&gt;
&lt;p&gt;Here's the data referred to in the above article:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;df = pd.DataFrame({
  &amp;#39;x&amp;#39;:[1,5,4,3,4,5],
  &amp;#39;y&amp;#39;:[.1,.5,.4,.3,.4,.5],
  &amp;#39;w&amp;#39;:[11,15,14,13,14,15]})
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The quest with this toy data is to change all values in the &lt;em&gt;y&lt;/em&gt; column to 50 when
the &lt;em&gt;x&lt;/em&gt; column is greater than 3. Most try to do this by using indexing operations
to filter out the appropriate rows and then doing an index assignment.&lt;/p&gt;
&lt;p&gt;If you do an index operation directly on &lt;code&gt;df&lt;/code&gt;, the update does not work. And you see
the dreaded &lt;code&gt;SettingWithCopyWarning&lt;/code&gt; warning:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;df[df[&amp;#39;x&amp;#39;]&amp;gt;3][&amp;#39;y&amp;#39;] = 50
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;figure class='border'&gt;
&lt;img src='/static/img/pandas-err1.png' class='img-fluid' alt=Python TLDR'/&gt;
&lt;figcaption&gt;The dreaded warning!&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;The issue with this code is that &lt;code&gt;df[df['x']&amp;gt;3]&lt;/code&gt; does not return the original dataframe. So the subsequent index assignment, &lt;code&gt;['y'] = 50&lt;/code&gt;, updates the new dataframe and not &lt;code&gt;df&lt;/code&gt;. In effect, you create a new dataframe, assign to it, and do not capture it to a variable, so it is immediately available to be garbage collected.&lt;/p&gt;
&lt;p&gt;The warning includes a tip to use &lt;code&gt;.loc&lt;/code&gt; for indexing instead. Here is an example that works:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;df.loc[df[&amp;#39;x&amp;#39;]&amp;gt;3,&amp;#39;y&amp;#39;] = 50
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I recommend that you do &lt;em&gt;not&lt;/em&gt; use this solution. I have a better solution below.&lt;/p&gt;
&lt;h2&gt;Three Rules for Better Pandas&lt;/h2&gt;
&lt;p&gt;However, I'll add three more rules of thumb that I have found to make pandas
code easier to understand and less prone to errors:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do not mutate objects&lt;/li&gt;
&lt;li&gt;Favor chaining operations&lt;/li&gt;
&lt;li&gt;Do not use assignment (the &lt;code&gt;=&lt;/code&gt; operator) in pandas&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The above code mutates &lt;code&gt;df&lt;/code&gt;. However, most pandas operations do not mutate. For feeble-minded people like me, mixing mutation and immutability leads to confusing code and errors. My preferred solution to the above is this code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;50&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In fact, whenever I create a new column (or update one) in pandas, I try to do it with the &lt;code&gt;.assign&lt;/code&gt; method. This code also requires understanding the &lt;code&gt;.where&lt;/code&gt;, method which takes some getting used to but obeys my three rules above.&lt;/p&gt;
&lt;h2&gt;A Chained Example&lt;/h2&gt;
&lt;p&gt;Here is an example from Chapter 5 of the &lt;em&gt;The Pandas 1.x Cookbook&lt;/em&gt;. This code loads vehicle data from the fueleconomy.gov website. It applies a chain of operations to the dataframe when creating the &lt;code&gt;data&lt;/code&gt; variable. &lt;/p&gt;
&lt;p&gt;First, it filters out the makes that are Ford, Tesla, BMW, or Toyota with this line &lt;code&gt;[fueleco.make.isin(makes)]&lt;/code&gt;. (It could
have used &lt;code&gt;.loc&lt;/code&gt; here as well). Next, it assigns a column, &lt;em&gt;SClass&lt;/em&gt;, that is
created from an inlined function (a &lt;code&gt;lambda&lt;/code&gt; function) with this line &lt;code&gt;.assign(SClass=...&lt;/code&gt;. If you use a function
with the &lt;code&gt;.assign&lt;/code&gt; method, you have access to the intermediate dataframe, referred to as &lt;code&gt;df_&lt;/code&gt; in the code. The lambda function dispatches to the &lt;code&gt;generalize&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;generalize&lt;/code&gt; function is a function I use all over the place when refining data. It takes a column and a list of tuples with match names and new values in it. It also accepts a default value. It loop over the list returns a new series where the match names are replaced with the new names. If there is no match, the default value is used. This function can be useful if you have a bunch of 
categorical data and want to limit it to a smaller subset:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;#39;&lt;/span&gt;&lt;span class="nl"&gt;https:&lt;/span&gt;&lt;span class="c1"&gt;//github.com/mattharrison/datasets/raw/master/data/vehicles.csv.zip&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;fueleco&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;generalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;match_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;None&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;match_name:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;None:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mask&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mask&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;ser&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;ser&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ser&lt;/span&gt;

&lt;span class="n"&gt;makes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;Ford&lt;/span&gt;&lt;span class="p"&gt;&amp;#39;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;Tesla&lt;/span&gt;&lt;span class="p"&gt;&amp;#39;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;BMW&lt;/span&gt;&lt;span class="p"&gt;&amp;#39;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;Toyota&lt;/span&gt;&lt;span class="p"&gt;&amp;#39;]&lt;/span&gt;
&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fueleco&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;fueleco&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;makes&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SClass&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;lambda&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;df_:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;generalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;VClass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;[(&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;Seaters&lt;/span&gt;&lt;span class="p"&gt;&amp;#39;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;&amp;#39;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;&amp;#39;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;Car&lt;/span&gt;&lt;span class="p"&gt;&amp;#39;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;Utility&lt;/span&gt;&lt;span class="p"&gt;&amp;#39;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;SUV&lt;/span&gt;&lt;span class="p"&gt;&amp;#39;),&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="p"&gt;(&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;Truck&lt;/span&gt;&lt;span class="p"&gt;&amp;#39;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;Truck&lt;/span&gt;&lt;span class="p"&gt;&amp;#39;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;Van&lt;/span&gt;&lt;span class="p"&gt;&amp;#39;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;Van&lt;/span&gt;&lt;span class="p"&gt;&amp;#39;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;van&lt;/span&gt;&lt;span class="p"&gt;&amp;#39;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;Van&lt;/span&gt;&lt;span class="p"&gt;&amp;#39;),&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="p"&gt;(&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;Wagon&lt;/span&gt;&lt;span class="p"&gt;&amp;#39;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;Wagon&lt;/span&gt;&lt;span class="p"&gt;&amp;#39;)],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;&amp;#39;))&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[[&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;make&lt;/span&gt;&lt;span class="p"&gt;&amp;#39;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;SClass&lt;/span&gt;&lt;span class="p"&gt;&amp;#39;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;VClass&lt;/span&gt;&lt;span class="p"&gt;&amp;#39;]]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;figure class='border'&gt;
&lt;img src='/static/img/pandas-err3.png' class='img-fluid' alt=Python TLDR'/&gt;
&lt;figcaption&gt;Master the &lt;tt&gt;.assign&lt;/tt&gt; method and gain superpowers!&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Try not to mutate data when using pandas. Even the pandas core developers say that you should not use mutation. If you use method chaining (note that index assignment does not work with chaining!), you will not mutate data. You will not have to deal with the &lt;code&gt;SettingWithCopyWarning&lt;/code&gt;. Your code will be easier to read because it will read like processing steps. &lt;/p&gt;
&lt;p&gt;If you want to see examples of pandas code that obey these rules see my latest book, &lt;a href="https://store.metasnake.com/effective-pandas-book"&gt;Effective Pandas&lt;/a&gt; or &lt;a href="https://twitter.com/__mharrison__"&gt;follow me on twitter where I rant on such topics&lt;/a&gt;.&lt;/p&gt;</content><category term="misc"/><category term="python"/><category term="anaconda"/></entry><entry><title>Setting up a Python Data Science Environment</title><link href="https://metasnake.com/blog/pydata-dev.html" rel="alternate"/><published>2020-03-06T00:00:00-07:00</published><updated>2020-03-06T00:00:00-07:00</updated><author><name>Matt Harrison</name></author><id>tag:metasnake.com,2020-03-06:/blog/pydata-dev.html</id><summary type="html">&lt;p&gt;Anaconda or pip&lt;/p&gt;</summary><content type="html">&lt;figure&gt;
&lt;img src='/static/img/pipvsconda.png' class='img-fluid' alt=Python TLDR'/&gt;
&lt;figcaption&gt;You can set up your environment using either method with three commands&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Here are instructions for getting a Python stack installed on Windows, Mac, and Linux. These examples will use Python 3 (which I recommend as Python 2 is no longer being developed). &lt;/p&gt;
&lt;p&gt;You have two main options for installing a Python environment. You can use the Python executable that you can get from &lt;a href="www.python.org"&gt;the Python website&lt;/a&gt;, or you can use the Anaconda distribution. I will show you both. If you are new to Python, only doing "data science" work, or using Windows, I recommend using Anaconda.&lt;/p&gt;
&lt;h2&gt;Using Anaconda&lt;/h2&gt;
&lt;figure&gt;
&lt;img src='/static/img/conda-tldr.png' class='img-fluid' alt='Conda TLDR'/&gt;
&lt;figcaption&gt;&lt;strong&gt;TLDR:&lt;/strong&gt; Use these to create an environment using Anaconda&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Anaconda is both a product and a company. The company supports Python data tooling and makes the distribution available as a free product, but also as a supported enterprise paid for product. This page will demo the free option, but I'm assuming that the paid product is similar after install. There are versions for both Python 3.x and Python 2.7. Make sure you use the Python 3.x version.&lt;/p&gt;
&lt;p&gt;The Anaconda distribution includes "Python" it just has some extras. It uses its own package system to make it simple to create working Python environments on most platforms. The steps you need to take if you want to use it are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Install Anaconda (or Miniconda)&lt;/li&gt;
&lt;li&gt;Create an environment&lt;/li&gt;
&lt;li&gt;Install packages into the environment&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Install Anaconda&lt;/h3&gt;
&lt;p&gt;You have two options for installing Anaconda (the product). The first is to install the whole kitchen sink, which is referred to as the "Anaconda Distribution" a 600MB+ download that includes many common libraries for machine learning, data analysis, and visualization. You can &lt;a href="https://www.anaconda.com/distribution/"&gt;download it here&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;The other option is to use Miniconda. This is a smaller download that does not include various libraries but rather lets you download and install them on demand. You can &lt;a href="https://docs.conda.io/en/latest/miniconda.html"&gt;download it here&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;(Many of my clients work inside a firewall and should use the Anaconda distribution that is available locally inside of their firewall.)&lt;/p&gt;
&lt;p&gt;After you have installed Anaconda or Miniconda, you should have an executable called &lt;code&gt;conda&lt;/code&gt; available. Test out that you have it. On UNIX platforms (Mac and Linux) open a terminal (if you are on Mac, you can hit Command-space and then type "Terminal" and hit enter). On Windows, go to the start menu and search for "Conda Prompt". Type into the terminal or command prompt:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;conda --version
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And it should print out the version, something like &lt;code&gt;conda 4.7.5&lt;/code&gt;. If it does not do that, then you did not install Anaconda successfully.&lt;/p&gt;
&lt;h3&gt;Create an Environment&lt;/h3&gt;
&lt;p&gt;After you have installed the &lt;code&gt;conda&lt;/code&gt; utility, you will need to make an "environment." An environment is a sandboxed installation of Python libraries and utilities. The purpose of an environment is to allow per project library dependencies. &lt;/p&gt;
&lt;p&gt;For example, I consult with different clients. If I have multiple clients over the years, they might use different versions of libraries. Say, for example, client A is using Pandas 0.19, client B Pandas 0.25, and client C Pandas 1.0.1. Pandas in generally is pretty good about backward compatibility, but it does have breaking changes. Environments allow me to install specific packages for each client project. When I need to work on client A's work, I activate the environment for that project and have access to Pandas 0.19. When I need to shift to client C, I activate the corresponding environment and have access to Pandas 1.0.1.&lt;/p&gt;
&lt;p&gt;To create an environment, you need to give it a name. For a machine learning class, you might create an environment named &lt;code&gt;mlclass&lt;/code&gt; (you can specify another name if you like). You can also specify a Python version for the environment. To &lt;strong&gt;create an environment with Python 3.7&lt;/strong&gt; from a terminal or Anaconda Prompt (on Windows) type:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;conda create --name mlclass python=3.7
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can create as many environments as you like. I create them on a per-project basis. That way, I can jump into a project and make sure that I can work on it without dependency conflicts. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;After you create the environment, you should activate it&lt;/strong&gt;. This updates the PATH environment variable so that when you invoke &lt;code&gt;python&lt;/code&gt; or utilities, you use the executables found in the environment. Type:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;conda activate mlclass
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After running this, your prompt should indicate that you are in the environment, and it will change to look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;(mlclass)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that Anaconda, by default, stores its environments under your home directory. On my Mac, it would put it in &lt;code&gt;~/anaconda3/envs/mlclass&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Install Packages with Conda&lt;/h3&gt;
&lt;p&gt;At this point, you have installed &lt;code&gt;conda&lt;/code&gt;, created an environment, and activated the environment. You are now ready to install some packages. Here is &lt;strong&gt;a command to install some of the common data analysis packages&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;conda install notebook pandas scikit-learn seaborn xlrd districtdatalabs::yellowbrick py-xgboost notebook conda-forge::shap bokeh graphviz
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The packages with colons in their names are specified as &lt;em&gt;channel::package&lt;/em&gt; name. Anaconda ships with certain packages, but libraries can create their own "channel" to ship different versions of their library (or even package their library for Anaconda) on a different cadence than the Anaconda release cycle.&lt;/p&gt;
&lt;p&gt;At this point, you should be ready to go with a basic installation. &lt;strong&gt;To launch Jupyter notebook type&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;jupyter notebook
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To learn more about the &lt;code&gt;conda&lt;/code&gt; tool, check out &lt;a href="https://docs.conda.io/projects/conda/en/latest/user-guide/index.html"&gt;the User Guide&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Installation with Python&lt;/h2&gt;
&lt;figure&gt;
&lt;img src='/static/img/pip-tldr.png' class='img-fluid' alt='Python TLDR'/&gt;
&lt;figcaption&gt;&lt;strong&gt;TLDR:&lt;/strong&gt; Use these commands on Linux or Mac if you do not want to use Anaconda. On Windows replace the second line with &lt;tt&gt;env\Scripts\activate&lt;/tt&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;Note: If you are using Windows, it is recommended that you use Anaconda rather than this mechanism. Some of the scientific packages require installing and configuring a cross-compilation development environment, which can be annoying. If you are using a Mac and want to follow these instructions, it is recommended to install Python from Homebrew.&lt;/p&gt;
&lt;h3&gt;Install Python&lt;/h3&gt;
&lt;p&gt;If you are on Linux, use your package manager to install Python 3.x. On Mac, use Homebrew to install Python 3.x. If these instructions do not make sense to you, scroll up and use the instructions for Anaconda.&lt;/p&gt;
&lt;p&gt;After Python is installed, open a terminal and check the version. Type:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;python3 --version
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It should say something like &lt;code&gt;python 3.7.3&lt;/code&gt;. Make sure that you installed Python 3 and not Python 2.&lt;/p&gt;
&lt;h3&gt;Create a Virtual Environment&lt;/h3&gt;
&lt;p&gt;Python 3 ships with the ability to create environments that are very similar to Anaconda environments. However, they are not the same. Python calls its environments "Virtual Environments", so I will follow suit here. A virtual environment will allow you to create a sandbox for Python on a per-project basis.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;To create a virtual environment named &lt;code&gt;env&lt;/code&gt;&lt;/strong&gt; in the current directory type:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;python3 -m venv env
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;To activate this environment&lt;/strong&gt;, type:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;source env/bin/activate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that &lt;code&gt;source&lt;/code&gt; is a UNIX Bash command (so this will not work on Windows. If you really want to use Windows, even though I suggested against it with this mechanism, you would type &lt;code&gt;env\Scripts\activate.bat&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;At this point, your prompt should change to something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;(env)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Install Packages with Pip&lt;/h3&gt;
&lt;p&gt;With Python installed and your virtual environment activated, you are ready to install packages. We use the &lt;code&gt;pip&lt;/code&gt; tool to install packages from the &lt;a href="https://pypi.org"&gt;Python Package Index&lt;/a&gt;. If your virtual environment is activated, then &lt;code&gt;pip&lt;/code&gt; will install libraries into the virtual environment. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You should probably update pip with a new version&lt;/strong&gt;. Type:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pip install -U pip
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;To install a base data science system&lt;/strong&gt; type:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pip install notebook seaborn pandas scikit-learn xlrd yellowbrick xgboost shap bokeh graphviz
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To launch Jupyter Notebook type the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;jupyter notebook
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There is a &lt;a href="https://pip.pypa.io/en/stable/user_guide/"&gt;User Guide for pip&lt;/a&gt; as well.&lt;/p&gt;
&lt;h2&gt;Other Options&lt;/h2&gt;
&lt;p&gt;Can you use Docker? Probably. The above are the simplest ways of getting your environment set up. There may be an existing Docker container you can leverage. But Docker is usually orthogonal to what I'm teaching. Also, the Docker images will probably be using one of the two techniques I described above.&lt;/p&gt;
&lt;p&gt;What about hosted services? Google COLAB is a great service (disclaimer: I'm a subscriber to their PRO account), but requires access to Google COLAB, which many of my clients do not have.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Setting up a Python development environment is three lines of code once you have Python 3 or Anaconda installed. If you have questions about this, feel free to &lt;a href="https://twitter.com/__mharrison__"&gt;tweet them&lt;/a&gt;. I try to keep this page up to date as I point my clients to it.&lt;/p&gt;</content><category term="misc"/><category term="python"/><category term="anaconda"/></entry><entry><title>What I Use 2019 Edition</title><link href="https://metasnake.com/blog/what-i-use.html" rel="alternate"/><published>2019-11-20T00:00:00-07:00</published><updated>2019-11-20T00:00:00-07:00</updated><author><name>Matt Harrison</name></author><id>tag:metasnake.com,2019-11-20:/blog/what-i-use.html</id><summary type="html">&lt;p&gt;Physical gear I use&lt;/p&gt;</summary><content type="html">&lt;p&gt;It seems to be popular to share what gear you use. I'm pretty picky about some things, so I thought I would take a stab at it. Note that I'm just going to address physical hardware in this post, I'll save software for another post.&lt;/p&gt;
&lt;p&gt;&lt;img src='/static/img/desk.jpg' class='img-fluid' /&gt;&lt;/p&gt;
&lt;h2&gt;2015 Macbook Pro&lt;/h2&gt;
&lt;p&gt;I used Linux exclusively for over 15 years on ThinkPads, but due to the nature of my training and consulting got a Macbook in 2015. It is a decent piece of hardware. The screen went out on me a few years back, and now it seems the keyboard and trackpad are flaky sometimes. A few reboots seem to do the trick. Unless I'm training, I use an external keyboard and mouse, which oddly enough work even when the built-in devices do not. &lt;em&gt;Feb 2020 Update&lt;/em&gt; - I have &lt;a href="https://www.ifixit.com/Answers/View/313001/Keyboard+and+trackpad+don't+work,+loose+cable"&gt;replaced the cable for the trackpad&lt;/a&gt; which seems to have solved the problem. Annoying, but easy if you have the correct tools and spend five minutes watching Youtube.&lt;/p&gt;
&lt;p&gt;At the time it came out, the screen DPI was a wonder. Now it is a little more commonplace. I do most of my work on this machine. Again I will discuss software later. As of 2019, the 16inch version of this machine has reverted to using the same keyboard as I have. The butterfly keyboards of 2017-2019 were notoriously flaky, and I stayed away from those machines.&lt;/p&gt;
&lt;p&gt;The battery life is still decent on this. I take it with me when traveling, and it performs well. I like the aluminum body, which is unparalleled among laptop manufacturers. I wish I could upgrade the SSD and RAM like I could with my ThinkPads. Se la vie.&lt;/p&gt;
&lt;p&gt;My next laptop will probably be an Apple due to video editing software.&lt;/p&gt;
&lt;h2&gt;2017 Surface Book Pro&lt;/h2&gt;
&lt;p&gt;At some point, I realized that the vast majority of the world (and developers in said world) use Windows. I always missed my UNIX environment when forced to use Windows at work (in the NT days). Windows 10 is useable, and this device also travels with me (as both a backup and if I need to demo something on Windows).&lt;/p&gt;
&lt;p&gt;The device comes in handy as a tablet as well. I've used the stylus and found it to be nice when editing books. I was a technical reviewer for &lt;em&gt;Hands-On Unsupervised Learning Using Python&lt;/em&gt; and I liked being able to write on top of PDF's with the stylus.&lt;/p&gt;
&lt;p&gt;Another area where the Mac excels is the trackpad. It is far superior to the MS version. However, I much prefer the ThinkPad's TrackPoint to either. I know ... gasp, the terror!&lt;/p&gt;
&lt;h2&gt;Standing Desk&lt;/h2&gt;
&lt;p&gt;I started standing at work around 2011. My job at the time was pre-standing desk movement. So I needed to use an adapter on top of my sitting desk. I used an &lt;a href="https://amzn.to/2QzeKrD"&gt;Ergo Desktop Kangaroo Pro&lt;/a&gt; This worked ok. It was maybe not quite as stable but was able to transform from sitting to a standing position without falling over.&lt;/p&gt;
&lt;p&gt;I mostly work from my home office these days and use a &lt;a href="https://www.amazon.com/Tresanti-Adjustable-Height-Desk-1334060/dp/B081DBGJ8G/ref=sr_1_7?keywords=tresanti+desk&amp;amp;qid=1574288771&amp;amp;s=office-products&amp;amp;sr=1-7"&gt;Tresanti Desk&lt;/a&gt;. I purchased mine at Costco, and I have few regrets. I'm 6'4", and the top hieght works for me. It has a glass top which my kids have used to write notes to me with whiteboard markers. &lt;/p&gt;
&lt;p&gt;There are 3 USB outlets at the side, and no this is not a MIDI device, they are only used for charging. On that note, I have an &lt;a href="https://www.ikea.com/us/en/p/signum-cable-management-horizontal-silver-color-30200253/"&gt;IKEA Signum "Cable management"&lt;/a&gt; drilled into the bottom of the backside of the table. In the Signum is an &lt;a href="https://www.amazon.com/gp/product/B00C631EYU/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&amp;amp;psc=1"&gt;Anker Dual Display Universal Docking Station&lt;/a&gt;. As you can tell from the photo, I'm not particularly concerned with eliminating wires. But it would be nice to have USB hub functionality in the desk.&lt;/p&gt;
&lt;p&gt;Because it has a glass top mounting stands to it can prove challenging. My monitor stand works with it, but only because I could disassemble the clamp for it.&lt;/p&gt;
&lt;p&gt;My other issue with this desk is the size. When I'm running a remote training and using the Mac and Windows, it can get a little crowded. I also use physical notes, so almost every bit of space is used.&lt;/p&gt;
&lt;p&gt;I'm a huge fan of taking breaks during the day. Not only do they help your body; they help you with productivity. I recommend getting a standing desk. Do you have to stand eight hours a day? No. I used to stand most of the day. These days I sit most of the day because that feels better for my body. But I will still mix it up. I think the key is to be aware of your body and posture and take care of it.&lt;/p&gt;
&lt;p&gt;Another thing I recommend if you are going the standing route is to pick up a padded mat to stand on.&lt;/p&gt;
&lt;h2&gt;ErgoDox Keyboard&lt;/h2&gt;
&lt;p&gt;I picked up an ErgoDox keyboard when Drop (the company formerly known as MassDrop) offered them initially. I was using a Unicomp (with a TrackPoint!), but my fingers were telling me that I needed to do something else. Come to think of it, that time probably correlated to when I was writing the first edition of what is now &lt;em&gt;Illustrated Guide to Python 3&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I knew I wanted a keyboard that was ortholinear, mechanical, and split. Bonus points in that the firmware is opensource and you can program it how you want. This keyboard is an acquired taste, but I liked mine so much that when Drop offered them again, I picked up a second (partially out of fear that I would never be able to purchase one again, and partially because I wanted another for home use). Since that time, it appears that the DIY mechanical keyboard group has flourished. You can now purchase completely assembled models, and there are dozens of riffs on split keyboards.&lt;/p&gt;
&lt;p&gt;I do not use QWERTY on this keyboard, but rather the &lt;a href="https://normanlayout.info/"&gt;Norman layout&lt;/a&gt;. Again, this is another acquired taste, but it has worked out for me. Also, I have keys that have a dual purpose. For example, I use emacs a lot. So my left thumb has &lt;em&gt;Control&lt;/em&gt; under it. But if I tap it and release, it fires the &lt;em&gt;Escape&lt;/em&gt; key.&lt;/p&gt;
&lt;p&gt;I typically do not take this keyboard with me when traveling. I took it to PyCon once when it was novel, but it is not really portable (I have a travel case for it) in that it is bulky, and TSA gives you weird looks when you travel with it.&lt;/p&gt;
&lt;p&gt;Again, you need to take care of yourself. Your fingers, wrists, and arms can suffer damage from too much use or non-ergonomic use. Take note and do what feels best.&lt;/p&gt;
&lt;h2&gt;Clearly Superior Technologies Trackball&lt;/h2&gt;
&lt;p&gt;Award for best-named company of 1995 goes to CST. &lt;/p&gt;
&lt;p&gt;When I got my ErgoDox, I was considering modding it to add a TrackPoint. In the meantime, my previous blog had a couple of entries on the ErgoDox, and they were popular enough such that I sent Drop enough purchases of ErgoDoxen that they sent me a "Thank You" gift. The CST trackball mouse was in that gift box. It seemed to be the answer I was looking for. And it fits nicely in between the split keyboard.&lt;/p&gt;
&lt;p&gt;I have also added additional "buttons" that plug into the mouse. I have them situated so that I can tap them with my palm. The left palm is a command modifier, and the right palm is the spacemacs leader key.&lt;/p&gt;
&lt;p&gt;Apparently, the ball is the size of a billiard ball. I'm not a big pool player, but that seems like a fancy mod.&lt;/p&gt;
&lt;p&gt;I also have two of these trackballs (one for "work" and one for home). As of 2019, CST seems to have retired and sold the business. Whether these high-quality devices will remain in production by the new owner remains to be seen. They are no longer available on Amazon. eBay lists one at over $400!&lt;/p&gt;
&lt;p&gt;Again, listen to your body. If it tells you something when you are mousing about, maybe you should look into a change. A trackball certainly uses different body movements.&lt;/p&gt;
&lt;h2&gt;External Monitor&lt;/h2&gt;
&lt;p&gt;I bought an Auria 27 inch external monitor about ten years ago. At the time, it was a high DPI monitor that got good reviews for the price point. I will probably replace it with a 34 inch 4k monitor soon, but it has served me well.&lt;/p&gt;
&lt;h2&gt;Vivo Laptop Monitor Mount&lt;/h2&gt;
&lt;p&gt;I purchased the &lt;a href="https://www.amazon.com/VIVO-Motion-Monitor-Articulating-STAND-V102C/dp/B074GBF93N/ref=sr_1_4?keywords=vivo+monitor+stand+laptop&amp;amp;qid=1574293128&amp;amp;s=office-products&amp;amp;sr=1-4"&gt;Vivo Laptop Monitor Mount&lt;/a&gt; in an attempt to get a little more real estate on top of my desk. This is a sturdy product that I enjoy, not in spark joy type of enjoy, but it does the job. It also makes standing more ergonomic as it enables me to raise my screen higher.&lt;/p&gt;
&lt;h2&gt;Audio-Technica BPHS1 Headset and Mic&lt;/h2&gt;
&lt;p&gt;I do up to a dozen virtual trainings per month, and this mic has served me well. The &lt;a href="https://www.amazon.com/gp/product/B003D87JI2/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&amp;amp;psc=1"&gt;Audio-Technica BPHS1&lt;/a&gt; has a long cable which allows me to walk around when teaching and not have to worry about standing in front of a mic. I also have a Blue Yeti, but this mic is more of a set it and forget it type. I feel like I'm always fussing with the levels on the Blue Yeti.&lt;/p&gt;
&lt;p&gt;This is an XLR mic, so you will need a mic preamp like the &lt;a href="https://www.amazon.com/gp/product/B005EHILV4/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&amp;amp;psc=1"&gt;Behringer Xenyx 302USB&lt;/a&gt; to use it with a USB interface.&lt;/p&gt;
&lt;p&gt;I have a long power outlet attached to the Signum, and I hang the mic off of that when it is not in use.&lt;/p&gt;</content><category term="misc"/><category term="keyboard"/></entry></feed>