When exploring the properties of an individual function or a system of equations, I often find myself quickly writing up code in a Jupyter Notebook (overview) to plot functions using matplotlib. While a previous blogpost by Jazmin has gone over creating interactive plots with matplotlib, being able to actively change variables to quickly explore their sensitivity or even teach others is crucial.
The primary reason I started playing with interactive widgets in Jupyter Notebook was to teach individuals the basic mechanics of a simple crop allocation model that utilizes Positive Mathematical Programming (PMP) (Howitt, 1995). Beyond showing the simple calibration steps via code, allowing students to interact with individual variables allows for them to not only explore the sensitivity of models but also to find where such a model might break.
In this specific case, the marginal cost and revenues for a specific commodity (corn) versus the number of acres planted are graphed. A breaking case where the crop’s input data (the price per ton of corn) causes the model to produce unrealistic results (a negative marginal cost) is produced below.
Plotting Functions in matplotlib
One of the features that is often overlooked in matplotlib is that you can (indirectly) plot functions without much effort. The following commented code is used to demonstrate plotting marginal cost and revenue curves on the same graph.
import matplotlib.pyplot as plt from numpy import * #fixed inputs from calibrated model #alpha and gamma for quadratic formulation from Howitt, 1995 input_params = [743.0, 2.57] #crop revenue per acre, known crop_revenues = 1500 #Creating the x-axis domain, controls range of x inputs t = linspace(0, 350) #t is the independent variable, corn_MC is dependent variable corn_MC = input_params + input_params * t #note that you have to multiply t by 0 to populate entire line corn_MR = crop_revenues + 0 * t plt.figure(dpi=400) #set resolution of graph, can change. #label axes plt.ylabel('Marginal Cost ($/acre)') plt.xlabel('Acres of Corn Planted') #plot as many lines as you'd like, expand them here plt.plot(t, corn_MC, 'r', label='Marginal Cost') plt.plot(t, corn_MR, 'b', label='Marginal Revenue') #change size of ticks on axes plt.tick_params(labelsize=8) legend = plt.legend(loc=4, shadow=True) plt.show()
The resulting graph is generated inline in a Jupyter Notebook. Note that the resolution and size of the graphic is adjusted using plot.figure(dpi=400)—the default is much smaller and makes for hard-to-read graphs!
Interactive Inputs for Graphics in Jupyter Notebook
While graphing a function in Jupyter Notebook using matplotlib is rather straightforward, interactive plots with functions are rarely utilized. Using iPython widgets allows us to easily create these tools for any type of situation, from vertical and horizontal slider bars to progress bars to dropdown to boolean ‘click me’ buttons. You can find any of these features here. For the sake of simplicity, I have simply included three slider bars to demonstrate these features.
Notably, as an expansion of the example above, I have to run scipy.minimize multiple times whenever inputs are recalibrated (for questions on this end, I am more than happy to explain PMP if you contact me directly). To do this, I have to imbed multiple functions into a larger function to carry this out. However, I have not noticed any considerable slowdowns when running this much code.
To carry out the entirety of the example above, I have almost 300 lines of code to fully encapsulate a 3-crop allocation problem. For simplicity’s sake, I defined the function ‘pmp_example_sliders’ to contain all of the code required to calibrate and produce the graphic above (note that input parameters are what are calculated) using Scipy.optimize.minimize. Shadow prices are then derived using scipy.optimize.minimize and used to create the parameters necessary for the marginal cost curve shown in this example. Note that the full model can be found in the GitHub file.
from ipywidgets import * import scipy.optimize %matplotlib inline from numpy import * import matplotlib.pyplot as plt def pmp_example_sliders(corn_price=250, corn_initial_acres=200, x_axis_scale=350): return plot ###Code for Running Calibration and Graphing Resulting Graph### #create sliders for three variables to be changed #have to have function with changing parameters (shown above) #note that scale for corn_price is form 10 to 500 and stepping by 10 slider = interactive(pmp_example_sliders, corn_price=(10, 500, 10), corn_initial_acres=(0.01,400, 10), x_axis_scale=(30, 650, 10)) #display the resulting slider display(slider)
The following graphic results with the aforementioned sliders:
We can easily change the input slider on the corn price from 250 to 410 to change to the following results:
Because of this interactive feature, we not only see how the MC and MR curves interact, but we can observe that the Alpha Parameter (shown below the graph) becomes negative. While not important for this specific blog post, it shows where the crop allocation model produces negative marginal costs for low allocations of corn. Thus, utilizing interactive graphics can help highlight dynamics and drawbacks/limitations for models, helping assist in standalone tutorials.
If you would like to run this yourself, please feel free to download the Jupyter Notebooks from my GitHub. All code is written in Python 3. If you need assistance opening Jupyter Notebook, please refer to my blog post introducing Jupyter Notebook (link) or how to create a shortcut to launch Jupyter Notebook from your current working directory (link).