IC50 calculations using tidypredict

Why I decided to write this

I needed to calculate the IC50 of a drug and at first glance it looked like most folks use pre-built tools on PRISM. I haven’t yet taken the time to explore tidymodels, so I decided to take this opportunity to learn some basic features. Also I have always been a big fan of open access & equal opportunity, especially when it comes to data analyses. Hopefully, a few people will stumble on to this and find it helpful for their work.

What is IC50?

IC50, half maximal inhibitory concentration, is a measure of how much inhibitory substance is needed to inhibit 50% of a given population. Frequently IC50 is calculated to determine the concentration/dose of drug necessary to have a survival of 50% in vitro.

Here we use tidypredict, a package that is apart of the tidymodels universe, to create a simple function called ic50 to predict the IC50 given a dosage response curve.


We will take mtcars to create a fake dosage response data set to fit a linear model. We will reassign disp as inhibition data and wt as dosage.

p_load(tidypredict, tidyverse)
df <- mtcars %>% 
  select(disp, wt) %>%
  rename(inhibition = disp,
         dose = wt)

## Rows: 32
## Columns: 2
## $ inhibition <dbl> 160.0, 160.0, 108.0, 258.0, 360.0, 225.0, 360.0, 146.7, 14…
## $ dose       <dbl> 2.620, 2.875, 2.320, 3.215, 3.440, 3.460, 3.570, 3.190, 3.…

Since we know the maximum and minimum dosage and inhibition, we can calculate the 50 percentile inhibition. This will be used to predict the dosage necessary to achieve an IC50.

ic50 <- function(df){
  # Generate linear model of based on simulated dose response.
  model <- lm(dose ~ inhibition, data = df)
  # Get 50th-percentile inhibition
  test <- tibble(inhibition = max(df$inhibition) - (max(df$inhibition) - min(df$inhibition))/2)
  return(tidypredict_to_column(test, model, vars = "dose")) # print new column "dose" and append prediction

## # A tibble: 1 x 2
##   inhibition  dose
##        <dbl> <dbl>
## 1       272.  3.50

We calculate the 50% inhibition, 272, which we predict to be achieved using a dosage of 3.5.

Let’s plot the data to see what our hypothetical data would look like and how our prediction stacks up:

pred <- ic50(df)

ggplot(df, aes(dose, inhibition))+
  geom_point(data = pred, aes(x = dose, y = inhibition), 
             colour = "red", size = 5)+
  geom_smooth(method = "lm", se = F)
## `geom_smooth()` using formula 'y ~ x'

Here we show how our IC50 prediction stacks up in our dosage response data we simulated. At this point I would be comfortable using a dosage of 3.5 moving forward.