Economics for Tech People — Equilibrium (Part 3)
By: Tyler Harris
Tags: Economics, R, Data Science, Education, Tech

The Code

Load Libraries

To work through the project, we will need the readxl and tidyverse packages. If you do not have them installed in your R environment, simply remove the “#” sign before the “install.packages…” lines of code [1]. Once they are installed, you will not need to install them again on your machine.

Here’s the code:

# Load Libraries # If you do not have readxl or tidyverse installed, uncomment the following lines

# install.packages("readxl")

# install.packages("tidyverse") require(readxl)

require(tidyverse)

Check Data Import

Sometimes Excel files are temperamental when they interact with R, especially RStudio. Use the code below to import the data and check it looks okay.

Here’s the code:

# Import Data

Supply_Data <- read_excel("Supply_Data.xlsx")

Demand_Data <- read_excel("Demand_Data.xlsx") # Check data import

head(Supply_Data)

head(Demand_Data)

Here’s the output:

Screenshot of the first six rows of our supply data in RStudio on a Mac

Screenshot of the first six rows of our demand data in RStudio on a Mac

If you import the data and the Price and Qs_# or Qd_# are not the column labels and are the first row of data, you will probably see something like X1 X2 X3, etc. as the column headings. If that happens, you can follow the same process that I described in “The Setup” section of the previous article Economics for Tech People — Demand (Part 1). That will show you how to import the data a different way to get caught up to this article.

Get Caught Up

Once the data is in, it is time to copy/paste the relevant parts of the demand and supply code to do the heavy lifting. Please reference those two articles for explanations of what the following code chunks do.

Here’s the code:

# Demand first # Wrangle the data into a ggplot friendly structure

Wrangled_Data <- data.frame(Price = Demand_Data$Price, stack(Demand_Data[2:11]))

names(Wrangled_Data)[2] <- "Quantity"

names(Wrangled_Data)[3] <- "Qd_num" # Create market demand

Market_Demand <- data.frame(Price = Demand_Data$Price, Market_Demand = rowSums(Demand_Data[2:11])) # Add Elasticity zones

# 10-6.5 zone 1

# 6-4 zone 2

# 3.5-2 zone 3

# 1.5-0 zone 4

Market_Demand$Elasticity_Zone <- as.character(c(1,1,1,1,1,1,1,1,2,2,2,2,2,3,3,3,3,4,4,4,4)) # Supply second # Wrangle the data into a ggplot friendly structure

Wrangled_Supply_Data <- data.frame(Price = Supply_Data$Price, stack(Supply_Data[2:11]))

names(Wrangled_Supply_Data)[2] <- "Quantity"

names(Wrangled_Supply_Data)[3] <- "Qs_num" # Create market supply

Market_Supply <- data.frame(Price = Supply_Data$Price, Market_Supply = rowSums(Supply_Data[2:11])) # Add Elasticity zones

# 10-5.5 zone 1

# 5-4 zone 2

# 3.5-0 zone 3

Market_Supply$Elasticity_Zone <- as.character(c(1,1,1,1,1,1,1,1,1,1,2,2,2,3,3,3,3,3,3,3,3))

Plot the Equilibrium

Now that we are caught up, let’s plot the equilibrium.

To do this, we will use ggplot again. What is a little different this time around is that the code will start with just ggplot() as the first line then add lines one at a time to build the graph rather than merging everything together first and trying to do this the hard way.

The geom_line() parts of the code will declare which data we use, the aesthetics, and assign a couple other styling parameters. To make it look nice, this code adds vertical and horizontal lines to outline the axes. I also decided to limit the data range with the xlim() function because we will not really be doing anything with the data too far to the right. Finally, some labels are applied.

Here’s the code:

# Plot the equilibrium

ggplot() +

geom_line(data = Market_Supply, aes(x = Market_Supply, y = Price), color = "darkred", size = 1) +

geom_line(data = Market_Demand, aes(x = Market_Demand, y = Price), color = "steelblue", size = 1) +

geom_vline(xintercept = 0) +

geom_hline(yintercept = 0) +

xlim(0, 1150) +

ggtitle("Market Equlibrium") +

theme(plot.title = element_text(hjust = 0.5)) +

xlab("Quantity") +

ylab("Price")

Here’s the output:

Screenshot of the equilibrium plot in RStudio on a Mac

Well, it worked!

In economics markets that function well work at the equilibrium point where the quantity demanded equals the quantity supplied [2]. This is also often called the market clearing price because all the quantity that is produced is demanded [2]. In other words, there is no shortage and there is no surplus [2].

Just eyeballing the plot up there, it looks like we have a market clearing price of about $6 and an equilibrium quantity of about 200 units.

Let’s confirm those numbers and get a little more precise.

Find the Equilibrium Quantity and Market Clearing Price

First, let me say that there are probably at least a dozen ways we could go about doing this. For the sake of simplicity and keeping this article’s scope digestible, we are going to avoid calculus and take a “good enough for who it’s for” approach with this that will yield a virtually identical answer anyway.

Unrelated note: The phrase “good enough for who it’s for” is a quote from a contractor who did work on my house when I was a kid. It was a little concerning, but as far as I know the house is still in one piece!

Either way, we will adopt that mindset here.

We will start by joining the demand and supply data together into a single object. I am a fan of using the inner_join() function and joining by Price because of its simplicity. It also makes sure everything lines up properly. Since we have a column in common with the same number of rows in each data set, inner_join() basically acts as a “zipper” function to combine the data sets into a clean product [1].

Here’s the code:

# Join data together

Merged_data <- inner_join(Market_Demand, Market_Supply, by = "Price")

head(Merged_data)

Here’s the output:

Screenshot of the inner_join() function merging demand and supply data in RStudio on a Mac

Next, let’s find where the difference between the quantity supplied and the quantity demanded is smallest. We know they intersect because of the graph, so the price that corresponds to the smallest difference should be our market clearing price [2].

Here’s the code:

# Find difference between supply and demand

Merged_data$Diff <- Merged_data$Market_Supply - Merged_data$Market_Demand

Merged_data

Here’s the output:

Screenshot of the merged data with the column of quantity difference in RStudio on a Mac

Just looking at the chart, we can tell we’re looking at a minimum difference of 8 units at a price of $6. However, what happens if we have a lot more data and it is hard to tell visually?

We can use a combination of the which.min() function to find the minimum value we want, which is Diff in this case, and the abs() function to get the absolute value of Diff [1]. The absolute value function is necessary because rather than giving us the minimum difference as the smallest distance from 0 like we want, we will get the biggest negative number which is no what we want [1]. The comma at the end of the function specifies that we want the entire row [1]. For bonus points, try removing it and see what is different.

Here’s the code:

# Find row of minimum difference

min_diff_1 <- Merged_data[which.min(abs(Merged_data$Diff)),]

min_diff_1

Here’s the output:

Screenshot of the row with the smallest difference between demand and supply in RStudio on a Mac

Modify the Plot

Next, we are going to modify the plot from earlier in a couple ways. First, let’s change the values in the xlim() function so that we can zoom in closer to the intersection. Second, we can add some lines at the market clearing price and the quantities on top of the plot to better visualize our data. An advantage of creating a dedicated object for the minimum difference row is that we can reference it here rather than having to manually enter numbers or redo the calculation another way.

Here’s the code:

# Add lines to previous graph based on the min_diff_1 numbers # Plot the equilibrium

ggplot() +

geom_line(data = Market_Supply, aes(x = Market_Supply, y = Price), color = "darkred", size = 1) +

geom_line(data = Market_Demand, aes(x = Market_Demand, y = Price), color = "steelblue", size = 1) +

geom_vline(xintercept = 0) +

geom_hline(yintercept = 0) +

xlim(0, 300) + ### ZOOM IN A BIT ###

ggtitle("Market Equlibrium") +

theme(plot.title = element_text(hjust = 0.5)) +

xlab("Quantity") +

ylab("Price") +

### NEW LINES ###

geom_hline(yintercept = min_diff_1$Price) +

geom_vline(xintercept = min_diff_1$Market_Demand) +

geom_vline(xintercept = min_diff_1$Market_Supply)

Here’s the output:

Screenshot of the equilibrium plot with the market clearing price and approximate equilibrium quantity lines in RStudio on a Mac

So, why are there two vertical lines instead of one?

Well, it turns out that the precision of our data leads to a small issue where we do not have an exactly perfect intersection. I chose to put both on the graph to make this obvious.

What this is saying is that we should feel pretty good about saying the market clearing price is $6 and the true equilibrium quantity is in somewhere in the 190s [2].

Total Revenue

One of the most straightforward calculations in economics is total revenue because it is simply the price times the total number of units [2]. When at equilibrium, it is the market clearing price and the quantity demanded at that price [2].

For us, we will again reference the min_diff_1 object because that has our market clearing price along with the quantities. In the calculation, I chose to just average the demand and supply numbers together with the mean() function and multiply by the price. That gets us a total revenue of $1,134 in this market.

Here’s the code:

# Calculate total revenue

total_revenue <- min_diff_1$Price * mean(min_diff_1$Market_Demand, min_diff_1$Market_Supply)

total_revenue

Here’s the output:

Screenshot of the total revenue in our market at the market clearing price in RStudio on a Mac

Compare and Contrast Results

Remember from the previous two parts of this series that from the suppliers want to sell more units at higher prices.

For the data we had, the maximum total revenue on the supply side was $9,840 when selling at a price of $10 per unit.

On the demand side, we had a maximum total revenue of $1,850 at a price of $2.50 per unit.

When we combine our data in a real market scenario, the compromise of markets becomes clear. The market clearing price is where suppliers and demanders of goods and services meet in the middle [2]. Sure, neither side is getting their ideal or theoretical maximum, but that is just the nature of the market.

Bonus Material — Predict the Equilibrium with Linear Models

It’s all well and good that we have data that lines up pretty closely, but what happens if we are working with something a bit more theoretical? We know that from this article and the previous two that we have various zones of elasticity. Graphically, we can see that a price pretty close to $6 is right, but what if we only had a couple data points and good guess at the slope of the lines?

We will borrow some code from the first article and create some linear models to see where they intersect. What matters here is that we have our models explaining 99%+ of our dependent variable and the predictive ability is significant because we have very small p-values in both of the following models [1].

For a more complete discussion of what R-squared values and p-values mean, please refer to the previous two articles because I assume here that you know what I mean when I reference those values.

When we look at our min_diff_1 object, we can see that demand falls in elasticity zone 2 and supply is in zone 1. We only need to build linear models with that in mind.

Let’s start with supply.

Here’s the code:

# Create Supply linear model # Filter Data

supply_lm_data <- Market_Supply %>%

filter(Elasticity_Zone == 1) # Create linear model

Supply_Zone_1_lm <- lm(Market_Supply ~ Price, data = supply_lm_data) # Create and print summary

summary(Supply_Zone_1_lm)

Here’s the output:

Screenshot of the summary statistics for the linear model of the supply data in zone 1 in RStudio on a Mac

Demand is next.

Here’s the code:

# Create Demand linear model # Filter Data

demand_lm_data <- Market_Demand %>%

filter(Elasticity_Zone == 2) # Create linear model

Demand_Zone_2_lm <- lm(Market_Demand ~ Price, data = demand_lm_data) # Create and print summary

summary(Demand_Zone_2_lm)

Here’s the output:

Screenshot of the summary statistics for the linear model of the demand data in zone 2 in RStudio on a Mac

Next, we will create a quick sequence of numbers to be our new price data. I am going from a price of $5.50 to $6.50 because that range fits neatly into the same elasticity zone for both supply and demand data. We will increment it by $0.01, so we can get much more precise.

Here’s the code:

# new price data

new_price_data <- data.frame(Price = seq(5.5, 6.5, by = 0.01))

After that, we will use the classic algebra formula y=mx + b to put together our new data. That will add the intercept from our linear models plus the slope coefficient times the price into a big data frame. We can also round this to 0 decimal places with the round() function because if you think about it, it does not make a lot of sense to sell a fraction of a SaaS subscription.

Here’s the code:

# Use coefficient and intercept data to fill in the blanks # Supply first

new_price_data$supply <- round(Supply_Zone_1_lm$coefficients[1] + (Supply_Zone_1_lm$coefficients[2] * new_price_data$Price), 0) # Demand next

new_price_data$demand <- round(Demand_Zone_2_lm$coefficients[1] + (Demand_Zone_2_lm$coefficients[2] * new_price_data$Price), 0)

Finally, we will do the same basic subtraction to get a Diff column of the difference between demand and supply like we did earlier. We can also check the data to make sure it came out right.

Here’s the code:

# Find difference between supply and demand

new_price_data$Diff <- new_price_data$demand - new_price_data$supply # Check the data

head(new_price_data)

Here’s the output:

Screenshot of the table of predicted supply, demand, and difference values for each new price in RStudio on a Mac

To wrap it up, we will find the smallest absolute value difference again to get a price and quantity.

Here’s the code:

# Find row of minimum difference

min_diff_2 <- new_price_data[which.min(abs(new_price_data$Diff)),]

min_diff_2

Here’s the output:

Screenshot of the minimum absolute value difference in our predicted data in RStudio on a Mac

The result here is very similar. The predicted values give a market clearing price of $6.02 and an equilibrium quantity of 184 or 185, which is very close to the real data.

In the reality of business, this is great support to just set the price to $6 and be happy with the result.