9  Single experiment

9.1 Plate map

To dive a bit deeper into a single seahtrue experiment, we will first generate an overview of what the experimental set-up was.

Let’s load the data first


Next, we make a theme that we can use for the heatmap


Then we make a nice default heatmap with the geom_tile function.


The default ggplot colors are quite colorfull, but might hurt your eyes… If we want colors that are different than the default ggplot colors, and we want the legend to be nicely in order we need to add some additional code.


Another option would be to manually arrange the factors in a way that suits you best.


9.2 Background

In Seahorse experiments the corners of the plate are by default assigned as Background wells, meaning that in these wells there is no sample but does have the same conditions and culture medium as your sample wells. Background wells need to be checked for outliers. This is not obvious from the Wave software interface, because the backgroung is by default substracted and users will never see the actual background data, unless they really select for it in the point-and-click software Wave. So let’s make some plots of the raw background O2 data.

We will now use the raw_data table for plotting, and we assume you allready loaded the data file above in this session.


This is a nice plot of the background O2 readings. It does look weird, especailly beause there is one well H01 which has a comppletely different trend then the other wells. This might be suspected as a technical outlier. Possibly in this well there was not enough culture medium or the sensor was damaged. The lab details and observations should be aligned with the outlier calling to make sure to not erroneously flag a well as an outlier.

To make an even better visual representation of the background and to account for the different aspects how the background well data behaves we can plot only the first ticks of each measurement. We will also shift here now to the fluorescence readings of the Seahorse. Since the O2 is derived from fluorescence values in our experiments it would be good to really look at the most raw data that we get out of our experiment. The fluorescence is given as the parameter O2_em_corr

We have a plotting function that automates this.


We can use this function when we provide the right arguments. The argument option for the var are: O2_em_corr, pH_em_corr and O2_mmHg.


Exercise 1

To calculate O2 from emission, Seahorse uses the Stern-Volmer equation. Find out (using google or chatGPT) what the stern-volmer equation is and write it in the form of a function. Use the arguments x, KSV, and F0.

You can also use the Gerenscer et al. paper that describes the calculations. The method and algorithms described in this Analytical Chemistry paper from 2009 are still used today. Gerencser et al. Anal Chem 2009



Where x is the emission (O2_em_corr), KSV is a constant, the stern-volmer konstant, and F0 is the emission at zero oxygen. The values of these two constants is are unique to the cartridge that you used during your experiment. Seahorse provides these numbers when updating your Wave software and matches them via a barcode read on the cartridge each run.

Exercise 2

The KSV and F0 are provided in the assay configuration sheet of the excel output. Seahtrue puts that information in the assay_info table. You can access it using the pluck function. In this case you have to use pluck two times, first to get to the assay_info and next to the KSV or F0



Exercise 3

Now use the two constants KSV and F0, and the function stern_volmer to calculate the O2 from O2_em_corr. Also use select(well, measurement, tick, O2_mmHg, O2) to compare the O2 with the O2_mmHg in the output.



Exercise 4

Plot the O2 background values that you just calculated using the plot_raw_BKGD function. Compare the plot to when plotting the O2_mmHg that was derived from the Seahorse output xlsx.



Exercise 5

Apparently the O2_mmHg is different from our own calculated O2 concentrations. When looking at the O2_mmHg background plot it looks like that these O2 values are also corrected for a background. Let’s see if that is indeed the case.

Seahorse Wave substracts the mean background from all samples. So the mean O2_mmHg of the “Background group is substracted from all samples wells (and background wells apparently). We can also do that with our seahtrue data. We should take care of what we need to summarize here, each tick is a unique measurement in the raw_data, do let’s take tick as the .by parameter


Now we need to substract the background O2 from all other wells and the backgrounds wells themselves.

The way this is done in the Seahorse algorithm is to take into account the ambient O2 levels. Basically what Seahorse calculates is the following:


Now compare the O2_corrected with the original O2_mmHg. Do this in two ways. 1) make a ggplot with the O2_corrected on x-axis and O2_mmHg on the y-axis. and 2) use the plot_raw_BKGD function with O2_corrected and compare with the output from the O2_mmHg plot_raw_BKGD plot



Although the values are not identical, they are pretty close. Indicating that the O2_mmHg background data is likely also corrected for background in this dataset.

9.3 Low signals

Sometimes we don’t have much sample. In most cases the sample is cells, and with low cell number the O2 consumption and extracellular acidification can be low. Seahorse defines an pretty arbitrary cut-off for basal respiration at 20 pmol/min. Below this value OCR becomes less reliable.

In the loaded experiment seahtrue_output_donor_A, we have a group labeled with 50.000. In these wells we only have 50.000 cells in each well, which makes its signal difficult to detect.


Please notice that the OCR signal for the 50.000 group is definitely below 20 pmol/min.

What if we want to investigate in more detail how our signals are for our samples with this low respiration?

We can make use of the raw_data again and plot the background O2 signal with the sample O2 signal in one plot. Since in the previous section we saw that O2 signals for the background wells were also corrected for background (?!), we will work with our own calculated O2 levels using the stern_volmer function we wrote in the previous section.

Also, we will use quite a big plotting function for this. It offers some flexibility on whether we want to plot means and/or scale the data. Also we can select specific wells and which measurements.


Let’s explore this huge function (with not so tidy coding in it….), by using it:


You can see in this plot that background O2 levels rise in each measurment. This drift is consistently seen in all instruments and experimental condtions. The upward drift in O2, is also why OCRs for background wells are often negative in your Seahorse software Wave graphs (when you point-and-click to have the background not substracted). The explanation that Gerenscer et al. gave for the drift was that either 1) temperature is not stable during a measurement and the fluorescent sensors are temperature sensitive or 2) that O2 levels in the microchamber that is formed when probe is at its measuring position is entering from the plastic or culture medium above. Both reasons are debatable I think.

Although the O2 levels of backgrounds increase, it can be seen that the O2 levels of well D02 increase less. Meaning that there oxygen consumption is higher than the background.

Exercise 6

Change the inputs for the plot_raw_whichGroup_dots (in a meaningful way), to plot the 1) O2_em_corr, 2) plot another well D04 (please note that you also have change the group name because it is from the 100.000 group)



9.4 Plotting basal and maximal respiration

Pluck the injection_info table from the seahtrue_output_donor_A dataset to see what how the injections were defined in the experimental set-up before running the seahorse.


This is not a typical mito-stress test experiment, where we inject oligomycin, FCCP and antimycinA/rotenone sequentially. Instead we inject only FCCP and antimycinA/rotenone.

To get the maximal and basal respiration out of the ocr rate table, we need to do some calculations. We first make some assumptions and defintions:

We call each interval between two injections or between start and an injection or between an injection and the end a phase

Each phase has a unique name that is named after the injection that was last. The first phase after the start is called init_ocr and we also typically have the phases om_ocr, fccp_ocr and amrot_ocr. Phases are marked with either _ocr or _ecar, because these are distinct parameters.

To calculate respiration parameters, like basal respiration (= basal_ocr), we define the following:

  • basal_ocr = init_ocr - amrot_ocr.
  • max_ocr = fccp_ocr - amrot_ocr
  • spare_ocr = fccp_ocr - init_ocr
  • proton_leak = om_ocr = amrot_ocr
  • atp_linked = init_ocr - om_ocr

We also use indices to have relative parameters:

  • spare_ocr_index = (spare_ocr / basal_ocr)*100
  • basal_ocr_index = (basal_ocr / max_ocr)*100
  • leak_index = (proton_leak / basal_ocr)*100
  • coupling_index = (atp_linked / basal_ocr)*100) %>%

Another important assumption is that we are not using average values to represent each phase, but intead we use a specific measurement. The reason for this is that we assume that for all phases, except FCCP, three measurements are needed in time to get to steady-state. For FCCP injection, we assume that it reaches steady-state fast, or at least its maximal ocr, so we take the first measurement after injection as the measurement representing the FCCP phase.

Let’s now put that into R code. We call the type of experiment we did in this dataset a maximal capacity (maxcap) test.

We also injected monensin, which can maximize ECAR, but we don’t need it for OCR calculations.


Now for each well we have the parameters related to the phases that we defined in the parameter set.

Next we want to calculate the respiration parameters and indices.


With this data we can plot our typical basal and maximal bar/scatter plots that we see in our lovely papers, presentations and theses.


Exercise 7

The plot above shows maximal ocr. Now make your own plot with 1) basal_ocr and 2) all groups except background. Make sure to order the group legend tidyly and have reable x-axis labels.