How to use patchclampplotteR
patchclampplotteR
will help you analyze and plot your
patch clamp data efficiently. This vignette will walk you through the
complete process of transforming raw data into publication-quality
plots!
Set up R Project
To use this package, set up a new folder on your computer. Give the
folder a short, distinctive name with no spaces (use hyphens instead). I
would strongly recommend adding subfolders with names like
Data
, Figures
, and Thesis
. This
will help with organization and make it easier to expand to future
projects, like writing your thesis in R.

A screenshot of the file structure of a typical project. There are folders for data, figures, scripts and the thesis. Files with a ‘.’ in the name, like .gitignore or .Rhistory, are automatically generated when you set up your project. If you aren’t using git to track your files, don’t worry about the filenames that start with ‘git’
In RStudio, click on File
-> New project
-> Existing Directory
and choose the folder you just
created. Click on Create Project
and R will refresh to a
blank, new project. You’re now ready to create a new R Markdown (.Rmd)
file and start your analysis!
Install and load package
You can install the development version of patchclampplotteR from GitHub. Only do this once per computer, or if there’s a major update.
pak::pak("christelinda-laureijs/patchclampplotteR")
And then load the package each time you want to use it:
About the data
This sample dataset consists of whole-cell patch clamp recordings of neurons within the dorsomedial hypothalamus (DMH), a brain region critical for appetite regulation, stress responses and other processes. I recorded evoked excitatory post-synaptic currents for five minutes under baseline conditions, then added 500 nM insulin to the perfusion solution, and I continued recording for 25 minutes.
My goal is to determine if insulin affects evoked current amplitude in DMH neurons.
Analyze data in Clampfit
Please see the vignettes in the Articles page to learn about how to analyze data in Clampfit. These include Evoked Current Analysis and Action Potential Analysis.
Import raw .csv files
Cell Characteristics
First, I must import a .csv
file containing information
about factors such as the animal’s age and sex, the cell ID number, and
other details. Please see the Required columns section
of the documentation for import_cell_characteristics_df()
for full explanations of the required columns and what you should
include. See below for a link to download an empty .csv
file with the headers required for this dataset:
Note: Since this vignette is included within an R package, the following code requires the function
import_ext_data()
to properly locate the.csv
file in the package folder. This won’t be required when you are using the package within your own project folder.Do NOT include
import_ext_data()
in your code, because it won’t work. You can just write the path to the filename directly withinimport_cell_characteristics_df()
. For example, you should writeimport_cell_characteristics_df("Data/cell_info.csv")
to usecell_info.csv
located within theData/
folder.
cell_characteristics <- import_cell_characteristics_df(import_ext_data("sample_cell_characteristics.csv"))
#> Warning: There was 1 warning in `dplyr::mutate()`.
#> ℹ In argument: `R_a = lapply(stringr::str_split(.data$R_a, pattern = ", "), FUN
#> = as.numeric)`.
#> Caused by warning in `lapply()`:
#> ! NAs introduced by coercion
reactable(cell_characteristics)
Raw evoked current data
Next, I will import the raw evoked current data that has been copied
over from Clampfit (again, please see the Evoked
Current Analysis vignette for details about how to analyze this data
in Clampfit. This is a .csv
file containing four columns:
letter
, ID
, P1
and
P2
:
letter
: A unique identifier for a single recording, which allows you to link evoked current data, spontaneous current data, action potential, data, and information on cell characteristics.ID
: The name of the .abf filename used to obtain the data, which is useful for verifying the recordings and cross-referencing to your lab book.P1
: The amplitude of the first evoked current (pA).P2
: The amplitude of the second evoked current (pA).
Try to match the capitalization of the column names to the examples listed here. If you do forget to make them lowercase, don’t worry.
add_new_cells()
will automatically convert all column names to lowercase for consistency across functions. Capitalized letters will be retained for columns likeID
,X
,Y
,P1
, andP2
.
sample_eEPSC_data <- read.csv(import_ext_data("sample_new_eEPSC_data.csv"))
reactable(sample_eEPSC_data)
Add new cells
The next step is to merge the raw evoked current data with the cell
characteristics data. add_new_cells()
will merge these two
datasets, using letter
as the common column. This function
requires three .csv
files:
- The new raw data
- The cell characteristics
- An existing
.csv
with raw data that has been previously imported. As your project goes on, you will eventually be appending new data onto your existing datasheet, but if you are starting completely fresh, use a blank.csv
file containing just one value in cellA1
calledletter
. This column title is all that is needed to allow R to auto-fill in the rest of the new data.
WARNING!! If you are starting from an empty
.csv
file, the.csv
in theold_raw_data_csv
argument MUST contain at least theletter
column name in cell A1. If you try to use a completely empty.csv
sheet, R will not recognize it as a valid.csv
because there is “nothing” for it to read.
blank_csv <- read.csv(import_ext_data("empty_old_raw_data_sheet.csv"))
Use the add_new_cells()
function, and carefully read the
warning messages.
first_time_df <- add_new_cells(
new_raw_data_csv = import_ext_data("sample_new_eEPSC_data.csv"),
cell_characteristics_csv = import_ext_data("sample_cell_characteristics.csv"),
old_raw_data_csv = import_ext_data("empty_old_raw_data_sheet.csv"),
data_type = "eEPSC",
write_new_csv = "no",
new_file_name = "",
decimal_places = 2
)
#> ℹ Renamed dataframe columns to lowercase
#> ✔ All letters are present in both "/home/runner/work/_temp/Library/patchclampplotteR/extdata/sample_cell_characteristics.csv"
#> and "/home/runner/work/_temp/Library/patchclampplotteR/extdata/sample_new_eEPSC_data.csv".
#> ✔ Letter duplication check passed
#> ℹ All letters in "/home/runner/work/_temp/Library/patchclampplotteR/extdata/sample_new_eEPSC_data.csv" are new relative to "/home/runner/work/_temp/Library/patchclampplotteR/extdata/empty_old_raw_data_sheet.csv"
#> ✔ FX GR HC
Check output messages
add_new_cells()
produces several warnings and messages.
One warning lets you know you know that the column names have been
renamed to lowercase. This is to avoid case-sensitive issues from
appearing in later functions.
The first message generated with add_new_cells()
indicate that the sample_cell_characteristics.csv
and
sample_new_eEPSC_data.csv
have the same cells. This is
useful to catch if you forget to add the cell characteristics for the
new data.
The second message indicates that all letters in the new data are new relative to the existing dataset. This ensures that you don’t accidentally paste in the same data twice, resulting in duplicated data.
The final message prints a list of the letters that have been added to the dataset. In this case, these are “FX”, “GR” and “HC”. It is a good way to confirm that you’ve added the letters you were planning to add.
You can also ask R to produce a list of all of the unique letters in
the dataset. This won’t catch duplicates, but it can help you identify
if a letter is completely missing from the dataset. See, FX
is now included!
unique(first_time_df$letter)
#> [1] "FX" "GR" "HC"
This is an example of what the full few rows look like now:
As you collect more data, change the value of
old_raw_data_csv
from the empty sheet to your existing raw
data sheet. This function will automatically append new data onto your
existing sheet and save it to a new .csv
file (defined by
new_file_name
). I am saving it to the Data/
subfolder.
raw_eEPSC_data <- add_new_cells(
new_raw_data_csv = import_ext_data("sample_new_eEPSC_data.csv"),
cell_characteristics_csv = import_ext_data("sample_cell_characteristics.csv"),
old_raw_data_csv = import_ext_data("empty_raw_eEPSC_datasheet.csv"),
current_type = "eEPSC",
write_new_csv = "no",
new_file_name = "Data/20241118-Raw-eEPSC-Data.csv",
decimal_places = 2
)
Explore your data
Let’s look at an example of a full dataset. This is the sample raw evoked current dataset included in the package. To reduce the vignette size, I am printing just the first 20 rows. The full dataset contains > 5680 rows!)
You can use dplyr
functions to quickly explore your
data. Here’s just one example of a quick and useful analysis:
Count number of cells per sex and treatment
Quick Tip: Want to know how many experiments you still need to do? Run this line of code on the raw data. Here, I filtered the data to category 2 only (experiments where I added insulin) and grouped by treatment. I then counted the number of cells per sex.
Define your colour theme
In this package, you only need to specify your treatment groups and
colours once. You can later refer to this dataframe in
treatment_colour_theme
arguments for all of your plotting
functions. The package is loaded with a sample dataframe to help you get
started:
colours
and very_pale_colours
are specified
as hex codes or named R colours. The only difference between
treatment
and display_names
is that the
display_names
are re-written to look attractive in plots
and tables.
First, check out how many treatment groups you have using
unique(raw_eEPSC_df$treatment)
.
unique(raw_eEPSC_df$treatment)
#> [1] Control HNMPA PPP PPP_and_HNMPA
#> Levels: Control HNMPA PPP PPP_and_HNMPA
Next, modify this code to set up your own dataframe with your treatment names and colours.
my_theme_colours <- data.frame(
treatment = c("Control", "HNMPA", "PPP", "PPP_and_HNMPA"),
display_names = c("Control", "HNMPA", "PPP", "PPP\n&\nHNMPA"),
colours = c("#f07e05", "#f50599", "#70008c", "#DCE319"),
very_pale_colours = c("#fabb78", "#fa98d5", "#ce90de", "yellow")
)
Every time a plot contains the argument
treatment_colour_theme
, refer to your custom dataframe. To
see an example, see the Theme
FAQ
Analyze current amplitude
After you have finished a brief exploration of your data, it is time to analyze it!
Step 1: Normalize currents
The first step is to normalize the current amplitudes within each recording relative to the average current amplitude during the baseline period. This makes it easier to compare across cells that have a wide range of starting amplitudes, since all baseline values will be converted to (roughly) 100%.
Note how I set the minimum and maximum time values. This will limit the data to values between 0 min and 25 minutes.
I set the
interval_length
to 5 because I wanted to divide my data into 5-minute intervals for later statistical analyses.The baseline period (
baseline_length
) lasted 5 minutes. Clampfit recorded the current amplitude as negative values, so I setnegative_transform_currents
to “yes” which will flip the current amplitudes to positive values.
raw_eEPSC_df <- make_normalized_EPSC_data(
filename = import_ext_data("sample_eEPSC_data.csv"),
current_type = "eEPSC",
min_time_value = 0,
max_time_value = 25,
interval_length = 5,
baseline_length = 5,
negative_transform_currents = "yes"
)
make_normalized_EPSC_data()
will retain the cell
characteristics and P1
and P2
values from
before. However, you will notice some changes.
If you set negative_transform
to “yes”, P1
and P2
will be multiplied by -1
. This is to
“flip” current amplitude data that was recorded as negative values in
Clampfit. Since the data are evoked current data
(current_type = "eEPSC"
), some new columns are added. They
are:
PPR
: The paired-pulse ratio, which is the amplitude of the second evoked current divided by the first evoked current (PPR = P2/P1
).interval
: The interval that the data belongs to. I set theinterval_length
to 5, which means the data will be divided into 5-minute intervals. The intervals will have names like “t0to5”, “t5to10”, and so on up until the maximum interval.baseline_range
: You probably won’t interact with this much, but this is just a column stating “TRUE” if the time is within the baseline period, or “FALSE” if the time is outside of this range. This is required for the normalization function to identify which values are outside of the baseline (and should be transformed).baseline_mean
: This is one number that represents the average evoked current amplitude during the baseline period. This value is different for each recording.P1_transformed
: The first evoked current amplitude, normalized relative to the mean baseline amplitude. For example, if the mean baseline amplitude is 80 pA and aP1
value is 40 pA,P1_transformed
will be 50%.P2_transformed
: The second evoked current amplitude, normalized relative to the mean baseline amplitude of the first evoked current.
Plot raw data
Let’s see what the raw values look like over time!
plot_raw_current_data()
will generate a scatterplot of
evoked current amplitude (pA) over time (min) for all cells within the
treatment and category that you specify. Behind the scenes, this really
runs a loop over each letter, generating a ggplot object for each
recording.
Please see the documentation for plot_raw_current_data()
to learn about the arguments in more detail.
raw_eEPSC_control_plots <- plot_raw_current_data(
data = raw_eEPSC_df,
plot_treatment = "Control",
plot_category = 2,
current_type = "eEPSC",
y_variable = "P1",
pruned = "no",
hormone_added = "Insulin",
hormone_or_HFS_start_time = 5,
theme_options = sample_theme_options,
treatment_colour_theme = sample_treatment_names_and_colours
)
plot_raw_current_data()
will return a list of ggplot
objects. If you want to observe just one specific plot, you can select
it by letter.
raw_eEPSC_control_plots$L
Step 2: Prune data
It is often useful to summarize the data per minute. If you are
familiar with GraphPad Prism’s “prune rows” function,
make_pruned_EPSC_data()
will perform the same function.
In this vignette, I’ll use the example of pruning data per minute because this is what is typically used in the Crosby lab. You can change this value by changing the
interval_length
to something other than1
.
pruned_eEPSC_df <- make_pruned_EPSC_data(
data = raw_eEPSC_df,
current_type = "eEPSC",
min_time_value = 0,
max_time_value = 25,
baseline_length = 5,
interval_length = 1
)
This function will return a list of three dataframes. To access each
list, type the object name, followed by a dollar sign. For example,
write pruned_eEPSC_df$individual_cells
to access the first
dataframe in the list.
The three dataframes are:
$individual_cells
: This dataframe has the same structure as the raw evoked current data, except the data have been pruned per minute. New columns includemean_P1
andsd_P1
, and there are some other columns for variance analysis (please see the documentation formake_pruned_EPSC_data()
for more details).$for_table
: This dataframe has only two columns:letter
andP1_transformed
where the prunedP1
values have been collapsed into a list. This is used to create a sparkline inmake_interactive_summary_table()
.$all_cells
: This dataframe contains data that have been grouped by treatment and sex. In this dataframe, the data have been summarized and collapsed into one datapoint per minute for all cells per minute for a specific sex. This is useful for creating summary plots for publication (e.g.plot_summary_current_data()
) and for future statistical testing to compare groups.
Plot pruned data
You can use the same plot_raw_current_data()
to plot the
pruned data. You will need to make changes to the following
arguments:
-
data
: Refer to the third element of the list produced frommake_pruned_EPSC_data()
. This is$individual_cells
. -
y_variable
: Change this to “mean_P1”. -
pruned
: Change this to “yes”
pruned_eEPSC_control_plots <- plot_raw_current_data(
data = sample_pruned_eEPSC_df$individual_cells,
plot_treatment = "Control",
plot_category = 2,
current_type = "eEPSC",
y_variable = "mean_P1",
pruned = "yes",
hormone_added = "Insulin",
hormone_or_HFS_start_time = 5,
theme_options = sample_theme_options,
treatment_colour_theme = sample_treatment_names_and_colours
)
pruned_eEPSC_control_plots$L

See how this is the same as the raw data plot, except for it is pruned per minute?
The pruned data from all cells within a specific treatment and sex
($all_cells
) will enable you to make a summary plot using
plot_summary_current_data()
.
Notice how
data
is nowsample_pruned_eEPSC_df$all_cells
, andy_variable
is “amplitude”. There are lots of customization opportunities when plotting summary data, including adding a representative trace as a .png overlay! You can read more about in the documentation forplot_summary_current_data()
.
plot_summary_current_data(
plot_category = 2,
plot_treatment = "Control",
data = sample_pruned_eEPSC_df$all_cells,
current_type = "eEPSC",
y_variable = "amplitude",
include_representative_trace = "yes",
representative_trace_filename = import_ext_data("Control-trace.png"),
y_axis_limit = 175,
signif_stars = "yes",
t_test_df = sample_eEPSC_t_test_df,
hormone_added = "Insulin",
large_axis_text = "no",
shade_intervals = "no",
hormone_or_HFS_start_time = 5,
treatment_colour_theme = sample_treatment_names_and_colours,
theme_options = sample_theme_options
)
#> Warning: Removed 25 rows containing missing values or values outside the scale range
#> (`geom_segment()`).
Step 3: Summarize data
The next step is to group the data by treatment by sex and obtain
summary data. make_summary_EPSC_data()
will generate a list
of 2 dataframes. One dataframe (accessible with
$summary_data
) grouped the data into intervals and
generated summary statistics (like mean and standard error) for each
point. The interval length was already specified during the
make_normalized_EPSC_data()
function from earlier.
summary_eEPSC_df <- make_summary_EPSC_data(
data = sample_raw_eEPSC_df,
current_type = "eEPSC",
save_output_as_RDS = "no"
)
reactable(summary_eEPSC_df$summary_data)
The second dataframe (accessible with
$percent_change_data
) contains information on the percent
change in evoked current amplitude percent_change
during a
specific time interval (ending_interval
) relative to the
baseline (baseline_interval
). For example, if currents
decreased by 50% after the hormone, the value of
percent_change
is 0.5.
Analyze the paired-pulse ratio
Create PPR dataset
The function make_PPR_data()
is actually just a
filtering function that will limit the raw evoked current data to two
specific intervals. These represent the “before”
(baseline_interval
) and “after”
(post_hormone_interval
) states. You can also choose to
limit the PPR values to a certain range to exclude outliers.
PPR_df <- make_PPR_data(
data = raw_eEPSC_df,
include_all_treatments = "yes",
list_of_treatments = NULL,
PPR_min = 0,
PPR_max = 5,
baseline_interval = "t0to5",
post_hormone_interval = "t20to25",
treatment_colour_theme = sample_treatment_names_and_colours
)
head(PPR_df, n = 10) %>%
reactable()
Plot PPR data
For a specific treatment:
plot_PPR_data_single_treatment(
data = PPR_df,
plot_treatment = "Control",
plot_category = 2,
baseline_label = "Baseline",
post_hormone_label = "Insulin",
test_type = "t.test",
large_axis_text = "no",
treatment_colour_theme = sample_treatment_names_and_colours,
theme_options = sample_theme_options,
save_plot_png = "no"
)
For multiple treatments:
plot_PPR_data_multiple_treatments(
data = PPR_df,
include_all_treatments = "yes",
plot_category = 2,
baseline_label = "B",
post_hormone_label = "I",
test_type = "t.test",
theme_options = sample_theme_options,
treatment_colour_theme = sample_treatment_names_and_colours
)
Variance analysis
We can use variance measures like the coefficient of variation and
the variance-to-mean ratio (VMR) to help determine if a mechanism is
presynaptic or post-synaptic (see van Huijstee &
Kessels, 2020 for more details). This package contains functions
such as make_variance_data()
and
plot_variance_comparison_data()
to allow you to perform
variance analysis quickly from summary evoked current data (e.g. data
generated from make_summary_EPSC_data()
).
Create variance dataset
variance_df <- make_variance_data(
data = summary_eEPSC_df$summary_data,
df_category = 2,
include_all_treatments = "yes",
list_of_treatments = NULL,
baseline_interval = "t0to5",
post_hormone_interval = "t20to25",
treatment_colour_theme = sample_treatment_names_and_colours,
save_output_as_RDS = "no"
)
reactable(variance_df)
Plot variance comparisons
You can create plots comparing the inverse coefficient of variation squared, and the variance-to-mean ratio.
cv_comparison_control_plot <- plot_variance_comparison_data(
data = variance_df,
plot_category = 2,
plot_treatment = "Control",
variance_measure = "cv",
baseline_interval = "t0to5",
post_hormone_interval = "t20to25",
post_hormone_label = "Insulin",
test_type = "wilcox.test",
large_axis_text = "no",
treatment_colour_theme = sample_treatment_names_and_colours,
theme_options = sample_theme_options
)
vmr_comparison_control_plot <- plot_variance_comparison_data(
data = variance_df,
plot_category = 2,
plot_treatment = "Control",
variance_measure = "VMR",
baseline_interval = "t0to5",
post_hormone_interval = "t20to25",
post_hormone_label = "Insulin",
large_axis_text = "no",
test_type = "wilcox.test",
treatment_colour_theme = sample_treatment_names_and_colours,
theme_options = sample_theme_options
)
cv_comparison_control_plot
vmr_comparison_control_plot
Compare baseline parameters
You can compare parameters across treatments during the baseline
period. If current_type
= “eEPSC”, the allowed
y_variable
is “raw_amplitude”. If current_type
= “sEPSC”, the allowed y_variable
values are
“raw_amplitude” or “raw_frequency”.
Note: It does not make sense to use normalized/baseline transformed amplitudes, since these will all be 100, and the graph will be a flat line.
plot_baseline_data(
data = summary_eEPSC_df$summary_data,
current_type = "eEPSC",
plot_category = 2,
y_variable = "raw_amplitude",
include_all_treatments = "yes",
list_of_treatments = NULL,
baseline_interval = "t0to5",
large_axis_text = "no",
plot_width = 8,
treatment_colour_theme = sample_treatment_names_and_colours,
theme_options = sample_theme_options,
save_plot_png = "no"
)
Hopefully this vignette has given you an idea of some of the plotting functions that this package can do. The documentation for each function contains lots of additional information about each argument, and you can also explore the articles for Evoked Current Analysis and Action Potential Analysis.
If you have any questions about customizing your plots, read the FAQ page. There will likely be an answer there!

Ruby likes to help us with data analysis. She says ‘Have fun!’