–––––––– –––––––– archives investing twitter

Transcriptic E. coli Transformation

This is a basic analysis of results data I downloaded from Transcriptic. The protocol for this experiment was generated with Autodesk Wet Lab Accelerator (beta).

An E. coli strain (Zymo10B) is transformed with a luciferase- and ampR-expressing plasmid (PVIB) in a 96 well plate. Each row (A-H) is identical and each column (1-6) is different:

[A-H]1 : E. coli + PVIB + no amp broth
[A-H]2 : E. coli + no PVIB + no amp broth
[A-H]3 : no amp broth
[A-H]4 : E. coli + PVIB + amp broth (i.e., luciferase expression)
[A-H]5 : E. coli + amp broth
[A-H]6 : amp broth

For rows A-F, absorbance (OD600) and luminescence (to see luciferase expression) are captured once an hour for 18 hours.

Rows G and H are transferred to two "6-flat" plates, one without amp (G), one with amp (H). For each of the two sets of 6 plates a photo is taken, then the plates are incubated for 18 hours, then another photo is taken.

# Setup cell
from __future__ import print_function, division

import string
import numpy as np
import seaborn as sns
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt

from functools import partial
from IPython.display import display, HTML, SVG

def uprint(astr): print(astr + "\n" + "-"*len(astr))
def show_html(astr): return display(HTML('{}'.format(astr)))
def show_svg(astr, w=1000, h=1000):
    SVG_HEAD = '''<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'''
    SVG_START = '''<svg width="{w:}px" height="{h:}px" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink= "http://www.w3.org/1999/xlink">'''
    return display(SVG(SVG_HEAD + SVG_START.format(w=w, h=h) + astr + '</svg>'))

# Plotting style, matplotlib and seaborn
plt.rc("axes", titlesize=20, labelsize=15, linewidth=.25, edgecolor='#444444')
sns.set_context("notebook", font_scale=1.2, rc={})
%matplotlib inline
%config InlineBackend.figure_format = 'retina'

Read in files

Absorbance and luminescence files from all plates were downloaded as csv files from Transcriptic.

import glob
abs_files = sorted(glob.glob("absorbance_*.csv"), key=lambda x: (len(x), x))
lum_files = sorted(glob.glob("luminescence_*.csv"), key=lambda x: (len(x), x))
print("{} absorbance files:  ".format(len(abs_files)), abs_files[::5])
print("{} luminescence files:".format(len(lum_files)), lum_files[::5])
18 absorbance files:   ['absorbance_18.csv', 'absorbance_43.csv', 'absorbance_68.csv', 'absorbance_93.csv']
18 luminescence files: ['luminescence_19.csv', 'luminescence_44.csv', 'luminescence_69.csv', 'luminescence_94.csv']

Dataframes

Read absorbance and luminescence data into pandas dataframes.

The time at which readings were taken is not included in the csv files (these data are in another json file), but since I know readings were taken once per hour, I can simply increment the time myself.

import datetime

abs_data = [pd.read_csv(abs_file, index_col="Well") for abs_file in abs_files]
lum_data = [pd.read_csv(lum_file, index_col="Well") for lum_file in lum_files]

times = range(len(abs_data)) # or get the true time from the json file

# Each file is for a specific time. Concatenate all the files and reset the index
df_abs = pd.concat(abs_data, keys=times, names=["Time","Well"])
df_abs.reset_index(inplace=True)

df_lum = pd.concat(lum_data, keys=times, names=["Time","Well"])
df_lum.reset_index(inplace=True)

print(df_abs.head())
print()
print(df_lum.tail())
   Time Well  OD 600:nanometer
0     0   A1          0.047956
1     0   A2          0.049607
2     0   A3          0.046221
3     0   A4          0.047077
4     0   A5          0.048022

     Time Well  Luminescence
643    17   F2             5
644    17   F3            18
645    17   F4            12
646    17   F5             5
647    17   F6            19

Plot

Plot absorbance and luminescence data for each of the 48 wells using SVG.

well_params = {"1":["E coli", "PVIB"], "2":["E coli"], "3":[],
               "4":["E coli","PVIB","amp"], "5":["E coli", "amp"], "6":["amp"]}

def to_coords(w, h, min_x, max_x, min_y, max_y, x, y):
    return w*(x-min_x)/(max_x-min_x), h-h*(y-min_y)/(max_y-min_y)

def plot_df(df, x_col, y_col, wh=100):
    w, h = wh, wh
    sw, sh = w*1.2, h*1.2
    mx, my = w*.1, h*.1

    # min, max over all plates
    min_x, max_x = df[x_col].min(), df[x_col].max()
    min_y, max_y = df[y_col].min(), df[y_col].max()
    wells = set(df["Well"])
    num_rows = 1 + max(string.uppercase.index(well[0]) for well in wells)
    num_cols = max(int(well[1]) for well in wells)

    _to_coords = partial(to_coords, w, h, min_x, max_x, min_y, max_y)

    g = df.groupby("Well")
    ssvg = '<g transform="translate({},{})">'.format(w/2, h/2)
    for n, (key, _df) in enumerate(g):
        vals = _df[[x_col, y_col]].values

        cx = mx + sw * (int(key[1])-1) # well number (1-6) is the column
        cy = my + sh * string.uppercase.index(key[0]) # well letter (A-F) is the row

        ssvg += '''<circle cx="{cx:f}" cy="{cy:f}" r="{r:f}" stroke="rgba(0,0,0,.15)" fill="none"
                   />'''.format(cx=cx, cy=cy, r=w/2)

        # Text labels for the plates
        ssvg += '''<text x="{cx:f}" y="{cy:f}" text-anchor="middle" font-size="20" fill="rgba(0,0,0,.25)">{text:}
                   </text>'''.format(cx=cx, cy=cy, text=key)

        if "E coli" in well_params[key[1]]:
            ssvg += '''<text x="{cx:f}" y="{cy:f}" text-anchor="middle" font-size="12" fill="rgba(0,200,0,.25)">E coli
                       </text>'''.format(cx=cx, cy=cy+12)

        if "PVIB" in well_params[key[1]]:
            ssvg += '''<text x="{cx:f}" y="{cy:f}" text-anchor="middle" font-size="12" fill="rgba(0,0,200,.25)">PVIB
                       </text>'''.format(cx=cx, cy=cy+24)

        if "amp" in well_params[key[1]]:
            ssvg += '''<text x="{cx:f}" y="{cy:f}" text-anchor="middle" font-size="12" fill="rgba(200,0,0,.25)">amp
                       </text>'''.format(cx=cx, cy=cy-24)

        # Plot the absorbance/luminescence x/y values as a line
        points = ' '.join(','.join(map(str, _to_coords(x,y))) for x,y in vals)
        ssvg += '<g transform="translate({},{})">'.format(cx-w*.5, cy-h*.5)
        ssvg += '''<polyline points="{points:}" style="fill:none;stroke:rgba(100,140,180,1);stroke-width:2"
                   />'''.format(points=points)
        ssvg += '</g>'

    ssvg += '</g>'
    show_html("<h1>{}</h1>".format(y_col))
    show_svg(ssvg, w=sw*num_cols, h=sh*num_rows)

Absorbance

The absorbance data shows the growth of the E. coli and generally looks good. We expect growth in columns 1 and 2 for sure, and column 4 if the transformation works.

We see growth in columns 1 and 2, as expected. In three out of six wells in column 4 we see growth (i.e., 50% transformation efficiency). In these wells the E. coli have taken up PVIB so they can resist ampicillin.

plot_df(df_abs, "Time", "OD 600:nanometer")

OD 600:nanometer

Luminescence

The luminescence data show the success of the PVIB plasmid transforming E. coli and expressing luciferase. We only expect luminescence in column 4, since that is the only column with PVIB and ampicillin. We detect luminescence in only one of three transformed E. coli.

plot_df(df_lum, "Time", "Luminescence")

Luminescence

Image Capture

Finally, we take photos of E. coli growing in plates, before and after incubation.

G plates have no amp, H plates have amp
[GH]1 has E. coli and PVIB
[GH]2 has E. coli and no PVIB
[GH]3 has no E. coli and no PVIB

That the H plates are all empty is not surprising because we know from the experiment above that PVIB transformation is not efficient. There is something strange going on here with the Gs though, where G3 is showing growth despite having no E. coli spread, while G1 has no growth when it should.

I am currently assuming the images are reversed from what I expect, but I don't know why... The Transcriptic UI is quite explicit about which plate is which.

data = [{'n':'108', 'desc':'before incubation, no amp', 'plates':['G1,50','G2,50','G3,50','G1,100','G2,100','G3,100']},
        {'n':'112', 'desc':'after incubation, no amp', 'plates':['G1,50','G2,50','G3,50','G1,100','G2,100','G3,100']},
        {'n':'113', 'desc':'before incubation, with amp', 'plates':['H1,50','H2,50','H3,50','H1,100','H2,100','H3,100']},
        {'n':'117', 'desc':'after incubation, with amp', 'plates':['H1,50','H2,50','H3,50','H1,100','H2,100','H3,100']}
        ]

w, h = 512, 384 # hardcoded image size
sw, sh = w*1.02, h*1.02
mx, my = 100, 190
px, py = 120, 130

ssvg = ''
for n, d in enumerate(data):
    ssvg += '<g transform="translate({},{})">'.format(sw*(n%2), sh*(n//2))
    ssvg += '''<image xlink:href="/static/Transcriptic%20E.%20coli%20transformation_files/imagePlate_{}.png" x="0" y="0" width="{}px" height="{}px"
               />'''.format(d['n'], w, h)
    ssvg += '<text x="20" y="20" fill="#dd2222" font-size="20">{}</text>'.format(d['desc'])
    for n2, pl in enumerate(d["plates"]):
        ssvg += '''<text x="{}" y="{}" fill="#2222dd" font-size="15">{}μl
                   </text>'''.format(mx+px*(n2%3), my+py*(n2//3), pl)
    ssvg += '</g>'
show_svg(ssvg, w=1200, h=800)

Conclusion

I think this experiment was moderately successful. It was not that expensive at about $20 for labor and $35 for reagents. The transformation and luciferase expression efficiency was pretty low, but I don't know what I'd expect if the experiment were performed by a skilled technician.

There were a few minor issues on the Transcriptic side:

  • The PNG images get downloaded as .raw files. Initially, I thought it was a camera RAW file, which didn't work...
  • The UI mixes 0-indexing and 1-indexing, which is a bit confusing. I would enforce 1-indexing.
  • The plate images may be reversed compared to the UI? This could be an error on my part though.
  • The "Download all" feature did not download all the data (it missed the images).
  • The csv files did not include the time at which readings were taken. I'd have to dig into the json files to get that.

Comments


Boolean Biotech © Brian Naughton Powered by Pelican and Twitter Bootstrap. Icons by Font Awesome and Font Awesome More