If you just started using Python to analyze historical stock prices with the aim of visualizing trends and build investment strategies, or if you are a more experienced coder tired to use loops, you should stick around and learn how to improve your scripts with the reduce()
function.
For instance, let’s suppose you selected a number of stock tickers and your task was to download the historical adjusted close prices for each company and merge them in a unique clean dataset similar to the one below:
in this case, after appending the stock prices to a list (or an alternative iterable object) you could merge them by using the following code:
#The historical stock prices saved in adj_prices are passed to #reduce()to apply the merge function recursively from left to right reduce(lambda x, y: pd.merge(x, y, left_index = True, right_index = True, how=’outer’), adj_prices)
The function above follows the syntax:
reduce(function, iterable [, initializer])
where the iterable object is the list adj_pricest
the (lambda) function is lambda x,y: pd.merge(x,y,left_index = True, right_index = True, how='outer')
and the initializer has been omitted.
The full script used to generate the dataset above, is shared later in the article, but before commenting on that, let’s understand what the reduce()
function does by reviewing some theory.
A Little Bit Of Theory
In Python 3, the reduce()
function belongs to the functools
module, meaning that it can be imported by using one of the following two ways:
import functools # Imports the full module and then use functools.reduce() from functools import reduce # Only imports reduce() from functools to use it directly
The reduce()
function is considered a higher-order function as it takes other functions as arguments and operates on them. More in detail the reduce()
function implements a technique known as reduction (or folding) that consists in:
- Applying a two argument function (from left to right) to the first two items of the iterable object to generate a partial result,
- Using the partial result with the following item in the iterable to obtain another partial result,
- Reducing the iterable to a single cumulative value by repeating the process until there are no items left.
For example, let’s use reduce()
to compute the product of a list of numbers:
from functools import reduce numbers = [7,2,5,9]def simple_prod(x,y): return x * y reduce(simple_prod, numbers) Output: 630
alternatively, the same computation can be performed using a lambda function:
from functools import reduce numbers = [7,2,5,9] reduce(lambda x,y: x * y, numbers) Output: 630
A note of caution: because reduce()
works by applying functions multiple times, it could in some cases, lead to poor performance and the use of self-defined functions, affect the readability of the code. For this reason, it’s always advisable to assess weather a more Pythonic way exists to solve each specific use case. As you will see below, relying on reduce()
makes sense for the task performed in the article as it avoids the creation of loops, making the code more compact.
Put Theory Into Practice
The full script to download historical adjusted prices and merge them into a unique DataFrame is presented below:
import pandas as pd import yfinance as yf from functools import reduce # Changes numeric fields format to include 2 digits only pd.set_option('display.float_format', '{:.2f}'.format) tickers = ['AAPL', 'AMZN', 'BLK' ,'CSCO', 'FB', 'MSFT', 'NFLX', 'T', 'UBER' ,'ZM'] adj_prices = [] for i in range(len(tickers)): close_price = pd.DataFrame(yf.download(tickers[i])['Adj Close'].dropna(axis=0, how='any')) close_price = close_price.loc[~close_price.index.duplicated(keep='last')] close_price.columns = [tickers[i]] adj_prices.append(close_price) df = reduce(lambda x, y: pd.merge(x, y, left_index = True, right_index = True ,how='outer'), adj_prices) df.sort_index(ascending = False, inplace = True) df.index = pd.to_datetime(df.index ).date df.head(5)
Below you find a brief comment on each step I took:
- Import Modules: First of all, I imported the pandas, yfinance and functools modules (or just the reduce function as shown above). In particular, the yfinance module has a key role here, as it offers numerous methods to fetch historical prices as well as company’s fundamentals.
- Save Tickers To Iterable: My original code included more than 200 tickers imported via csv and saved into a list. In effect, in practice you will probably work with dozens of tickers, but here I selected only 10 for simplicity.
- Dowload Adj Close Prices From Yahoo Finance: I then used the
yf.download()
function to easily download the historical stock prices of each company in thetickers
list. Among the available columns, I specifically selected the Adj. Close column:
4. Remove Missing Values and Duplicates: Missing row values have been removed altogether using dropna()
and duplicated dates filtered out to obtain a unique adj. close price for each date. Eventually each group of prices has been appended to the (originally empty) adj_prices list:
5. Merge Historical Prices With Reduce(): I have then passed the adj_prices list to the reduce()
function to apply the merge function to each group of prices in pairs, from left to right, joining them using the common Date
index. Note that I used an outer
join since different stocks were quoted at different times and the goal was to keep the entirety of available prices. The last action has been to sort dates in descending order (most recent first).
If you run this code, you will obtain a DataFrame identical to the one I shared at the beginning of the article. As you can see, it took just a few lines of code to download the stock prices history and generate a single clean dataset that can then feed your investment strategies.
Conclusion
The reduce()
function is only one of many powerful functional programming features available in Python. Despite it is often not the most efficient way to apply recursion, in this article I have shown how to effectively apply the reduction technique on merge to generate a clean DataFrame including adj, close prices for multiple tickers.
This article has been published from the source link without modifications to the text. Only the headline has been changed.