A lot of analytics code I've read is a very long procedural chain. These can be hard to follow because the only way to really know what's going on in any point is to insert a probe to inspect the inputs and outputs at that stage. Breaking these into functions is a really useful way of making the code easier to understand, change and find bugs in.

In Martin Fowler's Refactoring he mentions that whenever there's a block of code that has (or requires) a comment to describe what it does, that's a good opportunity to package that code into a function. I've found that a very good rule of thumb to follow; if the functions are well named it makes the code much clearer. Here's a typical sort of simple example:

# Read in the dataframe
df = df.merge(temperature_range, how='left', left_on=['celcius'], right_index=True)
df = read_data('data.csv')
enrich_temperature_range(df, temperature_range)