For an assignment, I am identifying the first quarter of the 2008 recession in the United States. The Excel data I'm using can be downloaded here: gdplev.xls. How can I improve this pandas code to make it more idiomatic or optimized?

def get_recession_start():
    '''Returns the year and quarter of the recession start time as a 
    string value in a format such as 2005q3'''
    GDP_df = pd.read_excel("gdplev.xls", 
                           names=["Quarter", "GDP in 2009 dollars"], 
                           parse_cols = "E,G", 
                           skiprows = 7)
    GDP_df = GDP_df.query("Quarter >= '2000q1'")
    GDP_df["Growth"] = GDP_df["GDP in 2009 dollars"].pct_change()
    GDP_df = GDP_df.reset_index(drop=True)
    # recession defined as two consecutive quarters of negative growth
    GDP_df["Recession"] = (GDP_df.Growth < 0) & (GDP_df.Growth.shift(-1) < 0) 
    return GDP_df.iloc[GDP_df["Recession"].idxmax()]["Quarter"]

1 Answer 1


Your function does too many things: reading Excel file, filtering necessary rows, and calculating the "recession_start". My advice is to take the first two out.

Also, supply quarters and GDP as separate objects (pd.Series) to the function instead of the DataFrame. Like this you will remove hardcoded strings from your function, and, what's more important, you will get rid of SettingWithCopyWarning that you should get right now:

df = pd.read_excel('gdplev.xls',
                   names=['Quarter', 'GDP in 2009 dollars'],
mask = df['Quarter'] >= '2000q1'
print(get_recession_start(quarters=df.loc[mask, 'Quarter'],
                          gdps=df.loc[mask, 'GDP in 2009 dollars']))

Note that I use usecols instead of parse_cols as it is deprecated. Also, I removed df.query in favor of boolean masking and .loc.

Then, the function would look like this:

def get_recession_start(quarters: pd.Series,
                        gdps: pd.Series) -> str:
    Returns the year and quarter of the recession start time
    as a string value in a format such as 2005q3
    growth = gdps.pct_change()
    recession = (growth < 0) & (growth.shift(-1) < 0)
    recession = recession.reset_index(drop=True)
    return quarters.iloc[recession.idxmax()]

Here I also used triple double quotes for the docstring and type hints. IMHO, this looks much cleaner.

Probably, it would also make sense to return only the recession.idxmax() index and get corresponding quarters value outside of the function.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.