# import the usual modules
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
# nice plots
import seaborn as sns
sns.set(style="ticks") # turns the grid lines off
# show images in viridis by default
plt.rcParams["image.cmap"] = "viridis"
# for getting file names
import glob
# skimage submodules we need
import skimage.io
import skimage.measure
import skimage.filters
import skimage.exposure
# read in a sample image
im = skimage.io.imread("./data/lacI_titration/O2_R124_phase_pos_02.tif")
plt.imshow(im)
# normalize the image
im_norm = (im - np.min(im))/(np.max(im) - np.min(im))
# find the background image
gauss_radius = 50
im_bg = skimage.filters.gaussian(im_norm, gauss_radius)
# show this background image
plt.imshow(im_bg)
# perform background subtraction
im_bs = im_norm - im_bg
# show original the original and background subtracted side-by-side
fig, ax = plt.subplots(1,2)
ax[0].imshow(im_norm)
ax[1].imshow(im_bs)
Let's plot pixel intensities
hist, pixel_vals = skimage.exposure.histogram(im_bs)
plt.plot(pixel_vals, hist, '.')
plt.yscale("log")
plt.xlabel("pixel values")
plt.ylabel("counts")
# choose a threshold based on plot above
threshold = -0.2
# threshold the image
im_thresh = im_bs < threshold
# show the thresholded image, bacteria are 1, backround is 0
plt.imshow(im_thresh)
# label the objects in the binary image
im_label = skimage.measure.label(im_thresh)
# show the labeled image
plt.imshow(im_label)
# find the number of object in the image
num_objs = np.max(im_label)
num_objs
plot the size of the objects in the image
# length calibration
pixel_size = 0.16 # um
# array to store are values in
areas = np.zeros(num_objs+1)
# obtain the areas of all labeled objects
for i in range(1,num_objs+1):
# determine area for given object
area = np.sum(im_label == i)
areas[i] = area * pixel_size**2
# plot these areas
_ = plt.hist(areas, bins=20)
plt.yscale("log")
plt.xlabel("area (um)^2")
plt.ylabel("count")
# set threshold on what we call a bacterium
lower_area = 1 # um^2
upper_area = 4 # um^2
# create a blank image with dimensions of im_label
im_mask = np.zeros_like(im_label)
# loop through objects of im_label
# add to im_mask *only* if the right size
for i in range(1,num_objs+1):
# get area for given object
pixel_area = np.sum(im_label == i)
um_area = pixel_area * pixel_size**2
# if area is within bounds, add this object to the mask
if (um_area > lower_area) and (um_area < upper_area):
im_mask = im_mask + (im_label == i)
# show the masked image
plt.imshow(im_mask)
import pboc_utils as pboc
test_mask = pboc.create_mask(im, area_high=4)
plt.imshow(test_mask)
Let's show our phase, yfp, and mask image side by side
# read in the matching YFP image
im_yfp = skimage.io.imread("./data/lacI_titration/O2_R124_yfp_pos_02.tif")
# show images next to each other
fig, ax = plt.subplots(1,3, figsize=[10,3])
ax[0].imshow(im)
ax[1].imshow(im_yfp)
ax[2].imshow(test_mask)
now let's determine the intensities of all the cells in the image
# list to store yfp intensities
intensities = []
# number of cells in the mask
n_cells = np.max(test_mask)
# loop through cells and find the mean intensity for each
for i in range(1,n_cells):
cell_mask = (test_mask == i)
num_pixels = np.sum(cell_mask)
cell_yfp = im_yfp * cell_mask
cell_mean_intensity = np.sum(cell_yfp) / num_pixels
intensities.append(cell_mean_intensity)
sns.swarmplot(intensities)
plt.xlabel("YFP expression (a.u.)")
example of how to write a function
def plus_one(x):
ans = x + 1
return ans
write a function to find the intensities of a given phase/yfp image pair
def find_intensities(im_phase,im_yfp):
mask = pboc.create_mask(im_phase)
# list to store yfp intensities
intensities = []
# number of cells in the mask
n_cells = np.max(mask)
# loop through cells and find the mean intensity for each
for i in range(1,n_cells):
cell_mask = (mask == i)
num_pixels = np.sum(cell_mask)
cell_yfp = im_yfp * cell_mask
cell_mean_intensity = np.sum(cell_yfp) / num_pixels
intensities.append(cell_mean_intensity)
return intensities
test out our function
find_intensities(im, im_yfp)
# strain information
operator = "O2"
repressor = "R124"
# filename structure
filename_phase = "./data/lacI_titration/" + operator + "_" + repressor + \
"_phase_pos_*.tif"
filename_yfp = "./data/lacI_titration/" + operator + "_" + repressor + \
"_yfp_pos_*.tif"
# grab all the images for this given strain
phase_names = np.sort(glob.glob(filename_phase))
yfp_names = np.sort(glob.glob(filename_yfp))
# number of positions
n_images = len(phase_names)
# list to store intensity values for *all* the images
intensities_all = []
# loop through the images
for i in range(n_images):
# read in the image
im_phase = skimage.io.imread(phase_names[i])
im_yfp = skimage.io.imread(yfp_names[i])
# get the intensities from from the YFP image
intensities = find_intensities(im_phase, im_yfp)
# update the all_intensities
intensities_all = intensities_all + intensities
plt.hist(intensities_all,bins=20);
again, let's turn this into a function
def find_intensities_all(operator,repressor):
# filename structure
filename_phase = "./data/lacI_titration/" + operator + "_" + repressor + \
"_phase_pos_*.tif"
filename_yfp = "./data/lacI_titration/" + operator + "_" + repressor + \
"_yfp_pos_*.tif"
# grab all the images for this given strain
phase_names = np.sort(glob.glob(filename_phase))
yfp_names = np.sort(glob.glob(filename_yfp))
# number of positions
n_images = len(phase_names)
# list to store intensity values for *all* the images
intensities_all = []
# loop through the images
for i in range(n_images):
# read in the image
im_phase = skimage.io.imread(phase_names[i])
im_yfp = skimage.io.imread(yfp_names[i])
# get the intensities from from the YFP image
intensities = find_intensities(im_phase, im_yfp)
# update the all_intensities
intensities_all = intensities_all + intensities
return intensities_all
let's see if our function works
plt.hist(find_intensities_all("O3","R1740"))
def find_fold_change(operator, repressor):
# mean intensities for "auto", "delta", and "Rxxx"
mean_auto = np.mean(find_intensities_all(operator,"auto"))
mean_delta = np.mean(find_intensities_all(operator,"delta"))
mean_R = np.mean(find_intensities_all(operator,repressor))
# calculate the fold change
fold_change = (mean_R - mean_auto) / (mean_delta - mean_auto)
return fold_change
test out our fold change function
find_fold_change("O1","R1220")
# names of the strains we are dealing with
operators = ["O1","O2","O3"]
repressors = ["R22","R60","R124","R260","R1220","R1740"]
repressor_counts = [22,60,124,260,1220,1740]
# number of operators and repressor
n_operators = len(operators)
n_repressors = len(repressors)
# array to store our fold change values in
fc_values = np.zeros([n_operators, n_repressors])
# first, loop through operators
for i in range(n_operators):
# second, loop through number of repressors
for j in range(n_repressors):
# grab the operator / repressor pair
operator = operators[i]
repressor = repressors[j]
# print output to see progress through for loop
print(operator, repressor)
# find the find-change
fc_values[i,j] = find_fold_change(operator, repressor)
Plotting the three states over increasing repressors
# parameters
dE_R = -9 # binding energy of repressor to DNA, units of kBT
dE_P = -6 # binding energy of polymerase to DNA, units of kBT
P = 1000 # number of RNAP per cell
N_NS = 5E6
# range of repressor copy numbers to look at
R = np.logspace(0,4,100)
# weights
empty_weight = np.ones(len(R))
R_bound_weight = (R / N_NS) * np.exp(-dE_R)
P_bound_weight = np.ones(len(R)) * (P / N_NS) * np.exp(-dE_P)
tot_weight = empty_weight + R_bound_weight + P_bound_weight
# compute the probabilities
empty_prob = empty_weight/tot_weight
R_bound_prob = R_bound_weight/tot_weight
P_bound_prob = P_bound_weight/tot_weight
tot_prob = empty_prob + R_bound_prob + P_bound_prob
# plot
plt.plot(R, empty_prob)
plt.plot(R, R_bound_prob)
plt.plot(R, P_bound_prob)
plt.plot(R, tot_prob, "k")
# plot labeling
plt.xscale("log")
plt.xlabel("repressor copy number")
plt.ylabel("probability")
plt.legend(["empty", "repressor bound", "polymerase bound", "total"])
plot the theory curves for fold change
# repressor binding energies in kBT units
dE = [-15.3, -13.9, -9.3]
# colors for plotting
colors = ["k", "g", "b"]
# calculate and plot the theoritcal prediction
for i in range(len(dE)):
fold_change = 1 / (1 + R / N_NS * np.exp(-dE[i]))
plt.loglog(R, fold_change, color=colors[i])
plt.loglog(repressor_counts, fc_values[i,:], '.', color=colors[i])
plt.xlabel("repressor copy number")
plt.ylabel("fold-change")
plt.legend(["O1 theory", "O1 data", \
"O2 theory", "O2 data", \
"O3 theory", "O3 data"])
# energies, in kBT
dEI = -1 # energy of ligand bound to inactive
dEA = -5 # energy of ligand bound to active
dES = 3 # energy of the active state (regardless of ligand)
# ligand concentration range to look at
ligand_conc = np.logspace(-3, 2, 100)
# weights
# I for inactive, A for active, U for unbound, B for bound
IU_w = np.ones(len(ligand_conc))
IB_w = ligand_conc * np.exp(-dEI)
AU_w = np.exp(-dES) * np.ones(len(ligand_conc))
AB_w = ligand_conc * np.exp(-dES) * np.exp(-dEA)
tot_w = IU_w + IB_w + AU_w + AB_w
# convert the weights to probabilities
IU_p = IU_w / tot_w
IB_p = IB_w / tot_w
AU_p = AU_w / tot_w
AB_p = AB_w / tot_w
# plot
plt.plot(ligand_conc, IU_p, "r--")
plt.plot(ligand_conc, IB_p, "r-")
plt.plot(ligand_conc, AU_p, "k--")
plt.plot(ligand_conc, AB_p, "k-")
plt.xscale("log")
plt.legend(["inactive unbound", "inactive bound", "active unbound", "active bound"])